mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
7886abd243
Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/42397 Changelog: [Internal] Adds a stub `PageTarget` class to serve as the entry point to the modern CDP backend in React Native. The primary method exposed by `PageTarget` is `connect()` which is designed to fit directly as a connection callback passed to `InspectorPackagerConnection::addPage()`. This constructs a `PageTargetSession` containing a `PageAgent` where the actual CDP message handling/routing will occur. For now, `PageAgent` implements no CDP methods, and always responds with a "not implemented" error. Basic unit tests are included, though we might want to migrate to a more integration-style test suite (with fewer mocks and real bindings to RN) once we've implemented more of the protocol. ## What is a Page In Chrome's implementation of CDP, a Page represents a single browser tab. A Chrome DevTools session connects to one Page at a time (though it can potentially inspect multiple JavaScript contexts owned by that Page, such as those found in frames and workers). In our system, a Page will correspond 1:1 to React Native's concept of a *Host* (implemented as `RCTHost`, `RCTBridge`, `ReactHostImpl` or `ReactInstanceManager`, depending on the platform). In all cases, the Host is the object that has a stable identity across reloads, and manages the lifetime of an *Instance* where the JSVM and other application state lives. There can be multiple Hosts in a React Native process, though this is somewhat unusual; those would be treated as independent "tabs" from the perspective of the debugger. NOTE: The concepts of Target, Session and Agent are new (to this codebase) and are *broadly* inspired by the [corresponding Chromium / V8 concepts](https://chromium.googlesource.com/chromium/src/+/master/third_party/blink/public/devtools_protocol/#Agents_Targets-and-Sessions), though some details differ. ## Next steps Each core platform implementation in React Native (iOS Bridgeless, iOS Bridge, Android Bridgeless, Android Bridge), as well as out-of-tree platforms that want to support the new debugger, will need to create and register a `PageTarget` instance. We'll do this piecemeal in subsequent diffs. We'll also gradually add APIs and logic to `PageTarget` / `PageAgent` to allow us to implement some "interesting" CDP methods - some of them directly (e.g. handling reload commands) and others by dispatching to nested agents (e.g. a JS debugging agent powered by Hermes). Reviewed By: huntie Differential Revision: D50936932 fbshipit-source-id: ebe5856d7badb361d4971dd9aabeb9982f8aed1b
86 lines
2.6 KiB
C++
86 lines
2.6 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 "PageTarget.h"
|
|
#include "InspectorUtilities.h"
|
|
#include "PageAgent.h"
|
|
#include "Parsing.h"
|
|
|
|
#include <folly/dynamic.h>
|
|
#include <folly/json.h>
|
|
|
|
#include <memory>
|
|
|
|
namespace facebook::react::jsinspector_modern {
|
|
|
|
namespace {
|
|
|
|
/**
|
|
* A Session connected to a PageTarget, passing CDP messages to and from a
|
|
* PageAgent which it owns.
|
|
*/
|
|
class PageTargetSession {
|
|
public:
|
|
explicit PageTargetSession(std::unique_ptr<IRemoteConnection> remote)
|
|
: remote_(std::make_shared<RAIIRemoteConnection>(std::move(remote))),
|
|
frontendChannel_(
|
|
[remoteWeak = std::weak_ptr(remote_)](std::string_view message) {
|
|
if (auto remote = remoteWeak.lock()) {
|
|
remote->onMessage(std::string(message));
|
|
}
|
|
}),
|
|
pageAgent_(frontendChannel_) {}
|
|
/**
|
|
* Called by CallbackLocalConnection to send a message to this Session's
|
|
* Agent.
|
|
*/
|
|
void operator()(std::string message) {
|
|
cdp::PreparsedRequest request;
|
|
// Messages may be invalid JSON, or have unexpected types.
|
|
try {
|
|
request = cdp::preparse(message);
|
|
} catch (const cdp::ParseError& e) {
|
|
frontendChannel_(folly::toJson(folly::dynamic::object("id", nullptr)(
|
|
"error",
|
|
folly::dynamic::object("code", -32700)("message", e.what()))));
|
|
return;
|
|
} catch (const cdp::TypeError& e) {
|
|
frontendChannel_(folly::toJson(folly::dynamic::object("id", nullptr)(
|
|
"error",
|
|
folly::dynamic::object("code", -32600)("message", e.what()))));
|
|
return;
|
|
}
|
|
|
|
// Catch exceptions that may arise from accessing dynamic params during
|
|
// request handling.
|
|
try {
|
|
pageAgent_.handleRequest(request);
|
|
} catch (const cdp::TypeError& e) {
|
|
frontendChannel_(folly::toJson(folly::dynamic::object("id", request.id)(
|
|
"error",
|
|
folly::dynamic::object("code", -32600)("message", e.what()))));
|
|
return;
|
|
}
|
|
}
|
|
|
|
private:
|
|
// Owned by this instance, but shared (weakly) with the frontend channel
|
|
std::shared_ptr<RAIIRemoteConnection> remote_;
|
|
FrontendChannel frontendChannel_;
|
|
PageAgent pageAgent_;
|
|
};
|
|
|
|
} // namespace
|
|
|
|
std::unique_ptr<ILocalConnection> PageTarget::connect(
|
|
std::unique_ptr<IRemoteConnection> connectionToFrontend) {
|
|
return std::make_unique<CallbackLocalConnection>(
|
|
PageTargetSession(std::move(connectionToFrontend)));
|
|
}
|
|
|
|
} // namespace facebook::react::jsinspector_modern
|