Skip to content

Commit 5358bfd

Browse files
committed
Add HostTarget to CDP registry
Wires up the HostTarget in ReactNativeHost for react-native-windows. This code will only run if InspectorFlags::getEnableModernCDPRegistry() returns true.
1 parent 7d33a05 commit 5358bfd

File tree

11 files changed

+201
-4
lines changed

11 files changed

+201
-4
lines changed

vnext/Microsoft.ReactNative/Microsoft.ReactNative.vcxproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -281,6 +281,7 @@
281281
<SubType>Code</SubType>
282282
</ClInclude>
283283
<ClInclude Include="ReactHost\AsyncActionQueue.h" />
284+
<ClInclude Include="ReactHost\DebuggerNotifications.h" />
284285
<ClInclude Include="ReactHost\InstanceFactory.h" />
285286
<ClInclude Include="ReactHost\IReactInstanceInternal.h" />
286287
<ClInclude Include="ReactHost\JSBundle.h" />

vnext/Microsoft.ReactNative/Microsoft.ReactNative.vcxproj.filters

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -313,6 +313,9 @@
313313
<ClInclude Include="ReactHost\AsyncActionQueue.h">
314314
<Filter>ReactHost</Filter>
315315
</ClInclude>
316+
<ClInclude Include="ReactHost\DebuggerNotifications.h">
317+
<Filter>ReactHost</Filter>
318+
</ClInclude>
316319
<ClInclude Include="ReactHost\InstanceFactory.h">
317320
<Filter>ReactHost</Filter>
318321
</ClInclude>
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// Licensed under the MIT License.
3+
4+
#pragma once
5+
6+
#include <winrt/Microsoft.ReactNative.h>
7+
8+
namespace Microsoft::ReactNative {
9+
10+
struct DebuggerNotifications {
11+
static winrt::Microsoft::ReactNative::IReactPropertyName ShowDebuggerPausedOverlayEventName() noexcept {
12+
static winrt::Microsoft::ReactNative::IReactPropertyName propertyName{
13+
winrt::Microsoft::ReactNative::ReactPropertyBagHelper::GetName(
14+
winrt::Microsoft::ReactNative::ReactPropertyBagHelper::GetNamespace(L"ReactNative.Debugger"),
15+
L"ShowDebuggerPausedOverlay")};
16+
return propertyName;
17+
}
18+
19+
static void OnShowDebuggerPausedOverlay(
20+
winrt::Microsoft::ReactNative::IReactNotificationService const &service,
21+
std::string message,
22+
std::function<void()> onResume) {
23+
const winrt::Microsoft::ReactNative::ReactNonAbiValue<std::tuple<std::string, std::function<void()>>> nonAbiValue{
24+
std::in_place, std::tie(message, onResume)};
25+
service.SendNotification(ShowDebuggerPausedOverlayEventName(), nullptr, nonAbiValue);
26+
}
27+
28+
static void OnHideDebuggerPausedOverlay(winrt::Microsoft::ReactNative::IReactNotificationService const &service) {
29+
service.SendNotification(ShowDebuggerPausedOverlayEventName(), nullptr, nullptr);
30+
}
31+
32+
static winrt::Microsoft::ReactNative::IReactNotificationSubscription SubscribeShowDebuggerPausedOverlay(
33+
winrt::Microsoft::ReactNative::IReactNotificationService const &service,
34+
winrt::Microsoft::ReactNative::IReactDispatcher const &dispatcher,
35+
std::function<void(std::string, std::function<void()>)> showCallback,
36+
std::function<void()> hideCallback) {
37+
return service.Subscribe(
38+
ShowDebuggerPausedOverlayEventName(),
39+
dispatcher,
40+
[showCallback, hideCallback](auto &&, winrt::Microsoft::ReactNative::IReactNotificationArgs const &args) {
41+
if (args.Data()) {
42+
const auto [message, onResume] = args.Data()
43+
.as<winrt::Microsoft::ReactNative::ReactNonAbiValue<
44+
std::tuple<std::string, std::function<void()>>>>()
45+
.Value();
46+
showCallback(message, onResume);
47+
} else {
48+
hideCallback();
49+
}
50+
});
51+
}
52+
};
53+
54+
} // namespace Microsoft::ReactNative

vnext/Microsoft.ReactNative/ReactHost/React.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,10 @@
3232
#include <ViewManagerProvider.h>
3333
#include <winrt/Microsoft.ReactNative.h>
3434

35+
namespace facebook::react::jsinspector_modern {
36+
class HostTarget;
37+
} // namespace facebook::react::jsinspector_modern
38+
3539
namespace Mso::React {
3640

3741
// Forward declarations
@@ -343,6 +347,9 @@ struct ReactOptions {
343347
//! The callback is called when IReactInstance is destroyed and must not be used anymore.
344348
//! It is called from the native queue.
345349
OnReactInstanceDestroyedCallback OnInstanceDestroyed;
350+
351+
//! The HostTarget instance for Fusebox
352+
facebook::react::jsinspector_modern::HostTarget *InspectorTarget;
346353
};
347354

348355
//! IReactHost manages a ReactNative instance.

vnext/Microsoft.ReactNative/ReactHost/ReactInstanceWin.cpp

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -291,6 +291,18 @@ ReactInstanceWin::ReactInstanceWin(
291291
}
292292

293293
ReactInstanceWin::~ReactInstanceWin() noexcept {
294+
#ifdef USE_FABRIC
295+
if (m_bridgelessReactInstance && m_options.InspectorTarget) {
296+
auto messageDispatchQueue =
297+
Mso::React::MessageDispatchQueue(::Microsoft::ReactNative::FuseboxInspectorThread::Instance(), nullptr);
298+
messageDispatchQueue.runOnQueueSync([weakBridgelessReactInstance = std::weak_ptr(m_bridgelessReactInstance)]() {
299+
if (auto bridgelessReactInstance = weakBridgelessReactInstance.lock()) {
300+
bridgelessReactInstance->unregisterFromInspector();
301+
}
302+
});
303+
}
304+
#endif
305+
294306
std::scoped_lock lock{s_registryMutex};
295307
auto it = std::find(s_instanceRegistry.begin(), s_instanceRegistry.end(), this);
296308
if (it != s_instanceRegistry.end()) {
@@ -527,6 +539,8 @@ std::shared_ptr<facebook::react::DevSettings> ReactInstanceWin::CreateDevSetting
527539

528540
devSettings->useRuntimeScheduler = useRuntimeScheduler;
529541

542+
devSettings->inspectorTarget = m_options.InspectorTarget;
543+
530544
return devSettings;
531545
}
532546

@@ -618,8 +632,12 @@ void ReactInstanceWin::InitializeBridgeless() noexcept {
618632
devSettings, m_jsMessageThread.Load(), CreateHermesPreparedScriptStore());
619633
auto jsRuntime = std::make_unique<Microsoft::ReactNative::HermesJSRuntime>(m_jsiRuntimeHolder);
620634
jsRuntime->getRuntime();
621-
m_bridgelessReactInstance = std::make_unique<facebook::react::ReactInstance>(
622-
std::move(jsRuntime), m_jsMessageThread.Load(), timerManager, jsErrorHandlingFunc);
635+
m_bridgelessReactInstance = std::make_shared<facebook::react::ReactInstance>(
636+
std::move(jsRuntime),
637+
m_jsMessageThread.Load(),
638+
timerManager,
639+
jsErrorHandlingFunc,
640+
m_options.InpectorTarget);
623641

624642
auto bufferedRuntimeExecutor = m_bridgelessReactInstance->getBufferedRuntimeExecutor();
625643
timerManager->setRuntimeExecutor(bufferedRuntimeExecutor);

vnext/Microsoft.ReactNative/ReactHost/ReactInstanceWin.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -204,7 +204,7 @@ class ReactInstanceWin final : public Mso::ActiveObject<IReactInstanceInternal>
204204

205205
#ifdef USE_FABRIC
206206
// Bridgeless
207-
std::unique_ptr<facebook::react::ReactInstance> m_bridgelessReactInstance;
207+
std::shared_ptr<facebook::react::ReactInstance> m_bridgelessReactInstance;
208208
#endif
209209

210210
std::atomic<ReactInstanceState> m_state{ReactInstanceState::Loading};

vnext/Microsoft.ReactNative/ReactNativeHost.cpp

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,16 @@
55
#include "ReactNativeHost.h"
66
#include "ReactNativeHost.g.cpp"
77

8+
#include "FuseboxInspectorThread.h"
89
#include "ReactPackageBuilder.h"
910
#include "RedBox.h"
1011
#include "TurboModulesProvider.h"
1112

1213
#include <future/futureWinRT.h>
14+
#include <jsinspector-modern/InspectorFlags.h>
1315
#include <winrt/Windows.Foundation.Collections.h>
1416
#include "IReactContext.h"
17+
#include "ReactHost/DebuggerNotifications.h"
1518
#include "ReactInstanceSettings.h"
1619

1720
#ifdef USE_FABRIC
@@ -30,13 +33,85 @@ using namespace xaml::Controls;
3033

3134
namespace winrt::Microsoft::ReactNative::implementation {
3235

36+
class FuseboxHostTargetDelegate : public facebook::react::jsinspector_modern::HostTargetDelegate,
37+
public std::enable_shared_from_this<FuseboxHostTargetDelegate> {
38+
public:
39+
FuseboxHostTargetDelegate(ReactNativeHost *reactNativeHost) : m_reactNativeHost(reactNativeHost) {}
40+
41+
void onReload(facebook::react::jsinspector_modern::HostTargetDelegate::PageReloadRequest const &request) override {
42+
m_reactNativeHost->ReloadInstance();
43+
}
44+
45+
#ifdef HAS_FUSEBOX_PAUSED_OVERLAY // Remove after syncing past https://github.com/facebook/react-native/pull/44078
46+
void onSetPausedInDebuggerMessage(
47+
facebook::react::jsinspector_modern::HostTargetDelegate::OverlaySetPausedInDebuggerMessageRequest const &request)
48+
override {
49+
const auto instanceSettings = m_reactNativeHost->InstanceSettings();
50+
if (instanceSettings) {
51+
if (request.message.has_value()) {
52+
::Microsoft::ReactNative::DebuggerNotifications::OnShowDebuggerPausedOverlay(
53+
instanceSettings.Notifications(), request.message.value(), [weakThis = weak_from_this()]() {
54+
if (auto strongThis = weakThis.lock()) {
55+
strongThis->m_reactNativeHost->OnDebuggerResume();
56+
}
57+
});
58+
} else {
59+
::Microsoft::ReactNative::DebuggerNotifications::OnHideDebuggerPausedOverlay(instanceSettings.Notifications());
60+
}
61+
}
62+
}
63+
#endif
64+
65+
private:
66+
ReactNativeHost *m_reactNativeHost;
67+
};
68+
3369
ReactNativeHost::ReactNativeHost() noexcept : m_reactHost{Mso::React::MakeReactHost()} {
3470
#if _DEBUG
3571
facebook::react::InitializeLogging([](facebook::react::RCTLogLevel /*logLevel*/, const char *message) {
3672
std::string str = std::string("ReactNative:") + message;
3773
OutputDebugStringA(str.c_str());
3874
});
3975
#endif
76+
77+
auto &inspectorFlags = facebook::react::jsinspector_modern::InspectorFlags::getInstance();
78+
if (inspectorFlags.getEnableModernCDPRegistry() && !m_inspectorPageId.has_value()) {
79+
m_inspectorHostDelegate = std::make_shared<FuseboxHostTargetDelegate>(this);
80+
m_inspectorTarget = facebook::react::jsinspector_modern::HostTarget::create(
81+
*m_inspectorHostDelegate, [](std::function<void()> &&callback) {
82+
::Microsoft::ReactNative::FuseboxInspectorThread::Instance().InvokeElsePost([callback]() { callback(); });
83+
});
84+
85+
std::weak_ptr<facebook::react::jsinspector_modern::HostTarget> weakInspectorTarget = m_inspectorTarget;
86+
facebook::react::jsinspector_modern::InspectorTargetCapabilities capabilities;
87+
#ifdef HAS_FUSEBOX_CAPABILITIES // Remove after syncing past https://github.com/facebook/react-native/pull/43689
88+
capabilities.nativePageReloads = true;
89+
capabilities.prefersFuseboxFrontend = true;
90+
#endif
91+
m_inspectorPageId = facebook::react::jsinspector_modern::getInspectorInstance().addPage(
92+
"React Native Windows (Experimental)",
93+
/* vm */ "",
94+
[weakInspectorTarget](std::unique_ptr<facebook::react::jsinspector_modern::IRemoteConnection> remote)
95+
-> std::unique_ptr<facebook::react::jsinspector_modern::ILocalConnection> {
96+
if (const auto inspectorTarget = weakInspectorTarget.lock()) {
97+
facebook::react::jsinspector_modern::HostTarget::SessionMetadata sessionMetadata;
98+
sessionMetadata.integrationName = "React Native Windows (Host)";
99+
return inspectorTarget->connect(std::move(remote), sessionMetadata);
100+
}
101+
102+
// This can happen if we're about to be dealloc'd. Reject the connection.
103+
return nullptr;
104+
},
105+
capabilities);
106+
}
107+
}
108+
109+
ReactNativeHost::~ReactNativeHost() noexcept {
110+
if (m_inspectorPageId.has_value()) {
111+
facebook::react::jsinspector_modern::getInspectorInstance().removePage(*m_inspectorPageId);
112+
m_inspectorPageId.reset();
113+
m_inspectorTarget.reset();
114+
}
40115
}
41116

42117
/*static*/ ReactNative::ReactNativeHost ReactNativeHost::FromContext(
@@ -186,6 +261,7 @@ IAsyncAction ReactNativeHost::ReloadInstance() noexcept {
186261
}
187262

188263
reactOptions.Identity = jsBundleFile;
264+
reactOptions.InspectorTarget = m_inspectorTarget.get();
189265
return make<Mso::AsyncActionFutureAdapter>(m_reactHost->ReloadInstanceWithOptions(std::move(reactOptions)));
190266
}
191267

@@ -197,4 +273,15 @@ Mso::React::IReactHost *ReactNativeHost::ReactHost() noexcept {
197273
return m_reactHost.Get();
198274
}
199275

276+
void ReactNativeHost::OnDebuggerResume() noexcept {
277+
#ifdef HAS_FUSEBOX_PAUSED_OVERLAY // Remove after syncing past https://github.com/facebook/react-native/pull/44078
278+
::Microsoft::ReactNative::FuseboxInspectorThread::Instance().InvokeElsePost(
279+
[weakInspectorTarget = std::weak_ptr(m_inspectorTarget)]() {
280+
if (const auto inspectorTarget = weakInspectorTarget.lock()) {
281+
inspectorTarget->sendCommand(facebook::react::jsinspector_modern::HostCommand::DebuggerResume);
282+
}
283+
});
284+
#endif
285+
}
286+
200287
} // namespace winrt::Microsoft::ReactNative::implementation

vnext/Microsoft.ReactNative/ReactNativeHost.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
#include "ReactNativeHost.g.h"
77

8+
#include <jsinspector-modern/HostTarget.h>
89
#include "NativeModulesProvider.h"
910
#include "ReactHost/React.h"
1011
#include "ReactInstanceSettings.h"
@@ -16,6 +17,7 @@ namespace winrt::Microsoft::ReactNative::implementation {
1617
struct ReactNativeHost : ReactNativeHostT<ReactNativeHost> {
1718
public: // ReactNativeHost ABI API
1819
ReactNativeHost() noexcept;
20+
~ReactNativeHost() noexcept;
1921

2022
static ReactNative::ReactNativeHost FromContext(ReactNative::IReactContext const &reactContext) noexcept;
2123

@@ -25,6 +27,7 @@ struct ReactNativeHost : ReactNativeHostT<ReactNativeHost> {
2527
// property InstanceSettings
2628
ReactNative::ReactInstanceSettings InstanceSettings() noexcept;
2729
void InstanceSettings(ReactNative::ReactInstanceSettings const &value) noexcept;
30+
void OnDebuggerResume() noexcept;
2831

2932
winrt::Windows::Foundation::IAsyncAction LoadInstance() noexcept;
3033
winrt::Windows::Foundation::IAsyncAction ReloadInstance() noexcept;
@@ -39,6 +42,10 @@ struct ReactNativeHost : ReactNativeHostT<ReactNativeHost> {
3942

4043
ReactNative::ReactInstanceSettings m_instanceSettings{nullptr};
4144
ReactNative::IReactPackageBuilder m_packageBuilder;
45+
46+
std::shared_ptr<facebook::react::jsinspector_modern::HostTargetDelegate> m_inspectorHostDelegate{nullptr};
47+
std::shared_ptr<facebook::react::jsinspector_modern::HostTarget> m_inspectorTarget{nullptr};
48+
std::optional<int32_t> m_inspectorPageId{std::nullopt};
4249
};
4350

4451
} // namespace winrt::Microsoft::ReactNative::implementation

vnext/ReactCommon/ReactCommon.vcxproj

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@
109109
<ClInclude Include="$(ReactNativeDir)\ReactCommon\cxxreact\SystraceSection.h" />
110110
<ClInclude Include="$(ReactNativeDir)\ReactCommon\jsiexecutor\jsireact\JSIExecutor.h" />
111111
<ClInclude Include="$(ReactNativeDir)\ReactCommon\jsiexecutor\jsireact\JSINativeModules.h" />
112+
<ClInclude Include="$(ReactNativeDir)\ReactCommon\jsinspector-modern\InspectorFlags.h" />
112113
<ClInclude Include="$(ReactNativeDir)\ReactCommon\jsinspector-modern\InspectorInterfaces.h" />
113114
<ClInclude Include="$(ReactNativeDir)\ReactCommon\logger\react_native_log.h" />
114115
<ClInclude Include="$(YogaDir)\yoga\YGEnums.h" />
@@ -131,6 +132,7 @@
131132
<ClCompile Include="$(ReactNativeDir)\ReactCommon\jsi\jsi\JSIDynamic.cpp" />
132133
<ClCompile Include="$(ReactNativeDir)\ReactCommon\jsiexecutor\jsireact\JSIExecutor.cpp" />
133134
<ClCompile Include="$(ReactNativeDir)\ReactCommon\jsiexecutor\jsireact\JSINativeModules.cpp" />
135+
<ClCompile Include="$(ReactNativeDir)\ReactCommon\jsinspector-modern\InspectorFlags.cpp" />
134136
<ClCompile Include="$(ReactNativeDir)\ReactCommon\jsinspector-modern\InspectorInterfaces.cpp" />
135137
<ClCompile Include="$(ReactNativeDir)\ReactCommon\logger\react_native_log.cpp" />
136138
<CLCompile Include="$(ReactNativeDir)\ReactCommon\reactperflogger\reactperflogger\BridgeNativeModulePerfLogger.cpp" />

vnext/Shared/DevSettings.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,10 @@ struct RuntimeHolderLazyInit;
2323
namespace facebook {
2424
namespace react {
2525

26+
namespace jsinspector_modern {
27+
class HostTarget;
28+
} // namespace jsinspector_modern
29+
2630
enum class JSIEngineOverride : int32_t {
2731
Default = 0, // No JSI, will use the legacy ExecutorFactory
2832
Chakra = 1, // Use the JSIExecutorFactory with ChakraRuntime
@@ -114,6 +118,9 @@ struct DevSettings {
114118

115119
// Enable concurrent mode by installing runtimeScheduler
116120
bool useRuntimeScheduler{false};
121+
122+
// The HostTarget instance for Fusebox
123+
facebook::react::jsinspector_modern::HostTarget *inspectorTarget;
117124
};
118125

119126
} // namespace react

0 commit comments

Comments
 (0)