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 ee33a8467aab94..0df94140bcfa12 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<<99da5cf948e469e385bd70b4c59cd764>> + * @generated SignedSource<<662ad064ecdeb796979f66f0844984e8>> */ /** @@ -238,6 +238,12 @@ public object ReactNativeFeatureFlags { @JvmStatic public fun traceTurboModulePromiseRejectionsOnAndroid(): Boolean = accessor.traceTurboModulePromiseRejectionsOnAndroid() + /** + * When enabled, runtime shadow node references will be updated during the commit. This allows running RSNRU from any thread without corrupting the renderer state. + */ + @JvmStatic + public fun updateRuntimeShadowNodeReferencesOnCommit(): Boolean = accessor.updateRuntimeShadowNodeReferencesOnCommit() + /** * In Bridgeless mode, use the always available javascript error reporting pipeline. */ 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 f16e6b88855156..52b1cdc2d338d7 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<<5e1d8c0e728715d4110b6d1d156357c5>> + * @generated SignedSource<<18491add3d51026435eedc7b8881c17d>> */ /** @@ -55,6 +55,7 @@ public class ReactNativeFeatureFlagsCxxAccessor : ReactNativeFeatureFlagsAccesso private var lazyAnimationCallbacksCache: Boolean? = null private var loadVectorDrawablesOnImagesCache: Boolean? = null private var traceTurboModulePromiseRejectionsOnAndroidCache: Boolean? = null + private var updateRuntimeShadowNodeReferencesOnCommitCache: Boolean? = null private var useAlwaysAvailableJSErrorHandlingCache: Boolean? = null private var useFabricInteropCache: Boolean? = null private var useImmediateExecutorInAndroidBridgelessCache: Boolean? = null @@ -382,6 +383,15 @@ public class ReactNativeFeatureFlagsCxxAccessor : ReactNativeFeatureFlagsAccesso return cached } + override fun updateRuntimeShadowNodeReferencesOnCommit(): Boolean { + var cached = updateRuntimeShadowNodeReferencesOnCommitCache + if (cached == null) { + cached = ReactNativeFeatureFlagsCxxInterop.updateRuntimeShadowNodeReferencesOnCommit() + updateRuntimeShadowNodeReferencesOnCommitCache = cached + } + return cached + } + override fun useAlwaysAvailableJSErrorHandling(): Boolean { var cached = useAlwaysAvailableJSErrorHandlingCache 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 cd57aa387902ea..b7502e6d2facea 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<<995fb2d7c8aa01df311a43b6b415eb7f>> */ /** @@ -98,6 +98,8 @@ public object ReactNativeFeatureFlagsCxxInterop { @DoNotStrip @JvmStatic public external fun traceTurboModulePromiseRejectionsOnAndroid(): Boolean + @DoNotStrip @JvmStatic public external fun updateRuntimeShadowNodeReferencesOnCommit(): Boolean + @DoNotStrip @JvmStatic public external fun useAlwaysAvailableJSErrorHandling(): Boolean @DoNotStrip @JvmStatic public external fun useFabricInterop(): 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 bcc635f1689053..a403fc71c03f7c 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<<11d93a900862ed8ce98f90f9af2de47b>> + * @generated SignedSource<<165c4e89bdf59200d1cbf18aac69d991>> */ /** @@ -93,6 +93,8 @@ public open class ReactNativeFeatureFlagsDefaults : ReactNativeFeatureFlagsProvi override fun traceTurboModulePromiseRejectionsOnAndroid(): Boolean = false + override fun updateRuntimeShadowNodeReferencesOnCommit(): Boolean = true + override fun useAlwaysAvailableJSErrorHandling(): Boolean = false override fun useFabricInterop(): 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 7c22c3bdd3d0dd..af6647e60c9c49 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<<0614fa80cbc66806fa45aef70f34e2d7>> + * @generated SignedSource<> */ /** @@ -59,6 +59,7 @@ public class ReactNativeFeatureFlagsLocalAccessor : ReactNativeFeatureFlagsAcces private var lazyAnimationCallbacksCache: Boolean? = null private var loadVectorDrawablesOnImagesCache: Boolean? = null private var traceTurboModulePromiseRejectionsOnAndroidCache: Boolean? = null + private var updateRuntimeShadowNodeReferencesOnCommitCache: Boolean? = null private var useAlwaysAvailableJSErrorHandlingCache: Boolean? = null private var useFabricInteropCache: Boolean? = null private var useImmediateExecutorInAndroidBridgelessCache: Boolean? = null @@ -421,6 +422,16 @@ public class ReactNativeFeatureFlagsLocalAccessor : ReactNativeFeatureFlagsAcces return cached } + override fun updateRuntimeShadowNodeReferencesOnCommit(): Boolean { + var cached = updateRuntimeShadowNodeReferencesOnCommitCache + if (cached == null) { + cached = currentProvider.updateRuntimeShadowNodeReferencesOnCommit() + accessedFeatureFlags.add("updateRuntimeShadowNodeReferencesOnCommit") + updateRuntimeShadowNodeReferencesOnCommitCache = cached + } + return cached + } + override fun useAlwaysAvailableJSErrorHandling(): Boolean { var cached = useAlwaysAvailableJSErrorHandlingCache 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 31c4f90f10b730..222395be289445 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<<5f4ff90382b2d69df401535cb33e64c5>> + * @generated SignedSource<> */ /** @@ -93,6 +93,8 @@ public interface ReactNativeFeatureFlagsProvider { @DoNotStrip public fun traceTurboModulePromiseRejectionsOnAndroid(): Boolean + @DoNotStrip public fun updateRuntimeShadowNodeReferencesOnCommit(): Boolean + @DoNotStrip public fun useAlwaysAvailableJSErrorHandling(): Boolean @DoNotStrip public fun useFabricInterop(): 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 1ede11bdd9ed6a..5fd7ff9544d81a 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<<28069af1f34c79e9907c85697a291c0e>> + * @generated SignedSource<> */ /** @@ -249,6 +249,12 @@ class ReactNativeFeatureFlagsProviderHolder return method(javaProvider_); } + bool updateRuntimeShadowNodeReferencesOnCommit() override { + static const auto method = + getReactNativeFeatureFlagsProviderJavaClass()->getMethod("updateRuntimeShadowNodeReferencesOnCommit"); + return method(javaProvider_); + } + bool useAlwaysAvailableJSErrorHandling() override { static const auto method = getReactNativeFeatureFlagsProviderJavaClass()->getMethod("useAlwaysAvailableJSErrorHandling"); @@ -494,6 +500,11 @@ bool JReactNativeFeatureFlagsCxxInterop::traceTurboModulePromiseRejectionsOnAndr return ReactNativeFeatureFlags::traceTurboModulePromiseRejectionsOnAndroid(); } +bool JReactNativeFeatureFlagsCxxInterop::updateRuntimeShadowNodeReferencesOnCommit( + facebook::jni::alias_ref /*unused*/) { + return ReactNativeFeatureFlags::updateRuntimeShadowNodeReferencesOnCommit(); +} + bool JReactNativeFeatureFlagsCxxInterop::useAlwaysAvailableJSErrorHandling( facebook::jni::alias_ref /*unused*/) { return ReactNativeFeatureFlags::useAlwaysAvailableJSErrorHandling(); @@ -685,6 +696,9 @@ void JReactNativeFeatureFlagsCxxInterop::registerNatives() { makeNativeMethod( "traceTurboModulePromiseRejectionsOnAndroid", JReactNativeFeatureFlagsCxxInterop::traceTurboModulePromiseRejectionsOnAndroid), + makeNativeMethod( + "updateRuntimeShadowNodeReferencesOnCommit", + JReactNativeFeatureFlagsCxxInterop::updateRuntimeShadowNodeReferencesOnCommit), makeNativeMethod( "useAlwaysAvailableJSErrorHandling", JReactNativeFeatureFlagsCxxInterop::useAlwaysAvailableJSErrorHandling), 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 665dd08e57343d..789c05753a8622 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<<763d595784bdf31a852ebf2a492a1393>> + * @generated SignedSource<<9be70297304abaf0a7cfdea7f6c2562c>> */ /** @@ -135,6 +135,9 @@ class JReactNativeFeatureFlagsCxxInterop static bool traceTurboModulePromiseRejectionsOnAndroid( facebook::jni::alias_ref); + static bool updateRuntimeShadowNodeReferencesOnCommit( + facebook::jni::alias_ref); + static bool useAlwaysAvailableJSErrorHandling( facebook::jni::alias_ref); diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.cpp b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.cpp index 811d4fef5a177c..0f9deb403a6cd8 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<<511c1667fab247b77d771a7a26e87b46>> + * @generated SignedSource<<1908eb4b0357004888dbe5876aa9ef34>> */ /** @@ -166,6 +166,10 @@ bool ReactNativeFeatureFlags::traceTurboModulePromiseRejectionsOnAndroid() { return getAccessor().traceTurboModulePromiseRejectionsOnAndroid(); } +bool ReactNativeFeatureFlags::updateRuntimeShadowNodeReferencesOnCommit() { + return getAccessor().updateRuntimeShadowNodeReferencesOnCommit(); +} + bool ReactNativeFeatureFlags::useAlwaysAvailableJSErrorHandling() { return getAccessor().useAlwaysAvailableJSErrorHandling(); } diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.h b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.h index f082b18249f424..770f55a0bc6bc0 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<<19cfd8b41dd429c83a5e0f0df514d1d1>> + * @generated SignedSource<> */ /** @@ -214,6 +214,11 @@ class ReactNativeFeatureFlags { */ RN_EXPORT static bool traceTurboModulePromiseRejectionsOnAndroid(); + /** + * When enabled, runtime shadow node references will be updated during the commit. This allows running RSNRU from any thread without corrupting the renderer state. + */ + RN_EXPORT static bool updateRuntimeShadowNodeReferencesOnCommit(); + /** * In Bridgeless mode, use the always available javascript error reporting pipeline. */ diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.cpp b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.cpp index c8ef7996747ccf..952f9c75b300c5 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<> + * @generated SignedSource<<848eaeb01c92c48f9ffd9cb74e371c56>> */ /** @@ -659,6 +659,24 @@ bool ReactNativeFeatureFlagsAccessor::traceTurboModulePromiseRejectionsOnAndroid return flagValue.value(); } +bool ReactNativeFeatureFlagsAccessor::updateRuntimeShadowNodeReferencesOnCommit() { + auto flagValue = updateRuntimeShadowNodeReferencesOnCommit_.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(35, "updateRuntimeShadowNodeReferencesOnCommit"); + + flagValue = currentProvider_->updateRuntimeShadowNodeReferencesOnCommit(); + updateRuntimeShadowNodeReferencesOnCommit_ = flagValue; + } + + return flagValue.value(); +} + bool ReactNativeFeatureFlagsAccessor::useAlwaysAvailableJSErrorHandling() { auto flagValue = useAlwaysAvailableJSErrorHandling_.load(); @@ -668,7 +686,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(35, "useAlwaysAvailableJSErrorHandling"); + markFlagAsAccessed(36, "useAlwaysAvailableJSErrorHandling"); flagValue = currentProvider_->useAlwaysAvailableJSErrorHandling(); useAlwaysAvailableJSErrorHandling_ = flagValue; @@ -686,7 +704,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(36, "useFabricInterop"); + markFlagAsAccessed(37, "useFabricInterop"); flagValue = currentProvider_->useFabricInterop(); useFabricInterop_ = flagValue; @@ -704,7 +722,7 @@ bool ReactNativeFeatureFlagsAccessor::useImmediateExecutorInAndroidBridgeless() // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(37, "useImmediateExecutorInAndroidBridgeless"); + markFlagAsAccessed(38, "useImmediateExecutorInAndroidBridgeless"); flagValue = currentProvider_->useImmediateExecutorInAndroidBridgeless(); useImmediateExecutorInAndroidBridgeless_ = flagValue; @@ -722,7 +740,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(38, "useNativeViewConfigsInBridgelessMode"); + markFlagAsAccessed(39, "useNativeViewConfigsInBridgelessMode"); flagValue = currentProvider_->useNativeViewConfigsInBridgelessMode(); useNativeViewConfigsInBridgelessMode_ = flagValue; @@ -740,7 +758,7 @@ bool ReactNativeFeatureFlagsAccessor::useOptimisedViewPreallocationOnAndroid() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(39, "useOptimisedViewPreallocationOnAndroid"); + markFlagAsAccessed(40, "useOptimisedViewPreallocationOnAndroid"); flagValue = currentProvider_->useOptimisedViewPreallocationOnAndroid(); useOptimisedViewPreallocationOnAndroid_ = flagValue; @@ -758,7 +776,7 @@ bool ReactNativeFeatureFlagsAccessor::useOptimizedEventBatchingOnAndroid() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(40, "useOptimizedEventBatchingOnAndroid"); + markFlagAsAccessed(41, "useOptimizedEventBatchingOnAndroid"); flagValue = currentProvider_->useOptimizedEventBatchingOnAndroid(); useOptimizedEventBatchingOnAndroid_ = flagValue; @@ -776,7 +794,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(41, "useRawPropsJsiValue"); + markFlagAsAccessed(42, "useRawPropsJsiValue"); flagValue = currentProvider_->useRawPropsJsiValue(); useRawPropsJsiValue_ = flagValue; @@ -794,7 +812,7 @@ bool ReactNativeFeatureFlagsAccessor::useRuntimeShadowNodeReferenceUpdate() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(42, "useRuntimeShadowNodeReferenceUpdate"); + markFlagAsAccessed(43, "useRuntimeShadowNodeReferenceUpdate"); flagValue = currentProvider_->useRuntimeShadowNodeReferenceUpdate(); useRuntimeShadowNodeReferenceUpdate_ = flagValue; @@ -812,7 +830,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(43, "useShadowNodeStateOnClone"); + markFlagAsAccessed(44, "useShadowNodeStateOnClone"); flagValue = currentProvider_->useShadowNodeStateOnClone(); useShadowNodeStateOnClone_ = flagValue; @@ -830,7 +848,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(44, "useTurboModuleInterop"); + markFlagAsAccessed(45, "useTurboModuleInterop"); flagValue = currentProvider_->useTurboModuleInterop(); useTurboModuleInterop_ = flagValue; @@ -848,7 +866,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(45, "useTurboModules"); + markFlagAsAccessed(46, "useTurboModules"); flagValue = currentProvider_->useTurboModules(); useTurboModules_ = flagValue; diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.h b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.h index 1d453a762d518a..043954edd55d52 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<<2a6cbfd5de86a5bb840f1fc5f47c51a6>> + * @generated SignedSource<> */ /** @@ -67,6 +67,7 @@ class ReactNativeFeatureFlagsAccessor { bool lazyAnimationCallbacks(); bool loadVectorDrawablesOnImages(); bool traceTurboModulePromiseRejectionsOnAndroid(); + bool updateRuntimeShadowNodeReferencesOnCommit(); bool useAlwaysAvailableJSErrorHandling(); bool useFabricInterop(); bool useImmediateExecutorInAndroidBridgeless(); @@ -89,7 +90,7 @@ class ReactNativeFeatureFlagsAccessor { std::unique_ptr currentProvider_; bool wasOverridden_; - std::array, 46> accessedFeatureFlags_; + std::array, 47> accessedFeatureFlags_; std::atomic> commonTestFlag_; std::atomic> completeReactInstanceCreationOnBgThreadOnAndroid_; @@ -126,6 +127,7 @@ class ReactNativeFeatureFlagsAccessor { std::atomic> lazyAnimationCallbacks_; std::atomic> loadVectorDrawablesOnImages_; std::atomic> traceTurboModulePromiseRejectionsOnAndroid_; + std::atomic> updateRuntimeShadowNodeReferencesOnCommit_; std::atomic> useAlwaysAvailableJSErrorHandling_; std::atomic> useFabricInterop_; std::atomic> useImmediateExecutorInAndroidBridgeless_; diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDefaults.h b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDefaults.h index 4528ce9764beaf..c629bb96d031b9 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<<830cdd4b394262ee65abc63a54833674>> + * @generated SignedSource<<79619069dc734a8e6156a39210609703>> */ /** @@ -167,6 +167,10 @@ class ReactNativeFeatureFlagsDefaults : public ReactNativeFeatureFlagsProvider { return false; } + bool updateRuntimeShadowNodeReferencesOnCommit() override { + return true; + } + bool useAlwaysAvailableJSErrorHandling() override { return false; } @@ -200,7 +204,7 @@ class ReactNativeFeatureFlagsDefaults : public ReactNativeFeatureFlagsProvider { } bool useShadowNodeStateOnClone() override { - return false; + return true; } bool useTurboModuleInterop() override { diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDynamicProvider.h b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDynamicProvider.h index 9e436f6ecd0b18..20cb4ed6453222 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<<627094b444d8b7a513d7190272848a9e>> + * @generated SignedSource<<8ac3bcd27536b9b89dc3d1f2453a508c>> */ /** @@ -360,6 +360,15 @@ class ReactNativeFeatureFlagsDynamicProvider : public ReactNativeFeatureFlagsDef return ReactNativeFeatureFlagsDefaults::traceTurboModulePromiseRejectionsOnAndroid(); } + bool updateRuntimeShadowNodeReferencesOnCommit() override { + auto value = values_["updateRuntimeShadowNodeReferencesOnCommit"]; + if (!value.isNull()) { + return value.getBool(); + } + + return ReactNativeFeatureFlagsDefaults::updateRuntimeShadowNodeReferencesOnCommit(); + } + bool useAlwaysAvailableJSErrorHandling() override { auto value = values_["useAlwaysAvailableJSErrorHandling"]; if (!value.isNull()) { diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsProvider.h b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsProvider.h index e7f1ceaf8e6e67..a4fba3729e67c9 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<<190bad7388fb33884eebca0fed4ad61f>> + * @generated SignedSource<> */ /** @@ -60,6 +60,7 @@ class ReactNativeFeatureFlagsProvider { virtual bool lazyAnimationCallbacks() = 0; virtual bool loadVectorDrawablesOnImages() = 0; virtual bool traceTurboModulePromiseRejectionsOnAndroid() = 0; + virtual bool updateRuntimeShadowNodeReferencesOnCommit() = 0; virtual bool useAlwaysAvailableJSErrorHandling() = 0; virtual bool useFabricInterop() = 0; virtual bool useImmediateExecutorInAndroidBridgeless() = 0; diff --git a/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.cpp b/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.cpp index f2ea23f46d83f3..19f4be5f6c0636 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<<2911cd67b3a434c7d3610740befac4f9>> + * @generated SignedSource<> */ /** @@ -219,6 +219,11 @@ bool NativeReactNativeFeatureFlags::traceTurboModulePromiseRejectionsOnAndroid( return ReactNativeFeatureFlags::traceTurboModulePromiseRejectionsOnAndroid(); } +bool NativeReactNativeFeatureFlags::updateRuntimeShadowNodeReferencesOnCommit( + jsi::Runtime& /*runtime*/) { + return ReactNativeFeatureFlags::updateRuntimeShadowNodeReferencesOnCommit(); +} + bool NativeReactNativeFeatureFlags::useAlwaysAvailableJSErrorHandling( jsi::Runtime& /*runtime*/) { return ReactNativeFeatureFlags::useAlwaysAvailableJSErrorHandling(); diff --git a/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.h b/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.h index e6260245a1e94e..669df211a468dd 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<<9df551425337f50b9bb8837684bbbba0>> + * @generated SignedSource<<5cbf054072489b3b6960010debb86771>> */ /** @@ -107,6 +107,8 @@ class NativeReactNativeFeatureFlags bool traceTurboModulePromiseRejectionsOnAndroid(jsi::Runtime& runtime); + bool updateRuntimeShadowNodeReferencesOnCommit(jsi::Runtime& runtime); + bool useAlwaysAvailableJSErrorHandling(jsi::Runtime& runtime); bool useFabricInterop(jsi::Runtime& runtime); diff --git a/packages/react-native/ReactCommon/react/renderer/core/ShadowNode.cpp b/packages/react-native/ReactCommon/react/renderer/core/ShadowNode.cpp index 690c3d16645074..441f719f01be66 100644 --- a/packages/react-native/ReactCommon/react/renderer/core/ShadowNode.cpp +++ b/packages/react-native/ReactCommon/react/renderer/core/ShadowNode.cpp @@ -322,7 +322,7 @@ void ShadowNode::transferRuntimeShadowNodeReference( destinationShadowNode->runtimeShadowNodeReference_ = runtimeShadowNodeReference_; - if (!ReactNativeFeatureFlags::useRuntimeShadowNodeReferenceUpdate()) { + if (!ReactNativeFeatureFlags::useRuntimeShadowNodeReferenceUpdate() && !ReactNativeFeatureFlags::updateRuntimeShadowNodeReferencesOnCommit()) { updateRuntimeShadowNodeReference(destinationShadowNode); } } @@ -330,7 +330,8 @@ void ShadowNode::transferRuntimeShadowNodeReference( void ShadowNode::transferRuntimeShadowNodeReference( const Shared& destinationShadowNode, const ShadowNodeFragment& fragment) const { - if (useRuntimeShadowNodeReferenceUpdateOnThread && + if ((ReactNativeFeatureFlags::updateRuntimeShadowNodeReferencesOnCommit() || + useRuntimeShadowNodeReferenceUpdateOnThread) && fragment.runtimeShadowNodeReference && ReactNativeFeatureFlags::useRuntimeShadowNodeReferenceUpdate()) { transferRuntimeShadowNodeReference(destinationShadowNode); diff --git a/packages/react-native/ReactCommon/react/renderer/mounting/ShadowTree.cpp b/packages/react-native/ReactCommon/react/renderer/mounting/ShadowTree.cpp index 9b5ca3f6078e8f..f59e9e51651c1c 100644 --- a/packages/react-native/ReactCommon/react/renderer/mounting/ShadowTree.cpp +++ b/packages/react-native/ReactCommon/react/renderer/mounting/ShadowTree.cpp @@ -294,7 +294,7 @@ CommitStatus ShadowTree::tryCommit( // Run commit hooks. newRootShadowNode = delegate_.shadowTreeWillCommit( - *this, oldRootShadowNode, newRootShadowNode); + *this, oldRootShadowNode, newRootShadowNode, commitOptions); if (!newRootShadowNode) { return CommitStatus::Cancelled; @@ -335,7 +335,8 @@ CommitStatus ShadowTree::tryCommit( std::scoped_lock dispatchLock(EventEmitter::DispatchMutex()); updateMountedFlag( currentRevision_.rootShadowNode->getChildren(), - newRootShadowNode->getChildren()); + newRootShadowNode->getChildren(), + commitOptions.source); } telemetry.didCommit(); diff --git a/packages/react-native/ReactCommon/react/renderer/mounting/ShadowTree.h b/packages/react-native/ReactCommon/react/renderer/mounting/ShadowTree.h index b7b813ffd2d537..454391cde02d72 100644 --- a/packages/react-native/ReactCommon/react/renderer/mounting/ShadowTree.h +++ b/packages/react-native/ReactCommon/react/renderer/mounting/ShadowTree.h @@ -25,6 +25,49 @@ namespace facebook::react { using ShadowTreeCommitTransaction = std::function; +/* + * Represents a result of a `commit` operation. + */ +enum class ShadowTreeCommitStatus { + Succeeded, + Failed, + Cancelled, +}; + +/* + * Represents commits' side-effects propagation mode. + */ +enum class ShadowTreeCommitMode { + // Commits' side-effects are observable via `MountingCoordinator`. + // The rendering pipeline fully works end-to-end. + Normal, + + // Commits' side-effects are *not* observable via `MountingCoordinator`. + // The mounting phase is skipped in the rendering pipeline. + Suspended, +}; + +enum class ShadowTreeCommitSource { + Unknown, + React, +}; + +struct ShadowTreeCommitOptions { + // When set to true, Shadow Node state from current revision will be applied + // to the new revision. For more details see + // https://reactnative.dev/architecture/render-pipeline#react-native-renderer-state-updates + bool enableStateReconciliation{false}; + + // Indicates if mounting will be triggered synchronously and React will + // not get a chance to interrupt painting. + // This should be set to `false` when a commit is coming from React. It + // will then let React run layout effects and apply updates before paint. + // For all other commits, should be true. + bool mountSynchronously{true}; + + ShadowTreeCommitSource source{ShadowTreeCommitSource::Unknown}; +}; + /* * Represents the shadow tree and its lifecycle. */ @@ -32,41 +75,10 @@ class ShadowTree final { public: using Unique = std::unique_ptr; - /* - * Represents a result of a `commit` operation. - */ - enum class CommitStatus { - Succeeded, - Failed, - Cancelled, - }; - - /* - * Represents commits' side-effects propagation mode. - */ - enum class CommitMode { - // Commits' side-effects are observable via `MountingCoordinator`. - // The rendering pipeline fully works end-to-end. - Normal, - - // Commits' side-effects are *not* observable via `MountingCoordinator`. - // The mounting phase is skipped in the rendering pipeline. - Suspended, - }; - - struct CommitOptions { - // When set to true, Shadow Node state from current revision will be applied - // to the new revision. For more details see - // https://reactnative.dev/architecture/render-pipeline#react-native-renderer-state-updates - bool enableStateReconciliation{false}; - - // Indicates if mounting will be triggered synchronously and React will - // not get a chance to interrupt painting. - // This should be set to `false` when a commit is coming from React. It - // will then let React run layout effects and apply updates before paint. - // For all other commits, should be true. - bool mountSynchronously{true}; - }; + using CommitStatus = ShadowTreeCommitStatus; + using CommitMode = ShadowTreeCommitMode; + using CommitSource = ShadowTreeCommitSource; + using CommitOptions = ShadowTreeCommitOptions; /* * Creates a new shadow tree instance. diff --git a/packages/react-native/ReactCommon/react/renderer/mounting/ShadowTreeDelegate.h b/packages/react-native/ReactCommon/react/renderer/mounting/ShadowTreeDelegate.h index dbb0fecfc92c2e..14402722045ec0 100644 --- a/packages/react-native/ReactCommon/react/renderer/mounting/ShadowTreeDelegate.h +++ b/packages/react-native/ReactCommon/react/renderer/mounting/ShadowTreeDelegate.h @@ -12,6 +12,7 @@ namespace facebook::react { class ShadowTree; +struct ShadowTreeCommitOptions; /* * Abstract class for ShadowTree's delegate. @@ -27,7 +28,8 @@ class ShadowTreeDelegate { virtual RootShadowNode::Unshared shadowTreeWillCommit( const ShadowTree& shadowTree, const RootShadowNode::Shared& oldRootShadowNode, - const RootShadowNode::Unshared& newRootShadowNode) const = 0; + const RootShadowNode::Unshared& newRootShadowNode, + const ShadowTreeCommitOptions& commitOptions) const = 0; /* * Called right after Shadow Tree commit a new state of the tree. diff --git a/packages/react-native/ReactCommon/react/renderer/mounting/tests/StateReconciliationTest.cpp b/packages/react-native/ReactCommon/react/renderer/mounting/tests/StateReconciliationTest.cpp index 0a685ede067cd8..5dc71f7bf03370 100644 --- a/packages/react-native/ReactCommon/react/renderer/mounting/tests/StateReconciliationTest.cpp +++ b/packages/react-native/ReactCommon/react/renderer/mounting/tests/StateReconciliationTest.cpp @@ -29,7 +29,8 @@ class DummyShadowTreeDelegate : public ShadowTreeDelegate { RootShadowNode::Unshared shadowTreeWillCommit( const ShadowTree& /*shadowTree*/, const RootShadowNode::Shared& /*oldRootShadowNode*/, - const RootShadowNode::Unshared& newRootShadowNode) const override { + const RootShadowNode::Unshared& newRootShadowNode, + const ShadowTree::CommitOptions& /*commitOptions*/) const override { return newRootShadowNode; }; diff --git a/packages/react-native/ReactCommon/react/renderer/mounting/updateMountedFlag.cpp b/packages/react-native/ReactCommon/react/renderer/mounting/updateMountedFlag.cpp index 42d8695977fb2f..041305c71b2773 100644 --- a/packages/react-native/ReactCommon/react/renderer/mounting/updateMountedFlag.cpp +++ b/packages/react-native/ReactCommon/react/renderer/mounting/updateMountedFlag.cpp @@ -12,7 +12,8 @@ namespace facebook::react { void updateMountedFlag( const ShadowNode::ListOfShared& oldChildren, - const ShadowNode::ListOfShared& newChildren) { + const ShadowNode::ListOfShared& newChildren, + ShadowTreeCommitSource commitSource) { // This is a simplified version of Diffing algorithm that only updates // `mounted` flag on `ShadowNode`s. The algorithm sets "mounted" flag before // "unmounted" to allow `ShadowNode` detect a situation where the node was @@ -49,11 +50,12 @@ void updateMountedFlag( newChild->setMounted(true); oldChild->setMounted(false); - if (ReactNativeFeatureFlags::useRuntimeShadowNodeReferenceUpdate()) { + if (commitSource == ShadowTreeCommitSource::React && (ReactNativeFeatureFlags::useRuntimeShadowNodeReferenceUpdate() || ReactNativeFeatureFlags::updateRuntimeShadowNodeReferencesOnCommit())) { newChild->updateRuntimeShadowNodeReference(newChild); } - updateMountedFlag(oldChild->getChildren(), newChild->getChildren()); + updateMountedFlag( + oldChild->getChildren(), newChild->getChildren(), commitSource); } size_t lastIndexAfterFirstStage = index; @@ -62,14 +64,14 @@ void updateMountedFlag( for (index = lastIndexAfterFirstStage; index < newChildren.size(); index++) { const auto& newChild = newChildren[index]; newChild->setMounted(true); - updateMountedFlag({}, newChild->getChildren()); + updateMountedFlag({}, newChild->getChildren(), commitSource); } // State 3: Unmount old children. for (index = lastIndexAfterFirstStage; index < oldChildren.size(); index++) { const auto& oldChild = oldChildren[index]; oldChild->setMounted(false); - updateMountedFlag(oldChild->getChildren(), {}); + updateMountedFlag(oldChild->getChildren(), {}, commitSource); } } } // namespace facebook::react diff --git a/packages/react-native/ReactCommon/react/renderer/mounting/updateMountedFlag.h b/packages/react-native/ReactCommon/react/renderer/mounting/updateMountedFlag.h index 73e30109325a2d..ddee180ce29beb 100644 --- a/packages/react-native/ReactCommon/react/renderer/mounting/updateMountedFlag.h +++ b/packages/react-native/ReactCommon/react/renderer/mounting/updateMountedFlag.h @@ -8,6 +8,7 @@ #pragma once #include +#include namespace facebook::react { /* @@ -15,5 +16,6 @@ namespace facebook::react { */ void updateMountedFlag( const ShadowNode::ListOfShared& oldChildren, - const ShadowNode::ListOfShared& newChildren); + const ShadowNode::ListOfShared& newChildren, + ShadowTreeCommitSource commitSource); } // namespace facebook::react diff --git a/packages/react-native/ReactCommon/react/renderer/observers/mutation/MutationObserverManager.cpp b/packages/react-native/ReactCommon/react/renderer/observers/mutation/MutationObserverManager.cpp index afcbbe3c0d07d1..7db7a25764072a 100644 --- a/packages/react-native/ReactCommon/react/renderer/observers/mutation/MutationObserverManager.cpp +++ b/packages/react-native/ReactCommon/react/renderer/observers/mutation/MutationObserverManager.cpp @@ -106,8 +106,11 @@ void MutationObserverManager::commitHookWasUnregistered( RootShadowNode::Unshared MutationObserverManager::shadowTreeWillCommit( const ShadowTree& shadowTree, const RootShadowNode::Shared& oldRootShadowNode, - const RootShadowNode::Unshared& newRootShadowNode) noexcept { - runMutationObservations(shadowTree, *oldRootShadowNode, *newRootShadowNode); + const RootShadowNode::Unshared& newRootShadowNode, + const ShadowTree::CommitOptions& commitOptions) noexcept { + if (commitOptions.source == ShadowTree::CommitSource::React) { + runMutationObservations(shadowTree, *oldRootShadowNode, *newRootShadowNode); + } return newRootShadowNode; } diff --git a/packages/react-native/ReactCommon/react/renderer/observers/mutation/MutationObserverManager.h b/packages/react-native/ReactCommon/react/renderer/observers/mutation/MutationObserverManager.h index 60c53116c81e5c..ffaf3496751c0a 100644 --- a/packages/react-native/ReactCommon/react/renderer/observers/mutation/MutationObserverManager.h +++ b/packages/react-native/ReactCommon/react/renderer/observers/mutation/MutationObserverManager.h @@ -8,6 +8,7 @@ #pragma once #include +#include #include #include #include @@ -43,7 +44,8 @@ class MutationObserverManager final : public UIManagerCommitHook { RootShadowNode::Unshared shadowTreeWillCommit( const ShadowTree& shadowTree, const RootShadowNode::Shared& oldRootShadowNode, - const RootShadowNode::Unshared& newRootShadowNode) noexcept override; + const RootShadowNode::Unshared& newRootShadowNode, + const ShadowTree::CommitOptions& commitOptions) noexcept override; private: std::unordered_map< diff --git a/packages/react-native/ReactCommon/react/renderer/uimanager/UIManager.cpp b/packages/react-native/ReactCommon/react/renderer/uimanager/UIManager.cpp index d83c350c564e4e..f46da424db4b87 100644 --- a/packages/react-native/ReactCommon/react/renderer/uimanager/UIManager.cpp +++ b/packages/react-native/ReactCommon/react/renderer/uimanager/UIManager.cpp @@ -618,7 +618,8 @@ void UIManager::unregisterMountHook(UIManagerMountHook& mountHook) { RootShadowNode::Unshared UIManager::shadowTreeWillCommit( const ShadowTree& shadowTree, const RootShadowNode::Shared& oldRootShadowNode, - const RootShadowNode::Unshared& newRootShadowNode) const { + const RootShadowNode::Unshared& newRootShadowNode, + const ShadowTree::CommitOptions& commitOptions) const { TraceSection s("UIManager::shadowTreeWillCommit"); std::shared_lock lock(commitHookMutex_); @@ -626,7 +627,7 @@ RootShadowNode::Unshared UIManager::shadowTreeWillCommit( auto resultRootShadowNode = newRootShadowNode; for (auto* commitHook : commitHooks_) { resultRootShadowNode = commitHook->shadowTreeWillCommit( - shadowTree, oldRootShadowNode, resultRootShadowNode); + shadowTree, oldRootShadowNode, resultRootShadowNode, commitOptions); } return resultRootShadowNode; diff --git a/packages/react-native/ReactCommon/react/renderer/uimanager/UIManager.h b/packages/react-native/ReactCommon/react/renderer/uimanager/UIManager.h index cdd4f2d3ccd9f6..d876677c3f1fb5 100644 --- a/packages/react-native/ReactCommon/react/renderer/uimanager/UIManager.h +++ b/packages/react-native/ReactCommon/react/renderer/uimanager/UIManager.h @@ -127,7 +127,8 @@ class UIManager final : public ShadowTreeDelegate { RootShadowNode::Unshared shadowTreeWillCommit( const ShadowTree& shadowTree, const RootShadowNode::Shared& oldRootShadowNode, - const RootShadowNode::Unshared& newRootShadowNode) const override; + const RootShadowNode::Unshared& newRootShadowNode, + const ShadowTree::CommitOptions& commitOptions) const override; std::shared_ptr createNode( Tag tag, diff --git a/packages/react-native/ReactCommon/react/renderer/uimanager/UIManagerBinding.cpp b/packages/react-native/ReactCommon/react/renderer/uimanager/UIManagerBinding.cpp index 56c4a501bdbaee..6ea190885cb763 100644 --- a/packages/react-native/ReactCommon/react/renderer/uimanager/UIManagerBinding.cpp +++ b/packages/react-native/ReactCommon/react/renderer/uimanager/UIManagerBinding.cpp @@ -454,7 +454,9 @@ jsi::Value UIManagerBinding::get( uiManager->completeSurface( surfaceId, shadowNodeList, - {.enableStateReconciliation = true, .mountSynchronously = false}); + {.enableStateReconciliation = true, + .mountSynchronously = false, + .source = ShadowTree::CommitSource::React}); return jsi::Value::undefined(); }); diff --git a/packages/react-native/ReactCommon/react/renderer/uimanager/UIManagerCommitHook.h b/packages/react-native/ReactCommon/react/renderer/uimanager/UIManagerCommitHook.h index 1667aaf00ee61c..2e15c6ca89e595 100644 --- a/packages/react-native/ReactCommon/react/renderer/uimanager/UIManagerCommitHook.h +++ b/packages/react-native/ReactCommon/react/renderer/uimanager/UIManagerCommitHook.h @@ -12,6 +12,7 @@ namespace facebook::react { class ShadowTree; +struct ShadowTreeCommitOptions; class UIManager; /* @@ -34,7 +35,24 @@ class UIManagerCommitHook { virtual RootShadowNode::Unshared shadowTreeWillCommit( const ShadowTree& shadowTree, const RootShadowNode::Shared& oldRootShadowNode, - const RootShadowNode::Unshared& newRootShadowNode) noexcept = 0; + const RootShadowNode::Unshared& newRootShadowNode, + const ShadowTreeCommitOptions& /*commitOptions*/) noexcept { + return shadowTreeWillCommit( + shadowTree, oldRootShadowNode, newRootShadowNode); + } + + /* + * This is a version of `shadowTreeWillCommit` without `commitOptions` for + * backward compatibility. + */ + virtual RootShadowNode::Unshared shadowTreeWillCommit( + const ShadowTree& /*shadowTree*/, + const RootShadowNode::Shared& /*oldRootShadowNode*/, + const RootShadowNode::Unshared& newRootShadowNode) noexcept { + // No longer a pure method as subclasses are expected to implement the other + // flavor instead. + return newRootShadowNode; + } virtual ~UIManagerCommitHook() noexcept = default; }; diff --git a/packages/react-native/ReactCommon/react/renderer/uimanager/consistency/tests/LazyShadowTreeRevisionConsistencyManagerTest.cpp b/packages/react-native/ReactCommon/react/renderer/uimanager/consistency/tests/LazyShadowTreeRevisionConsistencyManagerTest.cpp index ae6312baa09cdf..af786dfdb8a596 100644 --- a/packages/react-native/ReactCommon/react/renderer/uimanager/consistency/tests/LazyShadowTreeRevisionConsistencyManagerTest.cpp +++ b/packages/react-native/ReactCommon/react/renderer/uimanager/consistency/tests/LazyShadowTreeRevisionConsistencyManagerTest.cpp @@ -19,7 +19,8 @@ class FakeShadowTreeDelegate : public ShadowTreeDelegate { RootShadowNode::Unshared shadowTreeWillCommit( const ShadowTree& /*shadowTree*/, const RootShadowNode::Shared& /*oldRootShadowNode*/, - const RootShadowNode::Unshared& newRootShadowNode) const override { + const RootShadowNode::Unshared& newRootShadowNode, + const ShadowTree::CommitOptions& /*commitOptions*/) const override { return newRootShadowNode; }; diff --git a/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js b/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js index 5b28c10110c32f..55335547231078 100644 --- a/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js +++ b/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js @@ -86,7 +86,7 @@ const definitions: FeatureFlagDefinitions = { }, }, enableAccumulatedUpdatesInRawPropsAndroid: { - defaultValue: false, + defaultValue: true, metadata: { dateAdded: '2024-12-10', description: @@ -381,6 +381,17 @@ const definitions: FeatureFlagDefinitions = { purpose: 'operational', }, }, + updateRuntimeShadowNodeReferencesOnCommit: { + defaultValue: true, + metadata: { + dateAdded: '2025-04-15', + description: + 'When enabled, runtime shadow node references will be updated during the commit. This allows running RSNRU from any thread without corrupting the renderer state.', + expectedReleaseValue: true, + purpose: 'experimentation', + }, + ossReleaseStage: 'none', + }, useAlwaysAvailableJSErrorHandling: { defaultValue: false, metadata: { @@ -459,7 +470,7 @@ const definitions: FeatureFlagDefinitions = { }, }, useShadowNodeStateOnClone: { - defaultValue: false, + defaultValue: true, metadata: { dateAdded: '2025-04-16', description: diff --git a/packages/react-native/src/private/featureflags/ReactNativeFeatureFlags.js b/packages/react-native/src/private/featureflags/ReactNativeFeatureFlags.js index b691486eb05601..3a0a5471119453 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<<63601ec83ade8f3774d185ebacf4c792>> + * @generated SignedSource<> * @flow strict */ @@ -86,6 +86,7 @@ export type ReactNativeFeatureFlags = $ReadOnly<{ lazyAnimationCallbacks: Getter, loadVectorDrawablesOnImages: Getter, traceTurboModulePromiseRejectionsOnAndroid: Getter, + updateRuntimeShadowNodeReferencesOnCommit: Getter, useAlwaysAvailableJSErrorHandling: Getter, useFabricInterop: Getter, useImmediateExecutorInAndroidBridgeless: Getter, @@ -328,6 +329,10 @@ export const loadVectorDrawablesOnImages: Getter = createNativeFlagGett * Enables storing js caller stack when creating promise in native module. This is useful in case of Promise rejection and tracing the cause. */ export const traceTurboModulePromiseRejectionsOnAndroid: Getter = createNativeFlagGetter('traceTurboModulePromiseRejectionsOnAndroid', false); +/** + * When enabled, runtime shadow node references will be updated during the commit. This allows running RSNRU from any thread without corrupting the renderer state. + */ +export const updateRuntimeShadowNodeReferencesOnCommit: Getter = createNativeFlagGetter('updateRuntimeShadowNodeReferencesOnCommit', true); /** * In Bridgeless mode, use the always available javascript error reporting pipeline. */ @@ -363,7 +368,7 @@ export const useRuntimeShadowNodeReferenceUpdate: Getter = createNative /** * Use the state stored on the source shadow node when cloning it instead of reading in the most recent state on the shadow node family. */ -export const useShadowNodeStateOnClone: Getter = createNativeFlagGetter('useShadowNodeStateOnClone', false); +export const useShadowNodeStateOnClone: Getter = createNativeFlagGetter('useShadowNodeStateOnClone', true); /** * In Bridgeless mode, should legacy NativeModules use the TurboModule system? */ diff --git a/packages/react-native/src/private/featureflags/specs/NativeReactNativeFeatureFlags.js b/packages/react-native/src/private/featureflags/specs/NativeReactNativeFeatureFlags.js index 03e8d13530798f..154546728b4b61 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<> + * @generated SignedSource<> * @flow strict */ @@ -59,6 +59,7 @@ export interface Spec extends TurboModule { +lazyAnimationCallbacks?: () => boolean; +loadVectorDrawablesOnImages?: () => boolean; +traceTurboModulePromiseRejectionsOnAndroid?: () => boolean; + +updateRuntimeShadowNodeReferencesOnCommit?: () => boolean; +useAlwaysAvailableJSErrorHandling?: () => boolean; +useFabricInterop?: () => boolean; +useImmediateExecutorInAndroidBridgeless?: () => boolean;