diff --git a/packages/playground/windows/playground-composition/Playground-Composition.cpp b/packages/playground/windows/playground-composition/Playground-Composition.cpp index 1222a414903..08c1cf2f900 100644 --- a/packages/playground/windows/playground-composition/Playground-Composition.cpp +++ b/packages/playground/windows/playground-composition/Playground-Composition.cpp @@ -140,10 +140,11 @@ struct WindowData { winrt::Microsoft::ReactNative::ReactNativeHost Host() noexcept { if (!m_host) { + OutputDebugStringA("Playground: Creating ReactNativeHost with debugger support\n"); m_host = winrt::Microsoft::ReactNative::ReactNativeHost(); m_host.InstanceSettings(InstanceSettings()); + OutputDebugStringA("Playground: ReactNativeHost created with debugging enabled\n"); } - return m_host; } winrt::Microsoft::ReactNative::ReactInstanceSettings InstanceSettings() noexcept { @@ -206,6 +207,7 @@ struct WindowData { } if (windowData->m_useLiftedComposition) { + OutputDebugStringA("Playground: Setting up lifted composition\n"); // By setting the compositor here we opt into using the new architecture. winrt::Microsoft::ReactNative::Composition::CompositionUIService::SetCompositor( InstanceSettings(), g_liftedCompositor); @@ -264,8 +266,10 @@ struct WindowData { m_compRootView.Arrange(constraints, {0, 0}); m_bridge.ResizePolicy(winrt::Microsoft::UI::Content::ContentSizePolicy::ResizeContentToParentWindow); + OutputDebugStringA("Playground: Lifted composition setup complete\n"); } else if (!m_target) { + OutputDebugStringA("Playground: Setting up system composition\n"); // General users of RNW should never set CompositionContext - this is an advanced usage to inject another // composition implementation. By using the SystemCompositionContextHelper here, React Native Windows will // use System Visuals for its tree. @@ -276,6 +280,7 @@ struct WindowData { L"ReactNative.Composition", L"CompositionContext"}, winrt::Microsoft::ReactNative::Composition::Experimental::SystemCompositionContextHelper:: CreateContext(g_compositor)); + OutputDebugStringA("Playground: CompositionContext set\n"); auto interop = g_compositor.as(); winrt::Windows::UI::Composition::Desktop::DesktopWindowTarget target{nullptr}; @@ -303,15 +308,32 @@ struct WindowData { contraints.MaximumSize = contraints.MinimumSize = {m_width / ScaleFactor(hwnd), m_height / ScaleFactor(hwnd)}; m_compRootView.Arrange(contraints, {0, 0}); + OutputDebugStringA("Playground: System composition setup complete\n"); } } // Nudge the ReactNativeHost to create the instance and wrapping context + OutputDebugStringA("Playground: About to ReloadInstance\n"); host.ReloadInstance(); + OutputDebugStringA("Playground: ReloadInstance complete\n"); + + // Verify composition context is ready before assigning ReactViewHost + OutputDebugStringA("Playground: Verifying composition context is available\n"); + if (windowData->m_useLiftedComposition) { + auto compositor = winrt::Microsoft::ReactNative::Composition::CompositionUIService::GetCompositor( + InstanceSettings().Properties()); + if (compositor) { + OutputDebugStringA("Playground: Lifted compositor verified - context ready\n"); + } else { + OutputDebugStringA("Playground: WARNING - Lifted compositor not found\n"); + } + } m_compRootView.ReactViewHost( winrt::Microsoft::ReactNative::ReactCoreInjection::MakeViewHost(host, viewOptions)); + OutputDebugStringA("Playground: ReactViewHost assigned successfully\n"); } + break; } @@ -635,8 +657,10 @@ LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam) constexpr PCWSTR c_windowClassName = L"MS_REACTNATIVE_PLAYGROUND_COMPOSITION"; int RunPlayground(int showCmd, bool /*useWebDebugger*/) { + OutputDebugStringA("Playground: RunPlayground starting\n"); + constexpr PCWSTR appName = L"React Native Playground (Composition)"; - + auto windowData = std::make_unique(); HWND hwnd = CreateWindow( c_windowClassName, @@ -673,8 +697,12 @@ int RunPlayground(int showCmd, bool /*useWebDebugger*/) { } _Use_decl_annotations_ int CALLBACK WinMain(HINSTANCE instance, HINSTANCE, PSTR /* commandLine */, int showCmd) { + OutputDebugStringA("Playground: WinMain starting\n"); + // Island-support: Call init_apartment to initialize COM and WinRT for the thread. + OutputDebugStringA("Playground: About to init_apartment\n"); winrt::init_apartment(winrt::apartment_type::single_threaded); + OutputDebugStringA("Playground: init_apartment completed\n"); WNDCLASSEXW wcex = {}; wcex.cbSize = sizeof(WNDCLASSEX); diff --git a/vnext/Microsoft.ReactNative/Fabric/Composition/ReactNativeIsland.cpp b/vnext/Microsoft.ReactNative/Fabric/Composition/ReactNativeIsland.cpp index d1424fa0ebd..04c17a1b52f 100644 --- a/vnext/Microsoft.ReactNative/Fabric/Composition/ReactNativeIsland.cpp +++ b/vnext/Microsoft.ReactNative/Fabric/Composition/ReactNativeIsland.cpp @@ -215,8 +215,10 @@ void ReactNativeIsland::ReactViewHost(winrt::Microsoft::ReactNative::IReactViewH m_reactViewHost = value; if (m_reactViewHost) { + OutputDebugStringA("ReactNativeIsland::ReactViewHost() - Attaching view instance\n"); auto viewInstance = winrt::make(this->get_weak()); m_reactViewHost.AttachViewInstance(viewInstance); + OutputDebugStringA("ReactNativeIsland::ReactViewHost() - View instance attached\n"); } } @@ -725,16 +727,25 @@ Composition::Experimental::IDrawingSurfaceBrush ReactNativeIsland::CreateLoading } void ReactNativeIsland::ShowInstanceLoading() noexcept { + OutputDebugStringA("ReactNativeIsland::ShowInstanceLoading() - START\n"); + if (!Mso::React::ReactOptions::UseDeveloperSupport(m_context.Properties().Handle())) return; if (m_loadingVisual) return; + OutputDebugStringA("ReactNativeIsland::ShowInstanceLoading() - Getting composition context\n"); auto compContext = winrt::Microsoft::ReactNative::Composition::implementation::CompositionUIService::GetCompositionContext( m_context.Properties().Handle()); + if (!compContext) { + OutputDebugStringA("ReactNativeIsland::ShowInstanceLoading() - ERROR: compContext is NULL - skipping loading UI\n"); + return; + } + + OutputDebugStringA("ReactNativeIsland::ShowInstanceLoading() - Creating sprite visual\n"); m_loadingVisual = compContext.CreateSpriteVisual(); auto foregroundBrush = compContext.CreateColorBrush({255, 255, 255, 255}); @@ -747,6 +758,7 @@ void ReactNativeIsland::ShowInstanceLoading() noexcept { UpdateLoadingVisualSize(); InternalRootVisual().InsertAt(m_loadingVisual, m_hasRenderedVisual ? 1 : 0); + OutputDebugStringA("ReactNativeIsland::ShowInstanceLoading() - COMPLETE\n"); } void ReactNativeIsland::InitTextScaleMultiplier() noexcept { diff --git a/vnext/Microsoft.ReactNative/Microsoft.ReactNative.vcxproj b/vnext/Microsoft.ReactNative/Microsoft.ReactNative.vcxproj index b50fab3cadb..f8097e89ed4 100644 --- a/vnext/Microsoft.ReactNative/Microsoft.ReactNative.vcxproj +++ b/vnext/Microsoft.ReactNative/Microsoft.ReactNative.vcxproj @@ -268,6 +268,7 @@ Code + diff --git a/vnext/Microsoft.ReactNative/Microsoft.ReactNative.vcxproj.filters b/vnext/Microsoft.ReactNative/Microsoft.ReactNative.vcxproj.filters index ad0c2b22282..e2480686490 100644 --- a/vnext/Microsoft.ReactNative/Microsoft.ReactNative.vcxproj.filters +++ b/vnext/Microsoft.ReactNative/Microsoft.ReactNative.vcxproj.filters @@ -313,6 +313,9 @@ ReactHost + + ReactHost + ReactHost diff --git a/vnext/Microsoft.ReactNative/ReactHost/DebuggerNotifications.h b/vnext/Microsoft.ReactNative/ReactHost/DebuggerNotifications.h new file mode 100644 index 00000000000..6a083140a34 --- /dev/null +++ b/vnext/Microsoft.ReactNative/ReactHost/DebuggerNotifications.h @@ -0,0 +1,54 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#pragma once + +#include + +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 onResume) { + const winrt::Microsoft::ReactNative::ReactNonAbiValue>> 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)> showCallback, + std::function 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>>>() + .Value(); + showCallback(message, onResume); + } else { + hideCallback(); + } + }); + } +}; + +} // namespace Microsoft::ReactNative diff --git a/vnext/Microsoft.ReactNative/ReactHost/React.h b/vnext/Microsoft.ReactNative/ReactHost/React.h index dc7867f39b8..2072dc3142b 100644 --- a/vnext/Microsoft.ReactNative/ReactHost/React.h +++ b/vnext/Microsoft.ReactNative/ReactHost/React.h @@ -33,6 +33,10 @@ #include #endif +namespace facebook::react::jsinspector_modern { +class HostTarget; +} // namespace facebook::react::jsinspector_modern + namespace Mso::React { // Forward declarations @@ -347,6 +351,9 @@ struct ReactOptions { //! The callback is called when IReactInstance is destroyed and must not be used anymore. //! It is called from the native queue. OnReactInstanceDestroyedCallback OnInstanceDestroyed; + + //! The HostTarget instance for Fusebox + facebook::react::jsinspector_modern::HostTarget *InspectorTarget; }; //! IReactHost manages a ReactNative instance. diff --git a/vnext/Microsoft.ReactNative/ReactHost/ReactHost.cpp b/vnext/Microsoft.ReactNative/ReactHost/ReactHost.cpp index 56ce0f97635..b5f3fbb04d3 100644 --- a/vnext/Microsoft.ReactNative/ReactHost/ReactHost.cpp +++ b/vnext/Microsoft.ReactNative/ReactHost/ReactHost.cpp @@ -286,6 +286,7 @@ class ReactNativeWindowsFeatureFlags : public facebook::react::ReactNativeFeatur public: bool enableBridgelessArchitecture() override { #ifdef USE_FABRIC + OutputDebugStringA("ReactNativeWindowsFeatureFlags::enableBridgelessArchitecture() called\n"); return true; #else return false; @@ -295,6 +296,17 @@ class ReactNativeWindowsFeatureFlags : public facebook::react::ReactNativeFeatur bool enableCppPropsIteratorSetter() override { return true; } + + bool fuseboxEnabledRelease() override { + // log when called + OutputDebugStringA("ReactNativeWindowsFeatureFlags::fuseboxEnabledRelease() called\n"); + return true; // Enable Fusebox (modern CDP backend) by default for React Native Windows + } + + bool fuseboxNetworkInspectionEnabled() override { + OutputDebugStringA("ReactNativeWindowsFeatureFlags::fuseboxNetworkInspectionEnabled() called\n"); + return true; // Enable network inspection support in Fusebox + } }; std::once_flag g_FlagInitFeatureFlags; diff --git a/vnext/Microsoft.ReactNative/ReactHost/ReactInstanceWin.cpp b/vnext/Microsoft.ReactNative/ReactHost/ReactInstanceWin.cpp index aefaaa3db63..95d5e8b9cfc 100644 --- a/vnext/Microsoft.ReactNative/ReactHost/ReactInstanceWin.cpp +++ b/vnext/Microsoft.ReactNative/ReactHost/ReactInstanceWin.cpp @@ -66,6 +66,7 @@ #include #include #include +#include "Inspector/ReactInspectorThread.h" #endif #if !defined(CORE_ABI) && !defined(USE_FABRIC) @@ -299,6 +300,18 @@ ReactInstanceWin::ReactInstanceWin( } ReactInstanceWin::~ReactInstanceWin() noexcept { +#ifdef USE_FABRIC + if (m_bridgelessReactInstance && m_options.InspectorTarget) { + auto messageDispatchQueue = + Mso::React::MessageDispatchQueue(::Microsoft::ReactNative::ReactInspectorThread::Instance(), nullptr); + messageDispatchQueue.runOnQueueSync([weakBridgelessReactInstance = std::weak_ptr(m_bridgelessReactInstance)]() { + if (auto bridgelessReactInstance = weakBridgelessReactInstance.lock()) { + bridgelessReactInstance->unregisterFromInspector(); + } + }); + } +#endif + std::scoped_lock lock{s_registryMutex}; auto it = std::find(s_instanceRegistry.begin(), s_instanceRegistry.end(), this); if (it != s_instanceRegistry.end()) { @@ -553,6 +566,8 @@ std::shared_ptr ReactInstanceWin::CreateDevSetting devSettings->useRuntimeScheduler = useRuntimeScheduler; + devSettings->inspectorTarget = m_options.InspectorTarget; + return devSettings; } @@ -665,15 +680,27 @@ void ReactInstanceWin::InitializeBridgeless() noexcept { if (devSettings->useDirectDebugger) { ::Microsoft::ReactNative::GetSharedDevManager()->EnsureHermesInspector( - devSettings->sourceBundleHost, devSettings->sourceBundlePort); + devSettings->sourceBundleHost, devSettings->sourceBundlePort, devSettings->bundleAppId); } m_jsiRuntimeHolder = std::make_shared( devSettings, jsMessageThread, CreatePreparedScriptStore()); auto jsRuntime = std::make_unique(m_jsiRuntimeHolder); jsRuntime->getRuntime(); - m_bridgelessReactInstance = std::make_unique( - std::move(jsRuntime), jsMessageThread, timerManager, jsErrorHandlingFunc); + + // Check InspectorTarget before creating ReactInstance + if (m_options.InspectorTarget) { + OutputDebugStringA("ReactInstanceWin: InspectorTarget is NOT null\n"); + } else { + OutputDebugStringA("ReactInstanceWin: InspectorTarget IS NULL\n"); + } + + m_bridgelessReactInstance = std::make_shared( + std::move(jsRuntime), + jsMessageThread, + timerManager, + jsErrorHandlingFunc, + m_options.InspectorTarget); auto bufferedRuntimeExecutor = m_bridgelessReactInstance->getBufferedRuntimeExecutor(); timerManager->setRuntimeExecutor(bufferedRuntimeExecutor); @@ -695,6 +722,10 @@ void ReactInstanceWin::InitializeBridgeless() noexcept { winrt::make(Mso::Copy(m_reactContext))); facebook::react::ReactInstance::JSRuntimeFlags options; + + // Add logging before initializeRuntime + OutputDebugStringA("ReactInstanceWin: About to call initializeRuntime\n"); + m_bridgelessReactInstance->initializeRuntime( options, [=, onCreated = m_options.OnInstanceCreated, reactContext = m_reactContext]( @@ -749,6 +780,7 @@ void ReactInstanceWin::InitializeBridgeless() noexcept { LoadJSBundlesBridgeless(devSettings); SetupHMRClient(); + OutputDebugStringA("ReactInstanceWin: initializeRuntime call completed\n"); } catch (std::exception &e) { OnErrorWithMessage(e.what()); OnErrorWithMessage("ReactInstanceWin: Failed to create React Instance."); diff --git a/vnext/Microsoft.ReactNative/ReactHost/ReactInstanceWin.h b/vnext/Microsoft.ReactNative/ReactHost/ReactInstanceWin.h index 36c153df4e9..4d5125bc166 100644 --- a/vnext/Microsoft.ReactNative/ReactHost/ReactInstanceWin.h +++ b/vnext/Microsoft.ReactNative/ReactHost/ReactInstanceWin.h @@ -205,7 +205,7 @@ class ReactInstanceWin final : public Mso::ActiveObject #ifdef USE_FABRIC // Bridgeless - std::unique_ptr m_bridgelessReactInstance; + std::shared_ptr m_bridgelessReactInstance; #endif std::atomic m_state{ReactInstanceState::Loading}; diff --git a/vnext/Microsoft.ReactNative/ReactNativeHost.cpp b/vnext/Microsoft.ReactNative/ReactNativeHost.cpp index 67f59d23822..12cd3be5b61 100644 --- a/vnext/Microsoft.ReactNative/ReactNativeHost.cpp +++ b/vnext/Microsoft.ReactNative/ReactNativeHost.cpp @@ -5,13 +5,16 @@ #include "ReactNativeHost.h" #include "ReactNativeHost.g.cpp" +#include "Inspector/ReactInspectorThread.h" #include "ReactPackageBuilder.h" #include "RedBox.h" #include "TurboModulesProvider.h" #include +#include #include #include "IReactContext.h" +#include "ReactHost/DebuggerNotifications.h" #include "ReactInstanceSettings.h" #ifdef USE_FABRIC @@ -32,6 +35,51 @@ using namespace xaml::Controls; namespace winrt::Microsoft::ReactNative::implementation { +class ModernInspectorHostTargetDelegate : public facebook::react::jsinspector_modern::HostTargetDelegate, + public std::enable_shared_from_this { + public: + ModernInspectorHostTargetDelegate(winrt::weak_ref &&reactNativeHost) noexcept + : m_reactNativeHost(std::move(reactNativeHost)) {} + + facebook::react::jsinspector_modern::HostTargetMetadata getMetadata() override { + return { + .integrationName = "React Native Windows (Host)", + }; + } + + void onReload(facebook::react::jsinspector_modern::HostTargetDelegate::PageReloadRequest const &request) override { + if (auto reactNativeHost = m_reactNativeHost.get()) { + reactNativeHost.ReloadInstance(); + } + } + + void onSetPausedInDebuggerMessage( + facebook::react::jsinspector_modern::HostTargetDelegate::OverlaySetPausedInDebuggerMessageRequest const &request) + override { + if (auto reactNativeHost = m_reactNativeHost.get()) { + const auto instanceSettings = 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()) { + if (auto reactNativeHost = strongThis->m_reactNativeHost.get()) { + winrt::get_self(reactNativeHost)->OnDebuggerResume(); + } + } + }); + } else { + ::Microsoft::ReactNative::DebuggerNotifications::OnHideDebuggerPausedOverlay( + instanceSettings.Notifications()); + } + } + } + } + + private: + winrt::weak_ref m_reactNativeHost; +}; + ReactNativeHost::ReactNativeHost() noexcept : m_reactHost{Mso::React::MakeReactHost()} { #if _DEBUG facebook::react::InitializeLogging([](facebook::react::RCTLogLevel /*logLevel*/, const char *message) { @@ -39,6 +87,75 @@ ReactNativeHost::ReactNativeHost() noexcept : m_reactHost{Mso::React::MakeReactH OutputDebugStringA(str.c_str()); }); #endif + + auto &inspectorFlags = facebook::react::jsinspector_modern::InspectorFlags::getInstance(); + + // Debug fusebox flag + if (inspectorFlags.getFuseboxEnabled()) { + OutputDebugStringA("ReactNativeHost: Fusebox is ENABLED\n"); + } else { + OutputDebugStringA("ReactNativeHost: Fusebox is DISABLED - InspectorTarget will be null!\n"); + } + + // Debug inspector page ID + if (!m_inspectorPageId.has_value()) { + OutputDebugStringA("ReactNativeHost: No existing inspector page ID\n"); + } else { + OutputDebugStringA("ReactNativeHost: Inspector page ID already exists\n"); + } + + if (inspectorFlags.getFuseboxEnabled() && !m_inspectorPageId.has_value()) { + OutputDebugStringA("ReactNativeHost: Creating inspector target...\n"); + m_inspectorHostDelegate = std::make_shared(*this); + m_inspectorTarget = facebook::react::jsinspector_modern::HostTarget::create( + *m_inspectorHostDelegate, [](std::function &&callback) { + ::Microsoft::ReactNative::ReactInspectorThread::Instance().Post( + [callback = std::move(callback)]() { callback(); }); + }); + + if (m_inspectorTarget) { + OutputDebugStringA("ReactNativeHost: Inspector target created successfully\n"); + } else { + OutputDebugStringA("ReactNativeHost: Failed to create inspector target!\n"); + } + + std::weak_ptr weakInspectorTarget = m_inspectorTarget; + facebook::react::jsinspector_modern::InspectorTargetCapabilities capabilities; + capabilities.nativePageReloads = true; + capabilities.prefersFuseboxFrontend = true; + m_inspectorPageId = facebook::react::jsinspector_modern::getInspectorInstance().addPage( + "React Native Windows (Experimental)", + /* vm */ "", + [weakInspectorTarget](std::unique_ptr remote) + -> std::unique_ptr { + 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); + return inspectorTarget->connect(std::move(remote)); + } + + // This can happen if we're about to be dealloc'd. Reject the connection. + return nullptr; + }, + capabilities); + + if (m_inspectorPageId.has_value()) { + OutputDebugStringA("ReactNativeHost: Inspector page added successfully\n"); + } else { + OutputDebugStringA("ReactNativeHost: Failed to add inspector page\n"); + } + } else { + OutputDebugStringA("ReactNativeHost: Skipping inspector creation - conditions not met\n"); + } +} + +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( @@ -199,6 +316,7 @@ IAsyncAction ReactNativeHost::ReloadInstance() noexcept { } reactOptions.Identity = jsBundleFile; + reactOptions.InspectorTarget = m_inspectorTarget.get(); return make(m_reactHost->ReloadInstanceWithOptions(std::move(reactOptions))); } @@ -210,4 +328,13 @@ Mso::React::IReactHost *ReactNativeHost::ReactHost() noexcept { return m_reactHost.Get(); } +void ReactNativeHost::OnDebuggerResume() noexcept { + ::Microsoft::ReactNative::ReactInspectorThread::Instance().Post( + [weakInspectorTarget = std::weak_ptr(m_inspectorTarget)]() { + if (const auto inspectorTarget = weakInspectorTarget.lock()) { + inspectorTarget->sendCommand(facebook::react::jsinspector_modern::HostCommand::DebuggerResume); + } + }); +} + } // namespace winrt::Microsoft::ReactNative::implementation diff --git a/vnext/Microsoft.ReactNative/ReactNativeHost.h b/vnext/Microsoft.ReactNative/ReactNativeHost.h index d133e8bd883..a78894039a3 100644 --- a/vnext/Microsoft.ReactNative/ReactNativeHost.h +++ b/vnext/Microsoft.ReactNative/ReactNativeHost.h @@ -5,6 +5,7 @@ #include "ReactNativeHost.g.h" +#include #include "NativeModulesProvider.h" #include "ReactHost/React.h" #include "ReactInstanceSettings.h" @@ -16,6 +17,7 @@ namespace winrt::Microsoft::ReactNative::implementation { struct ReactNativeHost : ReactNativeHostT { public: // ReactNativeHost ABI API ReactNativeHost() noexcept; + ~ReactNativeHost() noexcept; static ReactNative::ReactNativeHost FromContext(ReactNative::IReactContext const &reactContext) noexcept; @@ -25,6 +27,7 @@ struct ReactNativeHost : ReactNativeHostT { // property InstanceSettings ReactNative::ReactInstanceSettings InstanceSettings() noexcept; void InstanceSettings(ReactNative::ReactInstanceSettings const &value) noexcept; + void OnDebuggerResume() noexcept; winrt::Windows::Foundation::IAsyncAction LoadInstance() noexcept; winrt::Windows::Foundation::IAsyncAction ReloadInstance() noexcept; @@ -39,6 +42,10 @@ struct ReactNativeHost : ReactNativeHostT { ReactNative::ReactInstanceSettings m_instanceSettings{nullptr}; ReactNative::IReactPackageBuilder m_packageBuilder; + + std::shared_ptr m_inspectorHostDelegate{nullptr}; + std::shared_ptr m_inspectorTarget{nullptr}; + std::optional m_inspectorPageId{std::nullopt}; }; } // namespace winrt::Microsoft::ReactNative::implementation diff --git a/vnext/Microsoft.ReactNative/ReactRootView.cpp b/vnext/Microsoft.ReactNative/ReactRootView.cpp index a697f76aa39..e04b0b813c2 100644 --- a/vnext/Microsoft.ReactNative/ReactRootView.cpp +++ b/vnext/Microsoft.ReactNative/ReactRootView.cpp @@ -5,14 +5,19 @@ #include "ReactRootView.g.cpp" #include +#include #include +#include +#include #include #include #include #include #include +#include "InstanceManager.h" #include "ReactNativeHost.h" #include "ReactViewInstance.h" +#include "Utils/KeyboardUtils.h" #include "XamlUtils.h" #include @@ -34,6 +39,7 @@ ReactRootView::ReactRootView() noexcept : m_uiQueue(Mso::DispatchQueue::GetCurre UpdatePerspective(); Loaded([this](auto &&, auto &&) { ::Microsoft::ReactNative::SetCompositor(::Microsoft::ReactNative::GetCompositor(*this)); + SetupDevToolsShortcut(); }); } @@ -45,6 +51,20 @@ void ReactRootView::ReactNativeHost(ReactNative::ReactNativeHost const &value) n if (m_reactNativeHost != value) { ReactViewHost(nullptr); m_reactNativeHost = value; + const auto weakThis = this->get_weak(); + ::Microsoft::ReactNative::DebuggerNotifications::SubscribeShowDebuggerPausedOverlay( + m_reactNativeHost.InstanceSettings().Notifications(), + m_reactNativeHost.InstanceSettings().UIDispatcher(), + [weakThis](std::string message, std::function onResume) { + if (auto strongThis = weakThis.get()) { + strongThis->ShowDebuggerPausedOverlay(message, onResume); + } + }, + [weakThis]() { + if (auto strongThis = weakThis.get()) { + strongThis->HideDebuggerPausedOverlay(); + } + }); ReloadView(); } } @@ -283,6 +303,65 @@ void ReactRootView::EnsureLoadingUI() noexcept { } } +void ReactRootView::HideDebuggerPausedOverlay() noexcept { + m_isDebuggerPausedOverlayOpen = false; + if (m_debuggerPausedFlyout) { + m_debuggerPausedFlyout.Hide(); + m_debuggerPausedFlyout = nullptr; + } +} + +void ReactRootView::ShowDebuggerPausedOverlay( + const std::string &message, + const std::function &onResume) noexcept { + // Initialize content + const xaml::Controls::Grid contentGrid; + xaml::Controls::ColumnDefinition messageColumnDefinition; + xaml::Controls::ColumnDefinition buttonColumnDefinition; + messageColumnDefinition.MinWidth(60); + buttonColumnDefinition.MinWidth(36); + contentGrid.ColumnDefinitions().Append(messageColumnDefinition); + contentGrid.ColumnDefinitions().Append(buttonColumnDefinition); + xaml::Controls::TextBlock messageBlock; + messageBlock.Text(winrt::to_hstring(message)); + messageBlock.FontWeight(winrt::Windows::UI::Text::FontWeights::SemiBold()); + xaml::Controls::FontIcon resumeGlyph; + resumeGlyph.FontFamily(xaml::Media::FontFamily(L"Segoe MDL2 Assets")); + resumeGlyph.Foreground(xaml::Media::SolidColorBrush(winrt::Colors::Green())); + resumeGlyph.Glyph(L"\uF5B0"); + resumeGlyph.HorizontalAlignment(xaml::HorizontalAlignment::Right); + resumeGlyph.PointerReleased([onResume](auto &&...) { onResume(); }); + xaml::Controls::Grid::SetColumn(resumeGlyph, 1); + contentGrid.Children().Append(messageBlock); + contentGrid.Children().Append(resumeGlyph); + + // Configure flyout + m_isDebuggerPausedOverlayOpen = true; + xaml::Style flyoutStyle( + {XAML_NAMESPACE_STR L".Controls.FlyoutPresenter", winrt::Windows::UI::Xaml::Interop::TypeKind::Metadata}); + flyoutStyle.Setters().Append(winrt::Setter( + xaml::Controls::Control::CornerRadiusProperty(), winrt::box_value(xaml::CornerRadius{12, 12, 12, 12}))); + flyoutStyle.Setters().Append(winrt::Setter( + xaml::Controls::Control::BackgroundProperty(), + winrt::box_value(xaml::Media::SolidColorBrush{FromArgb(255, 255, 255, 193)}))); + flyoutStyle.Setters().Append( + winrt::Setter(xaml::FrameworkElement::MarginProperty(), winrt::box_value(xaml::Thickness{0, 12, 0, 0}))); + m_debuggerPausedFlyout = xaml::Controls::Flyout{}; + m_debuggerPausedFlyout.FlyoutPresenterStyle(flyoutStyle); + m_debuggerPausedFlyout.LightDismissOverlayMode(xaml::Controls::LightDismissOverlayMode::On); + m_debuggerPausedFlyout.Content(contentGrid); + + // Disable light dismiss + m_debuggerPausedFlyout.Closing([weakThis = this->get_weak()](auto &&, const auto &args) { + if (auto strongThis = weakThis.get()) { + args.Cancel(strongThis->m_isDebuggerPausedOverlayOpen); + } + }); + + // Show flyout + m_debuggerPausedFlyout.ShowAt(*this); +} + void ReactRootView::ShowInstanceLoaded() noexcept { if (m_xamlRootView) { ClearLoadingUI(); @@ -481,4 +560,33 @@ void ReactRootView::RemoveChildAt(uint32_t index) { Children().RemoveAt(RNIndexToXamlIndex(index)); } +bool IsCtrlShiftI(winrt::Windows::System::VirtualKey key) noexcept { + return ( + key == winrt::Windows::System::VirtualKey::I && + ::Microsoft::ReactNative::IsModifiedKeyPressed( + winrt::CoreWindow::GetForCurrentThread(), winrt::Windows::System::VirtualKey::Shift) && + ::Microsoft::ReactNative::IsModifiedKeyPressed( + winrt::CoreWindow::GetForCurrentThread(), winrt::Windows::System::VirtualKey::Control)); +} + +void ReactRootView::SetupDevToolsShortcut() noexcept { + if (auto xamlRoot = XamlRoot()) { + if (std::find(m_subscribedDebuggerRoots.begin(), m_subscribedDebuggerRoots.end(), xamlRoot) == + m_subscribedDebuggerRoots.end()) { + if (auto rootContent = xamlRoot.Content()) { + m_subscribedDebuggerRoots.push_back(xamlRoot); + rootContent.KeyDown( + [weakThis = this->get_weak()](const auto & /*sender*/, const xaml::Input::KeyRoutedEventArgs &args) { + if (const auto strongThis = weakThis.get()) { + if (IsCtrlShiftI(args.Key())) { + ::Microsoft::ReactNative::GetSharedDevManager()->OpenDevTools( + winrt::to_string(strongThis->m_reactNativeHost.InstanceSettings().BundleAppId())); + } + }; + }); + } + } + } +} + } // namespace winrt::Microsoft::ReactNative::implementation diff --git a/vnext/Microsoft.ReactNative/ReactRootView.h b/vnext/Microsoft.ReactNative/ReactRootView.h index f35a3c3fd60..876ac7e38cb 100644 --- a/vnext/Microsoft.ReactNative/ReactRootView.h +++ b/vnext/Microsoft.ReactNative/ReactRootView.h @@ -72,6 +72,7 @@ struct ReactRootView : ReactRootViewT, ::Microsoft::ReactNative:: bool m_isPerspectiveEnabled{true}; bool m_isInitialized{false}; bool m_isJSViewAttached{false}; + bool m_isDebuggerPausedOverlayOpen{false}; Mso::DispatchQueue m_uiQueue; int64_t m_rootTag{-1}; std::unique_ptr m_reactOptions; @@ -84,9 +85,11 @@ struct ReactRootView : ReactRootViewT, ::Microsoft::ReactNative:: std::shared_ptr<::Microsoft::ReactNative::PreviewKeyboardEventHandlerOnRoot> m_previewKeyboardEventHandlerOnRoot; xaml::Controls::ContentControl m_focusSafeHarbor{nullptr}; xaml::Controls::ContentControl::LosingFocus_revoker m_focusSafeHarborLosingFocusRevoker{}; + xaml::Controls::Flyout m_debuggerPausedFlyout{nullptr}; winrt::Grid m_greenBoxGrid{nullptr}; winrt::TextBlock m_waitingTextBlock{nullptr}; winrt::SystemNavigationManager::BackRequested_revoker m_backRequestedRevoker{}; + std::vector m_subscribedDebuggerRoots{}; // Visual tree to support safe harbor // this @@ -102,6 +105,8 @@ struct ReactRootView : ReactRootViewT, ::Microsoft::ReactNative:: void UpdateRootViewInternal() noexcept; void ClearLoadingUI() noexcept; void EnsureLoadingUI() noexcept; + void HideDebuggerPausedOverlay() noexcept; + void ShowDebuggerPausedOverlay(const std::string &message, const std::function &onResume) noexcept; void ShowInstanceLoaded() noexcept; void ShowInstanceError() noexcept; void ShowInstanceWaiting() noexcept; @@ -112,6 +117,7 @@ struct ReactRootView : ReactRootViewT, ::Microsoft::ReactNative:: bool OnBackRequested() noexcept; Mso::React::IReactViewHost *ReactViewHost() noexcept; void ReactViewHost(Mso::React::IReactViewHost *viewHost) noexcept; + void SetupDevToolsShortcut() noexcept; }; } // namespace winrt::Microsoft::ReactNative::implementation diff --git a/vnext/Microsoft.ReactNative/Views/DevMenu.cpp b/vnext/Microsoft.ReactNative/Views/DevMenu.cpp index 2d59627e885..38d1f17fcbd 100644 --- a/vnext/Microsoft.ReactNative/Views/DevMenu.cpp +++ b/vnext/Microsoft.ReactNative/Views/DevMenu.cpp @@ -6,7 +6,7 @@ #include "DevMenu.h" #include -#include "HermesSamplingProfiler.h" +#include "Hermes/HermesSamplingProfiler.h" #include "IReactDispatcher.h" #include "Modules/DevSettingsModule.h" diff --git a/vnext/ReactCommon/ReactCommon.vcxproj b/vnext/ReactCommon/ReactCommon.vcxproj index eb7c536fd5b..e7093f1aef1 100644 --- a/vnext/ReactCommon/ReactCommon.vcxproj +++ b/vnext/ReactCommon/ReactCommon.vcxproj @@ -114,6 +114,13 @@ + + + + + + + @@ -145,6 +152,13 @@ + + + + + + + diff --git a/vnext/ReactCommon/TEMP_UntilReactCommonUpdate/jsinspector-modern/NetworkIOAgent.cpp b/vnext/ReactCommon/TEMP_UntilReactCommonUpdate/jsinspector-modern/NetworkIOAgent.cpp index 744a44d7aee..4791504d6c7 100644 --- a/vnext/ReactCommon/TEMP_UntilReactCommonUpdate/jsinspector-modern/NetworkIOAgent.cpp +++ b/vnext/ReactCommon/TEMP_UntilReactCommonUpdate/jsinspector-modern/NetworkIOAgent.cpp @@ -471,4 +471,4 @@ void NetworkIOAgent::handleIoClose(const cdp::PreparsedRequest& req) { } } -} // namespace facebook::react::jsinspector_modern +} // namespace facebook::react::jsinspector_modern \ No newline at end of file diff --git a/vnext/ReactCommon/TEMP_UntilReactCommonUpdate/jsinspector-modern/NetworkIOAgent.h b/vnext/ReactCommon/TEMP_UntilReactCommonUpdate/jsinspector-modern/NetworkIOAgent.h index bfda911715e..050fc94a322 100644 --- a/vnext/ReactCommon/TEMP_UntilReactCommonUpdate/jsinspector-modern/NetworkIOAgent.h +++ b/vnext/ReactCommon/TEMP_UntilReactCommonUpdate/jsinspector-modern/NetworkIOAgent.h @@ -261,4 +261,4 @@ class NetworkIOAgent { void handleIoClose(const cdp::PreparsedRequest& req); }; -} // namespace facebook::react::jsinspector_modern +} // namespace facebook::react::jsinspector_modern \ No newline at end of file diff --git a/vnext/ReactCommon/TEMP_UntilReactCommonUpdate/jsitooling/react/runtime/JSRuntimeFactory.cpp b/vnext/ReactCommon/TEMP_UntilReactCommonUpdate/jsitooling/react/runtime/JSRuntimeFactory.cpp index c75d6d10787..550e3bfbab1 100644 --- a/vnext/ReactCommon/TEMP_UntilReactCommonUpdate/jsitooling/react/runtime/JSRuntimeFactory.cpp +++ b/vnext/ReactCommon/TEMP_UntilReactCommonUpdate/jsitooling/react/runtime/JSRuntimeFactory.cpp @@ -42,4 +42,12 @@ JSIRuntimeHolder::createAgentDelegate( std::move(frontendChannel), sessionState, runtime_->description()); } +//windows base implementation needed as getRuntimeTargetDelegate is not pure virtual +jsinspector_modern::RuntimeTargetDelegate& JSRuntime::getRuntimeTargetDelegate() { + if (!runtimeTargetDelegate_) { + runtimeTargetDelegate_.emplace("JSRuntime"); + } + return *runtimeTargetDelegate_; +} + } // namespace facebook::react \ No newline at end of file diff --git a/vnext/ReactCommon/TEMP_UntilReactCommonUpdate/jsitooling/react/runtime/JSRuntimeFactory.h b/vnext/ReactCommon/TEMP_UntilReactCommonUpdate/jsitooling/react/runtime/JSRuntimeFactory.h index 4f87670497c..585c3c4901f 100644 --- a/vnext/ReactCommon/TEMP_UntilReactCommonUpdate/jsitooling/react/runtime/JSRuntimeFactory.h +++ b/vnext/ReactCommon/TEMP_UntilReactCommonUpdate/jsitooling/react/runtime/JSRuntimeFactory.h @@ -32,7 +32,7 @@ class JSRuntime : public jsinspector_modern::RuntimeTargetDelegate { * this JSRuntime. This reference must remain valid for the duration of the * JSRuntime's lifetime. */ - // virtual jsinspector_modern::RuntimeTargetDelegate& getRuntimeTargetDelegate(); + virtual jsinspector_modern::RuntimeTargetDelegate& getRuntimeTargetDelegate(); /** * Run initialize work that must happen on the runtime's JS thread. Used for diff --git a/vnext/Shared/DevServerHelper.h b/vnext/Shared/DevServerHelper.h index 384a7619810..9fc4cbcf4d9 100644 --- a/vnext/Shared/DevServerHelper.h +++ b/vnext/Shared/DevServerHelper.h @@ -76,12 +76,20 @@ class DevServerHelper { const std::string &packagerHost, const uint16_t packagerPort, const std::string &deviceName, - const std::string &packageName) { + const std::string &packageName, + const std::string &deviceId) { return string_format( InspectorDeviceUrlFormat, GetDeviceLocalHost(packagerHost, packagerPort).c_str(), deviceName.c_str(), - packageName.c_str()); + packageName.c_str(), + deviceId.c_str()); + } + + static std::string + get_OpenDebuggerUrl(const std::string &packagerHost, const uint16_t packagerPort, const std::string &deviceId) { + return string_format( + OpenDebuggerUrlFormat, GetDeviceLocalHost(packagerHost, packagerPort).c_str(), deviceId.c_str()); } static constexpr const char DefaultPackagerHost[] = "localhost"; @@ -105,11 +113,13 @@ class DevServerHelper { static constexpr const char PackagerConnectionUrlFormat[] = "ws://%s/message"; static constexpr const char PackagerStatusUrlFormat[] = "http://%s/status"; static constexpr const char PackagerOpenStackFrameUrlFormat[] = "https://%s/open-stack-frame"; - static constexpr const char InspectorDeviceUrlFormat[] = "ws://%s/inspector/device?name=%s&app=%s"; + static constexpr const char InspectorDeviceUrlFormat[] = "ws://%s/inspector/device?name=%s&app=%s&device=%s"; + static constexpr const char OpenDebuggerUrlFormat[] = "http://%s/open-debugger?device=%s"; static constexpr const char PackagerOkStatus[] = "packager-status:running"; const int LongPollFailureDelayMs = 5000; + // TODO: [vmoroz] avoid using vaiadic args for the format and move it to a utility class. template static std::string string_format(const char *format, Args... args) { size_t size = snprintf(nullptr, 0, format, args...) + 1; diff --git a/vnext/Shared/DevSettings.h b/vnext/Shared/DevSettings.h index b6ddcb0745e..face6c26689 100644 --- a/vnext/Shared/DevSettings.h +++ b/vnext/Shared/DevSettings.h @@ -23,6 +23,10 @@ struct RuntimeHolderLazyInit; namespace facebook { namespace react { +namespace jsinspector_modern { +class HostTarget; +} // namespace jsinspector_modern + enum class JSIEngineOverride : int32_t { Default = 0, // No JSI, will use the legacy ExecutorFactory Chakra = 1, // Use the JSIExecutorFactory with ChakraRuntime @@ -117,6 +121,9 @@ struct DevSettings { // If true, then use only Turbo Modules instead of CxxModules. bool useTurboModulesOnly{false}; + + // The HostTarget instance for Fusebox + facebook::react::jsinspector_modern::HostTarget *inspectorTarget; }; } // namespace react diff --git a/vnext/Shared/DevSupportManager.cpp b/vnext/Shared/DevSupportManager.cpp index a6935bb8aa9..518f8dfc1be 100644 --- a/vnext/Shared/DevSupportManager.cpp +++ b/vnext/Shared/DevSupportManager.cpp @@ -9,14 +9,19 @@ #include #include +#include "Inspector/ReactInspectorPackagerConnectionDelegate.h" #include "PackagerConnection.h" #include "Unicode.h" #include "Utilities.h" #include +#include #include +#include +#include #include +#include #include #include #include @@ -171,6 +176,49 @@ bool IsIgnorablePollHResult(HRESULT hr) { return hr == WININET_E_INVALID_SERVER_RESPONSE; } +std::string GetDeviceId(const std::string &packageName) { + const auto hash = winrt::Windows::Security::Cryptography::Core::HashAlgorithmProvider::OpenAlgorithm( + winrt::Windows::Security::Cryptography::Core::HashAlgorithmNames::Sha256()) + .CreateHash(); + hash.Append(winrt::Windows::System::Profile::SystemIdentification::GetSystemIdForPublisher().Id()); + winrt::Windows::Storage::Streams::InMemoryRandomAccessStream stream; + winrt::Windows::Storage::Streams::DataWriter writer; + // If an app ID is provided, we will allow reconnection to DevTools. + // Apps must supply a unique app ID to each ReactNativeHost instance settings for this to behave correctly. + if (!packageName.empty()) { + const auto packageNameBuffer = winrt::Windows::Security::Cryptography::CryptographicBuffer::ConvertStringToBinary( + winrt::to_hstring(packageName), winrt::Windows::Security::Cryptography::BinaryStringEncoding::Utf16BE); + hash.Append(packageNameBuffer); + } else { + const auto processId = GetCurrentProcessId(); + std::vector processIdBytes( + reinterpret_cast(&processId), reinterpret_cast(&processId + 1)); + winrt::array_view processIdByteArray(processIdBytes); + const auto processIdBuffer = + winrt::Windows::Security::Cryptography::CryptographicBuffer::CreateFromByteArray(processIdByteArray); + hash.Append(processIdBuffer); + } + const auto hashBuffer = hash.GetValueAndReset(); + const auto hashString = winrt::Windows::Security::Cryptography::CryptographicBuffer::EncodeToHexString(hashBuffer); + return winrt::to_string(hashString); +} + +std::string GetPackageName(const std::string &bundleAppId) { + if (!bundleAppId.empty()) { + return bundleAppId; + } + + std::string packageName{"RNW"}; + wchar_t fullName[PACKAGE_FULL_NAME_MAX_LENGTH]{}; + uint32_t size = ARRAYSIZE(fullName); + if (SUCCEEDED(GetCurrentPackageFullName(&size, fullName))) { + // we are in an unpackaged app + packageName = winrt::to_string(fullName); + } + + return packageName; +} + std::future PollForLiveReload(const std::string &url) { winrt::Windows::Web::Http::HttpClient httpClient; winrt::Windows::Foundation::Uri uri(Microsoft::Common::Unicode::Utf8ToUtf16(url)); @@ -238,30 +286,50 @@ void DevSupportManager::StopPollingLiveReload() { m_cancellation_token = true; } +void DevSupportManager::OpenDevTools(const std::string &bundleAppId) { + winrt::Windows::Web::Http::Filters::HttpBaseProtocolFilter filter; + filter.CacheControl().ReadBehavior(winrt::Windows::Web::Http::Filters::HttpCacheReadBehavior::NoCache); + winrt::Windows::Web::Http::HttpClient httpClient(filter); + // TODO: Use currently configured dev server host + winrt::Windows::Foundation::Uri uri( + Microsoft::Common::Unicode::Utf8ToUtf16(facebook::react::DevServerHelper::get_OpenDebuggerUrl( + std::string{DevServerHelper::DefaultPackagerHost}, + DevServerHelper::DefaultPackagerPort, + GetDeviceId(GetPackageName(bundleAppId))))); + + winrt::Windows::Web::Http::HttpRequestMessage request(winrt::Windows::Web::Http::HttpMethod::Post(), uri); + httpClient.SendRequestAsync(request); +} + void DevSupportManager::EnsureHermesInspector( [[maybe_unused]] const std::string &packagerHost, - [[maybe_unused]] const uint16_t packagerPort) noexcept { + [[maybe_unused]] const uint16_t packagerPort, + [[maybe_unused]] const std::string &bundleAppId) noexcept { static std::once_flag once; - std::call_once(once, [this, &packagerHost, packagerPort]() { - // TODO: should we use the bundleAppId as the app param if available? - std::string packageName("RNW"); - wchar_t fullName[PACKAGE_FULL_NAME_MAX_LENGTH]{}; - UINT32 size = ARRAYSIZE(fullName); - if (SUCCEEDED(GetCurrentPackageFullName(&size, fullName))) { - // we are in an unpackaged app - packageName = winrt::to_string(fullName); - } - + std::call_once(once, [this, &packagerHost, packagerPort, &bundleAppId]() { + std::string packageName = GetPackageName(bundleAppId); std::string deviceName("RNWHost"); auto hostNames = winrt::Windows::Networking::Connectivity::NetworkInformation::GetHostNames(); if (hostNames && hostNames.First() && hostNames.First().Current()) { deviceName = winrt::to_string(hostNames.First().Current().DisplayName()); } - m_inspectorPackagerConnection = std::make_shared( - facebook::react::DevServerHelper::get_InspectorDeviceUrl(packagerHost, packagerPort, deviceName, packageName), - m_BundleStatusProvider); - m_inspectorPackagerConnection->connectAsync(); + const auto deviceId = GetDeviceId(packageName); + auto inspectorUrl = facebook::react::DevServerHelper::get_InspectorDeviceUrl( + packagerHost, packagerPort, deviceName, packageName, deviceId); + auto &inspectorFlags = jsinspector_modern::InspectorFlags::getInstance(); + if (inspectorFlags.getFuseboxEnabled()) { + m_fuseboxInspectorPackagerConnection = std::make_unique( + inspectorUrl, + deviceName, + packageName, + std::make_unique()); + m_fuseboxInspectorPackagerConnection->connect(); + } else { + m_inspectorPackagerConnection = + std::make_shared(std::move(inspectorUrl), m_BundleStatusProvider); + m_inspectorPackagerConnection->connectAsync(); + } }); } diff --git a/vnext/Shared/DevSupportManager.h b/vnext/Shared/DevSupportManager.h index 5c33ed1de02..271f81e2129 100644 --- a/vnext/Shared/DevSupportManager.h +++ b/vnext/Shared/DevSupportManager.h @@ -14,7 +14,8 @@ #include #include -#include +#include +#include namespace facebook { namespace react { @@ -48,14 +49,20 @@ class DevSupportManager final : public facebook::react::IDevSupportManager { const uint16_t sourceBundlePort, std::function onChangeCallback) override; virtual void StopPollingLiveReload() override; + virtual void OpenDevTools(const std::string &bundleAppId) override; - virtual void EnsureHermesInspector(const std::string &packagerHost, const uint16_t packagerPort) noexcept override; + virtual void EnsureHermesInspector( + const std::string &packagerHost, + const uint16_t packagerPort, + const std::string &bundleAppId) noexcept override; virtual void UpdateBundleStatus(bool isLastDownloadSuccess, int64_t updateTimestamp) noexcept override; private: std::atomic_bool m_cancellation_token; std::shared_ptr m_inspectorPackagerConnection; + std::unique_ptr + m_fuseboxInspectorPackagerConnection; struct BundleStatusProvider : public InspectorPackagerConnection::IBundleStatusProvider { virtual InspectorPackagerConnection::BundleStatus getBundleStatus() { diff --git a/vnext/Shared/Hermes/HermesRuntimeAgentDelegate.cpp b/vnext/Shared/Hermes/HermesRuntimeAgentDelegate.cpp new file mode 100644 index 00000000000..c8b00ef3960 --- /dev/null +++ b/vnext/Shared/Hermes/HermesRuntimeAgentDelegate.cpp @@ -0,0 +1,87 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#include "HermesRuntimeAgentDelegate.h" + +#include + +using namespace facebook::react::jsinspector_modern; +using namespace facebook::hermes; + +namespace Microsoft::ReactNative { + +namespace { + +struct HermesStateWrapper : public RuntimeAgentDelegate::ExportedState { + explicit HermesStateWrapper(HermesUniqueCdpState &&hermesCdpState) : hermesCdpState_(std::move(hermesCdpState)) {} + + static HermesUniqueCdpState unwrapDestructively(ExportedState *wrapper) { + if (!wrapper) { + return {}; + } + if (auto *typedWrapper = dynamic_cast(wrapper)) { + return std::move(typedWrapper->hermesCdpState_); + } + return {}; + } + + private: + HermesUniqueCdpState hermesCdpState_; +}; + +} // namespace + +HermesRuntimeAgentDelegate::HermesRuntimeAgentDelegate( + FrontendChannel frontendChannel, + SessionState &sessionState, + std::unique_ptr previouslyExportedState, + const ExecutionContextDescription &executionContextDescription, + hermes_runtime runtime, + HermesRuntimeTargetDelegate &runtimeTargetDelegate, + facebook::react::RuntimeExecutor runtimeExecutor) + : hermesCdpAgent_(HermesApi2().createCdpAgent( + runtimeTargetDelegate.getCdpDebugger(), + executionContextDescription.id, + // RuntimeTask takes a HermesRuntime whereas our RuntimeExecutor takes a jsi::Runtime. + AsFunctor([runtimeExecutor = std::move(runtimeExecutor), + runtime](hermes_runtime_task_functor runtimeTask) { + runtimeExecutor([runtime, fn = std::make_shared>(runtimeTask)]( + auto &) { (*fn)(runtime); }); + }), + AsFunctor( + [frontendChannel = std::move(frontendChannel)](const char *json_utf8, size_t json_size) { + frontendChannel(std::string_view(json_utf8, json_size)); + }), + HermesStateWrapper::unwrapDestructively(previouslyExportedState.get()).release())) { + // Always enable both domains for debugging + // TODO: move enableRuntimeDomain and enableDebuggerDomain for conditional handling + HermesApi2().enableRuntimeDomain(hermesCdpAgent_.get()); + HermesApi2().enableDebuggerDomain(hermesCdpAgent_.get()); + + // TODO: find isRuntimeDomainEnabled and isDebuggerDomainEnabled why not enabled + if (sessionState.isRuntimeDomainEnabled) { + OutputDebugStringA("[RNW] SessionState: Runtime domain was already enabled\n"); + } + if (sessionState.isDebuggerDomainEnabled) { + OutputDebugStringA("[RNW] SessionState: Debugger domain was already enabled\n"); + } +} + +HermesRuntimeAgentDelegate::~HermesRuntimeAgentDelegate() = default; + +bool HermesRuntimeAgentDelegate::handleRequest(const cdp::PreparsedRequest &req) { + if (req.method.starts_with("Log.")) { + // Hermes doesn't do anything useful with Log messages. + return false; + } + + std::string json = req.toJson(); + HermesApi2().handleCommand(hermesCdpAgent_.get(), json.c_str(), json.size()); + return true; +} + +std::unique_ptr HermesRuntimeAgentDelegate::getExportedState() { + return std::make_unique(HermesApi2().getCdpState(hermesCdpAgent_.get())); +} + +} // namespace Microsoft::ReactNative diff --git a/vnext/Shared/Hermes/HermesRuntimeAgentDelegate.h b/vnext/Shared/Hermes/HermesRuntimeAgentDelegate.h new file mode 100644 index 00000000000..df215bb90fa --- /dev/null +++ b/vnext/Shared/Hermes/HermesRuntimeAgentDelegate.h @@ -0,0 +1,37 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#pragma once + +#include "HermesRuntimeTargetDelegate.h" + +#include +#include +#include "HermesRuntimeHolder.h" + +namespace Microsoft::ReactNative { + +// A RuntimeAgentDelegate that handles requests from the Chrome DevTools +// Protocol for an instance of Hermes, using the modern CDPAgent API. +class HermesRuntimeAgentDelegate : public facebook::react::jsinspector_modern::RuntimeAgentDelegate { + public: + HermesRuntimeAgentDelegate( + facebook::react::jsinspector_modern::FrontendChannel frontendChannel, + facebook::react::jsinspector_modern::SessionState &sessionState, + std::unique_ptr previouslyExportedState, + const facebook::react::jsinspector_modern::ExecutionContextDescription &executionContextDescription, + hermes_runtime runtime, + HermesRuntimeTargetDelegate &runtimeTargetDelegate, + facebook::react::RuntimeExecutor runtimeExecutor); + + ~HermesRuntimeAgentDelegate() override; + + public: // RuntimeAgentDelegate implementation + bool handleRequest(const facebook::react::jsinspector_modern::cdp::PreparsedRequest &req) override; + std::unique_ptr getExportedState() override; + + private: + facebook::hermes::HermesUniqueCdpAgent hermesCdpAgent_; +}; + +} // namespace Microsoft::ReactNative diff --git a/vnext/Shared/HermesRuntimeHolder.cpp b/vnext/Shared/Hermes/HermesRuntimeHolder.cpp similarity index 93% rename from vnext/Shared/HermesRuntimeHolder.cpp rename to vnext/Shared/Hermes/HermesRuntimeHolder.cpp index 772b4fb6ba1..8477794ca7a 100644 --- a/vnext/Shared/HermesRuntimeHolder.cpp +++ b/vnext/Shared/Hermes/HermesRuntimeHolder.cpp @@ -15,7 +15,9 @@ #include #include #include +#include #include "SafeLoadLibrary.h" +#include "HermesRuntimeTargetDelegate.h" #define CRASH_ON_ERROR(result) VerifyElseCrash(result == napi_ok); @@ -364,6 +366,22 @@ std::shared_ptr HermesRuntimeHolder::getRuntime() noexce return m_jsiRuntime; } +const std::shared_ptr & +HermesRuntimeHolder::getSharedRuntimeTargetDelegate() { + if (!m_targetDelegate) { + hermes_api_vtable vtable = facebook::hermes::getHermesApiVTable(); + + if (vtable != nullptr) { + OutputDebugStringA("HermesRuntimeHolder: Creating CDP target delegate\n"); + auto shared_this = shared_from_this(); + m_targetDelegate = std::shared_ptr( + new Microsoft::ReactNative::HermesRuntimeTargetDelegate(shared_this)); + } + } + + return m_targetDelegate; +} + void HermesRuntimeHolder::crashHandler(int fileDescriptor) noexcept { CRASH_ON_ERROR(getHermesApi().hermes_dump_crash_data(m_runtime, fileDescriptor)); } @@ -403,6 +421,11 @@ void HermesRuntimeHolder::removeFromProfiling() const noexcept { CRASH_ON_ERROR(getHermesApi().hermes_sampling_profiler_dump_to_file(fileName.c_str())); } +hermes_runtime HermesRuntimeHolder::getHermesRuntime() noexcept { + // TODO: (@vmoroz) Implement + return nullptr; +} + //============================================================================== // HermesJSRuntime implementation //============================================================================== @@ -414,6 +437,11 @@ facebook::jsi::Runtime &HermesJSRuntime::getRuntime() noexcept { return *m_holder->getRuntime(); } +facebook::react::jsinspector_modern::RuntimeTargetDelegate &HermesJSRuntime::getRuntimeTargetDelegate() { + auto delegate = m_holder->getSharedRuntimeTargetDelegate(); + return *delegate; +} + void HermesJSRuntime::addConsoleMessage( facebook::jsi::Runtime &runtime, facebook::react::jsinspector_modern::ConsoleMessage message) { @@ -449,9 +477,13 @@ std::unique_ptr Herme std::unique_ptr previouslyExportedState, const facebook::react::jsinspector_modern::ExecutionContextDescription &executionContextDescription, facebook::react::RuntimeExecutor runtimeExecutor) { - (void)frontendChannel; - (void)sessionState; - return nullptr; + auto& targetDelegate = getRuntimeTargetDelegate(); + return targetDelegate.createAgentDelegate( + frontendChannel, + sessionState, + std::move(previouslyExportedState), + executionContextDescription, + runtimeExecutor); } } // namespace Microsoft::ReactNative diff --git a/vnext/Shared/Hermes/HermesRuntimeHolder.h b/vnext/Shared/Hermes/HermesRuntimeHolder.h new file mode 100644 index 00000000000..11e25e0f76b --- /dev/null +++ b/vnext/Shared/Hermes/HermesRuntimeHolder.h @@ -0,0 +1,315 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#pragma once +#include +#include +#include +#include +#include +#include +#include + +namespace facebook::hermes { + +// Direct DLL loading implementation that bypasses broken delay loading +inline hermes_api_vtable getHermesApiVTable() { + static hermes_api_vtable vtable = nullptr; + static bool initialization_attempted = false; + + if (!vtable && !initialization_attempted) { + initialization_attempted = true; + // TODO: dont call .dll twice + HMODULE hermesModule = LoadLibraryW(L"hermes.dll"); + // check where are we calling from + typedef napi_status (*hermes_get_cdp_vtable_func)(hermes_api_vtable *); + hermes_get_cdp_vtable_func getCdpVtable = + (hermes_get_cdp_vtable_func)GetProcAddress(hermesModule, "hermes_get_cdp_vtable"); + + if (getCdpVtable) { + napi_status status = getCdpVtable(&vtable); + if (status == napi_ok && vtable) { + OutputDebugStringA("[RNW] getHermesApiVTable() - CDP vtable loaded successfully!\n"); + } else { + OutputDebugStringA("[RNW] getHermesApiVTable() - Failed to get CDP vtable\n"); + vtable = nullptr; + } + } + } + + return vtable; +} + +template +struct FunctorAdapter { + static_assert(sizeof(TLambda) == -1, "Unsupported signature"); +}; + +template +struct FunctorAdapter { + static TResult Invoke(void *data, TArgs... args) { + return reinterpret_cast(data)->operator()(args...); + } +}; + +template +inline TFunctor AsFunctor(TLambda &&lambda) { + using TLambdaType = std::remove_reference_t; + using TAdapter = + FunctorAdapter::invoke)>>; + return TFunctor{ + static_cast(new TLambdaType(std::forward(lambda))), &TAdapter::Invoke, [](void *data) { + delete static_cast(data); + }}; +} + +template +struct FunctorWrapperBase { + static_assert(sizeof(TInvoke) == -1, "Unsupported signature"); +}; + +template +struct FunctorWrapperBase { + FunctorWrapperBase(TFunctor functor) : functor_(functor) {} + + ~FunctorWrapperBase() { + if (functor_.release != nullptr) { + functor_.release(functor_.data); + } + } + + TResult operator()(TArgs... args) { + return functor_.invoke(functor_.data, args...); + } + + private: + TFunctor functor_; +}; + +template +struct FunctorWrapper : FunctorWrapperBase().invoke)> { + using FunctorWrapperBase().invoke)>::FunctorWrapperBase; +}; + +struct HermesCdpDebuggerDeleter { + void operator()(hermes_cdp_debugger cdp_debugger) { + // Skip deletion for stub/dummy values to prevent crashes + if (cdp_debugger == nullptr || + cdp_debugger == (hermes_cdp_debugger)0x1 || + cdp_debugger == (hermes_cdp_debugger)0x2) { + return; + } + + auto vtable = getHermesApiVTable(); + if (vtable) { + vtable->release_cdp_debugger(cdp_debugger); + } + } +}; + +struct HermesCdpAgentDeleter { + void operator()(hermes_cdp_agent cdp_agent) { + // Skip deletion for stub/dummy values to prevent crashes + if (cdp_agent == nullptr || + cdp_agent == (hermes_cdp_agent)0x1 || + cdp_agent == (hermes_cdp_agent)0x2) { + return; + } + + auto vtable = getHermesApiVTable(); + if (vtable) { + vtable->release_cdp_agent(cdp_agent); + } + } +}; + +struct HermesCdpStateDeleter { + void operator()(hermes_cdp_state cdp_state) { + // Skip deletion for stub/dummy values to prevent crashes + if (cdp_state == nullptr || + cdp_state == (hermes_cdp_state)0x1 || + cdp_state == (hermes_cdp_state)0x2) { + return; + } + + auto vtable = getHermesApiVTable(); + if (vtable) { + vtable->release_cdp_state(cdp_state); + } + } +}; + +struct HermesStackTraceDeleter { + void operator()(hermes_stack_trace stack_trace) { + // Skip deletion for stub/dummy values to prevent crashes + if (stack_trace == nullptr || + stack_trace == (hermes_stack_trace)0x1 || + stack_trace == (hermes_stack_trace)0x2) { + return; + } + + auto vtable = getHermesApiVTable(); + if (vtable) { + vtable->release_stack_trace(stack_trace); + } + } +}; + +using HermesUniqueCdpDebugger = std::unique_ptr; +using HermesUniqueCdpAgent = std::unique_ptr; +using HermesUniqueCdpState = std::unique_ptr; +using HermesUniqueStackTrace = std::unique_ptr; + +struct HermesApi2 { + HermesApi2() = default; + + void checkStatus(hermes_status status) { + if (status != hermes_status_ok) { + throw std::runtime_error("Hermes API call failed"); + } + } + + HermesUniqueCdpDebugger createCdpDebugger(hermes_runtime runtime) { + hermes_cdp_debugger cdp_debugger{}; + checkStatus(vtable->create_cdp_debugger(runtime, &cdp_debugger)); + return HermesUniqueCdpDebugger{cdp_debugger}; + } + + HermesUniqueCdpAgent createCdpAgent( + hermes_cdp_debugger cdpDebugger, + int32_t execitionContextId, + hermes_enqueue_runtime_task_functor enqueueRuntimeTaskCallback, + hermes_enqueue_frontend_message_functor enqueueFrontendMessageCallback, + hermes_cdp_state cdp_state) { + hermes_cdp_agent cdp_agent{}; + checkStatus(vtable->create_cdp_agent( + cdpDebugger, + execitionContextId, + enqueueRuntimeTaskCallback, + enqueueFrontendMessageCallback, + cdp_state, + &cdp_agent)); + return HermesUniqueCdpAgent{cdp_agent}; + } + + HermesUniqueCdpState getCdpState(hermes_cdp_agent cdp_agent) { + hermes_cdp_state cdp_state{}; + checkStatus(vtable->get_cdp_state(cdp_agent, &cdp_state)); + return HermesUniqueCdpState{cdp_state}; + } + + HermesUniqueStackTrace captureStackTrace(hermes_runtime runtime) { + hermes_stack_trace stack_trace{}; + checkStatus(vtable->capture_stack_trace(runtime, &stack_trace)); + return HermesUniqueStackTrace{stack_trace}; + } + + void handleCommand(hermes_cdp_agent cdpAgent, const char *jsonUtf8, size_t jsonSize) { + checkStatus(vtable->cdp_agent_handle_command(cdpAgent, jsonUtf8, jsonSize)); + } + + void enableRuntimeDomain(hermes_cdp_agent cdpAgent) { + checkStatus(vtable->cdp_agent_enable_runtime_domain(cdpAgent)); + } + + void enableDebuggerDomain(hermes_cdp_agent cdpAgent) { + checkStatus(vtable->cdp_agent_enable_debugger_domain(cdpAgent)); + } + + private: + hermes_api_vtable vtable{getHermesApiVTable()}; + +}; + +} // namespace facebook::hermes + +namespace Microsoft::ReactNative { + +class HermesRuntimeHolder : public Microsoft::JSI::RuntimeHolderLazyInit, public std::enable_shared_from_this { + public: // RuntimeHolderLazyInit implementation. + std::shared_ptr getRuntime() noexcept override; + facebook::react::JSIEngineOverride getRuntimeType() noexcept override; + void crashHandler(int fileDescriptor) noexcept override; + void teardown() noexcept override; + + public: + HermesRuntimeHolder( + std::shared_ptr devSettings, + std::shared_ptr jsQueue, + std::shared_ptr preparedScriptStore) noexcept; + ~HermesRuntimeHolder(); + + const std::shared_ptr &getSharedRuntimeTargetDelegate() + override; + + static std::shared_ptr loadFrom( + winrt::Microsoft::ReactNative::ReactPropertyBag const &propertyBag) noexcept; + + static void storeTo( + winrt::Microsoft::ReactNative::ReactPropertyBag const &propertyBag, + std::shared_ptr const &holder) noexcept; + + void addToProfiling() const noexcept; + void removeFromProfiling() const noexcept; + + static void enableSamplingProfiler() noexcept; + static void disableSamplingProfiler() noexcept; + static void dumpSampledTraceToFile(const std::string &fileName) noexcept; + + hermes_runtime getHermesRuntime() noexcept; + + private: + void initRuntime() noexcept; + + private: + jsr_runtime m_runtime{}; + std::shared_ptr m_jsiRuntime; + std::shared_ptr m_targetDelegate; + std::once_flag m_onceFlag{}; + std::thread::id m_ownThreadId{}; + std::weak_ptr m_weakDevSettings; + std::shared_ptr m_jsQueue; + std::shared_ptr m_preparedScriptStore; +}; + +class HermesJSRuntime final : public facebook::react::JSRuntime { + public: + HermesJSRuntime(std::shared_ptr hermesRuntimeHolder); + + facebook::jsi::Runtime &getRuntime() noexcept override; + void addConsoleMessage(facebook::jsi::Runtime &runtime, facebook::react::jsinspector_modern::ConsoleMessage message) + override; + bool supportsConsole() const override; + std::unique_ptr captureStackTrace( + facebook::jsi::Runtime &runtime, + size_t framesToSkip = 0) override; + + /** + * Start sampling profiler. + */ + void enableSamplingProfiler() override; + + /** + * Stop sampling profiler. + */ + void disableSamplingProfiler() override; + + /** + * Return recorded sampling profile for the previous sampling session. + */ + facebook::react::jsinspector_modern::tracing::RuntimeSamplingProfile collectSamplingProfile() override; + + std::unique_ptr createAgentDelegate( + facebook::react::jsinspector_modern::FrontendChannel frontendChannel, + facebook::react::jsinspector_modern::SessionState &sessionState, + std::unique_ptr previouslyExportedState, + const facebook::react::jsinspector_modern::ExecutionContextDescription &executionContextDescription, + facebook::react::RuntimeExecutor runtimeExecutor) override; + + facebook::react::jsinspector_modern::RuntimeTargetDelegate &getRuntimeTargetDelegate() override; + + private: + std::shared_ptr m_holder; +}; + +} // namespace Microsoft::ReactNative diff --git a/vnext/Shared/Hermes/HermesRuntimeTargetDelegate.cpp b/vnext/Shared/Hermes/HermesRuntimeTargetDelegate.cpp new file mode 100644 index 00000000000..e88bf65a108 --- /dev/null +++ b/vnext/Shared/Hermes/HermesRuntimeTargetDelegate.cpp @@ -0,0 +1,150 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#include "HermesRuntimeTargetDelegate.h" +#include +#include +#include "HermesRuntimeAgentDelegate.h" + +using namespace facebook::react::jsinspector_modern; +using namespace facebook::hermes; + +namespace Microsoft::ReactNative { + +namespace { + +class HermesStackTraceWrapper : public StackTrace { + public: + explicit HermesStackTraceWrapper(HermesUniqueStackTrace &&hermesStackTrace) + : hermesStackTrace_{std::move(hermesStackTrace)} {} + + HermesUniqueStackTrace &operator*() { + return hermesStackTrace_; + } + + private: + HermesUniqueStackTrace hermesStackTrace_; +}; + +} // namespace + +HermesRuntimeTargetDelegate::HermesRuntimeTargetDelegate(std::shared_ptr hermesRuntimeHolder) + : hermesRuntimeHolder_(std::move(hermesRuntimeHolder)), + hermesCdpDebugger_(HermesApi2().createCdpDebugger(hermesRuntimeHolder_->getHermesRuntime())) {} + +HermesRuntimeTargetDelegate::~HermesRuntimeTargetDelegate() = default; + +hermes_cdp_debugger HermesRuntimeTargetDelegate::getCdpDebugger() { + return hermesCdpDebugger_.get(); +} + +std::unique_ptr HermesRuntimeTargetDelegate::createAgentDelegate( + FrontendChannel frontendChannel, + SessionState &sessionState, + std::unique_ptr previouslyExportedState, + const ExecutionContextDescription &executionContextDescription, + facebook::react::RuntimeExecutor runtimeExecutor) { + return std::unique_ptr(new HermesRuntimeAgentDelegate( + frontendChannel, + sessionState, + std::move(previouslyExportedState), + executionContextDescription, + hermesRuntimeHolder_->getHermesRuntime(), + *this, + std::move(runtimeExecutor))); +} + +void HermesRuntimeTargetDelegate::addConsoleMessage(facebook::jsi::Runtime & /*runtime*/, ConsoleMessage message) { + hermes_console_api_type type{}; + switch (message.type) { + case ConsoleAPIType::kLog: + type = hermes_console_api_type_log; + break; + case ConsoleAPIType::kDebug: + type = hermes_console_api_type_debug; + break; + case ConsoleAPIType::kInfo: + type = hermes_console_api_type_info; + break; + case ConsoleAPIType::kError: + type = hermes_console_api_type_error; + break; + case ConsoleAPIType::kWarning: + type = hermes_console_api_type_warning; + break; + case ConsoleAPIType::kDir: + type = hermes_console_api_type_dir; + break; + case ConsoleAPIType::kDirXML: + type = hermes_console_api_type_dir_xml; + break; + case ConsoleAPIType::kTable: + type = hermes_console_api_type_table; + break; + case ConsoleAPIType::kTrace: + type = hermes_console_api_type_trace; + break; + case ConsoleAPIType::kStartGroup: + type = hermes_console_api_type_start_group; + break; + case ConsoleAPIType::kStartGroupCollapsed: + type = hermes_console_api_type_start_group_collapsed; + break; + case ConsoleAPIType::kEndGroup: + type = hermes_console_api_type_end_group; + break; + case ConsoleAPIType::kClear: + type = hermes_console_api_type_clear; + break; + case ConsoleAPIType::kAssert: + type = hermes_console_api_type_assert; + break; + case ConsoleAPIType::kTimeEnd: + type = hermes_console_api_type_time_end; + break; + case ConsoleAPIType::kCount: + type = hermes_console_api_type_count; + break; + default: + throw std::logic_error{"Unknown console message type"}; + } + + HermesUniqueStackTrace hermesStackTrace{}; + if (auto hermesStackTraceWrapper = dynamic_cast(message.stackTrace.get())) { + hermesStackTrace = std::move(**hermesStackTraceWrapper); + } + + // TODO: Implement this + // HermesApi2().addConsoleMessage( + // hermesCdpDebugger_.get(), + // message.timestamp, + // type, + // std::move(message.args), + // hermesStackTrace.release()); +} + +bool HermesRuntimeTargetDelegate::supportsConsole() const { + return true; +} + +std::unique_ptr HermesRuntimeTargetDelegate::captureStackTrace( + facebook::jsi::Runtime & /*runtime*/, + size_t /*framesToSkip*/) { + return std::make_unique( + HermesApi2().captureStackTrace(hermesRuntimeHolder_->getHermesRuntime())); +} + +void HermesRuntimeTargetDelegate::enableSamplingProfiler() { + return; // [Windows TODO: stubbed implementation #14700] +} + +void HermesRuntimeTargetDelegate::disableSamplingProfiler() { + return; // [Windows TODO: stubbed implementation #14700] +} + +facebook::react::jsinspector_modern::tracing::RuntimeSamplingProfile HermesRuntimeTargetDelegate::collectSamplingProfile() { + return facebook::react::jsinspector_modern::tracing::RuntimeSamplingProfile( + "stubbed_impl", {}); // [Windows TODO: stubbed implementation #14700] +} + +} // namespace Microsoft::ReactNative diff --git a/vnext/Shared/Hermes/HermesRuntimeTargetDelegate.h b/vnext/Shared/Hermes/HermesRuntimeTargetDelegate.h new file mode 100644 index 00000000000..aa82e8d9aa0 --- /dev/null +++ b/vnext/Shared/Hermes/HermesRuntimeTargetDelegate.h @@ -0,0 +1,61 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#pragma once + +#include + +#include +#include + +#include +#include "HermesRuntimeHolder.h" + +namespace Microsoft::ReactNative { + +// A RuntimeTargetDelegate that enables debugging a Hermes runtime over CDP. +class HermesRuntimeTargetDelegate : public facebook::react::jsinspector_modern::RuntimeTargetDelegate { + public: + explicit HermesRuntimeTargetDelegate(std::shared_ptr hermesRuntimeHolder); + ~HermesRuntimeTargetDelegate() override; + hermes_cdp_debugger getCdpDebugger(); + + public: // RuntimeTargetDelegate implementation + std::unique_ptr createAgentDelegate( + facebook::react::jsinspector_modern::FrontendChannel frontendChannel, + facebook::react::jsinspector_modern::SessionState &sessionState, + std::unique_ptr previouslyExportedState, + const facebook::react::jsinspector_modern::ExecutionContextDescription &executionContextDescription, + facebook::react::RuntimeExecutor runtimeExecutor) override; + + void addConsoleMessage(facebook::jsi::Runtime &runtime, facebook::react::jsinspector_modern::ConsoleMessage message) + override; + + bool supportsConsole() const override; + + std::unique_ptr captureStackTrace( + facebook::jsi::Runtime &runtime, + size_t framesToSkip) override; + +/** + * Start sampling profiler. + */ + void enableSamplingProfiler() override; + + /** + * Stop sampling profiler. + */ + void disableSamplingProfiler() override; + + /** + * Return recorded sampling profile for the previous sampling session. + */ + facebook::react::jsinspector_modern::tracing::RuntimeSamplingProfile collectSamplingProfile() override; + + + private: + std::shared_ptr hermesRuntimeHolder_; + const facebook::hermes::HermesUniqueCdpDebugger hermesCdpDebugger_; +}; + +} // namespace Microsoft::ReactNative diff --git a/vnext/Shared/HermesSamplingProfiler.cpp b/vnext/Shared/Hermes/HermesSamplingProfiler.cpp similarity index 100% rename from vnext/Shared/HermesSamplingProfiler.cpp rename to vnext/Shared/Hermes/HermesSamplingProfiler.cpp diff --git a/vnext/Shared/HermesSamplingProfiler.h b/vnext/Shared/Hermes/HermesSamplingProfiler.h similarity index 100% rename from vnext/Shared/HermesSamplingProfiler.h rename to vnext/Shared/Hermes/HermesSamplingProfiler.h diff --git a/vnext/Shared/IDevSupportManager.h b/vnext/Shared/IDevSupportManager.h index dd90ff1bfc7..7744ef7837e 100644 --- a/vnext/Shared/IDevSupportManager.h +++ b/vnext/Shared/IDevSupportManager.h @@ -23,8 +23,12 @@ struct IDevSupportManager { const uint16_t sourceBundlePort, std::function onChangeCallback) = 0; virtual void StopPollingLiveReload() = 0; + virtual void OpenDevTools(const std::string &bundleAppId) = 0; - virtual void EnsureHermesInspector(const std::string &packagerHost, const uint16_t packagerPort) noexcept = 0; + virtual void EnsureHermesInspector( + const std::string &packagerHost, + const uint16_t packagerPort, + const std::string &bundleAppId) noexcept = 0; virtual void UpdateBundleStatus(bool isLastDownloadSuccess, int64_t updateTimestamp) noexcept = 0; }; diff --git a/vnext/Shared/Inspector/ReactInspectorPackagerConnectionDelegate.cpp b/vnext/Shared/Inspector/ReactInspectorPackagerConnectionDelegate.cpp new file mode 100644 index 00000000000..030ee434f8e --- /dev/null +++ b/vnext/Shared/Inspector/ReactInspectorPackagerConnectionDelegate.cpp @@ -0,0 +1,95 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#include "ReactInspectorPackagerConnectionDelegate.h" + +#include +#include +#include +#include "ReactInspectorThread.h" + +namespace Microsoft::ReactNative { + +namespace { + +class ReactInspectorWebSocket : public facebook::react::jsinspector_modern::IWebSocket { + public: + ReactInspectorWebSocket( + std::string const &url, + std::weak_ptr delegate); + void send(std::string_view message) override; + ~ReactInspectorWebSocket() override; + + private: + std::shared_ptr m_packagerWebSocketConnection; + std::weak_ptr m_weakDelegate; +}; + +ReactInspectorWebSocket::ReactInspectorWebSocket( + std::string const &url, + std::weak_ptr delegate) + : m_weakDelegate{delegate} { + std::vector certExceptions; + + m_packagerWebSocketConnection = + std::make_shared(std::move(certExceptions)); + + m_packagerWebSocketConnection->SetOnMessage([delegate](auto &&, const std::string &message, bool isBinary) { + ReactInspectorThread::Instance().InvokeElsePost([delegate, message]() { + if (const auto strongDelegate = delegate.lock()) { + strongDelegate->didReceiveMessage(message); + } + }); + }); + m_packagerWebSocketConnection->SetOnError( + [delegate](const Microsoft::React::Networking::IWebSocketResource::Error &error) { + ReactInspectorThread::Instance().InvokeElsePost([delegate, error]() { + if (const auto strongDelegate = delegate.lock()) { + strongDelegate->didFailWithError(std::nullopt, error.Message); + } + }); + }); + m_packagerWebSocketConnection->SetOnClose([delegate](auto &&...) { + ReactInspectorThread::Instance().InvokeElsePost([delegate]() { + if (const auto strongDelegate = delegate.lock()) { + strongDelegate->didClose(); + } + }); + }); + + Microsoft::React::Networking::IWebSocketResource::Protocols protocols; + Microsoft::React::Networking::IWebSocketResource::Options options; + m_packagerWebSocketConnection->Connect(std::string{url}, protocols, options); +} + +void ReactInspectorWebSocket::send(std::string_view message) { + m_packagerWebSocketConnection->Send(std::string{message}); +} + +ReactInspectorWebSocket::~ReactInspectorWebSocket() { + std::string reason{"Explicit close"}; + m_packagerWebSocketConnection->Close( + Microsoft::React::Networking::WinRTWebSocketResource::CloseCode::GoingAway, reason); +} + +} // namespace + +std::unique_ptr +ReactInspectorPackagerConnectionDelegate::connectWebSocket( + const std::string &url, + std::weak_ptr delegate) { + return std::make_unique(url, delegate); +} + +winrt::fire_and_forget RunWithDelayAsync(std::function callback, std::chrono::milliseconds delayMs) { + co_await winrt::resume_after(delayMs); + ReactInspectorThread::Instance().InvokeElsePost([callback]() { callback(); }); +} + +void ReactInspectorPackagerConnectionDelegate::scheduleCallback( + std::function callback, + std::chrono::milliseconds delayMs) { + RunWithDelayAsync(callback, delayMs); +} + +} // namespace Microsoft::ReactNative diff --git a/vnext/Shared/Inspector/ReactInspectorPackagerConnectionDelegate.h b/vnext/Shared/Inspector/ReactInspectorPackagerConnectionDelegate.h new file mode 100644 index 00000000000..b7c0e16d9d5 --- /dev/null +++ b/vnext/Shared/Inspector/ReactInspectorPackagerConnectionDelegate.h @@ -0,0 +1,19 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#pragma once +#include + +namespace Microsoft::ReactNative { + +class ReactInspectorPackagerConnectionDelegate final + : public facebook::react::jsinspector_modern::InspectorPackagerConnectionDelegate { + public: // InspectorPackagerConnectionDelegate implementation + std::unique_ptr connectWebSocket( + const std::string &url, + std::weak_ptr delegate) override; + + void scheduleCallback(std::function callback, std::chrono::milliseconds delayMs) override; +}; + +} // namespace Microsoft::ReactNative diff --git a/vnext/Shared/Inspector/ReactInspectorThread.h b/vnext/Shared/Inspector/ReactInspectorThread.h new file mode 100644 index 00000000000..67eec4082de --- /dev/null +++ b/vnext/Shared/Inspector/ReactInspectorThread.h @@ -0,0 +1,18 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#pragma once + +#include + +namespace Microsoft::ReactNative { + +class ReactInspectorThread { + public: + static Mso::DispatchQueue &Instance() { + static Mso::DispatchQueue queue = Mso::DispatchQueue::MakeSerialQueue(); + return queue; + } +}; + +} // namespace Microsoft::ReactNative diff --git a/vnext/Shared/JSI/RuntimeHolder.h b/vnext/Shared/JSI/RuntimeHolder.h index 69a95b5a1cd..7c4be1655ed 100644 --- a/vnext/Shared/JSI/RuntimeHolder.h +++ b/vnext/Shared/JSI/RuntimeHolder.h @@ -4,6 +4,8 @@ #include #include +#include +#include namespace Microsoft::JSI { @@ -18,11 +20,24 @@ struct RuntimeHolderLazyInit { virtual std::shared_ptr getRuntime() noexcept = 0; virtual facebook::react::JSIEngineOverride getRuntimeType() noexcept = 0; + virtual const std::shared_ptr & + getSharedRuntimeTargetDelegate() { + if (!m_runtimeTargetDelegate) { + m_runtimeTargetDelegate = std::make_shared( + getRuntime()->description()); + } + return m_runtimeTargetDelegate; + } + virtual void teardown() noexcept {}; // You can call this when a crash happens to attempt recording additional data // The fileDescriptor supplied is a raw file stream an implementation might write JSON to. virtual void crashHandler(int fileDescriptor) noexcept {}; + + private: + // Initialized by \c getRuntimeTargetDelegate if not overridden, and then never changes. + std::shared_ptr m_runtimeTargetDelegate; }; } // namespace Microsoft::JSI diff --git a/vnext/Shared/InspectorPackagerConnection.cpp b/vnext/Shared/LegacyInspectorPackagerConnection.cpp similarity index 99% rename from vnext/Shared/InspectorPackagerConnection.cpp rename to vnext/Shared/LegacyInspectorPackagerConnection.cpp index 917382a5f3a..aa7a3853b13 100644 --- a/vnext/Shared/InspectorPackagerConnection.cpp +++ b/vnext/Shared/LegacyInspectorPackagerConnection.cpp @@ -5,7 +5,7 @@ #include #include -#include "InspectorPackagerConnection.h" +#include "LegacyInspectorPackagerConnection.h" namespace Microsoft::ReactNative { diff --git a/vnext/Shared/InspectorPackagerConnection.h b/vnext/Shared/LegacyInspectorPackagerConnection.h similarity index 100% rename from vnext/Shared/InspectorPackagerConnection.h rename to vnext/Shared/LegacyInspectorPackagerConnection.h diff --git a/vnext/Shared/OInstance.cpp b/vnext/Shared/OInstance.cpp index 82e66f38f86..3b922f3649c 100644 --- a/vnext/Shared/OInstance.cpp +++ b/vnext/Shared/OInstance.cpp @@ -45,12 +45,14 @@ #include #include #include +#include "Inspector/ReactInspectorThread.h" #include "PackagerConnection.h" +#include "Threading/MessageDispatchQueue.h" #if defined(USE_HERMES) && defined(ENABLE_DEVSERVER_HBCBUNDLES) #include #endif -#include "HermesRuntimeHolder.h" +#include "Hermes/HermesRuntimeHolder.h" #if defined(USE_V8) #include @@ -215,6 +217,26 @@ namespace react { namespace { +// OJSIExecutor is need to override getRuntimeTargetDelegate to support the modern JSI inspector. +class OJSIExecutor : public JSIExecutor { + public: + OJSIExecutor( + std::shared_ptr runtime, + std::shared_ptr delegate, + const JSIScopedTimeoutInvoker &timeoutInvoker, + RuntimeInstaller runtimeInstaller, + std::shared_ptr targetDelegate) noexcept + : JSIExecutor(std::move(runtime), std::move(delegate), timeoutInvoker, std::move(runtimeInstaller)), + targetDelegate_(std::move(targetDelegate_)) {} + // cant override from base class JSIExecutor as its not part of the JSIExecutor interface. + jsinspector_modern::RuntimeTargetDelegate &getRuntimeTargetDelegate() { + return *targetDelegate_; + } + + private: + std::shared_ptr targetDelegate_; +}; + class OJSIExecutorFactory : public JSExecutorFactory { public: std::unique_ptr createJSExecutor( @@ -231,7 +253,7 @@ class OJSIExecutorFactory : public JSExecutorFactory { } bindNativeLogger(*runtimeHolder_->getRuntime(), logger); - return std::make_unique( + return std::make_unique( runtimeHolder_->getRuntime(), std::move(delegate), JSIExecutor::defaultTimeoutInvoker, @@ -239,7 +261,8 @@ class OJSIExecutorFactory : public JSExecutorFactory { #ifdef ENABLE_JS_SYSTRACE_TO_ETW facebook::react::tracing::initializeJSHooks(runtime, isProfiling); #endif - }); + }, + runtimeHolder_->getSharedRuntimeTargetDelegate()); } OJSIExecutorFactory( @@ -373,7 +396,8 @@ InstanceImpl::InstanceImpl( #endif if (shouldStartHermesInspector(*m_devSettings)) { - m_devManager->EnsureHermesInspector(m_devSettings->sourceBundleHost, m_devSettings->sourceBundlePort); + m_devManager->EnsureHermesInspector( + m_devSettings->sourceBundleHost, m_devSettings->sourceBundlePort, m_devSettings->bundleAppId); } // Default (common) NativeModules @@ -483,7 +507,8 @@ InstanceImpl::InstanceImpl( } } - m_innerInstance->initializeBridge(std::move(callback), jsef, m_jsThread, m_moduleRegistry); + m_innerInstance->initializeBridge( + std::move(callback), jsef, m_jsThread, m_moduleRegistry, m_devSettings->inspectorTarget); // For RuntimeScheduler to work properly, we need to install TurboModuleManager with RuntimeSchedulerCallbackInvoker. // To be able to do that, we need to be able to call m_innerInstance->getRuntimeExecutor(), which we can only do after @@ -586,6 +611,16 @@ void InstanceImpl::loadBundleInternal(std::string &&jsBundleRelativePath, bool s } InstanceImpl::~InstanceImpl() { + if (m_devSettings->inspectorTarget) { + auto messageDispatchQueue = + Mso::React::MessageDispatchQueue(::Microsoft::ReactNative::ReactInspectorThread::Instance(), nullptr); + messageDispatchQueue.runOnQueueSync([weakInnerInstance = std::weak_ptr(m_innerInstance)]() { + if (auto innerInstance = weakInnerInstance.lock()) { + innerInstance->unregisterFromInspector(); + } + }); + } + if (shouldStartHermesInspector(*m_devSettings) && m_devSettings->jsiRuntimeHolder) { m_devSettings->jsiRuntimeHolder->teardown(); } diff --git a/vnext/Shared/Shared.vcxitems b/vnext/Shared/Shared.vcxitems index fbfc3740fb4..356ce3300c3 100644 --- a/vnext/Shared/Shared.vcxitems +++ b/vnext/Shared/Shared.vcxitems @@ -5,6 +5,8 @@ true {2049dbe9-8d13-42c9-ae4b-413ae38fffd0} Microsoft.ReactNative.Shared + $(MSBuildThisFileDirectory)..\..\node_modules\react-native + $(MSBuildThisFileDirectory)..\..\..\node_modules\react-native @@ -225,19 +227,17 @@ true - - true - - + + true - + true $(ReactNativeWindowsDir)Microsoft.ReactNative\ReactNativeAppBuilder.idl Code - + true $(ReactNativeWindowsDir)Microsoft.ReactNative\ReactNativeAppBuilder.idl Code @@ -250,9 +250,12 @@ - - - + + + + + + @@ -261,7 +264,7 @@ true - + @@ -344,8 +347,8 @@ - - true + + true $(MSBuildThisFileDirectory)..\Microsoft.ReactNative\ReactNativeAppBuilder.idl Code @@ -354,6 +357,11 @@ $(MSBuildThisFileDirectory)..\Microsoft.ReactNative\ReactNativeAppBuilder.idl Code + + true + $(MSBuildThisFileDirectory)..\Microsoft.ReactNative\ReactNativeAppBuilder.idl + Code + $(MSBuildThisFileDirectory)..\Microsoft.ReactNative\IJSValueReader.idl @@ -408,13 +416,17 @@ $(MSBuildThisFileDirectory)..\Microsoft.ReactNative\Timer.idl - - + + + + + + @@ -459,8 +471,7 @@ - - + @@ -495,6 +506,83 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -521,7 +609,11 @@ + + + + @@ -529,26 +621,35 @@ - - + + + + + + + + + + + + + + - - - @@ -556,6 +657,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -575,6 +707,7 @@ + @@ -675,7 +808,6 @@ - @@ -716,7 +848,7 @@ - + @@ -732,4 +864,4 @@ NotUsing - + \ No newline at end of file diff --git a/vnext/Shared/Shared.vcxitems.filters b/vnext/Shared/Shared.vcxitems.filters index 8a8fb167d88..006156b8be2 100644 --- a/vnext/Shared/Shared.vcxitems.filters +++ b/vnext/Shared/Shared.vcxitems.filters @@ -1,213 +1,385 @@  + + + {37d57028-146d-45f3-a56d-2ce7625a3449} + + + {da7f5a49-e3fb-46c3-906d-be42b8b651f7} + + + {87b365df-267b-4c00-860a-53d129a65a4e} + + + {a3399e44-cc5c-45bf-8e6e-ea5f3fcdc8f4} + + + {0ffdd86e-d4b2-4517-a8e3-8cb8ee8d8bca} + + + {2ad4b85f-faa4-4e14-905c-6c48c8ae1a20} + + + {07874914-e3b0-4d02-ad53-3518c2cb8cd8} + + + {deefba53-d1ca-457d-9293-1993510e6d5a} + + + {b32590e6-ae3d-4388-ab98-767345ce38c9} + + + {e045612e-bca3-4844-bce8-e75f83e89e05} + + + {c6241404-615b-4338-8955-e9282c440712} + + + {00b927dc-e868-4e01-80a7-03adbd7ef5af} + + + {81771765-0676-4cf8-baab-3656dd4d761f} + + + {b84ea036-35e5-4c33-b029-14b35dcbb5ce} + + + {0198c3a8-dcd1-4f17-aa80-769fa8df762f} + + + {9bbd1367-8911-4f05-8c3a-b1b82899413d} + + + {f34dccd6-7da9-46e6-b5e2-67f02690ff1d} + + + {0bd2cd2a-875b-4f98-851d-b90adac57415} + + + {1bc720d3-e07d-4a92-a569-11139f183736} + + + {5e9ae667-7a86-4d99-8996-0255534ef606} + + + {74e10d51-8301-4dfa-9e88-522d810f308f} + + + {5e32924b-0ec4-4876-a84b-0ecd8cecbade} + + + {b71c7172-2cde-4583-9afe-f1cae4c73f40} + + + {ce3bb9e0-ac85-4fa2-8cca-93d23a72b125} + + + {d2eae11a-b021-4d0f-bf0f-cc5a164f2579} + + + {103aab63-7882-4777-85a5-8fdbbc8e54f9} + + + {9e2dd06f-00fd-47bc-87cd-856bf393c504} + + + {c8c4a850-2ee5-4211-9c65-3dd074209b47} + + + {9c8e7a7a-c77d-479d-8611-1ece3cdacad7} + + + {eae1b827-8949-49dd-9740-501033bcd441} + + + {1072517c-f7e8-419d-a43a-b037bf46e691} + + + {4fa3cc8f-fe31-4e3d-88f3-625ef41f0a02} + + + {a6918f44-da21-414d-ad25-d9bdb8f8756b} + + + {2d49b4f6-fe41-45da-9942-81217accd474} + + + {55de2e00-aeb3-4ab3-a731-6edfc63c4db9} + + + {739297af-da89-4523-899d-02b454fc9330} + + + {c0b6b399-2f8c-4d54-8253-db898e6e7b12} + + + {21c8e767-5cda-40cd-902a-7f5b0e7bc652} + + + {d6f7e924-6bcc-49c7-b6c0-59f9199a1bf9} + + + {e3ff0f65-dba6-4a18-8ec5-acfe73178bb7} + + + {e17e9189-5a27-45fa-adf0-8b397716a9ce} + + + {d57ad0b8-df3f-469c-85d5-ee675f151595} + + + {61211521-40da-4211-b23d-bced1fefc5d8} + + + {fa6d6abd-769f-4c36-b881-173f3f218abe} + + + {cc363422-d654-4288-b8b8-ea8f38aa7604} + + + {297ffef3-6154-41cf-85b1-75eff3d8fba0} + + + {8fa16c21-60dd-4ab7-bb45-3e634a6e09b0} + + + {40bfe98f-f174-4c03-9296-d371bc230e53} + + + {2995ad49-ae65-4ec4-9392-838a2f725f0e} + + + {f3362933-5f11-4653-929d-e13607d919b7} + + + {4b54ec22-5b25-4053-ac9b-98574944531c} + + + {50a825f0-9a6d-454e-94f5-65cf7cad34cc} + + + {7f5b0b9a-6a9b-4927-b92e-b796d8722357} + + + {6f281c77-c556-413f-ab0e-e1da95aa47d2} + + + {23b0c36f-4a4b-4380-9e4c-f8cb0c2f2714} + + + {c4d010df-7391-4171-8658-9048078a5523} + + + {3db635c1-ad38-4099-b79d-872a6ef4319f} + + + {41c4ae5c-9e6f-4793-ba82-2f4cd83a6fb2} + + + {5baa0890-47d8-4f32-997c-d5a85d963778} + + + {c9d54fb1-2a86-4f62-b2e7-7c57a2fd9126} + + + {b81cc660-374c-4232-823b-6c76b48564b4} + + + {ca34baaf-b7b3-4399-a574-4c6970fd32a5} + + + {e0723a4d-6e5c-4a6e-a4f8-800c1c5c486c} + + + {97453974-4140-4286-a759-a2a3627c6236} + + + {523a308e-319e-4e68-a653-39d2d38cba9c} + + + {517a9755-ea3e-4eb2-a0a3-b52b79fffd3e} + + - Source Files\tracing + tracing - Source Files\Modules + Modules - Source Files\Modules + Modules - Source Files\Modules + Modules - Source Files\Modules + Modules - Source Files\Modules - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - + Modules + + + + + + + + + + + + - Source Files\Executors + Executors - Source Files\Executors + Executors - Source Files\Modules + Modules - Source Files\Threading + Threading - Source Files\Threading + Threading - Source Files\Threading - - - Source Files + Threading - Source Files\Modules + Modules - Source Files\JSI + JSI - Source Files\JSI - - - Source Files + JSI - Source Files\JSI + JSI - Source Files\JSI - - - Source Files + JSI - Source Files\Modules + Modules - Source Files\Utils + Utils - Source Files\Networking + Networking - Source Files\Networking + Networking - Source Files\Networking + Networking - Source Files\Modules + Modules - Source Files\Modules + Modules - Source Files\Networking + Networking - Source Files\Modules + Modules - Source Files\Fabric\codegen\react\components\rnwcore + Fabric\codegen\react\components\rnwcore - Source Files\Fabric\codegen\react\components\rnwcore + Fabric\codegen\react\components\rnwcore - Source Files\Fabric\codegen\react\components\rnwcore + Fabric\codegen\react\components\rnwcore - Source Files\Fabric + Fabric - Source Files\Fabric + Fabric - Source Files\Fabric + Fabric - Source Files\Fabric + Fabric - Source Files\Fabric + Fabric - + Source Files\Fabric - - Source Files + + Fabric - Source Files\Fabric\Composition + Fabric\Composition - Source Files\Fabric\Composition + Fabric\Composition - Source Files\Fabric\Composition + Fabric\Composition - Source Files\Fabric\Composition + Fabric\Composition - Source Files\Fabric\Composition + Fabric\Composition + Fabric\Composition + + Source Files\Fabric\Composition - Source Files\Fabric\Composition + Fabric\Composition - Source Files\Fabric\Composition + Fabric\Composition - Source Files\Fabric\Composition + Fabric\Composition - Source Files\Fabric\Composition\TextInput + Fabric\Composition\TextInput - Source Files\Fabric\Composition\TextInput + Fabric\Composition\TextInput - Source Files\Fabric\Composition\TextInput + Fabric\Composition\TextInput + Fabric\Composition\TextInput + + Source Files\Fabric\Composition\TextInput - Source Files\Fabric\Composition + Fabric\Composition - Source Files\Fabric\Composition + Fabric\Composition - Source Files\Fabric\Composition + Fabric\Composition - Source Files\Fabric\Composition + Fabric\Composition Source Files\Fabric\Composition @@ -219,24 +391,26 @@ Source Files\Fabric\Composition - Source Files\Fabric\Composition + Fabric\Composition - Source Files\Fabric\Composition + Fabric\Composition - Source Files\Fabric + Fabric - Source Files\Fabric + Fabric - Source Files\Fabric + Fabric - Source Files\Fabric + Fabric + + + Source Files\Fabric\platform\react\renderer\textlayoutmanager - @@ -259,30 +433,26 @@ - - Source Files - - + Hermes - + Hermes - - - Source Files\Networking + Networking - Source Files\Modules + Modules + @@ -290,6 +460,7 @@ + @@ -319,7 +490,7 @@ - + @@ -327,23 +498,8 @@ - - - - - - - - - - - - - - - @@ -481,361 +637,538 @@ - Header Files\Modules + Modules - Header Files\Modules + Modules - Header Files\Modules + Modules - Header Files\Modules + Modules - Header Files\Modules + Modules - Header Files\Modules + Modules - Header Files\tracing + tracing - Header Files\Pch - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - + Pch + + + + + + + + + + + + + + + + + + + + + + + + + + - Header Files\Executors - - - Header Files + Executors + - Header Files\Modules + Modules - Header Files\Threading + Threading - Header Files\Threading + Threading - Header Files\Threading + Threading - Header Files\Utils + Utils - Header Files\JSI + JSI - Header Files\JSI + JSI - Header Files\JSI + JSI - Header Files\JSI + JSI - Header Files\JSI + JSI - Header Files\JSI + JSI - Header Files\JSI + JSI - Header Files\JSI + JSI - Header Files\JSI + JSI - - Header Files - - Header Files\tracing + tracing - Header Files\Modules + Modules - Header Files\Utils + Utils - Header Files\Networking + Networking - Header Files\Networking + Networking - Header Files\Networking + Networking - Header Files\Networking + Networking - Header Files\Networking + Networking - Header Files\Networking + Networking - Header Files\Networking - - - Header Files + Networking + - Header Files\Modules + Modules - Header Files\Modules + Modules - Header Files\Modules + Modules - Header Files\Modules + Modules - Header Files\Modules + Modules - Header Files\Modules + Modules - Header Files\Networking + Networking - Header Files\Networking + Networking - Header Files\Networking + Networking - Header Files\Modules + Modules - Header Files\Modules + Modules - Header Files\Modules + Modules - Header Files\Fabric\codegen\react\components\rnwcore + Fabric\codegen\react\components\rnwcore - Header Files\Fabric\codegen\react\components\rnwcore + Fabric\codegen\react\components\rnwcore - Header Files\Fabric\codegen\react\components\rnwcore + Fabric\codegen\react\components\rnwcore - Header Files\Fabric + Fabric - Header Files\Fabric + Fabric - + Header Files\Fabric - - Header Files + + Fabric + - Header Files\Fabric\Composition + Fabric\Composition - Header Files\Fabric\Composition + Fabric\Composition - Header Files\Fabric\Composition + Fabric\Composition - Header Files\Fabric\Composition + Fabric\Composition - Header Files\Fabric\Composition + Fabric\Composition - Header Files\Fabric\Composition + Fabric\Composition - Header Files\Fabric\Composition + Fabric\Composition - Header Files\Fabric\Composition + Fabric\Composition - Header Files\Fabric\Composition + Fabric\Composition - Header Files\Fabric\Composition\TextInput + Fabric\Composition\TextInput - Header Files\Fabric\Composition\TextInput + Fabric\Composition\TextInput - Header Files\Fabric\Composition\TextInput + Fabric\Composition\TextInput + Fabric\Composition\TextInput + + Header Files\Fabric\Composition\TextInput - Header Files\Fabric\Composition + Fabric\Composition - Header Files\Fabric\Composition + Fabric\Composition - Header Files\Fabric\Composition + Fabric\Composition - Header Files\Fabric\Composition + Fabric\Composition - Header Files\Fabric\Composition + Fabric\Composition - Header Files\Fabric\Composition + Fabric\Composition - Header Files\Fabric\platform\react\renderer\graphics + Fabric\platform\react\renderer\graphics - Header Files\Fabric\platform\react\renderer\textlayoutmanager + Fabric\platform\react\renderer\textlayoutmanager - - Header Files - - - Header Files - + + - Header Files\Fabric\platform\react\renderer\core + Fabric\platform\react\renderer\core - Header Files\Modules + Modules - Header Files\Networking + Networking - Header Files\Networking - - - Header Files + Networking + - Header Files\Modules + Modules - + Hermes - + Hermes - - + + + ReactCommon\stubs\sys + + + ReactCommon\stubs\sys + + + ReactCommon\callinvoker\ReactCommon + + + ReactCommon\callinvoker\ReactCommon + + + ReactCommon\cxxreact + + + ReactCommon\cxxreact + + + ReactCommon\cxxreact + + + ReactCommon\cxxreact + + + ReactCommon\cxxreact + + + ReactCommon\cxxreact + + + ReactCommon\cxxreact + + + ReactCommon\cxxreact + + + ReactCommon\cxxreact + + + ReactCommon\cxxreact + + + ReactCommon\cxxreact + + + ReactCommon\cxxreact + + + ReactCommon\cxxreact + + + ReactCommon\cxxreact + + + ReactCommon\cxxreact + + + ReactCommon\cxxreact + + + ReactCommon\cxxreact + + + ReactCommon\cxxreact + + + ReactCommon\cxxreact + + + ReactCommon\jsi\jsi + + + ReactCommon\jsiexecutor\jsireact + + + ReactCommon\jsiexecutor\jsireact + + + ReactCommon\logger + + + ReactCommon\react\runtime + + + ReactCommon\react\nativemodule\core\ReactCommon + + + ReactCommon\react\bridging + + + ReactCommon\react\bridging + + + ReactCommon\react\bridging + + + ReactCommon\react\bridging + + + ReactCommon\react\bridging + + + ReactCommon\react\nativemodule\core\ReactCommon + + + ReactCommon\react\nativemodule\core\ReactCommon + + + ReactCommon\react\nativemodule\core\ReactCommon + + + ReactCommon\react\nativemodule\core\ReactCommon + + + ReactCommon\react\renderer\core + + + ReactCommon\react\renderer\runtimescheduler + + + ReactCommon\react\renderer\runtimescheduler + + + ReactCommon\jsinspector-modern + + + ReactCommon\jsinspector-modern + + + ReactCommon\jsinspector-modern + + + ReactCommon\jsinspector-modern + + + ReactCommon\jsinspector-modern + + + ReactCommon\jsinspector-modern + + + ReactCommon\jsinspector-modern + + + ReactCommon\jsinspector-modern + + + ReactCommon\jsinspector-modern + + + ReactCommon\jsinspector-modern + + + ReactCommon\jsinspector-modern + + + ReactCommon\jsinspector-modern + + + ReactCommon\jsinspector-modern + + + ReactCommon\jsinspector-modern + + + ReactCommon\jsinspector-modern + + + ReactCommon\jsinspector-modern + + + ReactCommon\jsinspector-modern + + + ReactCommon\jsinspector-modern + + + ReactCommon\jsinspector-modern + + + ReactCommon\jsinspector-modern + + + ReactCommon\jsinspector-modern + + + ReactCommon\jsinspector-modern + + + ReactCommon\jsinspector-modern + + + ReactCommon\jsinspector-modern + + + ReactCommon\jsinspector-modern + + + ReactCommon\jsinspector-modern + + + ReactCommon\jsinspector-modern + + + ReactCommon\jsinspector-modern + + + ReactCommon\jsinspector-modern + + + ReactCommon\yoga + + + ReactCommon\yoga + + + ReactCommon\yoga + + + V8 + + + V8 + + + Chakra + + + Inspector + + + Inspector + + + Hermes + + + Hermes + - Source Files\tracing + tracing