mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
Exclude HostAgent implementation in release builds (#49813)
Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/49813 Making use of the recently standardised `REACT_NATIVE_DEBUGGER_ENABLED` preprocessor flag, conditionally swap out `HostAgent` for a stub in production (non dev, non profiling) builds. This uses the same private implementation idiom as `HermesRuntimeTargetDelegate`. This results in a **42.2kiB** size reduction (uncompressed) in production builds, due to excluding the CDP agent components of `jsinspector-modern`. - Intentionally, we're gating just this logic, rather than several outer integration points/native bindings for `jsinspector-modern`. This is for predictability and maintenance — the Fusebox subsystem is always loaded/bound, but can fork to an empty implementation. {F1975625351} Changelog: [Internal] Reviewed By: hoxyq Differential Revision: D70495853 fbshipit-source-id: 00253ac71444819a917a3781efe38365acb91e85
This commit is contained in:
committed by
Facebook GitHub Bot
parent
6bf6cebcf0
commit
f94dbb30ee
@@ -5,267 +5,363 @@
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#include "HostAgent.h"
|
||||
#include "CdpJson.h"
|
||||
#include "InstanceAgent.h"
|
||||
|
||||
#ifdef REACT_NATIVE_DEBUGGER_ENABLED
|
||||
#include "NetworkIOAgent.h"
|
||||
#include "SessionState.h"
|
||||
#include "TracingAgent.h"
|
||||
#endif // REACT_NATIVE_DEBUGGER_ENABLED
|
||||
|
||||
#include <folly/dynamic.h>
|
||||
#include <folly/json.h>
|
||||
#include <jsinspector-modern/HostAgent.h>
|
||||
#include <jsinspector-modern/HostTarget.h>
|
||||
#include <jsinspector-modern/InstanceAgent.h>
|
||||
|
||||
#include <chrono>
|
||||
#include <functional>
|
||||
#include <string_view>
|
||||
|
||||
using namespace std::chrono;
|
||||
using namespace std::literals::string_view_literals;
|
||||
|
||||
namespace facebook::react::jsinspector_modern {
|
||||
|
||||
#ifdef REACT_NATIVE_DEBUGGER_ENABLED
|
||||
|
||||
#define ANSI_WEIGHT_BOLD "\x1B[1m"
|
||||
#define ANSI_WEIGHT_RESET "\x1B[22m"
|
||||
#define ANSI_COLOR_BG_YELLOW "\x1B[48;2;253;247;231m\x1B[30m"
|
||||
|
||||
class HostAgent::Impl final {
|
||||
public:
|
||||
explicit Impl(
|
||||
HostAgent& hostAgent,
|
||||
FrontendChannel frontendChannel,
|
||||
HostTargetController& targetController,
|
||||
HostTargetMetadata hostMetadata,
|
||||
SessionState& sessionState,
|
||||
VoidExecutor executor)
|
||||
: frontendChannel_(frontendChannel),
|
||||
targetController_(targetController),
|
||||
hostMetadata_(std::move(hostMetadata)),
|
||||
sessionState_(sessionState),
|
||||
networkIOAgent_(NetworkIOAgent(frontendChannel, std::move(executor))),
|
||||
tracingAgent_(TracingAgent(frontendChannel)) {}
|
||||
|
||||
~Impl() {
|
||||
if (isPausedInDebuggerOverlayVisible_) {
|
||||
// In case of a non-graceful shutdown of the session, ensure we clean up
|
||||
// the "paused on debugger" overlay if we've previously asked the
|
||||
// integrator to display it.
|
||||
isPausedInDebuggerOverlayVisible_ = false;
|
||||
if (!targetController_.decrementPauseOverlayCounter()) {
|
||||
targetController_.getDelegate().onSetPausedInDebuggerMessage({
|
||||
.message = std::nullopt,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void handleRequest(const cdp::PreparsedRequest& req) {
|
||||
bool shouldSendOKResponse = false;
|
||||
bool isFinishedHandlingRequest = false;
|
||||
|
||||
// Domain enable/disable requests: write to state (because we're the
|
||||
// top-level Agent in the Session), trigger any side effects, and decide
|
||||
// whether we are finished handling the request (or need to delegate to the
|
||||
// InstanceAgent).
|
||||
if (req.method == "Log.enable") {
|
||||
sessionState_.isLogDomainEnabled = true;
|
||||
|
||||
if (fuseboxClientType_ == FuseboxClientType::Fusebox) {
|
||||
sendFuseboxNotice();
|
||||
}
|
||||
|
||||
// Send a log entry with the integration name.
|
||||
if (hostMetadata_.integrationName) {
|
||||
sendInfoLogEntry(
|
||||
ANSI_COLOR_BG_YELLOW "Debugger integration: " +
|
||||
*hostMetadata_.integrationName);
|
||||
}
|
||||
|
||||
shouldSendOKResponse = true;
|
||||
isFinishedHandlingRequest = false;
|
||||
} else if (req.method == "Log.disable") {
|
||||
sessionState_.isLogDomainEnabled = false;
|
||||
|
||||
shouldSendOKResponse = true;
|
||||
isFinishedHandlingRequest = false;
|
||||
} else if (req.method == "Runtime.enable") {
|
||||
sessionState_.isRuntimeDomainEnabled = true;
|
||||
|
||||
if (fuseboxClientType_ == FuseboxClientType::Unknown) {
|
||||
// Since we know the Fusebox frontend sends
|
||||
// FuseboxClient.setClientMetadata before enabling the Runtime domain,
|
||||
// we can conclude that we're dealing with some other client.
|
||||
fuseboxClientType_ = FuseboxClientType::NonFusebox;
|
||||
sendNonFuseboxNotice();
|
||||
}
|
||||
|
||||
shouldSendOKResponse = true;
|
||||
isFinishedHandlingRequest = false;
|
||||
} else if (req.method == "Runtime.disable") {
|
||||
sessionState_.isRuntimeDomainEnabled = false;
|
||||
|
||||
shouldSendOKResponse = true;
|
||||
isFinishedHandlingRequest = false;
|
||||
} else if (req.method == "Debugger.enable") {
|
||||
sessionState_.isDebuggerDomainEnabled = true;
|
||||
|
||||
shouldSendOKResponse = true;
|
||||
isFinishedHandlingRequest = false;
|
||||
} else if (req.method == "Debugger.disable") {
|
||||
sessionState_.isDebuggerDomainEnabled = false;
|
||||
|
||||
shouldSendOKResponse = true;
|
||||
isFinishedHandlingRequest = false;
|
||||
}
|
||||
// Methods other than domain enables/disables: handle anything we know how
|
||||
// to handle, and delegate to the InstanceAgent otherwise.
|
||||
else if (req.method == "Page.reload") {
|
||||
targetController_.getDelegate().onReload({
|
||||
.ignoreCache =
|
||||
req.params.isObject() && req.params.count("ignoreCache")
|
||||
? std::optional(req.params.at("ignoreCache").asBool())
|
||||
: std::nullopt,
|
||||
.scriptToEvaluateOnLoad = req.params.isObject() &&
|
||||
req.params.count("scriptToEvaluateOnLoad")
|
||||
? std::optional(
|
||||
req.params.at("scriptToEvaluateOnLoad").asString())
|
||||
: std::nullopt,
|
||||
});
|
||||
|
||||
shouldSendOKResponse = true;
|
||||
isFinishedHandlingRequest = true;
|
||||
} else if (req.method == "Overlay.setPausedInDebuggerMessage") {
|
||||
auto message = req.params.isObject() && req.params.count("message")
|
||||
? std::optional(req.params.at("message").asString())
|
||||
: std::nullopt;
|
||||
if (!isPausedInDebuggerOverlayVisible_ && message.has_value()) {
|
||||
targetController_.incrementPauseOverlayCounter();
|
||||
} else if (isPausedInDebuggerOverlayVisible_ && !message.has_value()) {
|
||||
targetController_.decrementPauseOverlayCounter();
|
||||
}
|
||||
isPausedInDebuggerOverlayVisible_ = message.has_value();
|
||||
targetController_.getDelegate().onSetPausedInDebuggerMessage({
|
||||
.message = message,
|
||||
});
|
||||
|
||||
shouldSendOKResponse = true;
|
||||
isFinishedHandlingRequest = true;
|
||||
} else if (req.method == "ReactNativeApplication.enable") {
|
||||
sessionState_.isReactNativeApplicationDomainEnabled = true;
|
||||
fuseboxClientType_ = FuseboxClientType::Fusebox;
|
||||
|
||||
if (sessionState_.isLogDomainEnabled) {
|
||||
sendFuseboxNotice();
|
||||
}
|
||||
|
||||
frontendChannel_(cdp::jsonNotification(
|
||||
"ReactNativeApplication.metadataUpdated",
|
||||
createHostMetadataPayload(hostMetadata_)));
|
||||
|
||||
shouldSendOKResponse = true;
|
||||
isFinishedHandlingRequest = true;
|
||||
} else if (req.method == "ReactNativeApplication.disable") {
|
||||
sessionState_.isReactNativeApplicationDomainEnabled = false;
|
||||
|
||||
shouldSendOKResponse = true;
|
||||
isFinishedHandlingRequest = true;
|
||||
} else if (req.method == "Tracing.start") {
|
||||
if (sessionState_.isDebuggerDomainEnabled) {
|
||||
frontendChannel_(cdp::jsonError(
|
||||
req.id,
|
||||
cdp::ErrorCode::InternalError,
|
||||
"Debugger domain is expected to be disabled before starting Tracing"));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// We delegate handling of this request to TracingAgent. If not handled,
|
||||
// then something unexpected happened - don't send an OK response.
|
||||
shouldSendOKResponse = false;
|
||||
isFinishedHandlingRequest = false;
|
||||
}
|
||||
|
||||
if (!isFinishedHandlingRequest &&
|
||||
networkIOAgent_.handleRequest(req, targetController_.getDelegate())) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isFinishedHandlingRequest && tracingAgent_.handleRequest(req)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isFinishedHandlingRequest && instanceAgent_ &&
|
||||
instanceAgent_->handleRequest(req)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (shouldSendOKResponse) {
|
||||
frontendChannel_(cdp::jsonResult(req.id));
|
||||
return;
|
||||
}
|
||||
|
||||
throw NotImplementedException(req.method);
|
||||
}
|
||||
|
||||
void setCurrentInstanceAgent(std::shared_ptr<InstanceAgent> instanceAgent) {
|
||||
tracingAgent_.setCurrentInstanceAgent(instanceAgent);
|
||||
|
||||
auto previousInstanceAgent = std::move(instanceAgent_);
|
||||
instanceAgent_ = std::move(instanceAgent);
|
||||
|
||||
if (!sessionState_.isRuntimeDomainEnabled) {
|
||||
return;
|
||||
}
|
||||
if (previousInstanceAgent != nullptr) {
|
||||
// TODO: Send Runtime.executionContextDestroyed here - at the moment we
|
||||
// expect the runtime to do it for us.
|
||||
|
||||
// Because we can only have a single instance, we can report all contexts
|
||||
// as cleared.
|
||||
frontendChannel_(
|
||||
cdp::jsonNotification("Runtime.executionContextsCleared"));
|
||||
}
|
||||
if (instanceAgent_) {
|
||||
// TODO: Send Runtime.executionContextCreated here - at the moment we
|
||||
// expect the runtime to do it for us.
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
enum class FuseboxClientType { Unknown, Fusebox, NonFusebox };
|
||||
|
||||
/**
|
||||
* Send a simple Log.entryAdded notification with the given
|
||||
* \param text. You must ensure that the frontend has enabled Log
|
||||
* notifications (using Log.enable) prior to calling this function. In Chrome
|
||||
* DevTools, the message will appear in the Console tab along with regular
|
||||
* console messages. The difference between Log.entryAdded and
|
||||
* Runtime.consoleAPICalled is that the latter requires an execution context
|
||||
* ID, which does not exist at the Host level.
|
||||
*/
|
||||
void sendInfoLogEntry(
|
||||
std::string_view text,
|
||||
std::initializer_list<std::string_view> args = {}) {
|
||||
folly::dynamic argsArray = folly::dynamic::array();
|
||||
for (auto arg : args) {
|
||||
argsArray.push_back(arg);
|
||||
}
|
||||
frontendChannel_(cdp::jsonNotification(
|
||||
"Log.entryAdded",
|
||||
folly::dynamic::object(
|
||||
"entry",
|
||||
folly::dynamic::object(
|
||||
"timestamp",
|
||||
duration_cast<milliseconds>(
|
||||
system_clock::now().time_since_epoch())
|
||||
.count())("source", "other")("level", "info")("text", text)(
|
||||
"args", std::move(argsArray)))));
|
||||
}
|
||||
|
||||
void sendFuseboxNotice() {
|
||||
static constexpr auto kFuseboxNotice =
|
||||
ANSI_COLOR_BG_YELLOW "Welcome to " ANSI_WEIGHT_BOLD
|
||||
"React Native DevTools" ANSI_WEIGHT_RESET ""sv;
|
||||
|
||||
sendInfoLogEntry(kFuseboxNotice);
|
||||
}
|
||||
|
||||
void sendNonFuseboxNotice() {
|
||||
static constexpr auto kNonFuseboxNotice =
|
||||
ANSI_COLOR_BG_YELLOW ANSI_WEIGHT_BOLD
|
||||
"NOTE: " ANSI_WEIGHT_RESET
|
||||
"You are using an unsupported debugging client. "
|
||||
"Use the Dev Menu in your app (or type `j` in the Metro terminal) to open React Native DevTools."sv;
|
||||
|
||||
std::vector<std::string> args;
|
||||
args.emplace_back(kNonFuseboxNotice);
|
||||
sendConsoleMessage({ConsoleAPIType::kInfo, args});
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a console message to the frontend, or buffer it to be sent later.
|
||||
*/
|
||||
void sendConsoleMessage(SimpleConsoleMessage message) {
|
||||
if (instanceAgent_) {
|
||||
instanceAgent_->sendConsoleMessage(std::move(message));
|
||||
} else {
|
||||
// Will be sent by the InstanceAgent eventually.
|
||||
sessionState_.pendingSimpleConsoleMessages.emplace_back(
|
||||
std::move(message));
|
||||
}
|
||||
}
|
||||
|
||||
FrontendChannel frontendChannel_;
|
||||
HostTargetController& targetController_;
|
||||
const HostTargetMetadata hostMetadata_;
|
||||
std::shared_ptr<InstanceAgent> instanceAgent_;
|
||||
FuseboxClientType fuseboxClientType_{FuseboxClientType::Unknown};
|
||||
bool isPausedInDebuggerOverlayVisible_{false};
|
||||
|
||||
/**
|
||||
* A shared reference to the session's state. This is only safe to access
|
||||
* during handleRequest and other method calls on the same thread.
|
||||
*/
|
||||
SessionState& sessionState_;
|
||||
|
||||
NetworkIOAgent networkIOAgent_;
|
||||
|
||||
TracingAgent tracingAgent_;
|
||||
};
|
||||
|
||||
#else
|
||||
|
||||
/**
|
||||
* A stub for HostAgent when React Native is compiled without debugging
|
||||
* support.
|
||||
*/
|
||||
class HostAgent::Impl final {
|
||||
public:
|
||||
explicit Impl(
|
||||
HostAgent&,
|
||||
FrontendChannel frontendChannel,
|
||||
HostTargetController& targetController,
|
||||
HostTargetMetadata hostMetadata,
|
||||
SessionState& sessionState,
|
||||
VoidExecutor executor) {}
|
||||
|
||||
void handleRequest(const cdp::PreparsedRequest& req) {}
|
||||
void setCurrentInstanceAgent(std::shared_ptr<InstanceAgent> agent) {}
|
||||
};
|
||||
|
||||
#endif // REACT_NATIVE_DEBUGGER_ENABLED
|
||||
|
||||
HostAgent::HostAgent(
|
||||
FrontendChannel frontendChannel,
|
||||
const FrontendChannel& frontendChannel,
|
||||
HostTargetController& targetController,
|
||||
HostTargetMetadata hostMetadata,
|
||||
SessionState& sessionState,
|
||||
VoidExecutor executor)
|
||||
: frontendChannel_(frontendChannel),
|
||||
targetController_(targetController),
|
||||
hostMetadata_(std::move(hostMetadata)),
|
||||
sessionState_(sessionState),
|
||||
networkIOAgent_(NetworkIOAgent(frontendChannel, std::move(executor))),
|
||||
tracingAgent_(TracingAgent(frontendChannel)) {}
|
||||
: impl_(std::make_unique<Impl>(
|
||||
*this,
|
||||
frontendChannel,
|
||||
targetController,
|
||||
std::move(hostMetadata),
|
||||
sessionState,
|
||||
std::move(executor))) {}
|
||||
|
||||
HostAgent::~HostAgent() = default;
|
||||
|
||||
void HostAgent::handleRequest(const cdp::PreparsedRequest& req) {
|
||||
bool shouldSendOKResponse = false;
|
||||
bool isFinishedHandlingRequest = false;
|
||||
|
||||
// Domain enable/disable requests: write to state (because we're the top-level
|
||||
// Agent in the Session), trigger any side effects, and decide whether we are
|
||||
// finished handling the request (or need to delegate to the InstanceAgent).
|
||||
if (req.method == "Log.enable") {
|
||||
sessionState_.isLogDomainEnabled = true;
|
||||
|
||||
if (fuseboxClientType_ == FuseboxClientType::Fusebox) {
|
||||
sendFuseboxNotice();
|
||||
}
|
||||
|
||||
// Send a log entry with the integration name.
|
||||
if (hostMetadata_.integrationName) {
|
||||
sendInfoLogEntry(
|
||||
ANSI_COLOR_BG_YELLOW "Debugger integration: " +
|
||||
*hostMetadata_.integrationName);
|
||||
}
|
||||
|
||||
shouldSendOKResponse = true;
|
||||
isFinishedHandlingRequest = false;
|
||||
} else if (req.method == "Log.disable") {
|
||||
sessionState_.isLogDomainEnabled = false;
|
||||
|
||||
shouldSendOKResponse = true;
|
||||
isFinishedHandlingRequest = false;
|
||||
} else if (req.method == "Runtime.enable") {
|
||||
sessionState_.isRuntimeDomainEnabled = true;
|
||||
|
||||
if (fuseboxClientType_ == FuseboxClientType::Unknown) {
|
||||
// Since we know the Fusebox frontend sends
|
||||
// FuseboxClient.setClientMetadata before enabling the Runtime domain, we
|
||||
// can conclude that we're dealing with some other client.
|
||||
fuseboxClientType_ = FuseboxClientType::NonFusebox;
|
||||
sendNonFuseboxNotice();
|
||||
}
|
||||
|
||||
shouldSendOKResponse = true;
|
||||
isFinishedHandlingRequest = false;
|
||||
} else if (req.method == "Runtime.disable") {
|
||||
sessionState_.isRuntimeDomainEnabled = false;
|
||||
|
||||
shouldSendOKResponse = true;
|
||||
isFinishedHandlingRequest = false;
|
||||
} else if (req.method == "Debugger.enable") {
|
||||
sessionState_.isDebuggerDomainEnabled = true;
|
||||
|
||||
shouldSendOKResponse = true;
|
||||
isFinishedHandlingRequest = false;
|
||||
} else if (req.method == "Debugger.disable") {
|
||||
sessionState_.isDebuggerDomainEnabled = false;
|
||||
|
||||
shouldSendOKResponse = true;
|
||||
isFinishedHandlingRequest = false;
|
||||
}
|
||||
// Methods other than domain enables/disables: handle anything we know how
|
||||
// to handle, and delegate to the InstanceAgent otherwise.
|
||||
else if (req.method == "Page.reload") {
|
||||
targetController_.getDelegate().onReload({
|
||||
.ignoreCache = req.params.isObject() && req.params.count("ignoreCache")
|
||||
? std::optional(req.params.at("ignoreCache").asBool())
|
||||
: std::nullopt,
|
||||
.scriptToEvaluateOnLoad =
|
||||
req.params.isObject() && req.params.count("scriptToEvaluateOnLoad")
|
||||
? std::optional(req.params.at("scriptToEvaluateOnLoad").asString())
|
||||
: std::nullopt,
|
||||
});
|
||||
|
||||
shouldSendOKResponse = true;
|
||||
isFinishedHandlingRequest = true;
|
||||
} else if (req.method == "Overlay.setPausedInDebuggerMessage") {
|
||||
auto message = req.params.isObject() && req.params.count("message")
|
||||
? std::optional(req.params.at("message").asString())
|
||||
: std::nullopt;
|
||||
if (!isPausedInDebuggerOverlayVisible_ && message.has_value()) {
|
||||
targetController_.incrementPauseOverlayCounter();
|
||||
} else if (isPausedInDebuggerOverlayVisible_ && !message.has_value()) {
|
||||
targetController_.decrementPauseOverlayCounter();
|
||||
}
|
||||
isPausedInDebuggerOverlayVisible_ = message.has_value();
|
||||
targetController_.getDelegate().onSetPausedInDebuggerMessage({
|
||||
.message = message,
|
||||
});
|
||||
|
||||
shouldSendOKResponse = true;
|
||||
isFinishedHandlingRequest = true;
|
||||
} else if (req.method == "ReactNativeApplication.enable") {
|
||||
sessionState_.isReactNativeApplicationDomainEnabled = true;
|
||||
fuseboxClientType_ = FuseboxClientType::Fusebox;
|
||||
|
||||
if (sessionState_.isLogDomainEnabled) {
|
||||
sendFuseboxNotice();
|
||||
}
|
||||
|
||||
frontendChannel_(cdp::jsonNotification(
|
||||
"ReactNativeApplication.metadataUpdated",
|
||||
createHostMetadataPayload(hostMetadata_)));
|
||||
|
||||
shouldSendOKResponse = true;
|
||||
isFinishedHandlingRequest = true;
|
||||
} else if (req.method == "ReactNativeApplication.disable") {
|
||||
sessionState_.isReactNativeApplicationDomainEnabled = false;
|
||||
|
||||
shouldSendOKResponse = true;
|
||||
isFinishedHandlingRequest = true;
|
||||
} else if (req.method == "Tracing.start") {
|
||||
if (sessionState_.isDebuggerDomainEnabled) {
|
||||
frontendChannel_(cdp::jsonError(
|
||||
req.id,
|
||||
cdp::ErrorCode::InternalError,
|
||||
"Debugger domain is expected to be disabled before starting Tracing"));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// We delegate handling of this request to TracingAgent. If not handled,
|
||||
// then something unexpected happened - don't send an OK response.
|
||||
shouldSendOKResponse = false;
|
||||
isFinishedHandlingRequest = false;
|
||||
}
|
||||
|
||||
if (!isFinishedHandlingRequest &&
|
||||
networkIOAgent_.handleRequest(req, targetController_.getDelegate())) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isFinishedHandlingRequest && tracingAgent_.handleRequest(req)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isFinishedHandlingRequest && instanceAgent_ &&
|
||||
instanceAgent_->handleRequest(req)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (shouldSendOKResponse) {
|
||||
frontendChannel_(cdp::jsonResult(req.id));
|
||||
return;
|
||||
}
|
||||
|
||||
throw NotImplementedException(req.method);
|
||||
}
|
||||
|
||||
HostAgent::~HostAgent() {
|
||||
if (isPausedInDebuggerOverlayVisible_) {
|
||||
// In case of a non-graceful shutdown of the session, ensure we clean up
|
||||
// the "paused on debugger" overlay if we've previously asked the
|
||||
// integrator to display it.
|
||||
isPausedInDebuggerOverlayVisible_ = false;
|
||||
if (!targetController_.decrementPauseOverlayCounter()) {
|
||||
targetController_.getDelegate().onSetPausedInDebuggerMessage({
|
||||
.message = std::nullopt,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void HostAgent::sendFuseboxNotice() {
|
||||
static constexpr auto kFuseboxNotice =
|
||||
ANSI_COLOR_BG_YELLOW "Welcome to " ANSI_WEIGHT_BOLD
|
||||
"React Native DevTools" ANSI_WEIGHT_RESET ""sv;
|
||||
|
||||
sendInfoLogEntry(kFuseboxNotice);
|
||||
}
|
||||
|
||||
void HostAgent::sendNonFuseboxNotice() {
|
||||
static constexpr auto kNonFuseboxNotice =
|
||||
ANSI_COLOR_BG_YELLOW ANSI_WEIGHT_BOLD
|
||||
"NOTE: " ANSI_WEIGHT_RESET
|
||||
"You are using an unsupported debugging client. "
|
||||
"Use the Dev Menu in your app (or type `j` in the Metro terminal) to open React Native DevTools."sv;
|
||||
|
||||
std::vector<std::string> args;
|
||||
args.emplace_back(kNonFuseboxNotice);
|
||||
sendConsoleMessage({ConsoleAPIType::kInfo, args});
|
||||
}
|
||||
|
||||
void HostAgent::sendInfoLogEntry(
|
||||
std::string_view text,
|
||||
std::initializer_list<std::string_view> args) {
|
||||
folly::dynamic argsArray = folly::dynamic::array();
|
||||
for (auto arg : args) {
|
||||
argsArray.push_back(arg);
|
||||
}
|
||||
frontendChannel_(cdp::jsonNotification(
|
||||
"Log.entryAdded",
|
||||
folly::dynamic::object(
|
||||
"entry",
|
||||
folly::dynamic::object(
|
||||
"timestamp",
|
||||
duration_cast<milliseconds>(
|
||||
system_clock::now().time_since_epoch())
|
||||
.count())("source", "other")("level", "info")("text", text)(
|
||||
"args", std::move(argsArray)))));
|
||||
impl_->handleRequest(req);
|
||||
}
|
||||
|
||||
void HostAgent::setCurrentInstanceAgent(
|
||||
std::shared_ptr<InstanceAgent> instanceAgent) {
|
||||
tracingAgent_.setCurrentInstanceAgent(instanceAgent);
|
||||
|
||||
auto previousInstanceAgent = std::move(instanceAgent_);
|
||||
instanceAgent_ = std::move(instanceAgent);
|
||||
|
||||
if (!sessionState_.isRuntimeDomainEnabled) {
|
||||
return;
|
||||
}
|
||||
if (previousInstanceAgent != nullptr) {
|
||||
// TODO: Send Runtime.executionContextDestroyed here - at the moment we
|
||||
// expect the runtime to do it for us.
|
||||
|
||||
// Because we can only have a single instance, we can report all contexts
|
||||
// as cleared.
|
||||
frontendChannel_(cdp::jsonNotification("Runtime.executionContextsCleared"));
|
||||
}
|
||||
if (instanceAgent_) {
|
||||
// TODO: Send Runtime.executionContextCreated here - at the moment we expect
|
||||
// the runtime to do it for us.
|
||||
}
|
||||
}
|
||||
|
||||
void HostAgent::sendConsoleMessage(SimpleConsoleMessage message) {
|
||||
if (instanceAgent_) {
|
||||
instanceAgent_->sendConsoleMessage(std::move(message));
|
||||
} else {
|
||||
// Will be sent by the InstanceAgent eventually.
|
||||
sessionState_.pendingSimpleConsoleMessages.emplace_back(std::move(message));
|
||||
}
|
||||
impl_->setCurrentInstanceAgent(std::move(instanceAgent));
|
||||
}
|
||||
|
||||
} // namespace facebook::react::jsinspector_modern
|
||||
|
||||
@@ -9,21 +9,13 @@
|
||||
|
||||
#include "CdpJson.h"
|
||||
#include "HostTarget.h"
|
||||
#include "NetworkIOAgent.h"
|
||||
#include "SessionState.h"
|
||||
#include "TracingAgent.h"
|
||||
|
||||
#include <jsinspector-modern/InspectorInterfaces.h>
|
||||
#include <jsinspector-modern/InstanceAgent.h>
|
||||
|
||||
#include <functional>
|
||||
#include <string_view>
|
||||
|
||||
namespace facebook::react::jsinspector_modern {
|
||||
|
||||
class HostTarget;
|
||||
class InstanceAgent;
|
||||
class InstanceTarget;
|
||||
|
||||
/**
|
||||
* An Agent that handles requests from the Chrome DevTools Protocol for the
|
||||
@@ -45,7 +37,7 @@ class HostAgent final {
|
||||
* \param executor A void executor to be used by async-aware handlers.
|
||||
*/
|
||||
HostAgent(
|
||||
FrontendChannel frontendChannel,
|
||||
const FrontendChannel& frontendChannel,
|
||||
HostTargetController& targetController,
|
||||
HostTargetMetadata hostMetadata,
|
||||
SessionState& sessionState,
|
||||
@@ -74,45 +66,13 @@ class HostAgent final {
|
||||
void setCurrentInstanceAgent(std::shared_ptr<InstanceAgent> agent);
|
||||
|
||||
private:
|
||||
enum class FuseboxClientType { Unknown, Fusebox, NonFusebox };
|
||||
// We use the private implementation idiom to ensure this class has the same
|
||||
// layout regardless of whether REACT_NATIVE_DEBUGGER_ENABLED is defined. The
|
||||
// net effect is that callers can include HostAgent.h without setting
|
||||
// HERMES_ENABLE_DEBUGGER one way or the other.
|
||||
class Impl;
|
||||
|
||||
/**
|
||||
* Send a simple Log.entryAdded notification with the given
|
||||
* \param text . You must ensure that the frontend has enabled Log
|
||||
* notifications (using Log.enable) prior to calling this function. In Chrome
|
||||
* DevTools, the message will appear in the Console tab along with regular
|
||||
* console messages. The difference between Log.entryAdded and
|
||||
* Runtime.consoleAPICalled is that the latter requires an execution context
|
||||
* ID, which does not exist at the Host level.
|
||||
*/
|
||||
void sendInfoLogEntry(
|
||||
std::string_view text,
|
||||
std::initializer_list<std::string_view> args = {});
|
||||
|
||||
void sendFuseboxNotice();
|
||||
void sendNonFuseboxNotice();
|
||||
|
||||
/**
|
||||
* Send a console message to the frontend, or buffer it to be sent later.
|
||||
*/
|
||||
void sendConsoleMessage(SimpleConsoleMessage message);
|
||||
|
||||
FrontendChannel frontendChannel_;
|
||||
HostTargetController& targetController_;
|
||||
const HostTargetMetadata hostMetadata_;
|
||||
std::shared_ptr<InstanceAgent> instanceAgent_;
|
||||
FuseboxClientType fuseboxClientType_{FuseboxClientType::Unknown};
|
||||
bool isPausedInDebuggerOverlayVisible_{false};
|
||||
|
||||
/**
|
||||
* A shared reference to the session's state. This is only safe to access
|
||||
* during handleRequest and other method calls on the same thread.
|
||||
*/
|
||||
SessionState& sessionState_;
|
||||
|
||||
NetworkIOAgent networkIOAgent_;
|
||||
|
||||
TracingAgent tracingAgent_;
|
||||
std::unique_ptr<Impl> impl_;
|
||||
};
|
||||
|
||||
} // namespace facebook::react::jsinspector_modern
|
||||
|
||||
Reference in New Issue
Block a user