Fix differentiator emitting updates with incorrect parentTag (#48055)

Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/48055

Address the test-case identified in D66557919, where Differentiator could emit updates for views referencing an incorrect parentTag.

The longer-term fix here is to avoid emitting any updates for nodes which are being reparented, but that requires bigger changes, including to the LayoutAnimation system. As a short-term patch, we're passing through an explicit `parentShadowViewForUpdate` which will be used as the current parent for update purposes.

 {F1971278019}

Changelog: [Android][Fixed] Fix Fabric mutations sometimes triggering a `getViewState` crash when referencing an invalid parentTag.

Reviewed By: rubennorte

Differential Revision: D66654293

fbshipit-source-id: cd5b3e577ad1eede1b6dea834582ac6d750cbb81
This commit is contained in:
Pieter De Baets
2024-12-03 10:17:02 -08:00
committed by Facebook GitHub Bot
parent 21c9491926
commit 34901d4861
23 changed files with 183 additions and 42 deletions
@@ -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<<6d8d8f4b81d7be882b315d0960499dcb>>
* @generated SignedSource<<4a219bb47b1b9d988a164bca19eb4fa9>>
*/
/**
@@ -202,6 +202,12 @@ public object ReactNativeFeatureFlags {
@JvmStatic
public fun excludeYogaFromRawProps(): Boolean = accessor.excludeYogaFromRawProps()
/**
* Fixes a bug in Differentiator where parent views may be referenced before they're created
*/
@JvmStatic
public fun fixDifferentiatorEmittingUpdatesWithWrongParentTag(): Boolean = accessor.fixDifferentiatorEmittingUpdatesWithWrongParentTag()
/**
* Uses the default event priority instead of the discreet event priority by default when dispatching events from Fabric to React.
*/
@@ -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<<031fce8e8b4c20a3e3d6dbecf94d138a>>
* @generated SignedSource<<d75efd6beee8dd9d38b5d648fbecbcda>>
*/
/**
@@ -49,6 +49,7 @@ public class ReactNativeFeatureFlagsCxxAccessor : ReactNativeFeatureFlagsAccesso
private var enableUIConsistencyCache: Boolean? = null
private var enableViewRecyclingCache: Boolean? = null
private var excludeYogaFromRawPropsCache: Boolean? = null
private var fixDifferentiatorEmittingUpdatesWithWrongParentTagCache: Boolean? = null
private var fixMappingOfEventPrioritiesBetweenFabricAndReactCache: Boolean? = null
private var fixMountingCoordinatorReportedPendingTransactionsOnAndroidCache: Boolean? = null
private var fuseboxEnabledDebugCache: Boolean? = null
@@ -328,6 +329,15 @@ public class ReactNativeFeatureFlagsCxxAccessor : ReactNativeFeatureFlagsAccesso
return cached
}
override fun fixDifferentiatorEmittingUpdatesWithWrongParentTag(): Boolean {
var cached = fixDifferentiatorEmittingUpdatesWithWrongParentTagCache
if (cached == null) {
cached = ReactNativeFeatureFlagsCxxInterop.fixDifferentiatorEmittingUpdatesWithWrongParentTag()
fixDifferentiatorEmittingUpdatesWithWrongParentTagCache = cached
}
return cached
}
override fun fixMappingOfEventPrioritiesBetweenFabricAndReact(): Boolean {
var cached = fixMappingOfEventPrioritiesBetweenFabricAndReactCache
if (cached == null) {
@@ -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<<35811667ac2543e1f64e27bbdb483ec1>>
* @generated SignedSource<<7454ab19a01cfbb0a54f14bd83fc3a90>>
*/
/**
@@ -86,6 +86,8 @@ public object ReactNativeFeatureFlagsCxxInterop {
@DoNotStrip @JvmStatic public external fun excludeYogaFromRawProps(): Boolean
@DoNotStrip @JvmStatic public external fun fixDifferentiatorEmittingUpdatesWithWrongParentTag(): Boolean
@DoNotStrip @JvmStatic public external fun fixMappingOfEventPrioritiesBetweenFabricAndReact(): Boolean
@DoNotStrip @JvmStatic public external fun fixMountingCoordinatorReportedPendingTransactionsOnAndroid(): Boolean
@@ -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<<9d829c58e49164a0b2b6b66bc0ce088a>>
* @generated SignedSource<<70d951b2956759280afae4af8f9a2869>>
*/
/**
@@ -81,6 +81,8 @@ public open class ReactNativeFeatureFlagsDefaults : ReactNativeFeatureFlagsProvi
override fun excludeYogaFromRawProps(): Boolean = false
override fun fixDifferentiatorEmittingUpdatesWithWrongParentTag(): Boolean = true
override fun fixMappingOfEventPrioritiesBetweenFabricAndReact(): Boolean = false
override fun fixMountingCoordinatorReportedPendingTransactionsOnAndroid(): Boolean = false
@@ -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<<0121e113410a5b0e14eaf74a3076df2f>>
* @generated SignedSource<<f60000cb58a9632c3aa193854be3de4e>>
*/
/**
@@ -53,6 +53,7 @@ public class ReactNativeFeatureFlagsLocalAccessor : ReactNativeFeatureFlagsAcces
private var enableUIConsistencyCache: Boolean? = null
private var enableViewRecyclingCache: Boolean? = null
private var excludeYogaFromRawPropsCache: Boolean? = null
private var fixDifferentiatorEmittingUpdatesWithWrongParentTagCache: Boolean? = null
private var fixMappingOfEventPrioritiesBetweenFabricAndReactCache: Boolean? = null
private var fixMountingCoordinatorReportedPendingTransactionsOnAndroidCache: Boolean? = null
private var fuseboxEnabledDebugCache: Boolean? = null
@@ -361,6 +362,16 @@ public class ReactNativeFeatureFlagsLocalAccessor : ReactNativeFeatureFlagsAcces
return cached
}
override fun fixDifferentiatorEmittingUpdatesWithWrongParentTag(): Boolean {
var cached = fixDifferentiatorEmittingUpdatesWithWrongParentTagCache
if (cached == null) {
cached = currentProvider.fixDifferentiatorEmittingUpdatesWithWrongParentTag()
accessedFeatureFlags.add("fixDifferentiatorEmittingUpdatesWithWrongParentTag")
fixDifferentiatorEmittingUpdatesWithWrongParentTagCache = cached
}
return cached
}
override fun fixMappingOfEventPrioritiesBetweenFabricAndReact(): Boolean {
var cached = fixMappingOfEventPrioritiesBetweenFabricAndReactCache
if (cached == null) {
@@ -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<<2787d9027695dd14ec6b917a32a1a6de>>
* @generated SignedSource<<d62af893c5d18a2152f098ff305ae41e>>
*/
/**
@@ -81,6 +81,8 @@ public interface ReactNativeFeatureFlagsProvider {
@DoNotStrip public fun excludeYogaFromRawProps(): Boolean
@DoNotStrip public fun fixDifferentiatorEmittingUpdatesWithWrongParentTag(): Boolean
@DoNotStrip public fun fixMappingOfEventPrioritiesBetweenFabricAndReact(): Boolean
@DoNotStrip public fun fixMountingCoordinatorReportedPendingTransactionsOnAndroid(): Boolean
@@ -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<<902b269e45fcb4970c6f8a86818e1940>>
* @generated SignedSource<<640630d7a40b53f7d507569aa6409f69>>
*/
/**
@@ -213,6 +213,12 @@ class ReactNativeFeatureFlagsProviderHolder
return method(javaProvider_);
}
bool fixDifferentiatorEmittingUpdatesWithWrongParentTag() override {
static const auto method =
getReactNativeFeatureFlagsProviderJavaClass()->getMethod<jboolean()>("fixDifferentiatorEmittingUpdatesWithWrongParentTag");
return method(javaProvider_);
}
bool fixMappingOfEventPrioritiesBetweenFabricAndReact() override {
static const auto method =
getReactNativeFeatureFlagsProviderJavaClass()->getMethod<jboolean()>("fixMappingOfEventPrioritiesBetweenFabricAndReact");
@@ -464,6 +470,11 @@ bool JReactNativeFeatureFlagsCxxInterop::excludeYogaFromRawProps(
return ReactNativeFeatureFlags::excludeYogaFromRawProps();
}
bool JReactNativeFeatureFlagsCxxInterop::fixDifferentiatorEmittingUpdatesWithWrongParentTag(
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop> /*unused*/) {
return ReactNativeFeatureFlags::fixDifferentiatorEmittingUpdatesWithWrongParentTag();
}
bool JReactNativeFeatureFlagsCxxInterop::fixMappingOfEventPrioritiesBetweenFabricAndReact(
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop> /*unused*/) {
return ReactNativeFeatureFlags::fixMappingOfEventPrioritiesBetweenFabricAndReact();
@@ -667,6 +678,9 @@ void JReactNativeFeatureFlagsCxxInterop::registerNatives() {
makeNativeMethod(
"excludeYogaFromRawProps",
JReactNativeFeatureFlagsCxxInterop::excludeYogaFromRawProps),
makeNativeMethod(
"fixDifferentiatorEmittingUpdatesWithWrongParentTag",
JReactNativeFeatureFlagsCxxInterop::fixDifferentiatorEmittingUpdatesWithWrongParentTag),
makeNativeMethod(
"fixMappingOfEventPrioritiesBetweenFabricAndReact",
JReactNativeFeatureFlagsCxxInterop::fixMappingOfEventPrioritiesBetweenFabricAndReact),
@@ -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<<17da0d7937c5c0c533293b86c8cdc9be>>
* @generated SignedSource<<4218168e779a2241d0752771c1f51b12>>
*/
/**
@@ -117,6 +117,9 @@ class JReactNativeFeatureFlagsCxxInterop
static bool excludeYogaFromRawProps(
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop>);
static bool fixDifferentiatorEmittingUpdatesWithWrongParentTag(
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop>);
static bool fixMappingOfEventPrioritiesBetweenFabricAndReact(
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop>);
@@ -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<<ef215623465d45c563030d724287b1c9>>
* @generated SignedSource<<2409869111055ff0b32c1f40c10042d7>>
*/
/**
@@ -142,6 +142,10 @@ bool ReactNativeFeatureFlags::excludeYogaFromRawProps() {
return getAccessor().excludeYogaFromRawProps();
}
bool ReactNativeFeatureFlags::fixDifferentiatorEmittingUpdatesWithWrongParentTag() {
return getAccessor().fixDifferentiatorEmittingUpdatesWithWrongParentTag();
}
bool ReactNativeFeatureFlags::fixMappingOfEventPrioritiesBetweenFabricAndReact() {
return getAccessor().fixMappingOfEventPrioritiesBetweenFabricAndReact();
}
@@ -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<<f741660e4cf2528defe0ab1f61858aab>>
* @generated SignedSource<<e628af8109a1d8bb6425515d824852a3>>
*/
/**
@@ -184,6 +184,11 @@ class ReactNativeFeatureFlags {
*/
RN_EXPORT static bool excludeYogaFromRawProps();
/**
* Fixes a bug in Differentiator where parent views may be referenced before they're created
*/
RN_EXPORT static bool fixDifferentiatorEmittingUpdatesWithWrongParentTag();
/**
* Uses the default event priority instead of the discreet event priority by default when dispatching events from Fabric to React.
*/
@@ -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<<4c3956150bbf826c2abf4f8daf569b88>>
* @generated SignedSource<<1e5b87b564e880cfb1423a85692092ba>>
*/
/**
@@ -551,6 +551,24 @@ bool ReactNativeFeatureFlagsAccessor::excludeYogaFromRawProps() {
return flagValue.value();
}
bool ReactNativeFeatureFlagsAccessor::fixDifferentiatorEmittingUpdatesWithWrongParentTag() {
auto flagValue = fixDifferentiatorEmittingUpdatesWithWrongParentTag_.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(29, "fixDifferentiatorEmittingUpdatesWithWrongParentTag");
flagValue = currentProvider_->fixDifferentiatorEmittingUpdatesWithWrongParentTag();
fixDifferentiatorEmittingUpdatesWithWrongParentTag_ = flagValue;
}
return flagValue.value();
}
bool ReactNativeFeatureFlagsAccessor::fixMappingOfEventPrioritiesBetweenFabricAndReact() {
auto flagValue = fixMappingOfEventPrioritiesBetweenFabricAndReact_.load();
@@ -560,7 +578,7 @@ bool ReactNativeFeatureFlagsAccessor::fixMappingOfEventPrioritiesBetweenFabricAn
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(29, "fixMappingOfEventPrioritiesBetweenFabricAndReact");
markFlagAsAccessed(30, "fixMappingOfEventPrioritiesBetweenFabricAndReact");
flagValue = currentProvider_->fixMappingOfEventPrioritiesBetweenFabricAndReact();
fixMappingOfEventPrioritiesBetweenFabricAndReact_ = flagValue;
@@ -578,7 +596,7 @@ bool ReactNativeFeatureFlagsAccessor::fixMountingCoordinatorReportedPendingTrans
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(30, "fixMountingCoordinatorReportedPendingTransactionsOnAndroid");
markFlagAsAccessed(31, "fixMountingCoordinatorReportedPendingTransactionsOnAndroid");
flagValue = currentProvider_->fixMountingCoordinatorReportedPendingTransactionsOnAndroid();
fixMountingCoordinatorReportedPendingTransactionsOnAndroid_ = flagValue;
@@ -596,7 +614,7 @@ bool ReactNativeFeatureFlagsAccessor::fuseboxEnabledDebug() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(31, "fuseboxEnabledDebug");
markFlagAsAccessed(32, "fuseboxEnabledDebug");
flagValue = currentProvider_->fuseboxEnabledDebug();
fuseboxEnabledDebug_ = flagValue;
@@ -614,7 +632,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(32, "fuseboxEnabledRelease");
markFlagAsAccessed(33, "fuseboxEnabledRelease");
flagValue = currentProvider_->fuseboxEnabledRelease();
fuseboxEnabledRelease_ = flagValue;
@@ -632,7 +650,7 @@ bool ReactNativeFeatureFlagsAccessor::initEagerTurboModulesOnNativeModulesQueueA
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(33, "initEagerTurboModulesOnNativeModulesQueueAndroid");
markFlagAsAccessed(34, "initEagerTurboModulesOnNativeModulesQueueAndroid");
flagValue = currentProvider_->initEagerTurboModulesOnNativeModulesQueueAndroid();
initEagerTurboModulesOnNativeModulesQueueAndroid_ = flagValue;
@@ -650,7 +668,7 @@ bool ReactNativeFeatureFlagsAccessor::lazyAnimationCallbacks() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(34, "lazyAnimationCallbacks");
markFlagAsAccessed(35, "lazyAnimationCallbacks");
flagValue = currentProvider_->lazyAnimationCallbacks();
lazyAnimationCallbacks_ = flagValue;
@@ -668,7 +686,7 @@ bool ReactNativeFeatureFlagsAccessor::loadVectorDrawablesOnImages() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(35, "loadVectorDrawablesOnImages");
markFlagAsAccessed(36, "loadVectorDrawablesOnImages");
flagValue = currentProvider_->loadVectorDrawablesOnImages();
loadVectorDrawablesOnImages_ = flagValue;
@@ -686,7 +704,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(36, "traceTurboModulePromiseRejectionsOnAndroid");
markFlagAsAccessed(37, "traceTurboModulePromiseRejectionsOnAndroid");
flagValue = currentProvider_->traceTurboModulePromiseRejectionsOnAndroid();
traceTurboModulePromiseRejectionsOnAndroid_ = flagValue;
@@ -704,7 +722,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(37, "useAlwaysAvailableJSErrorHandling");
markFlagAsAccessed(38, "useAlwaysAvailableJSErrorHandling");
flagValue = currentProvider_->useAlwaysAvailableJSErrorHandling();
useAlwaysAvailableJSErrorHandling_ = flagValue;
@@ -722,7 +740,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(38, "useFabricInterop");
markFlagAsAccessed(39, "useFabricInterop");
flagValue = currentProvider_->useFabricInterop();
useFabricInterop_ = flagValue;
@@ -740,7 +758,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(39, "useImmediateExecutorInAndroidBridgeless");
markFlagAsAccessed(40, "useImmediateExecutorInAndroidBridgeless");
flagValue = currentProvider_->useImmediateExecutorInAndroidBridgeless();
useImmediateExecutorInAndroidBridgeless_ = flagValue;
@@ -758,7 +776,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(40, "useNativeViewConfigsInBridgelessMode");
markFlagAsAccessed(41, "useNativeViewConfigsInBridgelessMode");
flagValue = currentProvider_->useNativeViewConfigsInBridgelessMode();
useNativeViewConfigsInBridgelessMode_ = flagValue;
@@ -776,7 +794,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(41, "useOptimisedViewPreallocationOnAndroid");
markFlagAsAccessed(42, "useOptimisedViewPreallocationOnAndroid");
flagValue = currentProvider_->useOptimisedViewPreallocationOnAndroid();
useOptimisedViewPreallocationOnAndroid_ = flagValue;
@@ -794,7 +812,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(42, "useOptimizedEventBatchingOnAndroid");
markFlagAsAccessed(43, "useOptimizedEventBatchingOnAndroid");
flagValue = currentProvider_->useOptimizedEventBatchingOnAndroid();
useOptimizedEventBatchingOnAndroid_ = flagValue;
@@ -812,7 +830,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(43, "useRuntimeShadowNodeReferenceUpdate");
markFlagAsAccessed(44, "useRuntimeShadowNodeReferenceUpdate");
flagValue = currentProvider_->useRuntimeShadowNodeReferenceUpdate();
useRuntimeShadowNodeReferenceUpdate_ = 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;
@@ -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<<3d98085a73dfc51541342dbb42ed89ab>>
* @generated SignedSource<<eb44aabe7e352481267aa9a6bf035ff1>>
*/
/**
@@ -61,6 +61,7 @@ class ReactNativeFeatureFlagsAccessor {
bool enableUIConsistency();
bool enableViewRecycling();
bool excludeYogaFromRawProps();
bool fixDifferentiatorEmittingUpdatesWithWrongParentTag();
bool fixMappingOfEventPrioritiesBetweenFabricAndReact();
bool fixMountingCoordinatorReportedPendingTransactionsOnAndroid();
bool fuseboxEnabledDebug();
@@ -89,7 +90,7 @@ class ReactNativeFeatureFlagsAccessor {
std::unique_ptr<ReactNativeFeatureFlagsProvider> currentProvider_;
bool wasOverridden_;
std::array<std::atomic<const char*>, 46> accessedFeatureFlags_;
std::array<std::atomic<const char*>, 47> accessedFeatureFlags_;
std::atomic<std::optional<bool>> commonTestFlag_;
std::atomic<std::optional<bool>> completeReactInstanceCreationOnBgThreadOnAndroid_;
@@ -120,6 +121,7 @@ class ReactNativeFeatureFlagsAccessor {
std::atomic<std::optional<bool>> enableUIConsistency_;
std::atomic<std::optional<bool>> enableViewRecycling_;
std::atomic<std::optional<bool>> excludeYogaFromRawProps_;
std::atomic<std::optional<bool>> fixDifferentiatorEmittingUpdatesWithWrongParentTag_;
std::atomic<std::optional<bool>> fixMappingOfEventPrioritiesBetweenFabricAndReact_;
std::atomic<std::optional<bool>> fixMountingCoordinatorReportedPendingTransactionsOnAndroid_;
std::atomic<std::optional<bool>> fuseboxEnabledDebug_;
@@ -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<<aff3c46b8d2db3bde519e1392569d53d>>
* @generated SignedSource<<ee899be30798eb6d386b44bc6bc027ea>>
*/
/**
@@ -143,6 +143,10 @@ class ReactNativeFeatureFlagsDefaults : public ReactNativeFeatureFlagsProvider {
return false;
}
bool fixDifferentiatorEmittingUpdatesWithWrongParentTag() override {
return true;
}
bool fixMappingOfEventPrioritiesBetweenFabricAndReact() override {
return false;
}
@@ -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<<8f8c7a372cdf9a3c06bc0d71f1ed85ad>>
* @generated SignedSource<<e5d1c60102f7444332bd34627c02eddd>>
*/
/**
@@ -54,6 +54,7 @@ class ReactNativeFeatureFlagsProvider {
virtual bool enableUIConsistency() = 0;
virtual bool enableViewRecycling() = 0;
virtual bool excludeYogaFromRawProps() = 0;
virtual bool fixDifferentiatorEmittingUpdatesWithWrongParentTag() = 0;
virtual bool fixMappingOfEventPrioritiesBetweenFabricAndReact() = 0;
virtual bool fixMountingCoordinatorReportedPendingTransactionsOnAndroid() = 0;
virtual bool fuseboxEnabledDebug() = 0;
@@ -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<<4808c1455f8e17c42036055dd6a81d7d>>
* @generated SignedSource<<4055a9b5e34ff6740a99d4e08853fe7d>>
*/
/**
@@ -189,6 +189,11 @@ bool NativeReactNativeFeatureFlags::excludeYogaFromRawProps(
return ReactNativeFeatureFlags::excludeYogaFromRawProps();
}
bool NativeReactNativeFeatureFlags::fixDifferentiatorEmittingUpdatesWithWrongParentTag(
jsi::Runtime& /*runtime*/) {
return ReactNativeFeatureFlags::fixDifferentiatorEmittingUpdatesWithWrongParentTag();
}
bool NativeReactNativeFeatureFlags::fixMappingOfEventPrioritiesBetweenFabricAndReact(
jsi::Runtime& /*runtime*/) {
return ReactNativeFeatureFlags::fixMappingOfEventPrioritiesBetweenFabricAndReact();
@@ -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<<6b4909879b76908792d89e3f24c1453a>>
* @generated SignedSource<<9541abe6da92d991557ca6d2f1e36a9a>>
*/
/**
@@ -95,6 +95,8 @@ class NativeReactNativeFeatureFlags
bool excludeYogaFromRawProps(jsi::Runtime& runtime);
bool fixDifferentiatorEmittingUpdatesWithWrongParentTag(jsi::Runtime& runtime);
bool fixMappingOfEventPrioritiesBetweenFabricAndReact(jsi::Runtime& runtime);
bool fixMountingCoordinatorReportedPendingTransactionsOnAndroid(jsi::Runtime& runtime);
@@ -13,7 +13,7 @@
#include <react/debug/flags.h>
#include <react/debug/react_native_assert.h>
#include <react/featureflags/ReactNativeFeatureFlags.h>
#include <react/renderer/animations/conversions.h>
#include <react/renderer/animations/utils.h>
#include <react/renderer/components/image/ImageProps.h>
@@ -392,6 +392,14 @@ LayoutAnimationKeyFrameManager::pullTransaction(
if (keyframe.type == AnimationConfigurationType::Update &&
mutation.newChildShadowView.tag > 0) {
keyframe.viewPrev = mutation.newChildShadowView;
if (ReactNativeFeatureFlags::
fixDifferentiatorEmittingUpdatesWithWrongParentTag()) {
keyframe.parentView = mutation.parentShadowView;
react_native_assert(
keyframe.finalMutationsForKeyFrame.size() == 1);
keyframe.finalMutationsForKeyFrame[0].parentShadowView =
mutation.parentShadowView;
}
}
}
}
@@ -403,6 +403,7 @@ static void calculateShadowViewMutationsFlattener(
const ShadowView& parentShadowView,
TinyMap<Tag, ShadowViewNodePair*>& unvisitedOtherNodes,
const ShadowViewNodePair& node,
const ShadowView& parentShadowViewForUpdate,
TinyMap<Tag, ShadowViewNodePair*>* parentSubVisitedOtherNewNodes = nullptr,
TinyMap<Tag, ShadowViewNodePair*>* parentSubVisitedOtherOldNodes = nullptr);
@@ -450,7 +451,8 @@ static void updateMatchedPairSubtrees(
mutationContainer,
parentShadowView,
newRemainingPairs,
oldPair);
oldPair,
oldPair.shadowView);
}
// Unflattening
else {
@@ -480,7 +482,8 @@ static void updateMatchedPairSubtrees(
mutationContainer,
parentShadowView,
unvisitedOldChildPairs,
newPair);
newPair,
parentShadowView);
// If old nodes were not visited, we know that we can delete
// them now. They will be removed from the hierarchy by the
@@ -621,6 +624,10 @@ static void updateMatchedPair(
* performed in the subtree. If it *is* in the map, it means the node is not
* in the Tree, and should be Deleted/Created **after this function is
* called**, by the caller.
*
* @param parentShadowView shadowView under which nodes should be mounted
* @param parentShadowViewForUpdate current parent in which node is mounted,
* used for update mutations
*/
static void calculateShadowViewMutationsFlattener(
ViewNodePairScope& scope,
@@ -629,6 +636,7 @@ static void calculateShadowViewMutationsFlattener(
const ShadowView& parentShadowView,
TinyMap<Tag, ShadowViewNodePair*>& unvisitedOtherNodes,
const ShadowViewNodePair& node,
const ShadowView& parentShadowViewForUpdate,
TinyMap<Tag, ShadowViewNodePair*>* parentSubVisitedOtherNewNodes,
TinyMap<Tag, ShadowViewNodePair*>* parentSubVisitedOtherOldNodes) {
DEBUG_LOGS({
@@ -838,11 +846,18 @@ static void calculateShadowViewMutationsFlattener(
// ShadowNode.
if (newTreeNodePair.shadowView != oldTreeNodePair.shadowView &&
newTreeNodePair.isConcreteView && oldTreeNodePair.isConcreteView) {
// We execute updates before creates, so pass the current parent in when
// unflattening.
// TODO: whenever we insert, we already update the relevant properties,
// so this update is redundant. We should remove this.
mutationContainer.updateMutations.push_back(
ShadowViewMutation::UpdateMutation(
oldTreeNodePair.shadowView,
newTreeNodePair.shadowView,
node.shadowView));
ReactNativeFeatureFlags::
fixDifferentiatorEmittingUpdatesWithWrongParentTag()
? parentShadowViewForUpdate
: node.shadowView));
}
// Update children if appropriate.
@@ -877,6 +892,9 @@ static void calculateShadowViewMutationsFlattener(
: newTreeNodePair.shadowView),
unvisitedOtherNodes,
treeChildPair,
(reparentMode == ReparentMode::Flatten
? oldTreeNodePair.shadowView
: parentShadowView),
subVisitedNewMap,
subVisitedOldMap);
} else {
@@ -918,6 +936,9 @@ static void calculateShadowViewMutationsFlattener(
: newTreeNodePair.shadowView),
unvisitedRecursiveChildPairs,
oldTreeNodePair,
(reparentMode == ReparentMode::Flatten
? oldTreeNodePair.shadowView
: parentShadowView),
subVisitedNewMap,
subVisitedOldMap);
}
@@ -933,6 +954,9 @@ static void calculateShadowViewMutationsFlattener(
: newTreeNodePair.shadowView),
unvisitedRecursiveChildPairs,
newTreeNodePair,
(reparentMode == ReparentMode::Flatten
? oldTreeNodePair.shadowView
: parentShadowView),
subVisitedNewMap,
subVisitedOldMap);
@@ -221,6 +221,11 @@ void StubViewTree::mutate(const ShadowViewMutationList& mutations) {
react_native_assert(hasTag(mutation.newChildShadowView.tag));
auto oldStubView = registry_[mutation.newChildShadowView.tag];
react_native_assert(oldStubView->tag != 0);
if (mutation.parentShadowView.tag != 0) {
react_native_assert(hasTag(mutation.parentShadowView.tag));
react_native_assert(
oldStubView->parentTag == mutation.parentShadowView.tag);
}
if ((ShadowView)(*oldStubView) != mutation.oldChildShadowView) {
LOG(ERROR)
<< "StubView: ASSERT FAILURE: UPDATE mutation assertion failure: oldChildShadowView does not match oldStubView: ["
@@ -558,8 +558,7 @@ TEST(MountingTest, testViewReparentingInstructionGeneration) {
EXPECT_EQ(mutations1[0].oldChildShadowView.tag, childG->getTag());
EXPECT_EQ(mutations1[1].type, ShadowViewMutation::Update);
EXPECT_EQ(mutations1[1].oldChildShadowView.tag, reparentedViewA->getTag());
// This is incorrect! ChildH does not exist yet at this point
EXPECT_EQ(mutations1[1].parentShadowView.tag, childH->getTag());
EXPECT_EQ(mutations1[1].parentShadowView.tag, childG->getTag());
EXPECT_EQ(mutations1[2].type, ShadowViewMutation::Remove);
EXPECT_EQ(mutations1[2].oldChildShadowView.tag, reparentedViewA->getTag());
EXPECT_EQ(mutations1[3].type, ShadowViewMutation::Create);
@@ -292,6 +292,14 @@ const definitions: FeatureFlagDefinitions = {
purpose: 'experimentation',
},
},
fixDifferentiatorEmittingUpdatesWithWrongParentTag: {
defaultValue: true,
metadata: {
description:
"Fixes a bug in Differentiator where parent views may be referenced before they're created",
purpose: 'release',
},
},
fixMappingOfEventPrioritiesBetweenFabricAndReact: {
defaultValue: false,
metadata: {
@@ -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<<38ad29621eeb29a9f82735dc187c13d4>>
* @generated SignedSource<<ceb682684f8c9df33a3f432033ab266a>>
* @flow strict
*/
@@ -80,6 +80,7 @@ export type ReactNativeFeatureFlags = {
enableUIConsistency: Getter<boolean>,
enableViewRecycling: Getter<boolean>,
excludeYogaFromRawProps: Getter<boolean>,
fixDifferentiatorEmittingUpdatesWithWrongParentTag: Getter<boolean>,
fixMappingOfEventPrioritiesBetweenFabricAndReact: Getter<boolean>,
fixMountingCoordinatorReportedPendingTransactionsOnAndroid: Getter<boolean>,
fuseboxEnabledDebug: Getter<boolean>,
@@ -304,6 +305,10 @@ export const enableViewRecycling: Getter<boolean> = createNativeFlagGetter('enab
* When enabled, rawProps in Props will not include Yoga specific props.
*/
export const excludeYogaFromRawProps: Getter<boolean> = createNativeFlagGetter('excludeYogaFromRawProps', false);
/**
* Fixes a bug in Differentiator where parent views may be referenced before they're created
*/
export const fixDifferentiatorEmittingUpdatesWithWrongParentTag: Getter<boolean> = createNativeFlagGetter('fixDifferentiatorEmittingUpdatesWithWrongParentTag', true);
/**
* Uses the default event priority instead of the discreet event priority by default when dispatching events from Fabric to React.
*/
@@ -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<<17c0e677f7b308795e836c558b15d1d1>>
* @generated SignedSource<<b36732a4a633b239ebd6d12d1ef460cb>>
* @flow strict
*/
@@ -53,6 +53,7 @@ export interface Spec extends TurboModule {
+enableUIConsistency?: () => boolean;
+enableViewRecycling?: () => boolean;
+excludeYogaFromRawProps?: () => boolean;
+fixDifferentiatorEmittingUpdatesWithWrongParentTag?: () => boolean;
+fixMappingOfEventPrioritiesBetweenFabricAndReact?: () => boolean;
+fixMountingCoordinatorReportedPendingTransactionsOnAndroid?: () => boolean;
+fuseboxEnabledDebug?: () => boolean;