Skip to content

Commit 519a693

Browse files
huntiefacebook-github-bot
authored andcommitted
Add single host inspector state assertion over CDP (#54407)
Summary: 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
1 parent cc3859f commit 519a693

27 files changed

+258
-48
lines changed

packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlags.kt

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
* This source code is licensed under the MIT license found in the
55
* LICENSE file in the root directory of this source tree.
66
*
7-
* @generated SignedSource<<9646ebeba75ec903be5ade7e2333f0c8>>
7+
* @generated SignedSource<<248d4e5e599c3af43dc352c8bb1a2c13>>
88
*/
99

1010
/**
@@ -360,6 +360,12 @@ public object ReactNativeFeatureFlags {
360360
@JvmStatic
361361
public fun fixMappingOfEventPrioritiesBetweenFabricAndReact(): Boolean = accessor.fixMappingOfEventPrioritiesBetweenFabricAndReact()
362362

363+
/**
364+
* 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`.
365+
*/
366+
@JvmStatic
367+
public fun fuseboxAssertSingleHostState(): Boolean = accessor.fuseboxAssertSingleHostState()
368+
363369
/**
364370
* 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.
365371
*/

packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsCxxAccessor.kt

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
* This source code is licensed under the MIT license found in the
55
* LICENSE file in the root directory of this source tree.
66
*
7-
* @generated SignedSource<<9d6ccbe6d02608901fc18ad88baab176>>
7+
* @generated SignedSource<<fe1d9a861817477da12de7f29d01f3d6>>
88
*/
99

1010
/**
@@ -75,6 +75,7 @@ internal class ReactNativeFeatureFlagsCxxAccessor : ReactNativeFeatureFlagsAcces
7575
private var enableVirtualViewWindowFocusDetectionCache: Boolean? = null
7676
private var enableWebPerformanceAPIsByDefaultCache: Boolean? = null
7777
private var fixMappingOfEventPrioritiesBetweenFabricAndReactCache: Boolean? = null
78+
private var fuseboxAssertSingleHostStateCache: Boolean? = null
7879
private var fuseboxEnabledReleaseCache: Boolean? = null
7980
private var fuseboxNetworkInspectionEnabledCache: Boolean? = null
8081
private var hideOffscreenVirtualViewsOnIOSCache: Boolean? = null
@@ -598,6 +599,15 @@ internal class ReactNativeFeatureFlagsCxxAccessor : ReactNativeFeatureFlagsAcces
598599
return cached
599600
}
600601

602+
override fun fuseboxAssertSingleHostState(): Boolean {
603+
var cached = fuseboxAssertSingleHostStateCache
604+
if (cached == null) {
605+
cached = ReactNativeFeatureFlagsCxxInterop.fuseboxAssertSingleHostState()
606+
fuseboxAssertSingleHostStateCache = cached
607+
}
608+
return cached
609+
}
610+
601611
override fun fuseboxEnabledRelease(): Boolean {
602612
var cached = fuseboxEnabledReleaseCache
603613
if (cached == null) {

packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsCxxInterop.kt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
* This source code is licensed under the MIT license found in the
55
* LICENSE file in the root directory of this source tree.
66
*
7-
* @generated SignedSource<<fbc551ca005a7d8abcd2cf2e5d29a3a6>>
7+
* @generated SignedSource<<be15f5dd5f62198214c956d133de4671>>
88
*/
99

1010
/**
@@ -138,6 +138,8 @@ public object ReactNativeFeatureFlagsCxxInterop {
138138

139139
@DoNotStrip @JvmStatic public external fun fixMappingOfEventPrioritiesBetweenFabricAndReact(): Boolean
140140

141+
@DoNotStrip @JvmStatic public external fun fuseboxAssertSingleHostState(): Boolean
142+
141143
@DoNotStrip @JvmStatic public external fun fuseboxEnabledRelease(): Boolean
142144

143145
@DoNotStrip @JvmStatic public external fun fuseboxNetworkInspectionEnabled(): Boolean

packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsDefaults.kt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
* This source code is licensed under the MIT license found in the
55
* LICENSE file in the root directory of this source tree.
66
*
7-
* @generated SignedSource<<7b8a5ad9a3353ea32a39bd139e9174f7>>
7+
* @generated SignedSource<<30041179508b76146b4a6ff70f7d2090>>
88
*/
99

1010
/**
@@ -133,6 +133,8 @@ public open class ReactNativeFeatureFlagsDefaults : ReactNativeFeatureFlagsProvi
133133

134134
override fun fixMappingOfEventPrioritiesBetweenFabricAndReact(): Boolean = false
135135

136+
override fun fuseboxAssertSingleHostState(): Boolean = true
137+
136138
override fun fuseboxEnabledRelease(): Boolean = false
137139

138140
override fun fuseboxNetworkInspectionEnabled(): Boolean = false

packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsLocalAccessor.kt

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
* This source code is licensed under the MIT license found in the
55
* LICENSE file in the root directory of this source tree.
66
*
7-
* @generated SignedSource<<9bb9a7cf89c92f5a397b2328fa983dc6>>
7+
* @generated SignedSource<<78f60952fc7a5cd0efbf8ba8478251a7>>
88
*/
99

1010
/**
@@ -79,6 +79,7 @@ internal class ReactNativeFeatureFlagsLocalAccessor : ReactNativeFeatureFlagsAcc
7979
private var enableVirtualViewWindowFocusDetectionCache: Boolean? = null
8080
private var enableWebPerformanceAPIsByDefaultCache: Boolean? = null
8181
private var fixMappingOfEventPrioritiesBetweenFabricAndReactCache: Boolean? = null
82+
private var fuseboxAssertSingleHostStateCache: Boolean? = null
8283
private var fuseboxEnabledReleaseCache: Boolean? = null
8384
private var fuseboxNetworkInspectionEnabledCache: Boolean? = null
8485
private var hideOffscreenVirtualViewsOnIOSCache: Boolean? = null
@@ -657,6 +658,16 @@ internal class ReactNativeFeatureFlagsLocalAccessor : ReactNativeFeatureFlagsAcc
657658
return cached
658659
}
659660

661+
override fun fuseboxAssertSingleHostState(): Boolean {
662+
var cached = fuseboxAssertSingleHostStateCache
663+
if (cached == null) {
664+
cached = currentProvider.fuseboxAssertSingleHostState()
665+
accessedFeatureFlags.add("fuseboxAssertSingleHostState")
666+
fuseboxAssertSingleHostStateCache = cached
667+
}
668+
return cached
669+
}
670+
660671
override fun fuseboxEnabledRelease(): Boolean {
661672
var cached = fuseboxEnabledReleaseCache
662673
if (cached == null) {

packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsProvider.kt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
* This source code is licensed under the MIT license found in the
55
* LICENSE file in the root directory of this source tree.
66
*
7-
* @generated SignedSource<<eeb5d70e45eecdef0d9307cbe8ff17c2>>
7+
* @generated SignedSource<<865a11fa77d36e33d3bb6548cce01e66>>
88
*/
99

1010
/**
@@ -133,6 +133,8 @@ public interface ReactNativeFeatureFlagsProvider {
133133

134134
@DoNotStrip public fun fixMappingOfEventPrioritiesBetweenFabricAndReact(): Boolean
135135

136+
@DoNotStrip public fun fuseboxAssertSingleHostState(): Boolean
137+
136138
@DoNotStrip public fun fuseboxEnabledRelease(): Boolean
137139

138140
@DoNotStrip public fun fuseboxNetworkInspectionEnabled(): Boolean

packages/react-native/ReactAndroid/src/main/jni/react/featureflags/JReactNativeFeatureFlagsCxxInterop.cpp

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
* This source code is licensed under the MIT license found in the
55
* LICENSE file in the root directory of this source tree.
66
*
7-
* @generated SignedSource<<0527dbb4a838be34b80d76b11d18cea0>>
7+
* @generated SignedSource<<975dfce969af2eda791cd6dc37d6ccc2>>
88
*/
99

1010
/**
@@ -369,6 +369,12 @@ class ReactNativeFeatureFlagsJavaProvider
369369
return method(javaProvider_);
370370
}
371371

372+
bool fuseboxAssertSingleHostState() override {
373+
static const auto method =
374+
getReactNativeFeatureFlagsProviderJavaClass()->getMethod<jboolean()>("fuseboxAssertSingleHostState");
375+
return method(javaProvider_);
376+
}
377+
372378
bool fuseboxEnabledRelease() override {
373379
static const auto method =
374380
getReactNativeFeatureFlagsProviderJavaClass()->getMethod<jboolean()>("fuseboxEnabledRelease");
@@ -810,6 +816,11 @@ bool JReactNativeFeatureFlagsCxxInterop::fixMappingOfEventPrioritiesBetweenFabri
810816
return ReactNativeFeatureFlags::fixMappingOfEventPrioritiesBetweenFabricAndReact();
811817
}
812818

819+
bool JReactNativeFeatureFlagsCxxInterop::fuseboxAssertSingleHostState(
820+
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop> /*unused*/) {
821+
return ReactNativeFeatureFlags::fuseboxAssertSingleHostState();
822+
}
823+
813824
bool JReactNativeFeatureFlagsCxxInterop::fuseboxEnabledRelease(
814825
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop> /*unused*/) {
815826
return ReactNativeFeatureFlags::fuseboxEnabledRelease();
@@ -1141,6 +1152,9 @@ void JReactNativeFeatureFlagsCxxInterop::registerNatives() {
11411152
makeNativeMethod(
11421153
"fixMappingOfEventPrioritiesBetweenFabricAndReact",
11431154
JReactNativeFeatureFlagsCxxInterop::fixMappingOfEventPrioritiesBetweenFabricAndReact),
1155+
makeNativeMethod(
1156+
"fuseboxAssertSingleHostState",
1157+
JReactNativeFeatureFlagsCxxInterop::fuseboxAssertSingleHostState),
11441158
makeNativeMethod(
11451159
"fuseboxEnabledRelease",
11461160
JReactNativeFeatureFlagsCxxInterop::fuseboxEnabledRelease),

packages/react-native/ReactAndroid/src/main/jni/react/featureflags/JReactNativeFeatureFlagsCxxInterop.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
* This source code is licensed under the MIT license found in the
55
* LICENSE file in the root directory of this source tree.
66
*
7-
* @generated SignedSource<<a1deb0145d8c51d608903e1704e96b8d>>
7+
* @generated SignedSource<<97c126a1763860d25ee216150156e200>>
88
*/
99

1010
/**
@@ -195,6 +195,9 @@ class JReactNativeFeatureFlagsCxxInterop
195195
static bool fixMappingOfEventPrioritiesBetweenFabricAndReact(
196196
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop>);
197197

198+
static bool fuseboxAssertSingleHostState(
199+
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop>);
200+
198201
static bool fuseboxEnabledRelease(
199202
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop>);
200203

packages/react-native/ReactCommon/jsinspector-modern/HostAgent.cpp

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
#ifdef REACT_NATIVE_DEBUGGER_ENABLED
1212
#include "InspectorFlags.h"
13+
#include "InspectorInterfaces.h"
1314
#include "NetworkIOAgent.h"
1415
#include "SessionState.h"
1516
#include "TracingAgent.h"
@@ -216,6 +217,11 @@ class HostAgent::Impl final {
216217
cdp::jsonNotification(
217218
"ReactNativeApplication.metadataUpdated",
218219
createHostMetadataPayload(hostMetadata_)));
220+
auto& inspector = getInspectorInstance();
221+
bool isSingleHost = inspector.getSystemState().registeredPagesCount <= 1;
222+
if (!isSingleHost) {
223+
emitSystemStateChanged(isSingleHost);
224+
}
219225

220226
auto stashedTraceRecording =
221227
targetController_.getDelegate()
@@ -374,6 +380,17 @@ class HostAgent::Impl final {
374380
tracingAgent_.emitExternalTraceRecording(std::move(traceRecording));
375381
}
376382

383+
void emitSystemStateChanged(bool isSingleHost) {
384+
frontendChannel_(
385+
cdp::jsonNotification(
386+
"ReactNativeApplication.systemStateChanged",
387+
folly::dynamic::object("isSingleHost", isSingleHost)));
388+
389+
if (!isSingleHost) {
390+
frontendChannel_(cdp::jsonNotification("Network.disable"));
391+
}
392+
}
393+
377394
private:
378395
enum class FuseboxClientType { Unknown, Fusebox, NonFusebox };
379396

@@ -480,6 +497,7 @@ class HostAgent::Impl final {
480497
}
481498
void emitExternalTraceRecording(tracing::TraceRecordingState traceRecording) {
482499
}
500+
void emitSystemStateChanged(bool isSingleHost) {}
483501
};
484502

485503
#endif // REACT_NATIVE_DEBUGGER_ENABLED
@@ -519,6 +537,10 @@ void HostAgent::emitExternalTraceRecording(
519537
impl_->emitExternalTraceRecording(std::move(traceRecording));
520538
}
521539

540+
void HostAgent::emitSystemStateChanged(bool isSingleHost) const {
541+
impl_->emitSystemStateChanged(isSingleHost);
542+
}
543+
522544
#pragma mark - Tracing
523545

524546
HostTracingAgent::HostTracingAgent(tracing::TraceRecordingState& state)

packages/react-native/ReactCommon/jsinspector-modern/HostAgent.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,12 @@ class HostAgent final {
7979
*/
8080
void emitExternalTraceRecording(tracing::TraceRecordingState traceRecording) const;
8181

82+
/**
83+
* Emits a system state changed event when the number of ReactHost instances
84+
* changes.
85+
*/
86+
void emitSystemStateChanged(bool isSingleHost) const;
87+
8288
private:
8389
// We use the private implementation idiom to ensure this class has the same
8490
// layout regardless of whether REACT_NATIVE_DEBUGGER_ENABLED is defined. The

0 commit comments

Comments
 (0)