Skip to content

Commit 74d687c

Browse files
committed
Add single host inspector state assertion over CDP (#54407)
Summary: Pull Request resolved: #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] Reviewed By: cipolleschi Differential Revision: D86201689 fbshipit-source-id: b8525359fc01eb7ae666879f2c0fd4c94f0af81a
1 parent 8573650 commit 74d687c

27 files changed

Lines changed: 260 additions & 50 deletions

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<<d8dc8c3a2efe8b7e870546e5749ef5ad>>
7+
* @generated SignedSource<<09d1f1bdf5e156afcd7ce15f73ca1c3b>>
88
*/
99

1010
/**
@@ -354,6 +354,12 @@ public object ReactNativeFeatureFlags {
354354
@JvmStatic
355355
public fun fixMappingOfEventPrioritiesBetweenFabricAndReact(): Boolean = accessor.fixMappingOfEventPrioritiesBetweenFabricAndReact()
356356

357+
/**
358+
* 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`.
359+
*/
360+
@JvmStatic
361+
public fun fuseboxAssertSingleHostState(): Boolean = accessor.fuseboxAssertSingleHostState()
362+
357363
/**
358364
* 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.
359365
*/

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<<447ccc7271b71b0208a2297b7eba5995>>
7+
* @generated SignedSource<<017aba8e549dc289c27bb4fa93e6d038>>
88
*/
99

1010
/**
@@ -74,6 +74,7 @@ internal class ReactNativeFeatureFlagsCxxAccessor : ReactNativeFeatureFlagsAcces
7474
private var enableVirtualViewWindowFocusDetectionCache: Boolean? = null
7575
private var enableWebPerformanceAPIsByDefaultCache: Boolean? = null
7676
private var fixMappingOfEventPrioritiesBetweenFabricAndReactCache: Boolean? = null
77+
private var fuseboxAssertSingleHostStateCache: Boolean? = null
7778
private var fuseboxEnabledReleaseCache: Boolean? = null
7879
private var fuseboxNetworkInspectionEnabledCache: Boolean? = null
7980
private var hideOffscreenVirtualViewsOnIOSCache: Boolean? = null
@@ -590,6 +591,15 @@ internal class ReactNativeFeatureFlagsCxxAccessor : ReactNativeFeatureFlagsAcces
590591
return cached
591592
}
592593

594+
override fun fuseboxAssertSingleHostState(): Boolean {
595+
var cached = fuseboxAssertSingleHostStateCache
596+
if (cached == null) {
597+
cached = ReactNativeFeatureFlagsCxxInterop.fuseboxAssertSingleHostState()
598+
fuseboxAssertSingleHostStateCache = cached
599+
}
600+
return cached
601+
}
602+
593603
override fun fuseboxEnabledRelease(): Boolean {
594604
var cached = fuseboxEnabledReleaseCache
595605
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<<d6403b67e269ad80ef3e2389ac923c8a>>
7+
* @generated SignedSource<<278f3853a98e1dd72b66ad6168e11686>>
88
*/
99

1010
/**
@@ -136,6 +136,8 @@ public object ReactNativeFeatureFlagsCxxInterop {
136136

137137
@DoNotStrip @JvmStatic public external fun fixMappingOfEventPrioritiesBetweenFabricAndReact(): Boolean
138138

139+
@DoNotStrip @JvmStatic public external fun fuseboxAssertSingleHostState(): Boolean
140+
139141
@DoNotStrip @JvmStatic public external fun fuseboxEnabledRelease(): Boolean
140142

141143
@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<<ab48ba86a7cf3cd35ec530a216aea774>>
7+
* @generated SignedSource<<31da0143bec4eacc9884c950970f0a25>>
88
*/
99

1010
/**
@@ -131,6 +131,8 @@ public open class ReactNativeFeatureFlagsDefaults : ReactNativeFeatureFlagsProvi
131131

132132
override fun fixMappingOfEventPrioritiesBetweenFabricAndReact(): Boolean = false
133133

134+
override fun fuseboxAssertSingleHostState(): Boolean = true
135+
134136
override fun fuseboxEnabledRelease(): Boolean = false
135137

136138
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<<6f6b5abe79764b88cf3ed62fe0230786>>
7+
* @generated SignedSource<<14a5680355bb8c42e39a67edfa6a8974>>
88
*/
99

1010
/**
@@ -78,6 +78,7 @@ internal class ReactNativeFeatureFlagsLocalAccessor : ReactNativeFeatureFlagsAcc
7878
private var enableVirtualViewWindowFocusDetectionCache: Boolean? = null
7979
private var enableWebPerformanceAPIsByDefaultCache: Boolean? = null
8080
private var fixMappingOfEventPrioritiesBetweenFabricAndReactCache: Boolean? = null
81+
private var fuseboxAssertSingleHostStateCache: Boolean? = null
8182
private var fuseboxEnabledReleaseCache: Boolean? = null
8283
private var fuseboxNetworkInspectionEnabledCache: Boolean? = null
8384
private var hideOffscreenVirtualViewsOnIOSCache: Boolean? = null
@@ -648,6 +649,16 @@ internal class ReactNativeFeatureFlagsLocalAccessor : ReactNativeFeatureFlagsAcc
648649
return cached
649650
}
650651

652+
override fun fuseboxAssertSingleHostState(): Boolean {
653+
var cached = fuseboxAssertSingleHostStateCache
654+
if (cached == null) {
655+
cached = currentProvider.fuseboxAssertSingleHostState()
656+
accessedFeatureFlags.add("fuseboxAssertSingleHostState")
657+
fuseboxAssertSingleHostStateCache = cached
658+
}
659+
return cached
660+
}
661+
651662
override fun fuseboxEnabledRelease(): Boolean {
652663
var cached = fuseboxEnabledReleaseCache
653664
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<<6b61490a8d6b1df1d6264016455382f8>>
7+
* @generated SignedSource<<5a348c354bb6dd2ddfbc869a9e2f0dbe>>
88
*/
99

1010
/**
@@ -131,6 +131,8 @@ public interface ReactNativeFeatureFlagsProvider {
131131

132132
@DoNotStrip public fun fixMappingOfEventPrioritiesBetweenFabricAndReact(): Boolean
133133

134+
@DoNotStrip public fun fuseboxAssertSingleHostState(): Boolean
135+
134136
@DoNotStrip public fun fuseboxEnabledRelease(): Boolean
135137

136138
@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<<13ed11b5c260fae79048ea80745d22fe>>
7+
* @generated SignedSource<<5b2d44dcf36386f86f26c694d3f00229>>
88
*/
99

1010
/**
@@ -363,6 +363,12 @@ class ReactNativeFeatureFlagsJavaProvider
363363
return method(javaProvider_);
364364
}
365365

366+
bool fuseboxAssertSingleHostState() override {
367+
static const auto method =
368+
getReactNativeFeatureFlagsProviderJavaClass()->getMethod<jboolean()>("fuseboxAssertSingleHostState");
369+
return method(javaProvider_);
370+
}
371+
366372
bool fuseboxEnabledRelease() override {
367373
static const auto method =
368374
getReactNativeFeatureFlagsProviderJavaClass()->getMethod<jboolean()>("fuseboxEnabledRelease");
@@ -811,6 +817,11 @@ bool JReactNativeFeatureFlagsCxxInterop::fixMappingOfEventPrioritiesBetweenFabri
811817
return ReactNativeFeatureFlags::fixMappingOfEventPrioritiesBetweenFabricAndReact();
812818
}
813819

820+
bool JReactNativeFeatureFlagsCxxInterop::fuseboxAssertSingleHostState(
821+
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop> /*unused*/) {
822+
return ReactNativeFeatureFlags::fuseboxAssertSingleHostState();
823+
}
824+
814825
bool JReactNativeFeatureFlagsCxxInterop::fuseboxEnabledRelease(
815826
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop> /*unused*/) {
816827
return ReactNativeFeatureFlags::fuseboxEnabledRelease();
@@ -1149,6 +1160,9 @@ void JReactNativeFeatureFlagsCxxInterop::registerNatives() {
11491160
makeNativeMethod(
11501161
"fixMappingOfEventPrioritiesBetweenFabricAndReact",
11511162
JReactNativeFeatureFlagsCxxInterop::fixMappingOfEventPrioritiesBetweenFabricAndReact),
1163+
makeNativeMethod(
1164+
"fuseboxAssertSingleHostState",
1165+
JReactNativeFeatureFlagsCxxInterop::fuseboxAssertSingleHostState),
11521166
makeNativeMethod(
11531167
"fuseboxEnabledRelease",
11541168
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<<1e57bf8252c16fc10bbfcae08710af85>>
7+
* @generated SignedSource<<5a03db93d78fc1b14283ca3dc92db207>>
88
*/
99

1010
/**
@@ -192,6 +192,9 @@ class JReactNativeFeatureFlagsCxxInterop
192192
static bool fixMappingOfEventPrioritiesBetweenFabricAndReact(
193193
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop>);
194194

195+
static bool fuseboxAssertSingleHostState(
196+
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop>);
197+
195198
static bool fuseboxEnabledRelease(
196199
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop>);
197200

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)