diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/fabric/FabricUIManager.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/fabric/FabricUIManager.java index d96a0fa1e59..61f8cf63a0f 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/fabric/FabricUIManager.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/fabric/FabricUIManager.java @@ -27,6 +27,7 @@ import androidx.annotation.AnyThread; import androidx.annotation.Nullable; import androidx.annotation.UiThread; +import androidx.annotation.VisibleForTesting; import androidx.core.view.ViewCompat.FocusDirection; import com.facebook.common.logging.FLog; import com.facebook.infer.annotation.Assertions; @@ -65,6 +66,7 @@ import com.facebook.react.fabric.mounting.mountitems.MountItem; import com.facebook.react.fabric.mounting.mountitems.MountItemFactory; import com.facebook.react.fabric.mounting.mountitems.PrefetchResourcesMountItem; +import com.facebook.react.fabric.mounting.mountitems.PullTransactionMountItem; import com.facebook.react.fabric.mounting.mountitems.SynchronousMountItem; import com.facebook.react.internal.featureflags.ReactNativeFeatureFlags; import com.facebook.react.internal.featureflags.ReactNativeNewArchitectureFeatureFlags; @@ -934,7 +936,8 @@ private void scheduleMountItem( long layoutEndTime, long finishTransactionStartTime, long finishTransactionEndTime, - int affectedLayoutNodesCount) { + int affectedLayoutNodesCount, + boolean synchronous) { // When Binding.cpp calls scheduleMountItems during a commit phase, it always calls with // a BatchMountItem. No other sites call into this with a BatchMountItem, and Binding.cpp only // calls scheduleMountItems with a BatchMountItem. @@ -948,8 +951,9 @@ private void scheduleMountItem( } else { shouldSchedule = mountItem != null; } - // In case of sync rendering, this could be called on the UI thread. Otherwise, - // it should ~always be called on the JS thread. + // In the push model, this is ~always called on the JS thread, or on the UI + // thread in case of sync rendering. + // In the pull model, this is always called on the UI thread, at pull time. for (UIManagerListener listener : mListeners) { listener.didScheduleMountItems(this); } @@ -964,16 +968,22 @@ private void scheduleMountItem( if (shouldSchedule) { Assertions.assertNotNull(mountItem, "MountItem is null"); - mMountItemDispatcher.addMountItem(mountItem); - if (UiThreadUtil.isOnUiThread()) { - Runnable runnable = - new GuardedRunnable(mReactApplicationContext) { - @Override - public void runGuarded() { - mMountItemDispatcher.tryDispatchMountItems(); - } - }; - runnable.run(); + if (synchronous) { + // Pull model: we are already on the UI thread, inside the dispatcher's loop executing + // a PullTransactionMountItem. We don't schedule the item, we execute it directly. + mountItem.execute(mMountingManager); + } else { + mMountItemDispatcher.addMountItem(mountItem); + if (UiThreadUtil.isOnUiThread()) { + Runnable runnable = + new GuardedRunnable(mReactApplicationContext) { + @Override + public void runGuarded() { + mMountItemDispatcher.tryDispatchMountItems(); + } + }; + runnable.run(); + } } } @@ -1009,6 +1019,26 @@ public void runGuarded() { } } + /** + * Pull model: called from C++ via JNI (usually on the commit thread) to signal that a transaction + * is available for {@code surfaceId}. Enqueues a PullTransactionMountItem so the UI thread pulls + * and applies the transaction itself, preserving mount-item ordering. + */ + @SuppressWarnings("unused") + @AnyThread + @ThreadConfined(ANY) + @VisibleForTesting + void onTransactionAvailable(int surfaceId) { + FabricUIManagerBinding binding = mBinding; + if (binding == null) { + return; + } + mMountItemDispatcher.addMountItem(new PullTransactionMountItem(surfaceId, binding)); + if (UiThreadUtil.isOnUiThread()) { + mMountItemDispatcher.tryDispatchMountItems(); + } + } + @SuppressWarnings("unused") @AnyThread @ThreadConfined(ANY) diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/fabric/FabricUIManagerBinding.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/fabric/FabricUIManagerBinding.kt index 84a6a17b208..b1b3f12f50c 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/fabric/FabricUIManagerBinding.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/fabric/FabricUIManagerBinding.kt @@ -85,6 +85,8 @@ internal class FabricUIManagerBinding : HybridClassBase() { external fun reportMount(surfaceId: Int) + external fun pullAndExecuteTransaction(surfaceId: Int) + external fun mergeReactRevision(surfaceId: Int) fun register( diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/mountitems/PullTransactionMountItem.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/mountitems/PullTransactionMountItem.kt new file mode 100644 index 00000000000..68563cdf4ec --- /dev/null +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/mountitems/PullTransactionMountItem.kt @@ -0,0 +1,33 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +package com.facebook.react.fabric.mounting.mountitems + +import com.facebook.proguard.annotations.DoNotStripAny +import com.facebook.react.fabric.FabricUIManagerBinding +import com.facebook.react.fabric.mounting.MountingManager + +/** + * Pull model mount item. Enqueued (on the commit thread) when C++ notifies that a transaction is + * available for a surface. When it executes on the UI thread it asks C++ to pull the surface's + * pending transaction and apply it synchronously, so the diff + batch construction happens on the + * UI thread instead of the commit thread (matching iOS). + */ +@DoNotStripAny +internal class PullTransactionMountItem( + private val surfaceId: Int, + private val binding: FabricUIManagerBinding, +) : MountItem { + + override fun execute(mountingManager: MountingManager) { + binding.pullAndExecuteTransaction(surfaceId) + } + + override fun getSurfaceId(): Int = surfaceId + + override fun toString(): String = "PullTransactionMountItem [surfaceId: $surfaceId]" +} 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 9ab11a81459..732c511dfc1 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<> + * @generated SignedSource<<9117484347f2ff31f7ba6e5a2ac38d93>> */ /** @@ -204,6 +204,12 @@ public object ReactNativeFeatureFlags { @JvmStatic public fun enableImagePrefetchingAndroid(): Boolean = accessor.enableImagePrefetchingAndroid() + /** + * When enabled, Image `tintColor` is handled as a true optional: any defined color is applied as a tint — including `transparent` (alpha 0), which renders the image invisible — and an unset value clears a previously applied tint. When disabled, the prior behavior is preserved, where a transparent `tintColor` is treated as unassigned and the image renders untinted. + */ + @JvmStatic + public fun enableImageTransparentTintColor(): Boolean = accessor.enableImageTransparentTintColor() + /** * Dispatches state updates for content offset changes synchronously on the main thread. */ @@ -252,6 +258,12 @@ public object ReactNativeFeatureFlags { @JvmStatic public fun enableModuleArgumentNSNullConversionIOS(): Boolean = accessor.enableModuleArgumentNSNullConversionIOS() + /** + * When enabled, Android mounts transactions with the pull model (like iOS): the commit thread no longer pulls and builds the mount batch in schedulerShouldRenderTransactions. Instead the UI thread pulls the transaction itself via a PullTransactionMountItem enqueued in the MountItemDispatcher, builds the IntBufferBatchMountItem, and applies it synchronously. Implies rawProps accumulation (enableAccumulatedUpdatesInRawPropsAndroid behavior). + */ + @JvmStatic + public fun enableMountingCoordinatorPullModelAndroid(): Boolean = accessor.enableMountingCoordinatorPullModelAndroid() + /** * Enables the MutationObserver Web API in React Native. */ 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 7785ae8a8a7..59d53089af0 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<<628ede95c745280c1ceaa73c6dc45de0>> + * @generated SignedSource<> */ /** @@ -49,6 +49,7 @@ internal class ReactNativeFeatureFlagsCxxAccessor : ReactNativeFeatureFlagsAcces private var enableIOSTextBaselineOffsetPerLineCache: Boolean? = null private var enableIOSViewClipToPaddingBoxCache: Boolean? = null private var enableImagePrefetchingAndroidCache: Boolean? = null + private var enableImageTransparentTintColorCache: Boolean? = null private var enableImmediateUpdateModeForContentOffsetChangesCache: Boolean? = null private var enableImperativeFocusCache: Boolean? = null private var enableInteropViewManagerClassLookUpOptimizationIOSCache: Boolean? = null @@ -57,6 +58,7 @@ internal class ReactNativeFeatureFlagsCxxAccessor : ReactNativeFeatureFlagsAcces private var enableLayoutAnimationsOnAndroidCache: Boolean? = null private var enableLayoutAnimationsOnIOSCache: Boolean? = null private var enableModuleArgumentNSNullConversionIOSCache: Boolean? = null + private var enableMountingCoordinatorPullModelAndroidCache: Boolean? = null private var enableMutationObserverByDefaultCache: Boolean? = null private var enableNativeCSSParsingCache: Boolean? = null private var enablePreparedTextLayoutCache: Boolean? = null @@ -367,6 +369,15 @@ internal class ReactNativeFeatureFlagsCxxAccessor : ReactNativeFeatureFlagsAcces return cached } + override fun enableImageTransparentTintColor(): Boolean { + var cached = enableImageTransparentTintColorCache + if (cached == null) { + cached = ReactNativeFeatureFlagsCxxInterop.enableImageTransparentTintColor() + enableImageTransparentTintColorCache = cached + } + return cached + } + override fun enableImmediateUpdateModeForContentOffsetChanges(): Boolean { var cached = enableImmediateUpdateModeForContentOffsetChangesCache if (cached == null) { @@ -439,6 +450,15 @@ internal class ReactNativeFeatureFlagsCxxAccessor : ReactNativeFeatureFlagsAcces return cached } + override fun enableMountingCoordinatorPullModelAndroid(): Boolean { + var cached = enableMountingCoordinatorPullModelAndroidCache + if (cached == null) { + cached = ReactNativeFeatureFlagsCxxInterop.enableMountingCoordinatorPullModelAndroid() + enableMountingCoordinatorPullModelAndroidCache = cached + } + return cached + } + override fun enableMutationObserverByDefault(): Boolean { var cached = enableMutationObserverByDefaultCache 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 6b59c5c2d90..fbbef2ca658 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<<6a0e60fe118f3651dc76c895a37fbce2>> + * @generated SignedSource<<1ef72233f02973021b83bd2e2aa1f69b>> */ /** @@ -86,6 +86,8 @@ public object ReactNativeFeatureFlagsCxxInterop { @DoNotStrip @JvmStatic public external fun enableImagePrefetchingAndroid(): Boolean + @DoNotStrip @JvmStatic public external fun enableImageTransparentTintColor(): Boolean + @DoNotStrip @JvmStatic public external fun enableImmediateUpdateModeForContentOffsetChanges(): Boolean @DoNotStrip @JvmStatic public external fun enableImperativeFocus(): Boolean @@ -102,6 +104,8 @@ public object ReactNativeFeatureFlagsCxxInterop { @DoNotStrip @JvmStatic public external fun enableModuleArgumentNSNullConversionIOS(): Boolean + @DoNotStrip @JvmStatic public external fun enableMountingCoordinatorPullModelAndroid(): Boolean + @DoNotStrip @JvmStatic public external fun enableMutationObserverByDefault(): Boolean @DoNotStrip @JvmStatic public external fun enableNativeCSSParsing(): 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 fc8e25247c9..b9cd0522e87 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<<37e483d52d12a735e646c8ac06acb62c>> + * @generated SignedSource<> */ /** @@ -81,6 +81,8 @@ public open class ReactNativeFeatureFlagsDefaults : ReactNativeFeatureFlagsProvi override fun enableImagePrefetchingAndroid(): Boolean = false + override fun enableImageTransparentTintColor(): Boolean = false + override fun enableImmediateUpdateModeForContentOffsetChanges(): Boolean = false override fun enableImperativeFocus(): Boolean = false @@ -97,6 +99,8 @@ public open class ReactNativeFeatureFlagsDefaults : ReactNativeFeatureFlagsProvi override fun enableModuleArgumentNSNullConversionIOS(): Boolean = false + override fun enableMountingCoordinatorPullModelAndroid(): Boolean = false + override fun enableMutationObserverByDefault(): Boolean = false override fun enableNativeCSSParsing(): 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 2469918dff4..4ed61aa1911 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<<5b7a6ca47ca43f473596e35dfced16e0>> + * @generated SignedSource<<9ae32c46a5a6310ef96eb91c9ea5b12e>> */ /** @@ -53,6 +53,7 @@ internal class ReactNativeFeatureFlagsLocalAccessor : ReactNativeFeatureFlagsAcc private var enableIOSTextBaselineOffsetPerLineCache: Boolean? = null private var enableIOSViewClipToPaddingBoxCache: Boolean? = null private var enableImagePrefetchingAndroidCache: Boolean? = null + private var enableImageTransparentTintColorCache: Boolean? = null private var enableImmediateUpdateModeForContentOffsetChangesCache: Boolean? = null private var enableImperativeFocusCache: Boolean? = null private var enableInteropViewManagerClassLookUpOptimizationIOSCache: Boolean? = null @@ -61,6 +62,7 @@ internal class ReactNativeFeatureFlagsLocalAccessor : ReactNativeFeatureFlagsAcc private var enableLayoutAnimationsOnAndroidCache: Boolean? = null private var enableLayoutAnimationsOnIOSCache: Boolean? = null private var enableModuleArgumentNSNullConversionIOSCache: Boolean? = null + private var enableMountingCoordinatorPullModelAndroidCache: Boolean? = null private var enableMutationObserverByDefaultCache: Boolean? = null private var enableNativeCSSParsingCache: Boolean? = null private var enablePreparedTextLayoutCache: Boolean? = null @@ -400,6 +402,16 @@ internal class ReactNativeFeatureFlagsLocalAccessor : ReactNativeFeatureFlagsAcc return cached } + override fun enableImageTransparentTintColor(): Boolean { + var cached = enableImageTransparentTintColorCache + if (cached == null) { + cached = currentProvider.enableImageTransparentTintColor() + accessedFeatureFlags.add("enableImageTransparentTintColor") + enableImageTransparentTintColorCache = cached + } + return cached + } + override fun enableImmediateUpdateModeForContentOffsetChanges(): Boolean { var cached = enableImmediateUpdateModeForContentOffsetChangesCache if (cached == null) { @@ -480,6 +492,16 @@ internal class ReactNativeFeatureFlagsLocalAccessor : ReactNativeFeatureFlagsAcc return cached } + override fun enableMountingCoordinatorPullModelAndroid(): Boolean { + var cached = enableMountingCoordinatorPullModelAndroidCache + if (cached == null) { + cached = currentProvider.enableMountingCoordinatorPullModelAndroid() + accessedFeatureFlags.add("enableMountingCoordinatorPullModelAndroid") + enableMountingCoordinatorPullModelAndroidCache = cached + } + return cached + } + override fun enableMutationObserverByDefault(): Boolean { var cached = enableMutationObserverByDefaultCache 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 3282cdad209..1b52bd34d58 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<> + * @generated SignedSource<<1a1d47f2d85404c776e55db40f7dbc6e>> */ /** @@ -81,6 +81,8 @@ public interface ReactNativeFeatureFlagsProvider { @DoNotStrip public fun enableImagePrefetchingAndroid(): Boolean + @DoNotStrip public fun enableImageTransparentTintColor(): Boolean + @DoNotStrip public fun enableImmediateUpdateModeForContentOffsetChanges(): Boolean @DoNotStrip public fun enableImperativeFocus(): Boolean @@ -97,6 +99,8 @@ public interface ReactNativeFeatureFlagsProvider { @DoNotStrip public fun enableModuleArgumentNSNullConversionIOS(): Boolean + @DoNotStrip public fun enableMountingCoordinatorPullModelAndroid(): Boolean + @DoNotStrip public fun enableMutationObserverByDefault(): Boolean @DoNotStrip public fun enableNativeCSSParsing(): Boolean diff --git a/packages/react-native/ReactAndroid/src/main/jni/react/fabric/FabricMountingManager.cpp b/packages/react-native/ReactAndroid/src/main/jni/react/fabric/FabricMountingManager.cpp index b82bdab278c..8bef6c01ca8 100644 --- a/packages/react-native/ReactAndroid/src/main/jni/react/fabric/FabricMountingManager.cpp +++ b/packages/react-native/ReactAndroid/src/main/jni/react/fabric/FabricMountingManager.cpp @@ -52,6 +52,13 @@ void FabricMountingManager::onSurfaceStop(SurfaceId surfaceId) { allocatedViewRegistry_.erase(surfaceId); } +void FabricMountingManager::onTransactionAvailable(SurfaceId surfaceId) { + static auto onTransactionAvailable = + JFabricUIManager::javaClassStatic()->getMethod( + "onTransactionAvailable"); + onTransactionAvailable(javaUIManager_, surfaceId); +} + bool FabricMountingManager::isViewAllocated(SurfaceId surfaceId, Tag tag) { std::lock_guard lock(allocatedViewsMutex_); auto it = allocatedViewRegistry_.find(surfaceId); @@ -339,7 +346,10 @@ jni::local_ref getProps( return ReadableNativeMap::newObjectCxxArgs(std::move(diff)); } - if (ReactNativeFeatureFlags::enableAccumulatedUpdatesInRawPropsAndroid()) { + // With the pull model we need to have the accumulated props, as we might skip + // intermediate commits + if (ReactNativeFeatureFlags::enableAccumulatedUpdatesInRawPropsAndroid() || + ReactNativeFeatureFlags::enableMountingCoordinatorPullModelAndroid()) { if (oldProps == nullptr) { return ReadableNativeMap::newObjectCxxArgs(newProps->rawProps); } else { @@ -568,7 +578,8 @@ inline void writeUpdateOverflowInsetMountItem( } // namespace void FabricMountingManager::executeMount( - const MountingTransaction& transaction) { + const MountingTransaction& transaction, + bool synchronous) { TraceSection section("FabricMountingManager::executeMount"); std::scoped_lock lock(commitMutex_); @@ -830,7 +841,8 @@ void FabricMountingManager::executeMount( jlong, jlong, jlong, - jint)>("scheduleMountItem"); + jint, + jboolean)>("scheduleMountItem"); if (batchMountItemIntsSize == 0) { auto finishTransactionEndTime = telemetryTimePointNow(); @@ -845,7 +857,8 @@ void FabricMountingManager::executeMount( telemetryTimePointToMilliseconds(telemetry.getLayoutEndTime()), telemetryTimePointToMilliseconds(finishTransactionStartTime), telemetryTimePointToMilliseconds(finishTransactionEndTime), - telemetry.getAffectedLayoutNodesCount()); + telemetry.getAffectedLayoutNodesCount(), + static_cast(synchronous)); return; } @@ -1012,7 +1025,8 @@ void FabricMountingManager::executeMount( telemetryTimePointToMilliseconds(telemetry.getLayoutEndTime()), telemetryTimePointToMilliseconds(finishTransactionStartTime), telemetryTimePointToMilliseconds(finishTransactionEndTime), - telemetry.getAffectedLayoutNodesCount()); + telemetry.getAffectedLayoutNodesCount(), + static_cast(synchronous)); env->DeleteLocalRef(buffer.ints); } diff --git a/packages/react-native/ReactAndroid/src/main/jni/react/fabric/FabricMountingManager.h b/packages/react-native/ReactAndroid/src/main/jni/react/fabric/FabricMountingManager.h index 377880b658b..3d31c58db7e 100644 --- a/packages/react-native/ReactAndroid/src/main/jni/react/fabric/FabricMountingManager.h +++ b/packages/react-native/ReactAndroid/src/main/jni/react/fabric/FabricMountingManager.h @@ -54,7 +54,23 @@ class FabricMountingManager final { */ bool isViewAllocated(SurfaceId surfaceId, Tag tag); - void executeMount(const MountingTransaction &transaction); + /* + * Converts the transaction's mutations into an IntBufferBatchMountItem and + * hands it to Java. + * + * In the push model (`synchronous` = false), the batch is + * scheduled onto the UI thread asynchronously. + * + * In the pull model (`synchronous` = true) the batch is + * applied immediately on the calling (UI) thread. + */ + void executeMount(const MountingTransaction &transaction, bool synchronous = false); + + /* + * Pull model: notify Java that a transaction is available for `surfaceId` so + * the UI thread can pull it via a PullTransactionMountItem. + */ + void onTransactionAvailable(SurfaceId surfaceId); void dispatchCommand(const ShadowView &shadowView, const std::string &commandName, const folly::dynamic &args); diff --git a/packages/react-native/ReactAndroid/src/main/jni/react/fabric/FabricUIManagerBinding.cpp b/packages/react-native/ReactAndroid/src/main/jni/react/fabric/FabricUIManagerBinding.cpp index e1d3b3f32e0..0c367c528d2 100644 --- a/packages/react-native/ReactAndroid/src/main/jni/react/fabric/FabricUIManagerBinding.cpp +++ b/packages/react-native/ReactAndroid/src/main/jni/react/fabric/FabricUIManagerBinding.cpp @@ -120,6 +120,55 @@ void FabricUIManagerBinding::reportMount(SurfaceId surfaceId) { scheduler->reportMount(surfaceId); } +void FabricUIManagerBinding::pullAndExecuteTransaction(SurfaceId surfaceId) { + TraceSection section("FabricUIManagerBinding::pullAndExecuteTransaction"); + + std::shared_ptr mountingCoordinator; + { + std::shared_lock lock(surfaceHandlerRegistryMutex_); + auto iterator = surfaceHandlerRegistry_.find(surfaceId); + if (iterator == surfaceHandlerRegistry_.end()) { + return; + } + const auto* surfaceHandler = std::get_if(&iterator->second); + jni::local_ref javaSurfaceHandler; + if (surfaceHandler == nullptr) { + javaSurfaceHandler = + std::get>( + iterator->second) + .lockLocal(); + if (javaSurfaceHandler) { + surfaceHandler = &javaSurfaceHandler->cthis()->getSurfaceHandler(); + } + } + if (surfaceHandler != nullptr) { + mountingCoordinator = surfaceHandler->getMountingCoordinator(); + } + } + + if (mountingCoordinator == nullptr) { + return; + } + + auto mountingManager = getMountingManager("pullAndExecuteTransaction"); + if (!mountingManager) { + return; + } + + // The UI thread pulls the transaction itself (it may accumulate several + // revisions committed since the notification was enqueued, and may be empty + // if a previous pull already consumed them). willPerformAsynchronously = + // false: the transaction is applied synchronously right here, so no + // `didPerformAsyncTransactions` bookkeeping is needed. + auto mountingTransaction = + mountingCoordinator->pullTransaction(/* willPerformAsynchronously = */ + false); + if (mountingTransaction.has_value()) { + mountingManager->executeMount( + *mountingTransaction, /* synchronous = */ true); + } +} + #pragma mark - Surface management // Used by bridgeless @@ -628,9 +677,11 @@ FabricUIManagerBinding::getMountingManager(const char* locationHint) { void FabricUIManagerBinding::schedulerDidFinishTransaction( const std::shared_ptr& mountingCoordinator) { - if (ReactNativeFeatureFlags::enableAccumulatedUpdatesInRawPropsAndroid()) { - // We don't do anything here. We will pull the transaction in - // `schedulerShouldRenderTransactions`. + if (ReactNativeFeatureFlags::enableMountingCoordinatorPullModelAndroid() || + ReactNativeFeatureFlags::enableAccumulatedUpdatesInRawPropsAndroid()) { + // We don't do anything here. With the pull model, the UI thread pulls the + // transaction itself (see `pullAndExecuteTransaction`). Otherwise we will + // pull the transaction in `schedulerShouldRenderTransactions`. } else { // We shouldn't be pulling the transaction here (which triggers diffing of // the trees to determine the mutations to run on the host platform), @@ -678,7 +729,15 @@ void FabricUIManagerBinding::schedulerShouldRenderTransactions( } } - if (ReactNativeFeatureFlags::enableAccumulatedUpdatesInRawPropsAndroid()) { + if (ReactNativeFeatureFlags::enableMountingCoordinatorPullModelAndroid()) { + // Pull model: do NOT pull the transaction or build the batch here (on the + // commit thread). Just notify Java that a transaction is available; the UI + // thread will pull it via a PullTransactionMountItem and call back into + // `pullAndExecuteTransaction`. + mountingManager->onTransactionAvailable( + mountingCoordinator->getSurfaceId()); + } else if (ReactNativeFeatureFlags:: + enableAccumulatedUpdatesInRawPropsAndroid()) { auto mountingTransaction = mountingCoordinator->pullTransaction( /* willPerformAsynchronously = */ true); if (mountingTransaction.has_value()) { @@ -867,6 +926,9 @@ void FabricUIManagerBinding::registerNatives() { "drainPreallocateViewsQueue", FabricUIManagerBinding::drainPreallocateViewsQueue), makeNativeMethod("reportMount", FabricUIManagerBinding::reportMount), + makeNativeMethod( + "pullAndExecuteTransaction", + FabricUIManagerBinding::pullAndExecuteTransaction), makeNativeMethod( "uninstallFabricUIManager", FabricUIManagerBinding::uninstallFabricUIManager), diff --git a/packages/react-native/ReactAndroid/src/main/jni/react/fabric/FabricUIManagerBinding.h b/packages/react-native/ReactAndroid/src/main/jni/react/fabric/FabricUIManagerBinding.h index 60e73000677..ea4b928357e 100644 --- a/packages/react-native/ReactAndroid/src/main/jni/react/fabric/FabricUIManagerBinding.h +++ b/packages/react-native/ReactAndroid/src/main/jni/react/fabric/FabricUIManagerBinding.h @@ -133,6 +133,8 @@ class FabricUIManagerBinding : public jni::HybridClass, void reportMount(SurfaceId surfaceId); + void pullAndExecuteTransaction(SurfaceId surfaceId); + jint findNextFocusableElement(jint parentTag, jint focusedTag, jint direction); jintArray getRelativeAncestorList(jint rootTag, jint childTag); 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 e2cd698aed2..bd0bdc7ac32 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<<41ff49e9305aa1ff144582dfcac650d0>> + * @generated SignedSource<<28de1e205f30135e96d5cb94a902faec>> */ /** @@ -213,6 +213,12 @@ class ReactNativeFeatureFlagsJavaProvider return method(javaProvider_); } + bool enableImageTransparentTintColor() override { + static const auto method = + getReactNativeFeatureFlagsProviderJavaClass()->getMethod("enableImageTransparentTintColor"); + return method(javaProvider_); + } + bool enableImmediateUpdateModeForContentOffsetChanges() override { static const auto method = getReactNativeFeatureFlagsProviderJavaClass()->getMethod("enableImmediateUpdateModeForContentOffsetChanges"); @@ -261,6 +267,12 @@ class ReactNativeFeatureFlagsJavaProvider return method(javaProvider_); } + bool enableMountingCoordinatorPullModelAndroid() override { + static const auto method = + getReactNativeFeatureFlagsProviderJavaClass()->getMethod("enableMountingCoordinatorPullModelAndroid"); + return method(javaProvider_); + } + bool enableMutationObserverByDefault() override { static const auto method = getReactNativeFeatureFlagsProviderJavaClass()->getMethod("enableMutationObserverByDefault"); @@ -698,6 +710,11 @@ bool JReactNativeFeatureFlagsCxxInterop::enableImagePrefetchingAndroid( return ReactNativeFeatureFlags::enableImagePrefetchingAndroid(); } +bool JReactNativeFeatureFlagsCxxInterop::enableImageTransparentTintColor( + facebook::jni::alias_ref /*unused*/) { + return ReactNativeFeatureFlags::enableImageTransparentTintColor(); +} + bool JReactNativeFeatureFlagsCxxInterop::enableImmediateUpdateModeForContentOffsetChanges( facebook::jni::alias_ref /*unused*/) { return ReactNativeFeatureFlags::enableImmediateUpdateModeForContentOffsetChanges(); @@ -738,6 +755,11 @@ bool JReactNativeFeatureFlagsCxxInterop::enableModuleArgumentNSNullConversionIOS return ReactNativeFeatureFlags::enableModuleArgumentNSNullConversionIOS(); } +bool JReactNativeFeatureFlagsCxxInterop::enableMountingCoordinatorPullModelAndroid( + facebook::jni::alias_ref /*unused*/) { + return ReactNativeFeatureFlags::enableMountingCoordinatorPullModelAndroid(); +} + bool JReactNativeFeatureFlagsCxxInterop::enableMutationObserverByDefault( facebook::jni::alias_ref /*unused*/) { return ReactNativeFeatureFlags::enableMutationObserverByDefault(); @@ -1096,6 +1118,9 @@ void JReactNativeFeatureFlagsCxxInterop::registerNatives() { makeNativeMethod( "enableImagePrefetchingAndroid", JReactNativeFeatureFlagsCxxInterop::enableImagePrefetchingAndroid), + makeNativeMethod( + "enableImageTransparentTintColor", + JReactNativeFeatureFlagsCxxInterop::enableImageTransparentTintColor), makeNativeMethod( "enableImmediateUpdateModeForContentOffsetChanges", JReactNativeFeatureFlagsCxxInterop::enableImmediateUpdateModeForContentOffsetChanges), @@ -1120,6 +1145,9 @@ void JReactNativeFeatureFlagsCxxInterop::registerNatives() { makeNativeMethod( "enableModuleArgumentNSNullConversionIOS", JReactNativeFeatureFlagsCxxInterop::enableModuleArgumentNSNullConversionIOS), + makeNativeMethod( + "enableMountingCoordinatorPullModelAndroid", + JReactNativeFeatureFlagsCxxInterop::enableMountingCoordinatorPullModelAndroid), makeNativeMethod( "enableMutationObserverByDefault", JReactNativeFeatureFlagsCxxInterop::enableMutationObserverByDefault), 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 6e30545c906..a787b50e7c9 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<<9c5bd61122f068c919c70399828585d1>> + * @generated SignedSource<<535898dd9498c65f30d56122c06b408b>> */ /** @@ -117,6 +117,9 @@ class JReactNativeFeatureFlagsCxxInterop static bool enableImagePrefetchingAndroid( facebook::jni::alias_ref); + static bool enableImageTransparentTintColor( + facebook::jni::alias_ref); + static bool enableImmediateUpdateModeForContentOffsetChanges( facebook::jni::alias_ref); @@ -141,6 +144,9 @@ class JReactNativeFeatureFlagsCxxInterop static bool enableModuleArgumentNSNullConversionIOS( facebook::jni::alias_ref); + static bool enableMountingCoordinatorPullModelAndroid( + facebook::jni::alias_ref); + static bool enableMutationObserverByDefault( facebook::jni::alias_ref); diff --git a/packages/react-native/ReactAndroid/src/test/java/com/facebook/react/fabric/FabricUIManagerPullModelTest.kt b/packages/react-native/ReactAndroid/src/test/java/com/facebook/react/fabric/FabricUIManagerPullModelTest.kt new file mode 100644 index 00000000000..3a2a0aed3ac --- /dev/null +++ b/packages/react-native/ReactAndroid/src/test/java/com/facebook/react/fabric/FabricUIManagerPullModelTest.kt @@ -0,0 +1,108 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +@file:Suppress("DEPRECATION") + +package com.facebook.react.fabric + +import com.facebook.react.bridge.ReactApplicationContext +import com.facebook.react.bridge.ReactTestHelper +import com.facebook.react.internal.featureflags.ReactNativeFeatureFlagsForTests +import com.facebook.react.uimanager.ViewManagerRegistry +import com.facebook.testutils.fakes.FakeBatchEventDispatchedListener +import com.facebook.testutils.shadows.ShadowFabricUIManagerBinding +import com.facebook.testutils.shadows.ShadowNativeLoader +import com.facebook.testutils.shadows.ShadowPerformanceTracer +import com.facebook.testutils.shadows.ShadowSoLoader +import org.assertj.core.api.Assertions.assertThat +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.robolectric.RobolectricTestRunner +import org.robolectric.annotation.Config +import org.robolectric.shadow.api.Shadow + +/** + * Tests for the pull-model notification path: [FabricUIManager.onTransactionAvailable] enqueues a + * PullTransactionMountItem that asks the (shadowed, JNI-backed) [FabricUIManagerBinding] to pull + * and apply the surface's pending transaction. + * + * The Robolectric test thread is the UI thread, so a notification posted from it dispatches the + * queued mount items synchronously; notifications posted from a background thread stay queued until + * the next UI-thread dispatch (in production, the dispatcher's frame callback). + */ +@RunWith(RobolectricTestRunner::class) +@Config( + shadows = + [ + ShadowSoLoader::class, + ShadowNativeLoader::class, + ShadowPerformanceTracer::class, + ShadowFabricUIManagerBinding::class, + ] +) +class FabricUIManagerPullModelTest { + + private lateinit var reactContext: ReactApplicationContext + private lateinit var underTest: FabricUIManager + private lateinit var binding: FabricUIManagerBinding + private lateinit var shadowBinding: ShadowFabricUIManagerBinding + + @Before + fun setup() { + ReactNativeFeatureFlagsForTests.setUp() + reactContext = ReactTestHelper.createCatalystContextForTest() + underTest = + FabricUIManager( + reactContext, + ViewManagerRegistry(emptyList()), + FakeBatchEventDispatchedListener(), + ) + binding = FabricUIManagerBinding() + shadowBinding = Shadow.extract(binding) + underTest.setBinding(binding) + } + + private fun runOnBackgroundThread(block: () -> Unit) { + var error: Throwable? = null + val thread = Thread { + try { + block() + } catch (t: Throwable) { + error = t + } + } + thread.start() + thread.join() + error?.let { throw it } + } + + @Test + fun onTransactionAvailable_onUiThread_pullsSynchronously() { + underTest.onTransactionAvailable(1) + + assertThat(shadowBinding.pulledSurfaceIds).containsExactly(1) + } + + @Test + fun onTransactionAvailable_enqueuesOnePullPerNotification() { + // Mirrors iOS: one transaction block per commit, in notification order. Redundant pulls + // (revisions already consumed by an earlier accumulating pull) no-op in C++, not here. + runOnBackgroundThread { + underTest.onTransactionAvailable(1) + underTest.onTransactionAvailable(2) + underTest.onTransactionAvailable(1) + } + // Notifications from a background (commit) thread are queued, not dispatched. + assertThat(shadowBinding.pulledSurfaceIds).isEmpty() + + // The next UI-thread dispatch drains them in order. + underTest.onTransactionAvailable(3) + + assertThat(shadowBinding.pulledSurfaceIds).containsExactly(1, 2, 1, 3) + } +} diff --git a/packages/react-native/ReactAndroid/src/test/java/com/facebook/testutils/shadows/ShadowFabricUIManagerBinding.kt b/packages/react-native/ReactAndroid/src/test/java/com/facebook/testutils/shadows/ShadowFabricUIManagerBinding.kt new file mode 100644 index 00000000000..a9fbbf95e9f --- /dev/null +++ b/packages/react-native/ReactAndroid/src/test/java/com/facebook/testutils/shadows/ShadowFabricUIManagerBinding.kt @@ -0,0 +1,26 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +package com.facebook.testutils.shadows + +import org.robolectric.annotation.Implementation +import org.robolectric.annotation.Implements + +/** + * [FabricUIManagerBinding] is JNI-backed and Mockito can't mock native methods, so shadow the + * methods tests need instead. The binding class is `internal`, so it is referenced by name via + * [Implements.className] rather than a compile-time class literal. + */ +@Implements(className = "com.facebook.react.fabric.FabricUIManagerBinding") +class ShadowFabricUIManagerBinding { + val pulledSurfaceIds: MutableList = mutableListOf() + + @Implementation + fun pullAndExecuteTransaction(surfaceId: Int) { + pulledSurfaceIds.add(surfaceId) + } +} diff --git a/packages/react-native/ReactAndroid/src/test/java/com/facebook/testutils/shadows/ShadowPerformanceTracer.kt b/packages/react-native/ReactAndroid/src/test/java/com/facebook/testutils/shadows/ShadowPerformanceTracer.kt new file mode 100644 index 00000000000..5f180c75a4f --- /dev/null +++ b/packages/react-native/ReactAndroid/src/test/java/com/facebook/testutils/shadows/ShadowPerformanceTracer.kt @@ -0,0 +1,24 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +package com.facebook.testutils.shadows + +import com.facebook.react.internal.tracing.PerformanceTracer +import kotlin.jvm.JvmStatic +import org.robolectric.annotation.Implementation +import org.robolectric.annotation.Implements + +/** + * [PerformanceTracer] is JNI-backed (react_tracingjni); shadow it so code paths that check + * [PerformanceTracer.isTracing] can run on the JVM. Tracing reports as disabled. + */ +@Implements(PerformanceTracer::class) +class ShadowPerformanceTracer { + companion object { + @JvmStatic @Implementation fun isTracing(): Boolean = false + } +} diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.cpp b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.cpp index be68d0a22c6..2fa66b894d2 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<<77401595559dc52e1b2b206527deb341>> + * @generated SignedSource<<8fd664cb106945e5c6e436a0cf36120d>> */ /** @@ -142,6 +142,10 @@ bool ReactNativeFeatureFlags::enableImagePrefetchingAndroid() { return getAccessor().enableImagePrefetchingAndroid(); } +bool ReactNativeFeatureFlags::enableImageTransparentTintColor() { + return getAccessor().enableImageTransparentTintColor(); +} + bool ReactNativeFeatureFlags::enableImmediateUpdateModeForContentOffsetChanges() { return getAccessor().enableImmediateUpdateModeForContentOffsetChanges(); } @@ -174,6 +178,10 @@ bool ReactNativeFeatureFlags::enableModuleArgumentNSNullConversionIOS() { return getAccessor().enableModuleArgumentNSNullConversionIOS(); } +bool ReactNativeFeatureFlags::enableMountingCoordinatorPullModelAndroid() { + return getAccessor().enableMountingCoordinatorPullModelAndroid(); +} + bool ReactNativeFeatureFlags::enableMutationObserverByDefault() { return getAccessor().enableMutationObserverByDefault(); } diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.h b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.h index d8beb4efeb9..1d67d6fb0f0 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<> + * @generated SignedSource<> */ /** @@ -184,6 +184,11 @@ class ReactNativeFeatureFlags { */ RN_EXPORT static bool enableImagePrefetchingAndroid(); + /** + * When enabled, Image `tintColor` is handled as a true optional: any defined color is applied as a tint — including `transparent` (alpha 0), which renders the image invisible — and an unset value clears a previously applied tint. When disabled, the prior behavior is preserved, where a transparent `tintColor` is treated as unassigned and the image renders untinted. + */ + RN_EXPORT static bool enableImageTransparentTintColor(); + /** * Dispatches state updates for content offset changes synchronously on the main thread. */ @@ -224,6 +229,11 @@ class ReactNativeFeatureFlags { */ RN_EXPORT static bool enableModuleArgumentNSNullConversionIOS(); + /** + * When enabled, Android mounts transactions with the pull model (like iOS): the commit thread no longer pulls and builds the mount batch in schedulerShouldRenderTransactions. Instead the UI thread pulls the transaction itself via a PullTransactionMountItem enqueued in the MountItemDispatcher, builds the IntBufferBatchMountItem, and applies it synchronously. Implies rawProps accumulation (enableAccumulatedUpdatesInRawPropsAndroid behavior). + */ + RN_EXPORT static bool enableMountingCoordinatorPullModelAndroid(); + /** * Enables the MutationObserver Web API in React Native. */ diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.cpp b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.cpp index 0e0d92b2b19..213255b954b 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<<4b3689511d39c0ba8e1d78f34df7d348>> + * @generated SignedSource<<0500b2c3cb7e5f22e7344c7f5a2e6ca4>> */ /** @@ -551,6 +551,24 @@ bool ReactNativeFeatureFlagsAccessor::enableImagePrefetchingAndroid() { return flagValue.value(); } +bool ReactNativeFeatureFlagsAccessor::enableImageTransparentTintColor() { + auto flagValue = enableImageTransparentTintColor_.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, "enableImageTransparentTintColor"); + + flagValue = currentProvider_->enableImageTransparentTintColor(); + enableImageTransparentTintColor_ = flagValue; + } + + return flagValue.value(); +} + bool ReactNativeFeatureFlagsAccessor::enableImmediateUpdateModeForContentOffsetChanges() { auto flagValue = enableImmediateUpdateModeForContentOffsetChanges_.load(); @@ -560,7 +578,7 @@ bool ReactNativeFeatureFlagsAccessor::enableImmediateUpdateModeForContentOffsetC // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(29, "enableImmediateUpdateModeForContentOffsetChanges"); + markFlagAsAccessed(30, "enableImmediateUpdateModeForContentOffsetChanges"); flagValue = currentProvider_->enableImmediateUpdateModeForContentOffsetChanges(); enableImmediateUpdateModeForContentOffsetChanges_ = flagValue; @@ -578,7 +596,7 @@ bool ReactNativeFeatureFlagsAccessor::enableImperativeFocus() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(30, "enableImperativeFocus"); + markFlagAsAccessed(31, "enableImperativeFocus"); flagValue = currentProvider_->enableImperativeFocus(); enableImperativeFocus_ = flagValue; @@ -596,7 +614,7 @@ bool ReactNativeFeatureFlagsAccessor::enableInteropViewManagerClassLookUpOptimiz // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(31, "enableInteropViewManagerClassLookUpOptimizationIOS"); + markFlagAsAccessed(32, "enableInteropViewManagerClassLookUpOptimizationIOS"); flagValue = currentProvider_->enableInteropViewManagerClassLookUpOptimizationIOS(); enableInteropViewManagerClassLookUpOptimizationIOS_ = flagValue; @@ -614,7 +632,7 @@ bool ReactNativeFeatureFlagsAccessor::enableIntersectionObserverByDefault() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(32, "enableIntersectionObserverByDefault"); + markFlagAsAccessed(33, "enableIntersectionObserverByDefault"); flagValue = currentProvider_->enableIntersectionObserverByDefault(); enableIntersectionObserverByDefault_ = flagValue; @@ -632,7 +650,7 @@ bool ReactNativeFeatureFlagsAccessor::enableKeyEvents() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(33, "enableKeyEvents"); + markFlagAsAccessed(34, "enableKeyEvents"); flagValue = currentProvider_->enableKeyEvents(); enableKeyEvents_ = flagValue; @@ -650,7 +668,7 @@ bool ReactNativeFeatureFlagsAccessor::enableLayoutAnimationsOnAndroid() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(34, "enableLayoutAnimationsOnAndroid"); + markFlagAsAccessed(35, "enableLayoutAnimationsOnAndroid"); flagValue = currentProvider_->enableLayoutAnimationsOnAndroid(); enableLayoutAnimationsOnAndroid_ = flagValue; @@ -668,7 +686,7 @@ bool ReactNativeFeatureFlagsAccessor::enableLayoutAnimationsOnIOS() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(35, "enableLayoutAnimationsOnIOS"); + markFlagAsAccessed(36, "enableLayoutAnimationsOnIOS"); flagValue = currentProvider_->enableLayoutAnimationsOnIOS(); enableLayoutAnimationsOnIOS_ = flagValue; @@ -686,7 +704,7 @@ bool ReactNativeFeatureFlagsAccessor::enableModuleArgumentNSNullConversionIOS() // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(36, "enableModuleArgumentNSNullConversionIOS"); + markFlagAsAccessed(37, "enableModuleArgumentNSNullConversionIOS"); flagValue = currentProvider_->enableModuleArgumentNSNullConversionIOS(); enableModuleArgumentNSNullConversionIOS_ = flagValue; @@ -695,6 +713,24 @@ bool ReactNativeFeatureFlagsAccessor::enableModuleArgumentNSNullConversionIOS() return flagValue.value(); } +bool ReactNativeFeatureFlagsAccessor::enableMountingCoordinatorPullModelAndroid() { + auto flagValue = enableMountingCoordinatorPullModelAndroid_.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(38, "enableMountingCoordinatorPullModelAndroid"); + + flagValue = currentProvider_->enableMountingCoordinatorPullModelAndroid(); + enableMountingCoordinatorPullModelAndroid_ = flagValue; + } + + return flagValue.value(); +} + bool ReactNativeFeatureFlagsAccessor::enableMutationObserverByDefault() { auto flagValue = enableMutationObserverByDefault_.load(); @@ -704,7 +740,7 @@ bool ReactNativeFeatureFlagsAccessor::enableMutationObserverByDefault() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(37, "enableMutationObserverByDefault"); + markFlagAsAccessed(39, "enableMutationObserverByDefault"); flagValue = currentProvider_->enableMutationObserverByDefault(); enableMutationObserverByDefault_ = flagValue; @@ -722,7 +758,7 @@ bool ReactNativeFeatureFlagsAccessor::enableNativeCSSParsing() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(38, "enableNativeCSSParsing"); + markFlagAsAccessed(40, "enableNativeCSSParsing"); flagValue = currentProvider_->enableNativeCSSParsing(); enableNativeCSSParsing_ = flagValue; @@ -740,7 +776,7 @@ bool ReactNativeFeatureFlagsAccessor::enablePreparedTextLayout() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(39, "enablePreparedTextLayout"); + markFlagAsAccessed(41, "enablePreparedTextLayout"); flagValue = currentProvider_->enablePreparedTextLayout(); enablePreparedTextLayout_ = flagValue; @@ -758,7 +794,7 @@ bool ReactNativeFeatureFlagsAccessor::enablePropsUpdateReconciliationAndroid() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(40, "enablePropsUpdateReconciliationAndroid"); + markFlagAsAccessed(42, "enablePropsUpdateReconciliationAndroid"); flagValue = currentProvider_->enablePropsUpdateReconciliationAndroid(); enablePropsUpdateReconciliationAndroid_ = flagValue; @@ -776,7 +812,7 @@ bool ReactNativeFeatureFlagsAccessor::enableRuntimeSchedulerQueueClearingOnError // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(41, "enableRuntimeSchedulerQueueClearingOnError"); + markFlagAsAccessed(43, "enableRuntimeSchedulerQueueClearingOnError"); flagValue = currentProvider_->enableRuntimeSchedulerQueueClearingOnError(); enableRuntimeSchedulerQueueClearingOnError_ = flagValue; @@ -794,7 +830,7 @@ bool ReactNativeFeatureFlagsAccessor::enableSchedulerDelegateInvalidation() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(42, "enableSchedulerDelegateInvalidation"); + markFlagAsAccessed(44, "enableSchedulerDelegateInvalidation"); flagValue = currentProvider_->enableSchedulerDelegateInvalidation(); enableSchedulerDelegateInvalidation_ = flagValue; @@ -812,7 +848,7 @@ bool ReactNativeFeatureFlagsAccessor::enableSwiftUIBasedFilters() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(43, "enableSwiftUIBasedFilters"); + markFlagAsAccessed(45, "enableSwiftUIBasedFilters"); flagValue = currentProvider_->enableSwiftUIBasedFilters(); enableSwiftUIBasedFilters_ = flagValue; @@ -830,7 +866,7 @@ bool ReactNativeFeatureFlagsAccessor::enableViewCulling() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(44, "enableViewCulling"); + markFlagAsAccessed(46, "enableViewCulling"); flagValue = currentProvider_->enableViewCulling(); enableViewCulling_ = flagValue; @@ -848,7 +884,7 @@ bool ReactNativeFeatureFlagsAccessor::enableViewRecycling() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(45, "enableViewRecycling"); + markFlagAsAccessed(47, "enableViewRecycling"); flagValue = currentProvider_->enableViewRecycling(); enableViewRecycling_ = flagValue; @@ -866,7 +902,7 @@ bool ReactNativeFeatureFlagsAccessor::enableViewRecyclingForImage() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(46, "enableViewRecyclingForImage"); + markFlagAsAccessed(48, "enableViewRecyclingForImage"); flagValue = currentProvider_->enableViewRecyclingForImage(); enableViewRecyclingForImage_ = flagValue; @@ -884,7 +920,7 @@ bool ReactNativeFeatureFlagsAccessor::enableViewRecyclingForScrollView() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(47, "enableViewRecyclingForScrollView"); + markFlagAsAccessed(49, "enableViewRecyclingForScrollView"); flagValue = currentProvider_->enableViewRecyclingForScrollView(); enableViewRecyclingForScrollView_ = flagValue; @@ -902,7 +938,7 @@ bool ReactNativeFeatureFlagsAccessor::enableViewRecyclingForText() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(48, "enableViewRecyclingForText"); + markFlagAsAccessed(50, "enableViewRecyclingForText"); flagValue = currentProvider_->enableViewRecyclingForText(); enableViewRecyclingForText_ = flagValue; @@ -920,7 +956,7 @@ bool ReactNativeFeatureFlagsAccessor::enableViewRecyclingForView() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(49, "enableViewRecyclingForView"); + markFlagAsAccessed(51, "enableViewRecyclingForView"); flagValue = currentProvider_->enableViewRecyclingForView(); enableViewRecyclingForView_ = flagValue; @@ -938,7 +974,7 @@ bool ReactNativeFeatureFlagsAccessor::enableVirtualViewContainerStateExperimenta // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(50, "enableVirtualViewContainerStateExperimental"); + markFlagAsAccessed(52, "enableVirtualViewContainerStateExperimental"); flagValue = currentProvider_->enableVirtualViewContainerStateExperimental(); enableVirtualViewContainerStateExperimental_ = flagValue; @@ -956,7 +992,7 @@ bool ReactNativeFeatureFlagsAccessor::fixDifferentiatorParentTagForUnflattenCase // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(51, "fixDifferentiatorParentTagForUnflattenCase"); + markFlagAsAccessed(53, "fixDifferentiatorParentTagForUnflattenCase"); flagValue = currentProvider_->fixDifferentiatorParentTagForUnflattenCase(); fixDifferentiatorParentTagForUnflattenCase_ = flagValue; @@ -974,7 +1010,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(52, "fixMappingOfEventPrioritiesBetweenFabricAndReact"); + markFlagAsAccessed(54, "fixMappingOfEventPrioritiesBetweenFabricAndReact"); flagValue = currentProvider_->fixMappingOfEventPrioritiesBetweenFabricAndReact(); fixMappingOfEventPrioritiesBetweenFabricAndReact_ = flagValue; @@ -992,7 +1028,7 @@ bool ReactNativeFeatureFlagsAccessor::fixYogaFlexBasisFitContentInMainAxis() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(53, "fixYogaFlexBasisFitContentInMainAxis"); + markFlagAsAccessed(55, "fixYogaFlexBasisFitContentInMainAxis"); flagValue = currentProvider_->fixYogaFlexBasisFitContentInMainAxis(); fixYogaFlexBasisFitContentInMainAxis_ = flagValue; @@ -1010,7 +1046,7 @@ bool ReactNativeFeatureFlagsAccessor::fuseboxAssertSingleHostState() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(54, "fuseboxAssertSingleHostState"); + markFlagAsAccessed(56, "fuseboxAssertSingleHostState"); flagValue = currentProvider_->fuseboxAssertSingleHostState(); fuseboxAssertSingleHostState_ = flagValue; @@ -1028,7 +1064,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(55, "fuseboxEnabledRelease"); + markFlagAsAccessed(57, "fuseboxEnabledRelease"); flagValue = currentProvider_->fuseboxEnabledRelease(); fuseboxEnabledRelease_ = flagValue; @@ -1046,7 +1082,7 @@ bool ReactNativeFeatureFlagsAccessor::fuseboxFrameRecordingEnabled() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(56, "fuseboxFrameRecordingEnabled"); + markFlagAsAccessed(58, "fuseboxFrameRecordingEnabled"); flagValue = currentProvider_->fuseboxFrameRecordingEnabled(); fuseboxFrameRecordingEnabled_ = flagValue; @@ -1064,7 +1100,7 @@ bool ReactNativeFeatureFlagsAccessor::fuseboxScreenshotCaptureEnabled() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(57, "fuseboxScreenshotCaptureEnabled"); + markFlagAsAccessed(59, "fuseboxScreenshotCaptureEnabled"); flagValue = currentProvider_->fuseboxScreenshotCaptureEnabled(); fuseboxScreenshotCaptureEnabled_ = flagValue; @@ -1082,7 +1118,7 @@ bool ReactNativeFeatureFlagsAccessor::fuseboxWebSocketEventsEnabled() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(58, "fuseboxWebSocketEventsEnabled"); + markFlagAsAccessed(60, "fuseboxWebSocketEventsEnabled"); flagValue = currentProvider_->fuseboxWebSocketEventsEnabled(); fuseboxWebSocketEventsEnabled_ = flagValue; @@ -1100,7 +1136,7 @@ bool ReactNativeFeatureFlagsAccessor::optimizedAnimatedPropUpdates() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(59, "optimizedAnimatedPropUpdates"); + markFlagAsAccessed(61, "optimizedAnimatedPropUpdates"); flagValue = currentProvider_->optimizedAnimatedPropUpdates(); optimizedAnimatedPropUpdates_ = flagValue; @@ -1118,7 +1154,7 @@ bool ReactNativeFeatureFlagsAccessor::overrideBySynchronousMountPropsAtMountingA // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(60, "overrideBySynchronousMountPropsAtMountingAndroid"); + markFlagAsAccessed(62, "overrideBySynchronousMountPropsAtMountingAndroid"); flagValue = currentProvider_->overrideBySynchronousMountPropsAtMountingAndroid(); overrideBySynchronousMountPropsAtMountingAndroid_ = flagValue; @@ -1136,7 +1172,7 @@ bool ReactNativeFeatureFlagsAccessor::perfIssuesEnabled() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(61, "perfIssuesEnabled"); + markFlagAsAccessed(63, "perfIssuesEnabled"); flagValue = currentProvider_->perfIssuesEnabled(); perfIssuesEnabled_ = flagValue; @@ -1154,7 +1190,7 @@ bool ReactNativeFeatureFlagsAccessor::perfMonitorV2Enabled() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(62, "perfMonitorV2Enabled"); + markFlagAsAccessed(64, "perfMonitorV2Enabled"); flagValue = currentProvider_->perfMonitorV2Enabled(); perfMonitorV2Enabled_ = flagValue; @@ -1172,7 +1208,7 @@ double ReactNativeFeatureFlagsAccessor::preparedTextCacheSize() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(63, "preparedTextCacheSize"); + markFlagAsAccessed(65, "preparedTextCacheSize"); flagValue = currentProvider_->preparedTextCacheSize(); preparedTextCacheSize_ = flagValue; @@ -1190,7 +1226,7 @@ bool ReactNativeFeatureFlagsAccessor::preventShadowTreeCommitExhaustion() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(64, "preventShadowTreeCommitExhaustion"); + markFlagAsAccessed(66, "preventShadowTreeCommitExhaustion"); flagValue = currentProvider_->preventShadowTreeCommitExhaustion(); preventShadowTreeCommitExhaustion_ = flagValue; @@ -1208,7 +1244,7 @@ bool ReactNativeFeatureFlagsAccessor::redBoxV2Android() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(65, "redBoxV2Android"); + markFlagAsAccessed(67, "redBoxV2Android"); flagValue = currentProvider_->redBoxV2Android(); redBoxV2Android_ = flagValue; @@ -1226,7 +1262,7 @@ bool ReactNativeFeatureFlagsAccessor::redBoxV2IOS() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(66, "redBoxV2IOS"); + markFlagAsAccessed(68, "redBoxV2IOS"); flagValue = currentProvider_->redBoxV2IOS(); redBoxV2IOS_ = flagValue; @@ -1244,7 +1280,7 @@ bool ReactNativeFeatureFlagsAccessor::shouldPressibilityUseW3CPointerEventsForHo // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(67, "shouldPressibilityUseW3CPointerEventsForHover"); + markFlagAsAccessed(69, "shouldPressibilityUseW3CPointerEventsForHover"); flagValue = currentProvider_->shouldPressibilityUseW3CPointerEventsForHover(); shouldPressibilityUseW3CPointerEventsForHover_ = flagValue; @@ -1262,7 +1298,7 @@ bool ReactNativeFeatureFlagsAccessor::shouldTriggerResponderTransferOnScrollAndr // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(68, "shouldTriggerResponderTransferOnScrollAndroid"); + markFlagAsAccessed(70, "shouldTriggerResponderTransferOnScrollAndroid"); flagValue = currentProvider_->shouldTriggerResponderTransferOnScrollAndroid(); shouldTriggerResponderTransferOnScrollAndroid_ = flagValue; @@ -1280,7 +1316,7 @@ bool ReactNativeFeatureFlagsAccessor::skipActivityIdentityAssertionOnHostPause() // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(69, "skipActivityIdentityAssertionOnHostPause"); + markFlagAsAccessed(71, "skipActivityIdentityAssertionOnHostPause"); flagValue = currentProvider_->skipActivityIdentityAssertionOnHostPause(); skipActivityIdentityAssertionOnHostPause_ = flagValue; @@ -1298,7 +1334,7 @@ bool ReactNativeFeatureFlagsAccessor::syncAndroidClipBoundsWithOverflow() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(70, "syncAndroidClipBoundsWithOverflow"); + markFlagAsAccessed(72, "syncAndroidClipBoundsWithOverflow"); flagValue = currentProvider_->syncAndroidClipBoundsWithOverflow(); syncAndroidClipBoundsWithOverflow_ = flagValue; @@ -1316,7 +1352,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(71, "traceTurboModulePromiseRejectionsOnAndroid"); + markFlagAsAccessed(73, "traceTurboModulePromiseRejectionsOnAndroid"); flagValue = currentProvider_->traceTurboModulePromiseRejectionsOnAndroid(); traceTurboModulePromiseRejectionsOnAndroid_ = flagValue; @@ -1334,7 +1370,7 @@ bool ReactNativeFeatureFlagsAccessor::updateRuntimeShadowNodeReferencesOnCommit( // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(72, "updateRuntimeShadowNodeReferencesOnCommit"); + markFlagAsAccessed(74, "updateRuntimeShadowNodeReferencesOnCommit"); flagValue = currentProvider_->updateRuntimeShadowNodeReferencesOnCommit(); updateRuntimeShadowNodeReferencesOnCommit_ = flagValue; @@ -1352,7 +1388,7 @@ bool ReactNativeFeatureFlagsAccessor::updateRuntimeShadowNodeReferencesOnCommitT // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(73, "updateRuntimeShadowNodeReferencesOnCommitThread"); + markFlagAsAccessed(75, "updateRuntimeShadowNodeReferencesOnCommitThread"); flagValue = currentProvider_->updateRuntimeShadowNodeReferencesOnCommitThread(); updateRuntimeShadowNodeReferencesOnCommitThread_ = flagValue; @@ -1370,7 +1406,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(74, "useAlwaysAvailableJSErrorHandling"); + markFlagAsAccessed(76, "useAlwaysAvailableJSErrorHandling"); flagValue = currentProvider_->useAlwaysAvailableJSErrorHandling(); useAlwaysAvailableJSErrorHandling_ = flagValue; @@ -1388,7 +1424,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(75, "useFabricInterop"); + markFlagAsAccessed(77, "useFabricInterop"); flagValue = currentProvider_->useFabricInterop(); useFabricInterop_ = flagValue; @@ -1406,7 +1442,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(76, "useNativeViewConfigsInBridgelessMode"); + markFlagAsAccessed(78, "useNativeViewConfigsInBridgelessMode"); flagValue = currentProvider_->useNativeViewConfigsInBridgelessMode(); useNativeViewConfigsInBridgelessMode_ = flagValue; @@ -1424,7 +1460,7 @@ bool ReactNativeFeatureFlagsAccessor::useNestedScrollViewAndroid() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(77, "useNestedScrollViewAndroid"); + markFlagAsAccessed(79, "useNestedScrollViewAndroid"); flagValue = currentProvider_->useNestedScrollViewAndroid(); useNestedScrollViewAndroid_ = flagValue; @@ -1442,7 +1478,7 @@ bool ReactNativeFeatureFlagsAccessor::useSharedAnimatedBackend() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(78, "useSharedAnimatedBackend"); + markFlagAsAccessed(80, "useSharedAnimatedBackend"); flagValue = currentProvider_->useSharedAnimatedBackend(); useSharedAnimatedBackend_ = flagValue; @@ -1460,7 +1496,7 @@ bool ReactNativeFeatureFlagsAccessor::useTraitHiddenOnAndroid() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(79, "useTraitHiddenOnAndroid"); + markFlagAsAccessed(81, "useTraitHiddenOnAndroid"); flagValue = currentProvider_->useTraitHiddenOnAndroid(); useTraitHiddenOnAndroid_ = flagValue; @@ -1478,7 +1514,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(80, "useTurboModuleInterop"); + markFlagAsAccessed(82, "useTurboModuleInterop"); flagValue = currentProvider_->useTurboModuleInterop(); useTurboModuleInterop_ = flagValue; @@ -1496,7 +1532,7 @@ double ReactNativeFeatureFlagsAccessor::viewCullingOutsetRatio() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(81, "viewCullingOutsetRatio"); + markFlagAsAccessed(83, "viewCullingOutsetRatio"); flagValue = currentProvider_->viewCullingOutsetRatio(); viewCullingOutsetRatio_ = flagValue; @@ -1514,7 +1550,7 @@ bool ReactNativeFeatureFlagsAccessor::viewTransitionEnabled() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(82, "viewTransitionEnabled"); + markFlagAsAccessed(84, "viewTransitionEnabled"); flagValue = currentProvider_->viewTransitionEnabled(); viewTransitionEnabled_ = flagValue; @@ -1532,7 +1568,7 @@ bool ReactNativeFeatureFlagsAccessor::viewTransitionUseHardwareBitmapAndroid() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(83, "viewTransitionUseHardwareBitmapAndroid"); + markFlagAsAccessed(85, "viewTransitionUseHardwareBitmapAndroid"); flagValue = currentProvider_->viewTransitionUseHardwareBitmapAndroid(); viewTransitionUseHardwareBitmapAndroid_ = flagValue; @@ -1550,7 +1586,7 @@ double ReactNativeFeatureFlagsAccessor::virtualViewPrerenderRatio() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(84, "virtualViewPrerenderRatio"); + markFlagAsAccessed(86, "virtualViewPrerenderRatio"); flagValue = currentProvider_->virtualViewPrerenderRatio(); virtualViewPrerenderRatio_ = flagValue; diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.h b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.h index 7e81ac32234..76686e57e2a 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<> + * @generated SignedSource<<88dd99a5753390988c878519a0f468c6>> */ /** @@ -61,6 +61,7 @@ class ReactNativeFeatureFlagsAccessor { bool enableIOSTextBaselineOffsetPerLine(); bool enableIOSViewClipToPaddingBox(); bool enableImagePrefetchingAndroid(); + bool enableImageTransparentTintColor(); bool enableImmediateUpdateModeForContentOffsetChanges(); bool enableImperativeFocus(); bool enableInteropViewManagerClassLookUpOptimizationIOS(); @@ -69,6 +70,7 @@ class ReactNativeFeatureFlagsAccessor { bool enableLayoutAnimationsOnAndroid(); bool enableLayoutAnimationsOnIOS(); bool enableModuleArgumentNSNullConversionIOS(); + bool enableMountingCoordinatorPullModelAndroid(); bool enableMutationObserverByDefault(); bool enableNativeCSSParsing(); bool enablePreparedTextLayout(); @@ -128,7 +130,7 @@ class ReactNativeFeatureFlagsAccessor { std::unique_ptr currentProvider_; bool wasOverridden_; - std::array, 85> accessedFeatureFlags_; + std::array, 87> accessedFeatureFlags_; std::atomic> commonTestFlag_; std::atomic> cdpInteractionMetricsEnabled_; @@ -159,6 +161,7 @@ class ReactNativeFeatureFlagsAccessor { std::atomic> enableIOSTextBaselineOffsetPerLine_; std::atomic> enableIOSViewClipToPaddingBox_; std::atomic> enableImagePrefetchingAndroid_; + std::atomic> enableImageTransparentTintColor_; std::atomic> enableImmediateUpdateModeForContentOffsetChanges_; std::atomic> enableImperativeFocus_; std::atomic> enableInteropViewManagerClassLookUpOptimizationIOS_; @@ -167,6 +170,7 @@ class ReactNativeFeatureFlagsAccessor { std::atomic> enableLayoutAnimationsOnAndroid_; std::atomic> enableLayoutAnimationsOnIOS_; std::atomic> enableModuleArgumentNSNullConversionIOS_; + std::atomic> enableMountingCoordinatorPullModelAndroid_; std::atomic> enableMutationObserverByDefault_; std::atomic> enableNativeCSSParsing_; std::atomic> enablePreparedTextLayout_; diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDefaults.h b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDefaults.h index d3fd47faff9..c3d56039521 100644 --- a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDefaults.h +++ b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDefaults.h @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<> + * @generated SignedSource<<9809c179e61abe55f544d6c8227a5c01>> */ /** @@ -143,6 +143,10 @@ class ReactNativeFeatureFlagsDefaults : public ReactNativeFeatureFlagsProvider { return false; } + bool enableImageTransparentTintColor() override { + return false; + } + bool enableImmediateUpdateModeForContentOffsetChanges() override { return false; } @@ -175,6 +179,10 @@ class ReactNativeFeatureFlagsDefaults : public ReactNativeFeatureFlagsProvider { return false; } + bool enableMountingCoordinatorPullModelAndroid() override { + return false; + } + bool enableMutationObserverByDefault() override { return false; } diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDynamicProvider.h b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDynamicProvider.h index 10f61382198..4a3915b7b4c 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<<518b44f36bbff8631afae71847c943f0>> + * @generated SignedSource<> */ /** @@ -306,6 +306,15 @@ class ReactNativeFeatureFlagsDynamicProvider : public ReactNativeFeatureFlagsDef return ReactNativeFeatureFlagsDefaults::enableImagePrefetchingAndroid(); } + bool enableImageTransparentTintColor() override { + auto value = values_["enableImageTransparentTintColor"]; + if (!value.isNull()) { + return value.getBool(); + } + + return ReactNativeFeatureFlagsDefaults::enableImageTransparentTintColor(); + } + bool enableImmediateUpdateModeForContentOffsetChanges() override { auto value = values_["enableImmediateUpdateModeForContentOffsetChanges"]; if (!value.isNull()) { @@ -378,6 +387,15 @@ class ReactNativeFeatureFlagsDynamicProvider : public ReactNativeFeatureFlagsDef return ReactNativeFeatureFlagsDefaults::enableModuleArgumentNSNullConversionIOS(); } + bool enableMountingCoordinatorPullModelAndroid() override { + auto value = values_["enableMountingCoordinatorPullModelAndroid"]; + if (!value.isNull()) { + return value.getBool(); + } + + return ReactNativeFeatureFlagsDefaults::enableMountingCoordinatorPullModelAndroid(); + } + bool enableMutationObserverByDefault() override { auto value = values_["enableMutationObserverByDefault"]; if (!value.isNull()) { diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsProvider.h b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsProvider.h index 6c39114c9f2..88c71ced84f 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<<31a9106d7fbb65a6bf61b87e23ba93e0>> + * @generated SignedSource<<2c49f7d9235fdb157fa1291cf4ee4c52>> */ /** @@ -54,6 +54,7 @@ class ReactNativeFeatureFlagsProvider { virtual bool enableIOSTextBaselineOffsetPerLine() = 0; virtual bool enableIOSViewClipToPaddingBox() = 0; virtual bool enableImagePrefetchingAndroid() = 0; + virtual bool enableImageTransparentTintColor() = 0; virtual bool enableImmediateUpdateModeForContentOffsetChanges() = 0; virtual bool enableImperativeFocus() = 0; virtual bool enableInteropViewManagerClassLookUpOptimizationIOS() = 0; @@ -62,6 +63,7 @@ class ReactNativeFeatureFlagsProvider { virtual bool enableLayoutAnimationsOnAndroid() = 0; virtual bool enableLayoutAnimationsOnIOS() = 0; virtual bool enableModuleArgumentNSNullConversionIOS() = 0; + virtual bool enableMountingCoordinatorPullModelAndroid() = 0; virtual bool enableMutationObserverByDefault() = 0; virtual bool enableNativeCSSParsing() = 0; virtual bool enablePreparedTextLayout() = 0; diff --git a/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.cpp b/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.cpp index 6e3de67d0d3..1e615d440c6 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<> + * @generated SignedSource<<9c49f2c73941bd43c3a2ab68eb561b6e>> */ /** @@ -189,6 +189,11 @@ bool NativeReactNativeFeatureFlags::enableImagePrefetchingAndroid( return ReactNativeFeatureFlags::enableImagePrefetchingAndroid(); } +bool NativeReactNativeFeatureFlags::enableImageTransparentTintColor( + jsi::Runtime& /*runtime*/) { + return ReactNativeFeatureFlags::enableImageTransparentTintColor(); +} + bool NativeReactNativeFeatureFlags::enableImmediateUpdateModeForContentOffsetChanges( jsi::Runtime& /*runtime*/) { return ReactNativeFeatureFlags::enableImmediateUpdateModeForContentOffsetChanges(); @@ -229,6 +234,11 @@ bool NativeReactNativeFeatureFlags::enableModuleArgumentNSNullConversionIOS( return ReactNativeFeatureFlags::enableModuleArgumentNSNullConversionIOS(); } +bool NativeReactNativeFeatureFlags::enableMountingCoordinatorPullModelAndroid( + jsi::Runtime& /*runtime*/) { + return ReactNativeFeatureFlags::enableMountingCoordinatorPullModelAndroid(); +} + bool NativeReactNativeFeatureFlags::enableMutationObserverByDefault( jsi::Runtime& /*runtime*/) { return ReactNativeFeatureFlags::enableMutationObserverByDefault(); diff --git a/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.h b/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.h index b6f92a94e61..e96aad17067 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<<31af47566a5543079cc3b09567033a57>> + * @generated SignedSource<<741340cf9c128417368324bb80501f18>> */ /** @@ -94,6 +94,8 @@ class NativeReactNativeFeatureFlags bool enableImagePrefetchingAndroid(jsi::Runtime& runtime); + bool enableImageTransparentTintColor(jsi::Runtime& runtime); + bool enableImmediateUpdateModeForContentOffsetChanges(jsi::Runtime& runtime); bool enableImperativeFocus(jsi::Runtime& runtime); @@ -110,6 +112,8 @@ class NativeReactNativeFeatureFlags bool enableModuleArgumentNSNullConversionIOS(jsi::Runtime& runtime); + bool enableMountingCoordinatorPullModelAndroid(jsi::Runtime& runtime); + bool enableMutationObserverByDefault(jsi::Runtime& runtime); bool enableNativeCSSParsing(jsi::Runtime& runtime); diff --git a/packages/react-native/ReactCommon/react/renderer/core/Props.cpp b/packages/react-native/ReactCommon/react/renderer/core/Props.cpp index 3e8845e9c31..c0b0abed30a 100644 --- a/packages/react-native/ReactCommon/react/renderer/core/Props.cpp +++ b/packages/react-native/ReactCommon/react/renderer/core/Props.cpp @@ -45,7 +45,10 @@ void Props::initializeDynamicProps( const Props& sourceProps, const RawProps& rawProps, const std::function& filterObjectKeys) { - if (ReactNativeFeatureFlags::enableAccumulatedUpdatesInRawPropsAndroid()) { + // The pull model also requires complete rawProps. A single pull may collapse + // several commits into one diff. + if (ReactNativeFeatureFlags::enableAccumulatedUpdatesInRawPropsAndroid() || + ReactNativeFeatureFlags::enableMountingCoordinatorPullModelAndroid()) { auto& oldRawProps = sourceProps.rawProps; auto newRawProps = rawProps.toDynamic(filterObjectKeys); auto mergedRawProps = mergeDynamicProps( diff --git a/packages/react-native/ReactCommon/react/renderer/mounting/ShadowTree.cpp b/packages/react-native/ReactCommon/react/renderer/mounting/ShadowTree.cpp index bdd301f5c3d..5eb6eeb8131 100644 --- a/packages/react-native/ReactCommon/react/renderer/mounting/ShadowTree.cpp +++ b/packages/react-native/ReactCommon/react/renderer/mounting/ShadowTree.cpp @@ -41,7 +41,9 @@ std::string getShadowTreeCommitSourceName(ShadowTreeCommitSource source) { inline bool isPropsUpdatesAccumulationGuaranteed() { #ifdef __ANDROID__ - return ReactNativeFeatureFlags::enableAccumulatedUpdatesInRawPropsAndroid(); + // The pull model implies rawProps accumulation (see Props.cpp). + return ReactNativeFeatureFlags::enableAccumulatedUpdatesInRawPropsAndroid() || + ReactNativeFeatureFlags::enableMountingCoordinatorPullModelAndroid(); #else return true; #endif diff --git a/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js b/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js index 857abb9f7cd..c23ef6bcde3 100644 --- a/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js +++ b/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js @@ -359,6 +359,17 @@ const definitions: FeatureFlagDefinitions = { }, ossReleaseStage: 'none', }, + enableImageTransparentTintColor: { + defaultValue: false, + metadata: { + dateAdded: '2026-07-17', + description: + 'When enabled, Image `tintColor` is handled as a true optional: any defined color is applied as a tint — including `transparent` (alpha 0), which renders the image invisible — and an unset value clears a previously applied tint. When disabled, the prior behavior is preserved, where a transparent `tintColor` is treated as unassigned and the image renders untinted.', + expectedReleaseValue: true, + purpose: 'experimentation', + }, + ossReleaseStage: 'none', + }, enableImmediateUpdateModeForContentOffsetChanges: { defaultValue: false, metadata: { @@ -443,6 +454,17 @@ const definitions: FeatureFlagDefinitions = { }, ossReleaseStage: 'none', }, + enableMountingCoordinatorPullModelAndroid: { + defaultValue: false, + metadata: { + dateAdded: '2026-07-14', + description: + 'When enabled, Android mounts transactions with the pull model (like iOS): the commit thread no longer pulls and builds the mount batch in schedulerShouldRenderTransactions. Instead the UI thread pulls the transaction itself via a PullTransactionMountItem enqueued in the MountItemDispatcher, builds the IntBufferBatchMountItem, and applies it synchronously. Implies rawProps accumulation (enableAccumulatedUpdatesInRawPropsAndroid behavior).', + expectedReleaseValue: true, + purpose: 'experimentation', + }, + ossReleaseStage: 'none', + }, enableMutationObserverByDefault: { defaultValue: false, metadata: { diff --git a/packages/react-native/src/private/featureflags/ReactNativeFeatureFlags.js b/packages/react-native/src/private/featureflags/ReactNativeFeatureFlags.js index 81b25c3d0bd..4d1abe9db9c 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<<18f24b20806682b774fa5ba96f291fd2>> + * @generated SignedSource<<7450ddfb966c76290471815032903873>> * @flow strict * @noformat */ @@ -77,6 +77,7 @@ export type ReactNativeFeatureFlags = Readonly<{ enableIOSTextBaselineOffsetPerLine: Getter, enableIOSViewClipToPaddingBox: Getter, enableImagePrefetchingAndroid: Getter, + enableImageTransparentTintColor: Getter, enableImmediateUpdateModeForContentOffsetChanges: Getter, enableImperativeFocus: Getter, enableInteropViewManagerClassLookUpOptimizationIOS: Getter, @@ -85,6 +86,7 @@ export type ReactNativeFeatureFlags = Readonly<{ enableLayoutAnimationsOnAndroid: Getter, enableLayoutAnimationsOnIOS: Getter, enableModuleArgumentNSNullConversionIOS: Getter, + enableMountingCoordinatorPullModelAndroid: Getter, enableMutationObserverByDefault: Getter, enableNativeCSSParsing: Getter, enablePreparedTextLayout: Getter, @@ -320,6 +322,10 @@ export const enableIOSViewClipToPaddingBox: Getter = createNativeFlagGe * When enabled, Android will build and initiate image prefetch requests on ImageShadowNode::layout */ export const enableImagePrefetchingAndroid: Getter = createNativeFlagGetter('enableImagePrefetchingAndroid', false); +/** + * When enabled, Image `tintColor` is handled as a true optional: any defined color is applied as a tint — including `transparent` (alpha 0), which renders the image invisible — and an unset value clears a previously applied tint. When disabled, the prior behavior is preserved, where a transparent `tintColor` is treated as unassigned and the image renders untinted. + */ +export const enableImageTransparentTintColor: Getter = createNativeFlagGetter('enableImageTransparentTintColor', false); /** * Dispatches state updates for content offset changes synchronously on the main thread. */ @@ -352,6 +358,10 @@ export const enableLayoutAnimationsOnIOS: Getter = createNativeFlagGett * Enable NSNull conversion when handling module arguments on iOS */ export const enableModuleArgumentNSNullConversionIOS: Getter = createNativeFlagGetter('enableModuleArgumentNSNullConversionIOS', false); +/** + * When enabled, Android mounts transactions with the pull model (like iOS): the commit thread no longer pulls and builds the mount batch in schedulerShouldRenderTransactions. Instead the UI thread pulls the transaction itself via a PullTransactionMountItem enqueued in the MountItemDispatcher, builds the IntBufferBatchMountItem, and applies it synchronously. Implies rawProps accumulation (enableAccumulatedUpdatesInRawPropsAndroid behavior). + */ +export const enableMountingCoordinatorPullModelAndroid: Getter = createNativeFlagGetter('enableMountingCoordinatorPullModelAndroid', false); /** * Enables the MutationObserver Web API in React Native. */ diff --git a/packages/react-native/src/private/featureflags/specs/NativeReactNativeFeatureFlags.js b/packages/react-native/src/private/featureflags/specs/NativeReactNativeFeatureFlags.js index fde1df2ce4f..300b339d27b 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<<848355c91c5827383c35686d975ebb08>> + * @generated SignedSource<<721fb68d3038841ecd6bbaabafccd5cc>> * @flow strict * @noformat */ @@ -54,6 +54,7 @@ export interface Spec extends TurboModule { readonly enableIOSTextBaselineOffsetPerLine?: () => boolean; readonly enableIOSViewClipToPaddingBox?: () => boolean; readonly enableImagePrefetchingAndroid?: () => boolean; + readonly enableImageTransparentTintColor?: () => boolean; readonly enableImmediateUpdateModeForContentOffsetChanges?: () => boolean; readonly enableImperativeFocus?: () => boolean; readonly enableInteropViewManagerClassLookUpOptimizationIOS?: () => boolean; @@ -62,6 +63,7 @@ export interface Spec extends TurboModule { readonly enableLayoutAnimationsOnAndroid?: () => boolean; readonly enableLayoutAnimationsOnIOS?: () => boolean; readonly enableModuleArgumentNSNullConversionIOS?: () => boolean; + readonly enableMountingCoordinatorPullModelAndroid?: () => boolean; readonly enableMutationObserverByDefault?: () => boolean; readonly enableNativeCSSParsing?: () => boolean; readonly enablePreparedTextLayout?: () => boolean; diff --git a/scripts/cxx-api/api-snapshots/ReactAndroidDebugCxx.api b/scripts/cxx-api/api-snapshots/ReactAndroidDebugCxx.api index 0b9990f9ce0..a0f93b35deb 100644 --- a/scripts/cxx-api/api-snapshots/ReactAndroidDebugCxx.api +++ b/scripts/cxx-api/api-snapshots/ReactAndroidDebugCxx.api @@ -2370,12 +2370,13 @@ class facebook::react::FabricMountingManager { public void destroyUnmountedShadowNode(const facebook::react::ShadowNodeFamily& family); public void dispatchCommand(const facebook::react::ShadowView& shadowView, const std::string& commandName, const folly::dynamic& args); public void drainPreallocateViewsQueue(); - public void executeMount(const facebook::react::MountingTransaction& transaction); + public void executeMount(const facebook::react::MountingTransaction& transaction, bool synchronous = false); public void maybePreallocateShadowNode(const facebook::react::ShadowNode& shadowNode); public void onAllAnimationsComplete(); public void onAnimationStarted(); public void onSurfaceStart(facebook::react::SurfaceId surfaceId); public void onSurfaceStop(facebook::react::SurfaceId surfaceId); + public void onTransactionAvailable(facebook::react::SurfaceId surfaceId); public void preallocateShadowView(const facebook::react::ShadowView& shadowView); public void scheduleReactRevisionMerge(facebook::react::SurfaceId surfaceId); public void sendAccessibilityEvent(const facebook::react::ShadowView& shadowView, const std::string& eventType); diff --git a/scripts/cxx-api/api-snapshots/ReactAndroidNewarchCxx.api b/scripts/cxx-api/api-snapshots/ReactAndroidNewarchCxx.api index 173716f8789..ef87af6f64f 100644 --- a/scripts/cxx-api/api-snapshots/ReactAndroidNewarchCxx.api +++ b/scripts/cxx-api/api-snapshots/ReactAndroidNewarchCxx.api @@ -2353,12 +2353,13 @@ class facebook::react::FabricMountingManager { public void destroyUnmountedShadowNode(const facebook::react::ShadowNodeFamily& family); public void dispatchCommand(const facebook::react::ShadowView& shadowView, const std::string& commandName, const folly::dynamic& args); public void drainPreallocateViewsQueue(); - public void executeMount(const facebook::react::MountingTransaction& transaction); + public void executeMount(const facebook::react::MountingTransaction& transaction, bool synchronous = false); public void maybePreallocateShadowNode(const facebook::react::ShadowNode& shadowNode); public void onAllAnimationsComplete(); public void onAnimationStarted(); public void onSurfaceStart(facebook::react::SurfaceId surfaceId); public void onSurfaceStop(facebook::react::SurfaceId surfaceId); + public void onTransactionAvailable(facebook::react::SurfaceId surfaceId); public void preallocateShadowView(const facebook::react::ShadowView& shadowView); public void scheduleReactRevisionMerge(facebook::react::SurfaceId surfaceId); public void sendAccessibilityEvent(const facebook::react::ShadowView& shadowView, const std::string& eventType); diff --git a/scripts/cxx-api/api-snapshots/ReactAndroidReleaseCxx.api b/scripts/cxx-api/api-snapshots/ReactAndroidReleaseCxx.api index 3a77c8af8ef..11223eda98d 100644 --- a/scripts/cxx-api/api-snapshots/ReactAndroidReleaseCxx.api +++ b/scripts/cxx-api/api-snapshots/ReactAndroidReleaseCxx.api @@ -2368,12 +2368,13 @@ class facebook::react::FabricMountingManager { public void destroyUnmountedShadowNode(const facebook::react::ShadowNodeFamily& family); public void dispatchCommand(const facebook::react::ShadowView& shadowView, const std::string& commandName, const folly::dynamic& args); public void drainPreallocateViewsQueue(); - public void executeMount(const facebook::react::MountingTransaction& transaction); + public void executeMount(const facebook::react::MountingTransaction& transaction, bool synchronous = false); public void maybePreallocateShadowNode(const facebook::react::ShadowNode& shadowNode); public void onAllAnimationsComplete(); public void onAnimationStarted(); public void onSurfaceStart(facebook::react::SurfaceId surfaceId); public void onSurfaceStop(facebook::react::SurfaceId surfaceId); + public void onTransactionAvailable(facebook::react::SurfaceId surfaceId); public void preallocateShadowView(const facebook::react::ShadowView& shadowView); public void scheduleReactRevisionMerge(facebook::react::SurfaceId surfaceId); public void sendAccessibilityEvent(const facebook::react::ShadowView& shadowView, const std::string& eventType);