From f0ce600639b800469b888e067d40f4d08c894041 Mon Sep 17 00:00:00 2001 From: Alex Hunt Date: Mon, 10 Nov 2025 02:41:12 -0800 Subject: [PATCH] Add single host inspector state assertion over CDP (#54407) Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/54407 Introduces a new `InspectorSystemState` object and `ReactNativeApplication.systemStateChanged` CDP event, used to assert whether more than one React Native Host has been registered for the current app lifetime. This will be used to disable the Performance and Network features in React Native DevTools when the debugger backend is in this currently unsupported state. We intend to implement host lifecycle correctness across all features soon. Changelog: [Internal] Differential Revision: D86201689 --- .../featureflags/ReactNativeFeatureFlags.kt | 8 +- .../ReactNativeFeatureFlagsCxxAccessor.kt | 12 ++- .../ReactNativeFeatureFlagsCxxInterop.kt | 4 +- .../ReactNativeFeatureFlagsDefaults.kt | 4 +- .../ReactNativeFeatureFlagsLocalAccessor.kt | 13 +++- .../ReactNativeFeatureFlagsProvider.kt | 4 +- .../JReactNativeFeatureFlagsCxxInterop.cpp | 16 +++- .../JReactNativeFeatureFlagsCxxInterop.h | 5 +- .../jsinspector-modern/HostAgent.cpp | 22 ++++++ .../jsinspector-modern/HostAgent.h | 6 ++ .../jsinspector-modern/HostTarget.h | 5 ++ .../jsinspector-modern/InspectorFlags.cpp | 6 ++ .../jsinspector-modern/InspectorFlags.h | 7 ++ .../InspectorInterfaces.cpp | 37 ++++++++++ .../jsinspector-modern/InspectorInterfaces.h | 13 +++- .../featureflags/ReactNativeFeatureFlags.cpp | 6 +- .../featureflags/ReactNativeFeatureFlags.h | 7 +- .../ReactNativeFeatureFlagsAccessor.cpp | 74 ++++++++++++------- .../ReactNativeFeatureFlagsAccessor.h | 6 +- .../ReactNativeFeatureFlagsDefaults.h | 6 +- .../ReactNativeFeatureFlagsDynamicProvider.h | 11 ++- .../ReactNativeFeatureFlagsProvider.h | 3 +- .../NativeReactNativeFeatureFlags.cpp | 7 +- .../NativeReactNativeFeatureFlags.h | 4 +- .../ReactNativeFeatureFlags.config.js | 10 +++ .../featureflags/ReactNativeFeatureFlags.js | 7 +- .../specs/NativeReactNativeFeatureFlags.js | 3 +- 27 files changed, 258 insertions(+), 48 deletions(-) diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlags.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlags.kt index e2fe821c5507..bc68fdb222fd 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlags.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlags.kt @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<01feab16132bc07b79b359178e322327>> + * @generated SignedSource<<862c5a78fec94408ca78ef2349627a02>> */ /** @@ -372,6 +372,12 @@ public object ReactNativeFeatureFlags { @JvmStatic public fun fixMappingOfEventPrioritiesBetweenFabricAndReact(): Boolean = accessor.fixMappingOfEventPrioritiesBetweenFabricAndReact() + /** + * Enable system assertion validating that Fusebox is configured with a single host. When set, the CDP backend will dynamically disable features (Perf and Network) in the event that multiple hosts are registered (undefined behaviour), and broadcast this over `ReactNativeApplication.systemStateChanged`. + */ + @JvmStatic + public fun fuseboxAssertSingleHostState(): Boolean = accessor.fuseboxAssertSingleHostState() + /** * Flag determining if the React Native DevTools (Fusebox) CDP backend should be enabled in release builds. This flag is global and should not be changed across React Host lifetimes. */ diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsCxxAccessor.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsCxxAccessor.kt index 851f9bbc913e..5a4d0df7c0f4 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsCxxAccessor.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsCxxAccessor.kt @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<405bf283e4631e166cd57f5dfe30d5f9>> + * @generated SignedSource<<67b008ecdc29278a7019f60e24673fc1>> */ /** @@ -77,6 +77,7 @@ internal class ReactNativeFeatureFlagsCxxAccessor : ReactNativeFeatureFlagsAcces private var enableVirtualViewWindowFocusDetectionCache: Boolean? = null private var enableWebPerformanceAPIsByDefaultCache: Boolean? = null private var fixMappingOfEventPrioritiesBetweenFabricAndReactCache: Boolean? = null + private var fuseboxAssertSingleHostStateCache: Boolean? = null private var fuseboxEnabledReleaseCache: Boolean? = null private var fuseboxNetworkInspectionEnabledCache: Boolean? = null private var hideOffscreenVirtualViewsOnIOSCache: Boolean? = null @@ -618,6 +619,15 @@ internal class ReactNativeFeatureFlagsCxxAccessor : ReactNativeFeatureFlagsAcces return cached } + override fun fuseboxAssertSingleHostState(): Boolean { + var cached = fuseboxAssertSingleHostStateCache + if (cached == null) { + cached = ReactNativeFeatureFlagsCxxInterop.fuseboxAssertSingleHostState() + fuseboxAssertSingleHostStateCache = cached + } + return cached + } + override fun fuseboxEnabledRelease(): Boolean { var cached = fuseboxEnabledReleaseCache if (cached == null) { diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsCxxInterop.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsCxxInterop.kt index abed392d67fa..6121254eed85 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsCxxInterop.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsCxxInterop.kt @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<> + * @generated SignedSource<<553d71b2c9e8e89b2e5d48723f3009b4>> */ /** @@ -142,6 +142,8 @@ public object ReactNativeFeatureFlagsCxxInterop { @DoNotStrip @JvmStatic public external fun fixMappingOfEventPrioritiesBetweenFabricAndReact(): Boolean + @DoNotStrip @JvmStatic public external fun fuseboxAssertSingleHostState(): Boolean + @DoNotStrip @JvmStatic public external fun fuseboxEnabledRelease(): Boolean @DoNotStrip @JvmStatic public external fun fuseboxNetworkInspectionEnabled(): Boolean diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsDefaults.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsDefaults.kt index 76bcd18da79f..ad34ff557fd6 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsDefaults.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsDefaults.kt @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<> + * @generated SignedSource<> */ /** @@ -137,6 +137,8 @@ public open class ReactNativeFeatureFlagsDefaults : ReactNativeFeatureFlagsProvi override fun fixMappingOfEventPrioritiesBetweenFabricAndReact(): Boolean = false + override fun fuseboxAssertSingleHostState(): Boolean = true + override fun fuseboxEnabledRelease(): Boolean = false override fun fuseboxNetworkInspectionEnabled(): Boolean = false diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsLocalAccessor.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsLocalAccessor.kt index 0ed12a86aded..0787def515ec 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsLocalAccessor.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsLocalAccessor.kt @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<> + * @generated SignedSource<<7a2517498558f2c0f69831d5466ddec3>> */ /** @@ -81,6 +81,7 @@ internal class ReactNativeFeatureFlagsLocalAccessor : ReactNativeFeatureFlagsAcc private var enableVirtualViewWindowFocusDetectionCache: Boolean? = null private var enableWebPerformanceAPIsByDefaultCache: Boolean? = null private var fixMappingOfEventPrioritiesBetweenFabricAndReactCache: Boolean? = null + private var fuseboxAssertSingleHostStateCache: Boolean? = null private var fuseboxEnabledReleaseCache: Boolean? = null private var fuseboxNetworkInspectionEnabledCache: Boolean? = null private var hideOffscreenVirtualViewsOnIOSCache: Boolean? = null @@ -679,6 +680,16 @@ internal class ReactNativeFeatureFlagsLocalAccessor : ReactNativeFeatureFlagsAcc return cached } + override fun fuseboxAssertSingleHostState(): Boolean { + var cached = fuseboxAssertSingleHostStateCache + if (cached == null) { + cached = currentProvider.fuseboxAssertSingleHostState() + accessedFeatureFlags.add("fuseboxAssertSingleHostState") + fuseboxAssertSingleHostStateCache = cached + } + return cached + } + override fun fuseboxEnabledRelease(): Boolean { var cached = fuseboxEnabledReleaseCache if (cached == null) { diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsProvider.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsProvider.kt index 8bed0adf7976..fb880c961e90 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsProvider.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsProvider.kt @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<2325f5410537941e25998ca8c59bf51d>> + * @generated SignedSource<<0efef5db3ded62f312486ac2fb04bcdb>> */ /** @@ -137,6 +137,8 @@ public interface ReactNativeFeatureFlagsProvider { @DoNotStrip public fun fixMappingOfEventPrioritiesBetweenFabricAndReact(): Boolean + @DoNotStrip public fun fuseboxAssertSingleHostState(): Boolean + @DoNotStrip public fun fuseboxEnabledRelease(): Boolean @DoNotStrip public fun fuseboxNetworkInspectionEnabled(): Boolean diff --git a/packages/react-native/ReactAndroid/src/main/jni/react/featureflags/JReactNativeFeatureFlagsCxxInterop.cpp b/packages/react-native/ReactAndroid/src/main/jni/react/featureflags/JReactNativeFeatureFlagsCxxInterop.cpp index e0c1f2322f04..b10751f4d956 100644 --- a/packages/react-native/ReactAndroid/src/main/jni/react/featureflags/JReactNativeFeatureFlagsCxxInterop.cpp +++ b/packages/react-native/ReactAndroid/src/main/jni/react/featureflags/JReactNativeFeatureFlagsCxxInterop.cpp @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<95ebd4929bf0f98aa059ad4b8139f7b3>> + * @generated SignedSource<<09f32ad880a470e45d3e5ba67a6e5831>> */ /** @@ -381,6 +381,12 @@ class ReactNativeFeatureFlagsJavaProvider return method(javaProvider_); } + bool fuseboxAssertSingleHostState() override { + static const auto method = + getReactNativeFeatureFlagsProviderJavaClass()->getMethod("fuseboxAssertSingleHostState"); + return method(javaProvider_); + } + bool fuseboxEnabledRelease() override { static const auto method = getReactNativeFeatureFlagsProviderJavaClass()->getMethod("fuseboxEnabledRelease"); @@ -832,6 +838,11 @@ bool JReactNativeFeatureFlagsCxxInterop::fixMappingOfEventPrioritiesBetweenFabri return ReactNativeFeatureFlags::fixMappingOfEventPrioritiesBetweenFabricAndReact(); } +bool JReactNativeFeatureFlagsCxxInterop::fuseboxAssertSingleHostState( + facebook::jni::alias_ref /*unused*/) { + return ReactNativeFeatureFlags::fuseboxAssertSingleHostState(); +} + bool JReactNativeFeatureFlagsCxxInterop::fuseboxEnabledRelease( facebook::jni::alias_ref /*unused*/) { return ReactNativeFeatureFlags::fuseboxEnabledRelease(); @@ -1169,6 +1180,9 @@ void JReactNativeFeatureFlagsCxxInterop::registerNatives() { makeNativeMethod( "fixMappingOfEventPrioritiesBetweenFabricAndReact", JReactNativeFeatureFlagsCxxInterop::fixMappingOfEventPrioritiesBetweenFabricAndReact), + makeNativeMethod( + "fuseboxAssertSingleHostState", + JReactNativeFeatureFlagsCxxInterop::fuseboxAssertSingleHostState), makeNativeMethod( "fuseboxEnabledRelease", JReactNativeFeatureFlagsCxxInterop::fuseboxEnabledRelease), diff --git a/packages/react-native/ReactAndroid/src/main/jni/react/featureflags/JReactNativeFeatureFlagsCxxInterop.h b/packages/react-native/ReactAndroid/src/main/jni/react/featureflags/JReactNativeFeatureFlagsCxxInterop.h index 7f7829173cfa..dff2061e93d2 100644 --- a/packages/react-native/ReactAndroid/src/main/jni/react/featureflags/JReactNativeFeatureFlagsCxxInterop.h +++ b/packages/react-native/ReactAndroid/src/main/jni/react/featureflags/JReactNativeFeatureFlagsCxxInterop.h @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<155f615584a7738dc5b89205c2ecad8b>> + * @generated SignedSource<<414ebc294bc434bdf5f9eca094eac268>> */ /** @@ -201,6 +201,9 @@ class JReactNativeFeatureFlagsCxxInterop static bool fixMappingOfEventPrioritiesBetweenFabricAndReact( facebook::jni::alias_ref); + static bool fuseboxAssertSingleHostState( + facebook::jni::alias_ref); + static bool fuseboxEnabledRelease( facebook::jni::alias_ref); diff --git a/packages/react-native/ReactCommon/jsinspector-modern/HostAgent.cpp b/packages/react-native/ReactCommon/jsinspector-modern/HostAgent.cpp index 60a33116bd15..9464d625695b 100644 --- a/packages/react-native/ReactCommon/jsinspector-modern/HostAgent.cpp +++ b/packages/react-native/ReactCommon/jsinspector-modern/HostAgent.cpp @@ -10,6 +10,7 @@ #ifdef REACT_NATIVE_DEBUGGER_ENABLED #include "InspectorFlags.h" +#include "InspectorInterfaces.h" #include "NetworkIOAgent.h" #include "SessionState.h" #include "TracingAgent.h" @@ -216,6 +217,11 @@ class HostAgent::Impl final { cdp::jsonNotification( "ReactNativeApplication.metadataUpdated", createHostMetadataPayload(hostMetadata_))); + auto& inspector = getInspectorInstance(); + bool isSingleHost = inspector.getSystemState().registeredPagesCount <= 1; + if (!isSingleHost) { + emitSystemStateChanged(isSingleHost); + } auto stashedTraceRecording = targetController_.getDelegate() @@ -374,6 +380,17 @@ class HostAgent::Impl final { tracingAgent_.emitExternalTraceRecording(std::move(traceRecording)); } + void emitSystemStateChanged(bool isSingleHost) { + frontendChannel_( + cdp::jsonNotification( + "ReactNativeApplication.systemStateChanged", + folly::dynamic::object("isSingleHost", isSingleHost))); + + if (!isSingleHost) { + frontendChannel_(cdp::jsonNotification("Network.disable")); + } + } + private: enum class FuseboxClientType { Unknown, Fusebox, NonFusebox }; @@ -480,6 +497,7 @@ class HostAgent::Impl final { } void emitExternalTraceRecording(tracing::TraceRecordingState traceRecording) { } + void emitSystemStateChanged(bool isSingleHost) {} }; #endif // REACT_NATIVE_DEBUGGER_ENABLED @@ -519,6 +537,10 @@ void HostAgent::emitExternalTraceRecording( impl_->emitExternalTraceRecording(std::move(traceRecording)); } +void HostAgent::emitSystemStateChanged(bool isSingleHost) const { + impl_->emitSystemStateChanged(isSingleHost); +} + #pragma mark - Tracing HostTracingAgent::HostTracingAgent(tracing::TraceRecordingState& state) diff --git a/packages/react-native/ReactCommon/jsinspector-modern/HostAgent.h b/packages/react-native/ReactCommon/jsinspector-modern/HostAgent.h index afb8bf44ad40..ab4478816562 100644 --- a/packages/react-native/ReactCommon/jsinspector-modern/HostAgent.h +++ b/packages/react-native/ReactCommon/jsinspector-modern/HostAgent.h @@ -79,6 +79,12 @@ class HostAgent final { */ void emitExternalTraceRecording(tracing::TraceRecordingState traceRecording) const; + /** + * Emits a system state changed event when the number of ReactHost instances + * changes. + */ + void emitSystemStateChanged(bool isSingleHost) const; + private: // We use the private implementation idiom to ensure this class has the same // layout regardless of whether REACT_NATIVE_DEBUGGER_ENABLED is defined. The diff --git a/packages/react-native/ReactCommon/jsinspector-modern/HostTarget.h b/packages/react-native/ReactCommon/jsinspector-modern/HostTarget.h index e315893e8e11..72929a95a205 100644 --- a/packages/react-native/ReactCommon/jsinspector-modern/HostTarget.h +++ b/packages/react-native/ReactCommon/jsinspector-modern/HostTarget.h @@ -326,6 +326,11 @@ class JSINSPECTOR_EXPORT HostTarget : public EnableExecutorFromThis */ void emitTraceRecordingForFirstFuseboxClient(tracing::TraceRecordingState traceRecording) const; + /** + * Emits a system state changed event to all active sessions. + */ + void emitSystemStateChanged(bool isSingleHost) const; + private: /** * Constructs a new HostTarget. diff --git a/packages/react-native/ReactCommon/jsinspector-modern/InspectorFlags.cpp b/packages/react-native/ReactCommon/jsinspector-modern/InspectorFlags.cpp index 35092ec4e6f5..010813a24264 100644 --- a/packages/react-native/ReactCommon/jsinspector-modern/InspectorFlags.cpp +++ b/packages/react-native/ReactCommon/jsinspector-modern/InspectorFlags.cpp @@ -17,6 +17,10 @@ InspectorFlags& InspectorFlags::getInstance() { return instance; } +bool InspectorFlags::getAssertSingleHostState() const { + return loadFlagsAndAssertUnchanged().assertSingleHostState; +} + bool InspectorFlags::getFuseboxEnabled() const { if (fuseboxDisabledForTest_) { return false; @@ -48,6 +52,8 @@ void InspectorFlags::dangerouslyDisableFuseboxForTest() { const InspectorFlags::Values& InspectorFlags::loadFlagsAndAssertUnchanged() const { InspectorFlags::Values newValues = { + .assertSingleHostState = + ReactNativeFeatureFlags::fuseboxAssertSingleHostState(), .fuseboxEnabled = #if defined(REACT_NATIVE_DEBUGGER_ENABLED) true, diff --git a/packages/react-native/ReactCommon/jsinspector-modern/InspectorFlags.h b/packages/react-native/ReactCommon/jsinspector-modern/InspectorFlags.h index 60e260ea19bf..5e245d482eed 100644 --- a/packages/react-native/ReactCommon/jsinspector-modern/InspectorFlags.h +++ b/packages/react-native/ReactCommon/jsinspector-modern/InspectorFlags.h @@ -19,6 +19,12 @@ class InspectorFlags { public: static InspectorFlags &getInstance(); + /** + * Flag determining if the inspector backend should strictly assert that only + * a single host is registered. + */ + bool getAssertSingleHostState() const; + /** * Flag determining if the modern CDP backend should be enabled. */ @@ -54,6 +60,7 @@ class InspectorFlags { private: struct Values { + bool assertSingleHostState; bool fuseboxEnabled; bool isProfilingBuild; bool networkInspectionEnabled; diff --git a/packages/react-native/ReactCommon/jsinspector-modern/InspectorInterfaces.cpp b/packages/react-native/ReactCommon/jsinspector-modern/InspectorInterfaces.cpp index d660504a84d7..b0d2f7a5ffd4 100644 --- a/packages/react-native/ReactCommon/jsinspector-modern/InspectorInterfaces.cpp +++ b/packages/react-native/ReactCommon/jsinspector-modern/InspectorInterfaces.cpp @@ -7,6 +7,8 @@ #include "InspectorInterfaces.h" +#include "InspectorFlags.h" + #include #include #include @@ -35,6 +37,14 @@ namespace { class InspectorImpl : public IInspector { public: + InspectorImpl() { + systemStateListener_ = std::make_shared(systemState_); + auto& inspectorFlags = InspectorFlags::getInstance(); + if (inspectorFlags.getAssertSingleHostState()) { + registerPageStatusListener(systemStateListener_); + } + } + int addPage( const std::string& description, const std::string& vm, @@ -50,7 +60,21 @@ class InspectorImpl : public IInspector { void registerPageStatusListener( std::weak_ptr listener) override; + InspectorSystemState getSystemState() const override; + private: + class SystemStateListener : public IPageStatusListener { + public: + explicit SystemStateListener(InspectorSystemState& state) : state_(state) {} + + void onPageAdded(int /*pageId*/) override { + state_.registeredPagesCount++; + } + + private: + InspectorSystemState& state_; + }; + class Page { public: Page( @@ -74,6 +98,8 @@ class InspectorImpl : public IInspector { int nextPageId_{1}; std::map pages_; std::list> listeners_; + InspectorSystemState systemState_{0}; + std::shared_ptr systemStateListener_; }; InspectorImpl::Page::Page( @@ -116,6 +142,12 @@ int InspectorImpl::addPage( pageId, Page{pageId, description, vm, std::move(connectFunc), capabilities}); + for (const auto& listenerWeak : listeners_) { + if (auto listener = listenerWeak.lock()) { + listener->onPageAdded(pageId); + } + } + return pageId; } @@ -175,6 +207,11 @@ void InspectorImpl::registerPageStatusListener( } listeners_.push_back(listener); } + +InspectorSystemState InspectorImpl::getSystemState() const { + std::scoped_lock lock(mutex_); + return systemState_; +} } // namespace IInspector& getInspectorInstance() { diff --git a/packages/react-native/ReactCommon/jsinspector-modern/InspectorInterfaces.h b/packages/react-native/ReactCommon/jsinspector-modern/InspectorInterfaces.h index 04e45131e5e7..3117b216ac2d 100644 --- a/packages/react-native/ReactCommon/jsinspector-modern/InspectorInterfaces.h +++ b/packages/react-native/ReactCommon/jsinspector-modern/InspectorInterfaces.h @@ -51,6 +51,11 @@ struct InspectorPageDescription { // Alias for backwards compatibility. using InspectorPage = InspectorPageDescription; +struct InspectorSystemState { + /** The total count of pages registered during the app lifetime. */ + int registeredPagesCount; +}; + /// IRemoteConnection allows the VM to send debugger messages to the client. /// IRemoteConnection's methods are safe to call from any thread *if* /// InspectorPackagerConnection.cpp is in use. @@ -78,7 +83,8 @@ class JSINSPECTOR_EXPORT ILocalConnection : public IDestructible { class JSINSPECTOR_EXPORT IPageStatusListener : public IDestructible { public: virtual ~IPageStatusListener() = 0; - virtual void onPageRemoved(int pageId) = 0; + virtual void onPageAdded(int /*pageId*/) {} + virtual void onPageRemoved(int /*pageId*/) {} }; /// IInspector tracks debuggable JavaScript targets (pages). @@ -127,6 +133,11 @@ class JSINSPECTOR_EXPORT IInspector : public IDestructible { * when pages are removed. */ virtual void registerPageStatusListener(std::weak_ptr listener) = 0; + + /** + * Get the current \c InspectorSystemState object. + */ + virtual InspectorSystemState getSystemState() const = 0; }; class NotImplementedException : public std::exception { diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.cpp b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.cpp index b5782246d80c..096efc910233 100644 --- a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.cpp +++ b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.cpp @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<1a4d03b0d58e389dc9e9af0adf7afa9f>> + * @generated SignedSource<<1cc51e2c15762d3863d0e1fe7c30ef29>> */ /** @@ -254,6 +254,10 @@ bool ReactNativeFeatureFlags::fixMappingOfEventPrioritiesBetweenFabricAndReact() return getAccessor().fixMappingOfEventPrioritiesBetweenFabricAndReact(); } +bool ReactNativeFeatureFlags::fuseboxAssertSingleHostState() { + return getAccessor().fuseboxAssertSingleHostState(); +} + bool ReactNativeFeatureFlags::fuseboxEnabledRelease() { return getAccessor().fuseboxEnabledRelease(); } diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.h b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.h index 5a5533efdb8d..c7604b01a376 100644 --- a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.h +++ b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.h @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<62480a82004544f8365f4f0957eb7319>> + * @generated SignedSource<<0117140f60ecb65706734187cb0c2169>> */ /** @@ -324,6 +324,11 @@ class ReactNativeFeatureFlags { */ RN_EXPORT static bool fixMappingOfEventPrioritiesBetweenFabricAndReact(); + /** + * Enable system assertion validating that Fusebox is configured with a single host. When set, the CDP backend will dynamically disable features (Perf and Network) in the event that multiple hosts are registered (undefined behaviour), and broadcast this over `ReactNativeApplication.systemStateChanged`. + */ + RN_EXPORT static bool fuseboxAssertSingleHostState(); + /** * Flag determining if the React Native DevTools (Fusebox) CDP backend should be enabled in release builds. This flag is global and should not be changed across React Host lifetimes. */ diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.cpp b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.cpp index 18b813aac10e..2f15cda9ecf9 100644 --- a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.cpp +++ b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.cpp @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<58b84c54b09a6f56c37d1391f3e05e12>> + * @generated SignedSource<<9d264b6f4cbd1bde0de0948355eba034>> */ /** @@ -1055,6 +1055,24 @@ bool ReactNativeFeatureFlagsAccessor::fixMappingOfEventPrioritiesBetweenFabricAn return flagValue.value(); } +bool ReactNativeFeatureFlagsAccessor::fuseboxAssertSingleHostState() { + auto flagValue = fuseboxAssertSingleHostState_.load(); + + if (!flagValue.has_value()) { + // This block is not exclusive but it is not necessary. + // If multiple threads try to initialize the feature flag, we would only + // be accessing the provider multiple times but the end state of this + // instance and the returned flag value would be the same. + + markFlagAsAccessed(57, "fuseboxAssertSingleHostState"); + + flagValue = currentProvider_->fuseboxAssertSingleHostState(); + fuseboxAssertSingleHostState_ = flagValue; + } + + return flagValue.value(); +} + bool ReactNativeFeatureFlagsAccessor::fuseboxEnabledRelease() { auto flagValue = fuseboxEnabledRelease_.load(); @@ -1064,7 +1082,7 @@ bool ReactNativeFeatureFlagsAccessor::fuseboxEnabledRelease() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(57, "fuseboxEnabledRelease"); + markFlagAsAccessed(58, "fuseboxEnabledRelease"); flagValue = currentProvider_->fuseboxEnabledRelease(); fuseboxEnabledRelease_ = flagValue; @@ -1082,7 +1100,7 @@ bool ReactNativeFeatureFlagsAccessor::fuseboxNetworkInspectionEnabled() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(58, "fuseboxNetworkInspectionEnabled"); + markFlagAsAccessed(59, "fuseboxNetworkInspectionEnabled"); flagValue = currentProvider_->fuseboxNetworkInspectionEnabled(); fuseboxNetworkInspectionEnabled_ = flagValue; @@ -1100,7 +1118,7 @@ bool ReactNativeFeatureFlagsAccessor::hideOffscreenVirtualViewsOnIOS() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(59, "hideOffscreenVirtualViewsOnIOS"); + markFlagAsAccessed(60, "hideOffscreenVirtualViewsOnIOS"); flagValue = currentProvider_->hideOffscreenVirtualViewsOnIOS(); hideOffscreenVirtualViewsOnIOS_ = flagValue; @@ -1118,7 +1136,7 @@ bool ReactNativeFeatureFlagsAccessor::overrideBySynchronousMountPropsAtMountingA // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(60, "overrideBySynchronousMountPropsAtMountingAndroid"); + markFlagAsAccessed(61, "overrideBySynchronousMountPropsAtMountingAndroid"); flagValue = currentProvider_->overrideBySynchronousMountPropsAtMountingAndroid(); overrideBySynchronousMountPropsAtMountingAndroid_ = flagValue; @@ -1136,7 +1154,7 @@ bool ReactNativeFeatureFlagsAccessor::perfIssuesEnabled() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(61, "perfIssuesEnabled"); + markFlagAsAccessed(62, "perfIssuesEnabled"); flagValue = currentProvider_->perfIssuesEnabled(); perfIssuesEnabled_ = flagValue; @@ -1154,7 +1172,7 @@ bool ReactNativeFeatureFlagsAccessor::perfMonitorV2Enabled() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(62, "perfMonitorV2Enabled"); + markFlagAsAccessed(63, "perfMonitorV2Enabled"); flagValue = currentProvider_->perfMonitorV2Enabled(); perfMonitorV2Enabled_ = flagValue; @@ -1172,7 +1190,7 @@ double ReactNativeFeatureFlagsAccessor::preparedTextCacheSize() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(63, "preparedTextCacheSize"); + markFlagAsAccessed(64, "preparedTextCacheSize"); flagValue = currentProvider_->preparedTextCacheSize(); preparedTextCacheSize_ = flagValue; @@ -1190,7 +1208,7 @@ bool ReactNativeFeatureFlagsAccessor::preventShadowTreeCommitExhaustion() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(64, "preventShadowTreeCommitExhaustion"); + markFlagAsAccessed(65, "preventShadowTreeCommitExhaustion"); flagValue = currentProvider_->preventShadowTreeCommitExhaustion(); preventShadowTreeCommitExhaustion_ = flagValue; @@ -1208,7 +1226,7 @@ bool ReactNativeFeatureFlagsAccessor::shouldPressibilityUseW3CPointerEventsForHo // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(65, "shouldPressibilityUseW3CPointerEventsForHover"); + markFlagAsAccessed(66, "shouldPressibilityUseW3CPointerEventsForHover"); flagValue = currentProvider_->shouldPressibilityUseW3CPointerEventsForHover(); shouldPressibilityUseW3CPointerEventsForHover_ = flagValue; @@ -1226,7 +1244,7 @@ bool ReactNativeFeatureFlagsAccessor::shouldTriggerResponderTransferOnScrollAndr // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(66, "shouldTriggerResponderTransferOnScrollAndroid"); + markFlagAsAccessed(67, "shouldTriggerResponderTransferOnScrollAndroid"); flagValue = currentProvider_->shouldTriggerResponderTransferOnScrollAndroid(); shouldTriggerResponderTransferOnScrollAndroid_ = flagValue; @@ -1244,7 +1262,7 @@ bool ReactNativeFeatureFlagsAccessor::skipActivityIdentityAssertionOnHostPause() // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(67, "skipActivityIdentityAssertionOnHostPause"); + markFlagAsAccessed(68, "skipActivityIdentityAssertionOnHostPause"); flagValue = currentProvider_->skipActivityIdentityAssertionOnHostPause(); skipActivityIdentityAssertionOnHostPause_ = flagValue; @@ -1262,7 +1280,7 @@ bool ReactNativeFeatureFlagsAccessor::traceTurboModulePromiseRejectionsOnAndroid // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(68, "traceTurboModulePromiseRejectionsOnAndroid"); + markFlagAsAccessed(69, "traceTurboModulePromiseRejectionsOnAndroid"); flagValue = currentProvider_->traceTurboModulePromiseRejectionsOnAndroid(); traceTurboModulePromiseRejectionsOnAndroid_ = flagValue; @@ -1280,7 +1298,7 @@ bool ReactNativeFeatureFlagsAccessor::updateRuntimeShadowNodeReferencesOnCommit( // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(69, "updateRuntimeShadowNodeReferencesOnCommit"); + markFlagAsAccessed(70, "updateRuntimeShadowNodeReferencesOnCommit"); flagValue = currentProvider_->updateRuntimeShadowNodeReferencesOnCommit(); updateRuntimeShadowNodeReferencesOnCommit_ = flagValue; @@ -1298,7 +1316,7 @@ bool ReactNativeFeatureFlagsAccessor::useAlwaysAvailableJSErrorHandling() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(70, "useAlwaysAvailableJSErrorHandling"); + markFlagAsAccessed(71, "useAlwaysAvailableJSErrorHandling"); flagValue = currentProvider_->useAlwaysAvailableJSErrorHandling(); useAlwaysAvailableJSErrorHandling_ = flagValue; @@ -1316,7 +1334,7 @@ bool ReactNativeFeatureFlagsAccessor::useFabricInterop() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(71, "useFabricInterop"); + markFlagAsAccessed(72, "useFabricInterop"); flagValue = currentProvider_->useFabricInterop(); useFabricInterop_ = flagValue; @@ -1334,7 +1352,7 @@ bool ReactNativeFeatureFlagsAccessor::useNativeEqualsInNativeReadableArrayAndroi // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(72, "useNativeEqualsInNativeReadableArrayAndroid"); + markFlagAsAccessed(73, "useNativeEqualsInNativeReadableArrayAndroid"); flagValue = currentProvider_->useNativeEqualsInNativeReadableArrayAndroid(); useNativeEqualsInNativeReadableArrayAndroid_ = flagValue; @@ -1352,7 +1370,7 @@ bool ReactNativeFeatureFlagsAccessor::useNativeTransformHelperAndroid() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(73, "useNativeTransformHelperAndroid"); + markFlagAsAccessed(74, "useNativeTransformHelperAndroid"); flagValue = currentProvider_->useNativeTransformHelperAndroid(); useNativeTransformHelperAndroid_ = flagValue; @@ -1370,7 +1388,7 @@ bool ReactNativeFeatureFlagsAccessor::useNativeViewConfigsInBridgelessMode() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(74, "useNativeViewConfigsInBridgelessMode"); + markFlagAsAccessed(75, "useNativeViewConfigsInBridgelessMode"); flagValue = currentProvider_->useNativeViewConfigsInBridgelessMode(); useNativeViewConfigsInBridgelessMode_ = flagValue; @@ -1388,7 +1406,7 @@ bool ReactNativeFeatureFlagsAccessor::useRawPropsJsiValue() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(75, "useRawPropsJsiValue"); + markFlagAsAccessed(76, "useRawPropsJsiValue"); flagValue = currentProvider_->useRawPropsJsiValue(); useRawPropsJsiValue_ = flagValue; @@ -1406,7 +1424,7 @@ bool ReactNativeFeatureFlagsAccessor::useShadowNodeStateOnClone() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(76, "useShadowNodeStateOnClone"); + markFlagAsAccessed(77, "useShadowNodeStateOnClone"); flagValue = currentProvider_->useShadowNodeStateOnClone(); useShadowNodeStateOnClone_ = flagValue; @@ -1424,7 +1442,7 @@ bool ReactNativeFeatureFlagsAccessor::useSharedAnimatedBackend() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(77, "useSharedAnimatedBackend"); + markFlagAsAccessed(78, "useSharedAnimatedBackend"); flagValue = currentProvider_->useSharedAnimatedBackend(); useSharedAnimatedBackend_ = flagValue; @@ -1442,7 +1460,7 @@ bool ReactNativeFeatureFlagsAccessor::useTraitHiddenOnAndroid() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(78, "useTraitHiddenOnAndroid"); + markFlagAsAccessed(79, "useTraitHiddenOnAndroid"); flagValue = currentProvider_->useTraitHiddenOnAndroid(); useTraitHiddenOnAndroid_ = flagValue; @@ -1460,7 +1478,7 @@ bool ReactNativeFeatureFlagsAccessor::useTurboModuleInterop() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(79, "useTurboModuleInterop"); + markFlagAsAccessed(80, "useTurboModuleInterop"); flagValue = currentProvider_->useTurboModuleInterop(); useTurboModuleInterop_ = flagValue; @@ -1478,7 +1496,7 @@ bool ReactNativeFeatureFlagsAccessor::useTurboModules() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(80, "useTurboModules"); + markFlagAsAccessed(81, "useTurboModules"); flagValue = currentProvider_->useTurboModules(); useTurboModules_ = flagValue; @@ -1496,7 +1514,7 @@ double ReactNativeFeatureFlagsAccessor::viewCullingOutsetRatio() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(81, "viewCullingOutsetRatio"); + markFlagAsAccessed(82, "viewCullingOutsetRatio"); flagValue = currentProvider_->viewCullingOutsetRatio(); viewCullingOutsetRatio_ = flagValue; @@ -1514,7 +1532,7 @@ double ReactNativeFeatureFlagsAccessor::virtualViewHysteresisRatio() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(82, "virtualViewHysteresisRatio"); + markFlagAsAccessed(83, "virtualViewHysteresisRatio"); flagValue = currentProvider_->virtualViewHysteresisRatio(); virtualViewHysteresisRatio_ = flagValue; @@ -1532,7 +1550,7 @@ double ReactNativeFeatureFlagsAccessor::virtualViewPrerenderRatio() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(83, "virtualViewPrerenderRatio"); + markFlagAsAccessed(84, "virtualViewPrerenderRatio"); flagValue = currentProvider_->virtualViewPrerenderRatio(); virtualViewPrerenderRatio_ = flagValue; diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.h b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.h index 9282cbf4cb0b..1c897a86b72a 100644 --- a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.h +++ b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.h @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<4e1bcac109eecdaf11c62ce8c367b20d>> + * @generated SignedSource<<5e53aa1a6fbbdc6e04a747e76677960c>> */ /** @@ -89,6 +89,7 @@ class ReactNativeFeatureFlagsAccessor { bool enableVirtualViewWindowFocusDetection(); bool enableWebPerformanceAPIsByDefault(); bool fixMappingOfEventPrioritiesBetweenFabricAndReact(); + bool fuseboxAssertSingleHostState(); bool fuseboxEnabledRelease(); bool fuseboxNetworkInspectionEnabled(); bool hideOffscreenVirtualViewsOnIOS(); @@ -127,7 +128,7 @@ class ReactNativeFeatureFlagsAccessor { std::unique_ptr currentProvider_; bool wasOverridden_; - std::array, 84> accessedFeatureFlags_; + std::array, 85> accessedFeatureFlags_; std::atomic> commonTestFlag_; std::atomic> cdpInteractionMetricsEnabled_; @@ -186,6 +187,7 @@ class ReactNativeFeatureFlagsAccessor { std::atomic> enableVirtualViewWindowFocusDetection_; std::atomic> enableWebPerformanceAPIsByDefault_; std::atomic> fixMappingOfEventPrioritiesBetweenFabricAndReact_; + std::atomic> fuseboxAssertSingleHostState_; std::atomic> fuseboxEnabledRelease_; std::atomic> fuseboxNetworkInspectionEnabled_; std::atomic> hideOffscreenVirtualViewsOnIOS_; diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDefaults.h b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDefaults.h index 2442aeffde6b..a98eea20158d 100644 --- a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDefaults.h +++ b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDefaults.h @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<> + * @generated SignedSource<<28355cbf76db9aa5aca8ea05db43d5ab>> */ /** @@ -255,6 +255,10 @@ class ReactNativeFeatureFlagsDefaults : public ReactNativeFeatureFlagsProvider { return false; } + bool fuseboxAssertSingleHostState() override { + return true; + } + bool fuseboxEnabledRelease() override { return false; } diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDynamicProvider.h b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDynamicProvider.h index 86f187e742d2..58e4f92e8706 100644 --- a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDynamicProvider.h +++ b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDynamicProvider.h @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<> + * @generated SignedSource<<9d03fa7e131f02dc08a6ffa75d09bdba>> */ /** @@ -558,6 +558,15 @@ class ReactNativeFeatureFlagsDynamicProvider : public ReactNativeFeatureFlagsDef return ReactNativeFeatureFlagsDefaults::fixMappingOfEventPrioritiesBetweenFabricAndReact(); } + bool fuseboxAssertSingleHostState() override { + auto value = values_["fuseboxAssertSingleHostState"]; + if (!value.isNull()) { + return value.getBool(); + } + + return ReactNativeFeatureFlagsDefaults::fuseboxAssertSingleHostState(); + } + bool fuseboxEnabledRelease() override { auto value = values_["fuseboxEnabledRelease"]; if (!value.isNull()) { diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsProvider.h b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsProvider.h index 94df9d27657f..9c5096083432 100644 --- a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsProvider.h +++ b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsProvider.h @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<724a7046fefc47616249d6fac8367615>> + * @generated SignedSource<<71b74d31b17c008bbf5a0e381cdabe94>> */ /** @@ -82,6 +82,7 @@ class ReactNativeFeatureFlagsProvider { virtual bool enableVirtualViewWindowFocusDetection() = 0; virtual bool enableWebPerformanceAPIsByDefault() = 0; virtual bool fixMappingOfEventPrioritiesBetweenFabricAndReact() = 0; + virtual bool fuseboxAssertSingleHostState() = 0; virtual bool fuseboxEnabledRelease() = 0; virtual bool fuseboxNetworkInspectionEnabled() = 0; virtual bool hideOffscreenVirtualViewsOnIOS() = 0; diff --git a/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.cpp b/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.cpp index 8ebd5c0d73f4..6e42444887ca 100644 --- a/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.cpp +++ b/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.cpp @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<19cc3c19f4e6115556fa29320ab47ff2>> + * @generated SignedSource<<06396bfb1e5af10dd7ef72fd1d17607b>> */ /** @@ -329,6 +329,11 @@ bool NativeReactNativeFeatureFlags::fixMappingOfEventPrioritiesBetweenFabricAndR return ReactNativeFeatureFlags::fixMappingOfEventPrioritiesBetweenFabricAndReact(); } +bool NativeReactNativeFeatureFlags::fuseboxAssertSingleHostState( + jsi::Runtime& /*runtime*/) { + return ReactNativeFeatureFlags::fuseboxAssertSingleHostState(); +} + bool NativeReactNativeFeatureFlags::fuseboxEnabledRelease( jsi::Runtime& /*runtime*/) { return ReactNativeFeatureFlags::fuseboxEnabledRelease(); diff --git a/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.h b/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.h index 48b4a0cf84ce..6bd88ffec96e 100644 --- a/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.h +++ b/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.h @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<65a06880aea8299c68e57e5b48a2fed8>> + * @generated SignedSource<<9d84bc60152fe0f894d49c8b45eb6505>> */ /** @@ -150,6 +150,8 @@ class NativeReactNativeFeatureFlags bool fixMappingOfEventPrioritiesBetweenFabricAndReact(jsi::Runtime& runtime); + bool fuseboxAssertSingleHostState(jsi::Runtime& runtime); + bool fuseboxEnabledRelease(jsi::Runtime& runtime); bool fuseboxNetworkInspectionEnabled(jsi::Runtime& runtime); diff --git a/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js b/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js index 727b4cef3299..86d965bf6730 100644 --- a/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js +++ b/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js @@ -656,6 +656,16 @@ const definitions: FeatureFlagDefinitions = { }, ossReleaseStage: 'none', }, + fuseboxAssertSingleHostState: { + defaultValue: true, + metadata: { + description: + 'Enable system assertion validating that Fusebox is configured with a single host. When set, the CDP backend will dynamically disable features (Perf and Network) in the event that multiple hosts are registered (undefined behaviour), and broadcast this over `ReactNativeApplication.systemStateChanged`.', + expectedReleaseValue: true, + purpose: 'operational', + }, + ossReleaseStage: 'stable', + }, fuseboxEnabledRelease: { defaultValue: false, metadata: { diff --git a/packages/react-native/src/private/featureflags/ReactNativeFeatureFlags.js b/packages/react-native/src/private/featureflags/ReactNativeFeatureFlags.js index da2ad4d63a03..faf339cd6975 100644 --- a/packages/react-native/src/private/featureflags/ReactNativeFeatureFlags.js +++ b/packages/react-native/src/private/featureflags/ReactNativeFeatureFlags.js @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<77fabf68a75a0d8a563b032027e7f817>> + * @generated SignedSource<> * @flow strict * @noformat */ @@ -107,6 +107,7 @@ export type ReactNativeFeatureFlags = $ReadOnly<{ enableVirtualViewWindowFocusDetection: Getter, enableWebPerformanceAPIsByDefault: Getter, fixMappingOfEventPrioritiesBetweenFabricAndReact: Getter, + fuseboxAssertSingleHostState: Getter, fuseboxEnabledRelease: Getter, fuseboxNetworkInspectionEnabled: Getter, hideOffscreenVirtualViewsOnIOS: Getter, @@ -443,6 +444,10 @@ export const enableWebPerformanceAPIsByDefault: Getter = createNativeFl * Uses the default event priority instead of the discreet event priority by default when dispatching events from Fabric to React. */ export const fixMappingOfEventPrioritiesBetweenFabricAndReact: Getter = createNativeFlagGetter('fixMappingOfEventPrioritiesBetweenFabricAndReact', false); +/** + * Enable system assertion validating that Fusebox is configured with a single host. When set, the CDP backend will dynamically disable features (Perf and Network) in the event that multiple hosts are registered (undefined behaviour), and broadcast this over `ReactNativeApplication.systemStateChanged`. + */ +export const fuseboxAssertSingleHostState: Getter = createNativeFlagGetter('fuseboxAssertSingleHostState', true); /** * Flag determining if the React Native DevTools (Fusebox) CDP backend should be enabled in release builds. This flag is global and should not be changed across React Host lifetimes. */ diff --git a/packages/react-native/src/private/featureflags/specs/NativeReactNativeFeatureFlags.js b/packages/react-native/src/private/featureflags/specs/NativeReactNativeFeatureFlags.js index a57298b06d14..f4f7fedb3d4d 100644 --- a/packages/react-native/src/private/featureflags/specs/NativeReactNativeFeatureFlags.js +++ b/packages/react-native/src/private/featureflags/specs/NativeReactNativeFeatureFlags.js @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<5819b97ded2f415bdcff52f7f408aec6>> + * @generated SignedSource<<4faf238a79cec9bc961d5f080d54872a>> * @flow strict * @noformat */ @@ -82,6 +82,7 @@ export interface Spec extends TurboModule { +enableVirtualViewWindowFocusDetection?: () => boolean; +enableWebPerformanceAPIsByDefault?: () => boolean; +fixMappingOfEventPrioritiesBetweenFabricAndReact?: () => boolean; + +fuseboxAssertSingleHostState?: () => boolean; +fuseboxEnabledRelease?: () => boolean; +fuseboxNetworkInspectionEnabled?: () => boolean; +hideOffscreenVirtualViewsOnIOS?: () => boolean;