mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
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:
committed by
Facebook GitHub Bot
parent
b3a7a13ff8
commit
d4468fb1e4
@@ -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() && ¤tRuntime_.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() && ¤tInstance_.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)",
|
||||
|
||||
Reference in New Issue
Block a user