RuntimeTarget refactor - Add this-scoped async executors to all Targets

Summary:
Changelog: [Internal]

# This diff

1. Provides all Targets with an `executorFromThis()` method, which can be used from within a Target to access a *`this`-scoped main thread executor* = a `std::function` that will execute a callback asynchronously iff the current Target isn't destroyed first.
2. Refactors how (all) Target objects are constructed and retained, from a plain constructor to `static shared_ptr create()`. This is because `executorFromThis()` relies internally on `enable_shared_from_this` plus two-phase construction to populate the executor.
3. Creates utilities for deriving scoped executors from other executors and `shared_ptr`s.

The concept is very much like `RuntimeExecutor` in reverse: the #1 use case is moving from the JS thread back to the main thread - where "main thread" is defined loosely as "anywhere it's legal to call methods on Target/Agent objects, access session state, etc". The actual dispatching mechanism is left up to the owner of each `PageTarget` object; for now we only have an iOS integration, where we use `RCTExecuteOnMainQueue`.

Coupling the ownership/lifetime semantics with task scheduling is helpful, because it avoids the footgun of accidentally/nondeterministically moving `shared_ptr`s (and destructors!) to a different thread/queue .

# This stack
I'm refactoring the way the Runtime concept works in the modern CDP backend to bring it in line with the Page/Instance concepts.

Overall, this will let us:

* Integrate with engines that require us to instantiate a shared Target-like object (e.g. Hermes AsyncDebuggingAPI) in addition to an per-session Agent-like object.
* Access JSI in a CDP context (both at target setup/teardown time and during a CDP session) to implement our own engine-agnostic functionality (`console` interception, `Runtime.addBinding`, etc).
* Manage CDP execution contexts natively in RN, and (down the line) enable first-class debugging support for multiple Runtimes in an Instance.

The core diffs in this stack:

* ~~Introduce a `RuntimeTarget` class similar to `{Page,Instance}Target`. ~~
* ~~Make runtime registration explicit (`InstanceTarget::registerRuntime` similar to `PageTarget::registerInstance`). ~~
* ~~Rename the existing `RuntimeAgent` interface to `RuntimeAgentDelegate`.~~
* ~~Create a new concrete `RuntimeAgent` class similar to `{Page,Instance}Agent`.~~
* ~~Provide `RuntimeTarget` and `RuntimeAgent` with primitives for safe JSI access, namely a `RuntimeExecutor` for scheduling work on the JS thread.~~
  * Provide RuntimeTarget with mechanism for scheduling work on the "main" thread from the JS thread, for when we need to do more than just send a CDP message (which we can already do with the thread-safe `FrontendChannel`) in response to a JS event. *← This diff*

## Architecture diagrams

Before this stack:
https://pxl.cl/4h7m0

After this stack:
https://pxl.cl/4h7m7

Reviewed By: hoxyq

Differential Revision: D53356953

fbshipit-source-id: 152c784eb64e9b217fc2966743b33f61bd8fd97e
This commit is contained in:
Moti Zilberman
2024-02-14 07:13:19 -08:00
committed by Facebook GitHub Bot
parent b3a7a13ff8
commit d4468fb1e4
11 changed files with 253 additions and 54 deletions
@@ -209,7 +209,7 @@ class RCTBridgePageTargetDelegate : public facebook::react::jsinspector_modern::
NSURL *_delegateBundleURL;
std::unique_ptr<RCTBridgePageTargetDelegate> _inspectorPageDelegate;
std::unique_ptr<facebook::react::jsinspector_modern::PageTarget> _inspectorTarget;
std::shared_ptr<facebook::react::jsinspector_modern::PageTarget> _inspectorTarget;
std::optional<int> _inspectorPageId;
}
@@ -412,7 +412,12 @@ RCT_NOT_IMPLEMENTED(-(instancetype)init)
auto &inspectorFlags = facebook::react::jsinspector_modern::InspectorFlags::getInstance();
if (inspectorFlags.getEnableModernCDPRegistry() && !_inspectorPageId.has_value()) {
_inspectorTarget = std::make_unique<facebook::react::jsinspector_modern::PageTarget>(*_inspectorPageDelegate);
_inspectorTarget =
facebook::react::jsinspector_modern::PageTarget::create(*_inspectorPageDelegate, [](auto callback) {
RCTExecuteOnMainQueue(^{
callback();
});
});
__weak RCTBridge *weakSelf = self;
_inspectorPageId = facebook::react::jsinspector_modern::getInspectorInstance().addPage(
"React Native Bridge (Experimental)",
@@ -12,6 +12,14 @@
namespace facebook::react::jsinspector_modern {
std::shared_ptr<InstanceTarget> InstanceTarget::create(
InstanceTargetDelegate& delegate,
VoidExecutor executor) {
std::shared_ptr<InstanceTarget> instanceTarget{new InstanceTarget(delegate)};
instanceTarget->setExecutor(executor);
return instanceTarget;
}
InstanceTarget::InstanceTarget(InstanceTargetDelegate& delegate)
: delegate_(delegate) {
(void)delegate_;
@@ -24,8 +32,7 @@ std::shared_ptr<InstanceAgent> InstanceTarget::createAgent(
SessionState& sessionState) {
auto instanceAgent =
std::make_shared<InstanceAgent>(channel, *this, sessionState);
instanceAgent->setCurrentRuntime(
currentRuntime_.has_value() ? &*currentRuntime_ : nullptr);
instanceAgent->setCurrentRuntime(currentRuntime_.get());
agents_.push_back(instanceAgent);
return instanceAgent;
}
@@ -47,9 +54,11 @@ InstanceTarget::~InstanceTarget() {
RuntimeTarget& InstanceTarget::registerRuntime(
RuntimeTargetDelegate& delegate,
RuntimeExecutor executor) {
RuntimeExecutor jsExecutor) {
assert(!currentRuntime_ && "Only one Runtime allowed");
currentRuntime_.emplace(delegate, executor);
currentRuntime_ = RuntimeTarget::create(
delegate, jsExecutor, makeVoidExecutor(executorFromThis()));
forEachAgent([currentRuntime = &*currentRuntime_](InstanceAgent& agent) {
agent.setCurrentRuntime(currentRuntime);
});
@@ -58,7 +67,7 @@ RuntimeTarget& InstanceTarget::registerRuntime(
void InstanceTarget::unregisterRuntime(RuntimeTarget& Runtime) {
assert(
currentRuntime_.has_value() && &currentRuntime_.value() == &Runtime &&
currentRuntime_ && currentRuntime_.get() == &Runtime &&
"Invalid unregistration");
forEachAgent([](InstanceAgent& agent) { agent.setCurrentRuntime(nullptr); });
currentRuntime_.reset();
@@ -8,6 +8,7 @@
#pragma once
#include "RuntimeTarget.h"
#include "ScopedExecutor.h"
#include "SessionState.h"
#include <jsinspector-modern/InspectorInterfaces.h>
@@ -40,14 +41,20 @@ class InstanceTargetDelegate {
/**
* A Target that represents a single instance of React Native.
*/
class InstanceTarget final {
class InstanceTarget : public EnableExecutorFromThis<InstanceTarget> {
public:
/**
* Constructs a new InstanceTarget.
* \param delegate The object that will receive events from this target.
* The caller is responsible for ensuring that the delegate outlives this
* object.
* \param executor An executor that may be used to call methods on this
* InstanceTarget while it exists. \c create additionally guarantees that the
* executor will not be called after the InstanceTarget is destroyed.
*/
explicit InstanceTarget(InstanceTargetDelegate& delegate);
static std::shared_ptr<InstanceTarget> create(
InstanceTargetDelegate& delegate,
VoidExecutor executor);
InstanceTarget(const InstanceTarget&) = delete;
InstanceTarget(InstanceTarget&&) = delete;
@@ -65,8 +72,17 @@ class InstanceTarget final {
void unregisterRuntime(RuntimeTarget& runtime);
private:
/**
* Constructs a new InstanceTarget. The caller must call setExecutor
* immediately afterwards.
* \param delegate The object that will receive events from this target.
* The caller is responsible for ensuring that the delegate outlives this
* object.
*/
InstanceTarget(InstanceTargetDelegate& delegate);
InstanceTargetDelegate& delegate_;
std::optional<RuntimeTarget> currentRuntime_{std::nullopt};
std::shared_ptr<RuntimeTarget> currentRuntime_{nullptr};
std::list<std::weak_ptr<InstanceAgent>> agents_;
/**
@@ -99,6 +99,14 @@ class PageTargetSession {
SessionState state_;
};
std::shared_ptr<PageTarget> PageTarget::create(
PageTargetDelegate& delegate,
VoidExecutor executor) {
std::shared_ptr<PageTarget> pageTarget{new PageTarget(delegate)};
pageTarget->setExecutor(executor);
return pageTarget;
}
PageTarget::PageTarget(PageTargetDelegate& delegate) : delegate_(delegate) {}
std::unique_ptr<ILocalConnection> PageTarget::connect(
@@ -106,7 +114,7 @@ std::unique_ptr<ILocalConnection> PageTarget::connect(
SessionMetadata sessionMetadata) {
auto session = std::make_shared<PageTargetSession>(
std::move(connectionToFrontend), controller_, std::move(sessionMetadata));
session->setCurrentInstance(currentInstance_ ? &*currentInstance_ : nullptr);
session->setCurrentInstance(currentInstance_.get());
sessions_.push_back(std::weak_ptr(session));
return std::make_unique<CallbackLocalConnection>(
[session](std::string message) { (*session)(message); });
@@ -131,7 +139,8 @@ PageTargetDelegate::~PageTargetDelegate() {}
InstanceTarget& PageTarget::registerInstance(InstanceTargetDelegate& delegate) {
assert(!currentInstance_ && "Only one instance allowed");
currentInstance_.emplace(delegate);
currentInstance_ =
InstanceTarget::create(delegate, makeVoidExecutor(executorFromThis()));
forEachSession(
[currentInstance = &*currentInstance_](PageTargetSession& session) {
session.setCurrentInstance(currentInstance);
@@ -141,7 +150,7 @@ InstanceTarget& PageTarget::registerInstance(InstanceTargetDelegate& delegate) {
void PageTarget::unregisterInstance(InstanceTarget& instance) {
assert(
currentInstance_.has_value() && &currentInstance_.value() == &instance &&
currentInstance_ && currentInstance_.get() == &instance &&
"Invalid unregistration");
forEachSession(
[](PageTargetSession& session) { session.setCurrentInstance(nullptr); });
@@ -7,6 +7,8 @@
#pragma once
#include "ScopedExecutor.h"
#include <jsinspector-modern/InspectorInterfaces.h>
#include <jsinspector-modern/InstanceTarget.h>
@@ -95,7 +97,8 @@ class PageTargetController final {
* "Host" in React Native's architecture - the entity that manages the
* lifecycle of a React Instance.
*/
class JSINSPECTOR_EXPORT PageTarget final {
class JSINSPECTOR_EXPORT PageTarget
: public EnableExecutorFromThis<PageTarget> {
public:
struct SessionMetadata {
std::optional<std::string> integrationName;
@@ -103,11 +106,16 @@ class JSINSPECTOR_EXPORT PageTarget final {
/**
* Constructs a new PageTarget.
* \param delegate The PageTargetDelegate that will receive events from
* this PageTarget. The caller is responsible for ensuring that the
* PageTargetDelegate outlives this object.
* \param delegate The PageTargetDelegate that will
* receive events from this PageTarget. The caller is responsible for ensuring
* that the PageTargetDelegate outlives this object.
* \param executor An executor that may be used to call methods on this
* PageTarget while it exists. \c create additionally guarantees that the
* executor will not be called after the PageTarget is destroyed.
*/
explicit PageTarget(PageTargetDelegate& delegate);
static std::shared_ptr<PageTarget> create(
PageTargetDelegate& delegate,
VoidExecutor executor);
PageTarget(const PageTarget&) = delete;
PageTarget(PageTarget&&) = delete;
@@ -146,11 +154,19 @@ class JSINSPECTOR_EXPORT PageTarget final {
void unregisterInstance(InstanceTarget& instance);
private:
std::list<std::weak_ptr<PageTargetSession>> sessions_;
/**
* Constructs a new PageTarget.
* The caller must call setExecutor immediately afterwards.
* \param delegate The PageTargetDelegate that will
* receive events from this PageTarget. The caller is responsible for ensuring
* that the PageTargetDelegate outlives this object.
*/
PageTarget(PageTargetDelegate& delegate);
PageTargetDelegate& delegate_;
std::list<std::weak_ptr<PageTargetSession>> sessions_;
PageTargetController controller_{*this};
std::optional<InstanceTarget> currentInstance_{std::nullopt};
std::shared_ptr<InstanceTarget> currentInstance_{nullptr};
/**
* Call the given function for every active session, and clean up any
@@ -175,7 +191,7 @@ class JSINSPECTOR_EXPORT PageTarget final {
}
inline bool hasInstance() const {
return currentInstance_.has_value();
return currentInstance_ != nullptr;
}
// Necessary to allow PageAgent to access PageTarget's internals in a
@@ -11,4 +11,5 @@
#include <jsinspector-modern/InstanceTarget.h>
#include <jsinspector-modern/PageTarget.h>
#include <jsinspector-modern/RuntimeTarget.h>
#include <jsinspector-modern/ScopedExecutor.h>
#include <jsinspector-modern/SessionState.h>
@@ -9,10 +9,20 @@
namespace facebook::react::jsinspector_modern {
std::shared_ptr<RuntimeTarget> RuntimeTarget::create(
RuntimeTargetDelegate& delegate,
RuntimeExecutor jsExecutor,
VoidExecutor selfExecutor) {
std::shared_ptr<RuntimeTarget> runtimeTarget{
new RuntimeTarget(delegate, jsExecutor)};
runtimeTarget->setExecutor(selfExecutor);
return runtimeTarget;
}
RuntimeTarget::RuntimeTarget(
RuntimeTargetDelegate& delegate,
RuntimeExecutor executor)
: delegate_(delegate), executor_(executor) {}
RuntimeExecutor jsExecutor)
: delegate_(delegate), jsExecutor_(jsExecutor) {}
std::shared_ptr<RuntimeAgent> RuntimeTarget::createAgent(
FrontendChannel channel,
@@ -10,6 +10,7 @@
#include <ReactCommon/RuntimeExecutor.h>
#include "InspectorInterfaces.h"
#include "RuntimeAgent.h"
#include "ScopedExecutor.h"
#include "SessionState.h"
#include <list>
@@ -48,18 +49,27 @@ class RuntimeTargetDelegate {
/**
* A Target corresponding to a JavaScript runtime.
*/
class JSINSPECTOR_EXPORT RuntimeTarget final {
class JSINSPECTOR_EXPORT RuntimeTarget
: public EnableExecutorFromThis<RuntimeTarget> {
public:
/**
* Constructs a new RuntimeTarget. The caller must call setExecutor
* immediately afterwards.
* \param delegate The object that will receive events from this target.
* The caller is responsible for ensuring that the delegate outlives this
* object.
* \param executor A RuntimeExecutor that can be used to schedule work on
* \param jsExecutor A RuntimeExecutor that can be used to schedule work on
* the JS runtime's thread. The executor's queue should be empty when
* RuntimeTarget is constructed (i.e. anything scheduled during the
* constructor should be executed before any user code is run).
* \param selfExecutor An executor that may be used to call methods on this
* RuntimeTarget while it exists. \c create additionally guarantees that the
* executor will not be called after the RuntimeTarget is destroyed.
*/
RuntimeTarget(RuntimeTargetDelegate& delegate, RuntimeExecutor executor);
static std::shared_ptr<RuntimeTarget> create(
RuntimeTargetDelegate& delegate,
RuntimeExecutor jsExecutor,
VoidExecutor selfExecutor);
RuntimeTarget(const RuntimeTarget&) = delete;
RuntimeTarget(RuntimeTarget&&) = delete;
@@ -81,8 +91,21 @@ class JSINSPECTOR_EXPORT RuntimeTarget final {
SessionState& sessionState);
private:
/**
* Constructs a new RuntimeTarget. The caller must call setExecutor
* immediately afterwards.
* \param delegate The object that will receive events from this target.
* The caller is responsible for ensuring that the delegate outlives this
* object.
* \param jsExecutor A RuntimeExecutor that can be used to schedule work on
* the JS runtime's thread. The executor's queue should be empty when
* RuntimeTarget is constructed (i.e. anything scheduled during the
* constructor should be executed before any user code is run).
*/
RuntimeTarget(RuntimeTargetDelegate& delegate, RuntimeExecutor jsExecutor);
RuntimeTargetDelegate& delegate_;
RuntimeExecutor executor_;
RuntimeExecutor jsExecutor_;
std::list<std::weak_ptr<RuntimeAgent>> agents_;
/**
@@ -0,0 +1,97 @@
/*
* 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.
*/
#pragma once
#include <cassert>
#include <functional>
#include <memory>
namespace facebook::react::jsinspector_modern {
/**
* Takes a function and calls it when it is safe to access the Self&
* parameter without locking. The function is not called if
* the underlying Self object is destroyed while the function is pending.
*/
template <typename Self>
using ScopedExecutor =
std::function<void(std::function<void(Self& self)>&& callback)>;
using VoidExecutor = std::function<void(std::function<void()>&& callback)>;
/**
* Creates a ScopedExecutor<Self> from a shared (weak) pointer to Self plus some
* base executor for a "parent" object of Self. The resulting executor will call
* the callback with a valid reference to Self iff Self is still alive.
*/
template <typename Self, typename Parent>
ScopedExecutor<Self> makeScopedExecutor(
std::shared_ptr<Self> self,
ScopedExecutor<Parent> executor) {
return makeScopedExecutor(self, makeVoidExecutor(executor));
}
/**
* Creates a ScopedExecutor<Self> from a shared (weak) pointer to Self plus some
* base executor. The resulting executor will call the callback with a valid
* reference to Self iff Self is still alive.
*/
template <typename Self>
ScopedExecutor<Self> makeScopedExecutor(
std::shared_ptr<Self> self,
VoidExecutor executor) {
return [self = std::weak_ptr(self), executor](auto&& callback) {
executor([self, callback = std::move(callback)]() {
auto lockedSelf = self.lock();
if (!lockedSelf) {
return;
}
callback(*lockedSelf);
});
};
}
/**
* Creates a VoidExecutor from a ScopedExecutor<Self> by ignoring the Self&
* parameter.
*/
template <typename Self>
VoidExecutor makeVoidExecutor(ScopedExecutor<Self> executor) {
return [executor](auto&& callback) {
executor([callback = std::move(callback)](Self&) { callback(); });
};
}
template <typename Self>
class EnableExecutorFromThis : public std::enable_shared_from_this<Self> {
public:
/**
* Returns an executor that can be used to safely invoke methods on Self.
* Must not be called during the constructor of Self.
*/
ScopedExecutor<Self> executorFromThis() {
assert(baseExecutor_);
return makeScopedExecutor(this->shared_from_this(), baseExecutor_);
}
template <typename Other>
void setExecutor(ScopedExecutor<Other> executor) {
setExecutor(makeVoidExecutor(executor));
}
void setExecutor(VoidExecutor executor) {
assert(executor);
assert(!baseExecutor_);
baseExecutor_ = std::move(executor);
}
private:
VoidExecutor baseExecutor_;
};
}; // namespace facebook::react::jsinspector_modern
@@ -6,6 +6,7 @@
*/
#include <folly/dynamic.h>
#include <folly/executors/QueuedImmediateExecutor.h>
#include <folly/json.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
@@ -26,6 +27,8 @@ namespace facebook::react::jsinspector_modern {
namespace {
class PageTargetTest : public Test {
folly::QueuedImmediateExecutor immediateExecutor_;
protected:
PageTargetTest() {
EXPECT_CALL(runtimeTargetDelegate_, createAgentDelegate(_, _))
@@ -36,7 +39,7 @@ class PageTargetTest : public Test {
void connect() {
ASSERT_FALSE(toPage_) << "Can only connect once in a PageTargetTest.";
toPage_ = page_.connect(
toPage_ = page_->connect(
remoteConnections_.make_unique(),
{.integrationName = "PageTargetTest"});
@@ -52,7 +55,12 @@ class PageTargetTest : public Test {
return *remoteConnections_[0];
}
PageTarget page_{pageTargetDelegate_};
VoidExecutor inspectorExecutor_ = [this](auto callback) {
immediateExecutor_.add(callback);
};
std::shared_ptr<PageTarget> page_ =
PageTarget::create(pageTargetDelegate_, inspectorExecutor_);
MockInstanceTargetDelegate instanceTargetDelegate_;
MockRuntimeTargetDelegate runtimeTargetDelegate_;
@@ -197,17 +205,17 @@ TEST_F(PageTargetProtocolTest, PageReloadMethod) {
}
TEST_F(PageTargetProtocolTest, RegisterUnregisterInstanceWithoutEvents) {
auto& instanceTarget = page_.registerInstance(instanceTargetDelegate_);
auto& instanceTarget = page_->registerInstance(instanceTargetDelegate_);
page_.unregisterInstance(instanceTarget);
page_->unregisterInstance(instanceTarget);
}
TEST_F(PageTargetTest, ConnectToAlreadyRegisteredInstanceWithoutEvents) {
auto& instanceTarget = page_.registerInstance(instanceTargetDelegate_);
auto& instanceTarget = page_->registerInstance(instanceTargetDelegate_);
connect();
page_.unregisterInstance(instanceTarget);
page_->unregisterInstance(instanceTarget);
}
TEST_F(PageTargetProtocolTest, RegisterUnregisterInstanceWithEvents) {
@@ -222,16 +230,16 @@ TEST_F(PageTargetProtocolTest, RegisterUnregisterInstanceWithEvents) {
"method": "Runtime.enable"
})");
auto& instanceTarget = page_.registerInstance(instanceTargetDelegate_);
auto& instanceTarget = page_->registerInstance(instanceTargetDelegate_);
EXPECT_CALL(fromPage(), onMessage(JsonEq(R"({
"method": "Runtime.executionContextsCleared"
})")));
page_.unregisterInstance(instanceTarget);
page_->unregisterInstance(instanceTarget);
}
TEST_F(PageTargetTest, ConnectToAlreadyRegisteredInstanceWithEvents) {
auto& instanceTarget = page_.registerInstance(instanceTargetDelegate_);
auto& instanceTarget = page_->registerInstance(instanceTargetDelegate_);
connect();
@@ -250,11 +258,11 @@ TEST_F(PageTargetTest, ConnectToAlreadyRegisteredInstanceWithEvents) {
EXPECT_CALL(fromPage(), onMessage(JsonEq(R"({
"method": "Runtime.executionContextsCleared"
})")));
page_.unregisterInstance(instanceTarget);
page_->unregisterInstance(instanceTarget);
}
TEST_F(PageTargetTest, ConnectToAlreadyRegisteredRuntimeWithEvents) {
auto& instanceTarget = page_.registerInstance(instanceTargetDelegate_);
auto& instanceTarget = page_->registerInstance(instanceTargetDelegate_);
auto& runtimeTarget =
instanceTarget.registerRuntime(runtimeTargetDelegate_, runtimeExecutor_);
@@ -285,32 +293,32 @@ TEST_F(PageTargetTest, ConnectToAlreadyRegisteredRuntimeWithEvents) {
runtimeAgentDelegates_[0]->frontendChannel(kFooResponse);
instanceTarget.unregisterRuntime(runtimeTarget);
page_.unregisterInstance(instanceTarget);
page_->unregisterInstance(instanceTarget);
}
TEST_F(PageTargetProtocolTest, RuntimeAgentDelegateLifecycle) {
{
auto& instanceTarget = page_.registerInstance(instanceTargetDelegate_);
auto& instanceTarget = page_->registerInstance(instanceTargetDelegate_);
auto& runtimeTarget = instanceTarget.registerRuntime(
runtimeTargetDelegate_, runtimeExecutor_);
EXPECT_TRUE(runtimeAgentDelegates_[0]);
instanceTarget.unregisterRuntime(runtimeTarget);
page_.unregisterInstance(instanceTarget);
page_->unregisterInstance(instanceTarget);
}
EXPECT_FALSE(runtimeAgentDelegates_[0]);
{
auto& instanceTarget = page_.registerInstance(instanceTargetDelegate_);
auto& instanceTarget = page_->registerInstance(instanceTargetDelegate_);
auto& runtimeTarget = instanceTarget.registerRuntime(
runtimeTargetDelegate_, runtimeExecutor_);
EXPECT_TRUE(runtimeAgentDelegates_[1]);
instanceTarget.unregisterRuntime(runtimeTarget);
page_.unregisterInstance(instanceTarget);
page_->unregisterInstance(instanceTarget);
}
EXPECT_FALSE(runtimeAgentDelegates_[1]);
@@ -319,7 +327,7 @@ TEST_F(PageTargetProtocolTest, RuntimeAgentDelegateLifecycle) {
TEST_F(PageTargetProtocolTest, MethodNotHandledByRuntimeAgentDelegate) {
InSequence s;
auto& instanceTarget = page_.registerInstance(instanceTargetDelegate_);
auto& instanceTarget = page_->registerInstance(instanceTargetDelegate_);
auto& runtimeTarget =
instanceTarget.registerRuntime(runtimeTargetDelegate_, runtimeExecutor_);
@@ -339,13 +347,13 @@ TEST_F(PageTargetProtocolTest, MethodNotHandledByRuntimeAgentDelegate) {
})");
instanceTarget.unregisterRuntime(runtimeTarget);
page_.unregisterInstance(instanceTarget);
page_->unregisterInstance(instanceTarget);
}
TEST_F(PageTargetProtocolTest, MethodHandledByRuntimeAgentDelegate) {
InSequence s;
auto& instanceTarget = page_.registerInstance(instanceTargetDelegate_);
auto& instanceTarget = page_->registerInstance(instanceTargetDelegate_);
auto& runtimeTarget =
instanceTarget.registerRuntime(runtimeTargetDelegate_, runtimeExecutor_);
@@ -372,7 +380,7 @@ TEST_F(PageTargetProtocolTest, MethodHandledByRuntimeAgentDelegate) {
runtimeAgentDelegates_[0]->frontendChannel(kFooResponse);
instanceTarget.unregisterRuntime(runtimeTarget);
page_.unregisterInstance(instanceTarget);
page_->unregisterInstance(instanceTarget);
}
TEST_F(PageTargetProtocolTest, MessageRoutingWhileNoRuntimeAgentDelegate) {
@@ -389,7 +397,7 @@ TEST_F(PageTargetProtocolTest, MessageRoutingWhileNoRuntimeAgentDelegate) {
}
})");
auto& instanceTarget = page_.registerInstance(instanceTargetDelegate_);
auto& instanceTarget = page_->registerInstance(instanceTargetDelegate_);
auto& runtimeTarget =
instanceTarget.registerRuntime(runtimeTargetDelegate_, runtimeExecutor_);
@@ -416,7 +424,7 @@ TEST_F(PageTargetProtocolTest, MessageRoutingWhileNoRuntimeAgentDelegate) {
runtimeAgentDelegates_[0]->frontendChannel(kFooResponse);
instanceTarget.unregisterRuntime(runtimeTarget);
page_.unregisterInstance(instanceTarget);
page_->unregisterInstance(instanceTarget);
EXPECT_FALSE(runtimeAgentDelegates_[0]);
@@ -438,7 +446,7 @@ TEST_F(PageTargetProtocolTest, InstanceWithNullRuntimeAgentDelegate) {
EXPECT_CALL(runtimeTargetDelegate_, createAgentDelegate(_, _))
.WillRepeatedly(ReturnNull());
auto& instanceTarget = page_.registerInstance(instanceTargetDelegate_);
auto& instanceTarget = page_->registerInstance(instanceTargetDelegate_);
auto& runtimeTarget =
instanceTarget.registerRuntime(runtimeTargetDelegate_, runtimeExecutor_);
@@ -456,7 +464,7 @@ TEST_F(PageTargetProtocolTest, InstanceWithNullRuntimeAgentDelegate) {
})");
instanceTarget.unregisterRuntime(runtimeTarget);
page_.unregisterInstance(instanceTarget);
page_->unregisterInstance(instanceTarget);
}
TEST_F(PageTargetProtocolTest, RuntimeAgentDelegateHasAccessToSessionState) {
@@ -473,7 +481,7 @@ TEST_F(PageTargetProtocolTest, RuntimeAgentDelegateHasAccessToSessionState) {
"method": "Runtime.enable"
})");
auto& instanceTarget = page_.registerInstance(instanceTargetDelegate_);
auto& instanceTarget = page_->registerInstance(instanceTargetDelegate_);
instanceTarget.registerRuntime(runtimeTargetDelegate_, runtimeExecutor_);
ASSERT_TRUE(runtimeAgentDelegates_[0]);
@@ -68,7 +68,7 @@ class RCTHostPageTargetDelegate : public facebook::react::jsinspector_modern::Pa
RCTModuleRegistry *_moduleRegistry;
std::unique_ptr<RCTHostPageTargetDelegate> _inspectorPageDelegate;
std::unique_ptr<jsinspector_modern::PageTarget> _inspectorTarget;
std::shared_ptr<jsinspector_modern::PageTarget> _inspectorTarget;
std::optional<int> _inspectorPageId;
}
@@ -168,7 +168,12 @@ class RCTHostPageTargetDelegate : public facebook::react::jsinspector_modern::Pa
{
auto &inspectorFlags = jsinspector_modern::InspectorFlags::getInstance();
if (inspectorFlags.getEnableModernCDPRegistry() && !_inspectorPageId.has_value()) {
_inspectorTarget = std::make_unique<jsinspector_modern::PageTarget>(*_inspectorPageDelegate);
_inspectorTarget =
facebook::react::jsinspector_modern::PageTarget::create(*_inspectorPageDelegate, [](auto callback) {
RCTExecuteOnMainQueue(^{
callback();
});
});
__weak RCTHost *weakSelf = self;
_inspectorPageId = facebook::react::jsinspector_modern::getInspectorInstance().addPage(
"React Native Bridgeless (Experimental)",