mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/42635 Changelog: [Internal] Adds a RuntimeAgent interface to the modern CDP backend, plus an `InstanceTargetDelegate::createRuntimeAgent()` method. This allows the RN integration to provide an engine-specific CDP implementation. This diff includes all the plumbing in Bridge and Bridgeless to route `createRuntimeAgent()` calls to the right place - ending up at `JSExecutor::createRuntimeAgent()` and `JSIRuntimeHolder::createInspectorAgent` respectively - at which point we currently return `nullptr` to signify that JS debugging isn't supported. ## Next steps In upcoming diffs we'll add concrete implementations of `RuntimeAgent`, and teach both Bridge and Bridgeless to create them as appropriate: * `HermesRuntimeAgent` for Hermes * `FallbackRuntimeAgent` for all other JS engines (JSI or not) We'll also (likely) add assertions to ensure that any JSI runtime that reports itself as "inspectable" (a flag used to control some of the in-app debugging UI) comes with a non-default `createRuntimeAgent()` implementation. We avoid this for now to prevent crashing the modern backend on Hermes. NOTE: Like the rest of the modern CDP backend, the `RuntimeAgent` API is 100% experimental and subject to change without notice. A *future* version of this API will allow out-of-tree JSI engines to integrate with the modern CDP backend. Either way, it is intended strictly for the use case of integrating with a JS engine, not for adding any other framework-level CDP functionality. Reviewed By: huntie Differential Revision: D51231326 fbshipit-source-id: 81e87c5134df73cc4aac0f9d5793a5236b5720d6
155 lines
5.1 KiB
C++
155 lines
5.1 KiB
C++
/*
|
|
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
*
|
|
* This source code is licensed under the MIT license found in the
|
|
* LICENSE file in the root directory of this source tree.
|
|
*/
|
|
|
|
#include <folly/dynamic.h>
|
|
#include <folly/json.h>
|
|
#include <jsinspector-modern/InstanceAgent.h>
|
|
#include <jsinspector-modern/PageAgent.h>
|
|
#include <jsinspector-modern/PageTarget.h>
|
|
|
|
#include <chrono>
|
|
|
|
using namespace std::chrono;
|
|
using namespace std::literals::string_view_literals;
|
|
|
|
namespace facebook::react::jsinspector_modern {
|
|
|
|
#define ANSI_WEIGHT_BOLD "\x1B[1m"
|
|
#define ANSI_WEIGHT_RESET "\x1B[22m"
|
|
#define ANSI_STYLE_ITALIC "\x1B[3m"
|
|
#define ANSI_STYLE_RESET "\x1B[23m"
|
|
#define ANSI_COLOR_BG_YELLOW "\x1B[48;2;253;247;231m"
|
|
|
|
static constexpr auto kModernCDPBackendNotice =
|
|
ANSI_COLOR_BG_YELLOW ANSI_WEIGHT_BOLD
|
|
"NOTE:" ANSI_WEIGHT_RESET " You are using the " ANSI_STYLE_ITALIC
|
|
"modern" ANSI_STYLE_RESET " CDP backend for React Native (PageTarget)."sv;
|
|
|
|
PageAgent::PageAgent(
|
|
FrontendChannel frontendChannel,
|
|
PageTargetController& targetController,
|
|
PageTarget::SessionMetadata sessionMetadata)
|
|
: frontendChannel_(frontendChannel),
|
|
targetController_(targetController),
|
|
sessionMetadata_(std::move(sessionMetadata)) {}
|
|
|
|
void PageAgent::handleRequest(const cdp::PreparsedRequest& req) {
|
|
if (req.method == "Log.enable") {
|
|
// Send an "OK" response.
|
|
frontendChannel_(
|
|
folly::toJson(folly::dynamic::object("id", req.id)("result", nullptr)));
|
|
|
|
// Send a log entry identifying the modern CDP backend.
|
|
sendInfoLogEntry(kModernCDPBackendNotice);
|
|
|
|
// Send a log entry with the integration name.
|
|
if (sessionMetadata_.integrationName) {
|
|
sendInfoLogEntry("Integration: " + *sessionMetadata_.integrationName);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
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,
|
|
});
|
|
folly::dynamic res = folly::dynamic::object("id", req.id)(
|
|
"result", folly::dynamic::object());
|
|
std::string json = folly::toJson(res);
|
|
frontendChannel_(json);
|
|
return;
|
|
}
|
|
|
|
bool shouldSendOKResponse = false;
|
|
|
|
if (req.method == "Runtime.enable") {
|
|
runtimeEnabled_ = true;
|
|
|
|
// Fall through to letting the instance handle this request and send a
|
|
// response, but remember that we need to send a response in case the
|
|
// instance doesn't handle the request.
|
|
shouldSendOKResponse = true;
|
|
}
|
|
if (req.method == "Runtime.disable") {
|
|
runtimeEnabled_ = false;
|
|
folly::dynamic res = folly::dynamic::object("id", req.id)(
|
|
"result", folly::dynamic::object());
|
|
std::string json = folly::toJson(res);
|
|
frontendChannel_(json);
|
|
|
|
// Fall through to letting the instance handle this request and send a
|
|
// response, but remember that we need to send a response in case the
|
|
// instance doesn't handle the request.
|
|
shouldSendOKResponse = true;
|
|
}
|
|
|
|
if (instanceAgent_ && instanceAgent_->handleRequest(req)) {
|
|
return;
|
|
}
|
|
|
|
if (shouldSendOKResponse) {
|
|
folly::dynamic res = folly::dynamic::object("id", req.id)(
|
|
"result", folly::dynamic::object());
|
|
std::string json = folly::toJson(res);
|
|
frontendChannel_(json);
|
|
return;
|
|
}
|
|
|
|
folly::dynamic res = folly::dynamic::object("id", req.id)(
|
|
"error",
|
|
folly::dynamic::object("code", -32601)(
|
|
"message", req.method + " not implemented yet"));
|
|
std::string json = folly::toJson(res);
|
|
frontendChannel_(json);
|
|
}
|
|
|
|
void PageAgent::sendInfoLogEntry(std::string_view text) {
|
|
frontendChannel_(
|
|
folly::toJson(folly::dynamic::object("method", "Log.entryAdded")(
|
|
"params",
|
|
folly::dynamic::object(
|
|
"entry",
|
|
folly::dynamic::object(
|
|
"timestamp",
|
|
duration_cast<milliseconds>(
|
|
system_clock::now().time_since_epoch())
|
|
.count())("source", "other")(
|
|
"level", "info")("text", text)))));
|
|
}
|
|
|
|
void PageAgent::setCurrentInstanceAgent(
|
|
std::unique_ptr<InstanceAgent> instanceAgent) {
|
|
auto previousInstanceAgent = std::move(instanceAgent_);
|
|
instanceAgent_ = std::move(instanceAgent);
|
|
if (!runtimeEnabled_) {
|
|
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.
|
|
folly::dynamic contextsCleared =
|
|
folly::dynamic::object("method", "Runtime.executionContextsCleared");
|
|
frontendChannel_(folly::toJson(contextsCleared));
|
|
}
|
|
if (instanceAgent_) {
|
|
// TODO: Send Runtime.executionContextCreated here - at the moment we expect
|
|
// the runtime to do it for us.
|
|
}
|
|
}
|
|
|
|
} // namespace facebook::react::jsinspector_modern
|