-
Notifications
You must be signed in to change notification settings - Fork 1.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Wire up Fusebox in react-native-windows #13008
base: main
Are you sure you want to change the base?
Changes from all commits
23db8ef
7d33a05
5358bfd
0b83a21
7d197df
4740af3
bfed766
70881c1
102cf15
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
{ | ||
"type": "prerelease", | ||
"comment": "Wire up Fusebox InspectorPackagerConnection", | ||
"packageName": "react-native-windows", | ||
"email": "[email protected]", | ||
"dependentChangeType": "patch" | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
// Copyright (c) Microsoft Corporation. | ||
// Licensed under the MIT License. | ||
|
||
#pragma once | ||
|
||
#include <winrt/Microsoft.ReactNative.h> | ||
|
||
namespace Microsoft::ReactNative { | ||
|
||
struct DebuggerNotifications { | ||
static winrt::Microsoft::ReactNative::IReactPropertyName ShowDebuggerPausedOverlayEventName() noexcept { | ||
static winrt::Microsoft::ReactNative::IReactPropertyName propertyName{ | ||
winrt::Microsoft::ReactNative::ReactPropertyBagHelper::GetName( | ||
winrt::Microsoft::ReactNative::ReactPropertyBagHelper::GetNamespace(L"ReactNative.Debugger"), | ||
L"ShowDebuggerPausedOverlay")}; | ||
return propertyName; | ||
} | ||
|
||
static void OnShowDebuggerPausedOverlay( | ||
winrt::Microsoft::ReactNative::IReactNotificationService const &service, | ||
std::string message, | ||
std::function<void()> onResume) { | ||
const winrt::Microsoft::ReactNative::ReactNonAbiValue<std::tuple<std::string, std::function<void()>>> nonAbiValue{ | ||
std::in_place, std::tie(message, onResume)}; | ||
service.SendNotification(ShowDebuggerPausedOverlayEventName(), nullptr, nonAbiValue); | ||
} | ||
|
||
static void OnHideDebuggerPausedOverlay(winrt::Microsoft::ReactNative::IReactNotificationService const &service) { | ||
service.SendNotification(ShowDebuggerPausedOverlayEventName(), nullptr, nullptr); | ||
} | ||
|
||
static winrt::Microsoft::ReactNative::IReactNotificationSubscription SubscribeShowDebuggerPausedOverlay( | ||
winrt::Microsoft::ReactNative::IReactNotificationService const &service, | ||
winrt::Microsoft::ReactNative::IReactDispatcher const &dispatcher, | ||
std::function<void(std::string, std::function<void()>)> showCallback, | ||
std::function<void()> hideCallback) { | ||
return service.Subscribe( | ||
ShowDebuggerPausedOverlayEventName(), | ||
dispatcher, | ||
[showCallback, hideCallback](auto &&, winrt::Microsoft::ReactNative::IReactNotificationArgs const &args) { | ||
if (args.Data()) { | ||
const auto [message, onResume] = args.Data() | ||
.as<winrt::Microsoft::ReactNative::ReactNonAbiValue< | ||
std::tuple<std::string, std::function<void()>>>>() | ||
.Value(); | ||
showCallback(message, onResume); | ||
} else { | ||
hideCallback(); | ||
} | ||
}); | ||
} | ||
}; | ||
|
||
} // namespace Microsoft::ReactNative |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -204,7 +204,7 @@ class ReactInstanceWin final : public Mso::ActiveObject<IReactInstanceInternal> | |
|
||
#ifdef USE_FABRIC | ||
// Bridgeless | ||
std::unique_ptr<facebook::react::ReactInstance> m_bridgelessReactInstance; | ||
std::shared_ptr<facebook::react::ReactInstance> m_bridgelessReactInstance; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. converted to shared_ptr to provide weak_ptr to host target de-init |
||
#endif | ||
|
||
std::atomic<ReactInstanceState> m_state{ReactInstanceState::Loading}; | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,13 +5,16 @@ | |
#include "ReactNativeHost.h" | ||
#include "ReactNativeHost.g.cpp" | ||
|
||
#include "FuseboxInspectorThread.h" | ||
#include "ReactPackageBuilder.h" | ||
#include "RedBox.h" | ||
#include "TurboModulesProvider.h" | ||
|
||
#include <future/futureWinRT.h> | ||
#include <jsinspector-modern/InspectorFlags.h> | ||
#include <winrt/Windows.Foundation.Collections.h> | ||
#include "IReactContext.h" | ||
#include "ReactHost/DebuggerNotifications.h" | ||
#include "ReactInstanceSettings.h" | ||
|
||
#ifdef USE_FABRIC | ||
|
@@ -30,13 +33,85 @@ using namespace xaml::Controls; | |
|
||
namespace winrt::Microsoft::ReactNative::implementation { | ||
|
||
class FuseboxHostTargetDelegate : public facebook::react::jsinspector_modern::HostTargetDelegate, | ||
public std::enable_shared_from_this<FuseboxHostTargetDelegate> { | ||
public: | ||
FuseboxHostTargetDelegate(ReactNativeHost *reactNativeHost) : m_reactNativeHost(reactNativeHost) {} | ||
|
||
void onReload(facebook::react::jsinspector_modern::HostTargetDelegate::PageReloadRequest const &request) override { | ||
m_reactNativeHost->ReloadInstance(); | ||
} | ||
|
||
#ifdef HAS_FUSEBOX_PAUSED_OVERLAY // Remove after syncing past https://github.com/facebook/react-native/pull/44078 | ||
void onSetPausedInDebuggerMessage( | ||
facebook::react::jsinspector_modern::HostTargetDelegate::OverlaySetPausedInDebuggerMessageRequest const &request) | ||
override { | ||
const auto instanceSettings = m_reactNativeHost->InstanceSettings(); | ||
if (instanceSettings) { | ||
if (request.message.has_value()) { | ||
::Microsoft::ReactNative::DebuggerNotifications::OnShowDebuggerPausedOverlay( | ||
instanceSettings.Notifications(), request.message.value(), [weakThis = weak_from_this()]() { | ||
if (auto strongThis = weakThis.lock()) { | ||
strongThis->m_reactNativeHost->OnDebuggerResume(); | ||
} | ||
}); | ||
} else { | ||
::Microsoft::ReactNative::DebuggerNotifications::OnHideDebuggerPausedOverlay(instanceSettings.Notifications()); | ||
} | ||
} | ||
} | ||
#endif | ||
|
||
private: | ||
ReactNativeHost *m_reactNativeHost; | ||
}; | ||
|
||
ReactNativeHost::ReactNativeHost() noexcept : m_reactHost{Mso::React::MakeReactHost()} { | ||
#if _DEBUG | ||
facebook::react::InitializeLogging([](facebook::react::RCTLogLevel /*logLevel*/, const char *message) { | ||
std::string str = std::string("ReactNative:") + message; | ||
OutputDebugStringA(str.c_str()); | ||
}); | ||
#endif | ||
|
||
auto &inspectorFlags = facebook::react::jsinspector_modern::InspectorFlags::getInstance(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This should all be lazy init'd on the first reload, otherwise you have to set the feature flag before calling the ReactNativeHost ctor, rather than just before attaching the ReactNativeHost to the ReactRootView. |
||
if (inspectorFlags.getEnableModernCDPRegistry() && !m_inspectorPageId.has_value()) { | ||
m_inspectorHostDelegate = std::make_shared<FuseboxHostTargetDelegate>(this); | ||
m_inspectorTarget = facebook::react::jsinspector_modern::HostTarget::create( | ||
*m_inspectorHostDelegate, [](std::function<void()> &&callback) { | ||
::Microsoft::ReactNative::FuseboxInspectorThread::Instance().InvokeElsePost([callback]() { callback(); }); | ||
}); | ||
|
||
std::weak_ptr<facebook::react::jsinspector_modern::HostTarget> weakInspectorTarget = m_inspectorTarget; | ||
facebook::react::jsinspector_modern::InspectorTargetCapabilities capabilities; | ||
#ifdef HAS_FUSEBOX_CAPABILITIES // Remove after syncing past https://github.com/facebook/react-native/pull/43689 | ||
capabilities.nativePageReloads = true; | ||
capabilities.prefersFuseboxFrontend = true; | ||
#endif | ||
m_inspectorPageId = facebook::react::jsinspector_modern::getInspectorInstance().addPage( | ||
"React Native Windows (Experimental)", | ||
/* vm */ "", | ||
[weakInspectorTarget](std::unique_ptr<facebook::react::jsinspector_modern::IRemoteConnection> remote) | ||
-> std::unique_ptr<facebook::react::jsinspector_modern::ILocalConnection> { | ||
if (const auto inspectorTarget = weakInspectorTarget.lock()) { | ||
facebook::react::jsinspector_modern::HostTarget::SessionMetadata sessionMetadata; | ||
sessionMetadata.integrationName = "React Native Windows (Host)"; | ||
return inspectorTarget->connect(std::move(remote), sessionMetadata); | ||
} | ||
|
||
// This can happen if we're about to be dealloc'd. Reject the connection. | ||
return nullptr; | ||
}, | ||
capabilities); | ||
} | ||
} | ||
|
||
ReactNativeHost::~ReactNativeHost() noexcept { | ||
if (m_inspectorPageId.has_value()) { | ||
facebook::react::jsinspector_modern::getInspectorInstance().removePage(*m_inspectorPageId); | ||
m_inspectorPageId.reset(); | ||
m_inspectorTarget.reset(); | ||
} | ||
} | ||
|
||
/*static*/ ReactNative::ReactNativeHost ReactNativeHost::FromContext( | ||
|
@@ -186,6 +261,7 @@ IAsyncAction ReactNativeHost::ReloadInstance() noexcept { | |
} | ||
|
||
reactOptions.Identity = jsBundleFile; | ||
reactOptions.InspectorTarget = m_inspectorTarget.get(); | ||
return make<Mso::AsyncActionFutureAdapter>(m_reactHost->ReloadInstanceWithOptions(std::move(reactOptions))); | ||
} | ||
|
||
|
@@ -197,4 +273,15 @@ Mso::React::IReactHost *ReactNativeHost::ReactHost() noexcept { | |
return m_reactHost.Get(); | ||
} | ||
|
||
void ReactNativeHost::OnDebuggerResume() noexcept { | ||
#ifdef HAS_FUSEBOX_PAUSED_OVERLAY // Remove after syncing past https://github.com/facebook/react-native/pull/44078 | ||
::Microsoft::ReactNative::FuseboxInspectorThread::Instance().InvokeElsePost( | ||
[weakInspectorTarget = std::weak_ptr(m_inspectorTarget)]() { | ||
if (const auto inspectorTarget = weakInspectorTarget.lock()) { | ||
inspectorTarget->sendCommand(facebook::react::jsinspector_modern::HostCommand::DebuggerResume); | ||
} | ||
}); | ||
#endif | ||
} | ||
|
||
} // namespace winrt::Microsoft::ReactNative::implementation |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Using MessageDispatchQueue to get the runOnQueueSync function on an Mso::DispatchQueue. We need to ensure the ReactInstance stays alive for as long as it takes to unregister the inspector.