From 00e18da15f5531d8722c0fb4aa99930824210e3e Mon Sep 17 00:00:00 2001 From: Christoph Purrer Date: Fri, 19 Sep 2025 20:48:46 -0700 Subject: [PATCH 1/2] Fix potential race conditions when executing commitHooks (#53862) Summary: Recently we saw `use-after-free race condition` where the ImageFetcher object was being destroyed while still registered as a UIManagerCommitHook. The crash occurred in `std::vector::size()` at line 635 when accessing corrupted memory. The root cause could be improper lifecycle management between ImageFetcher destruction and commit hook execution. The fix here modifies `UIManager::shadowTreeWillCommit()` to create a stable snapshot by copying the commitHooks_ vector while holding the lock. ## Reason - If a thread is iterating over `commitHooks_` with a `shared_lock`, and another thread acquires a `unique_lock` to modify the vector (add/remove), the iterator in the first thread can become invalid, leading to a crash (use-after-free or out-of-bounds). - This can happen if the lock is not held for the entire duration of the read or write, or if the lock is not correctly used everywhere commitHooks_ is accessed. Changelog: [Internal] Differential Revision: D82846245 --- .../ReactCommon/react/renderer/uimanager/UIManager.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/react-native/ReactCommon/react/renderer/uimanager/UIManager.cpp b/packages/react-native/ReactCommon/react/renderer/uimanager/UIManager.cpp index 4855071d9abda9..87101283d9cb6b 100644 --- a/packages/react-native/ReactCommon/react/renderer/uimanager/UIManager.cpp +++ b/packages/react-native/ReactCommon/react/renderer/uimanager/UIManager.cpp @@ -615,10 +615,14 @@ RootShadowNode::Unshared UIManager::shadowTreeWillCommit( const ShadowTree::CommitOptions& commitOptions) const { TraceSection s("UIManager::shadowTreeWillCommit"); - std::shared_lock lock(commitHookMutex_); + std::vector commitHooks; + { + std::shared_lock lock(commitHookMutex_); + commitHooks = commitHooks_; + } auto resultRootShadowNode = newRootShadowNode; - for (auto* commitHook : commitHooks_) { + for (auto* commitHook : commitHooks) { resultRootShadowNode = commitHook->shadowTreeWillCommit( shadowTree, oldRootShadowNode, resultRootShadowNode, commitOptions); } From 5210f00ea5462e1e07f1f28a2e8e7ed15491a774 Mon Sep 17 00:00:00 2001 From: Christoph Purrer Date: Fri, 19 Sep 2025 20:48:46 -0700 Subject: [PATCH 2/2] Add ImagePrefetching feature flag for JNI calls (#53865) Summary: This adds an additional feature flag to enable/disable image prefetch requests via JNI batching Changelog: [Internal] Differential Revision: D82871006 --- .../featureflags/ReactNativeFeatureFlags.kt | 8 +- .../ReactNativeFeatureFlagsCxxAccessor.kt | 12 +- .../ReactNativeFeatureFlagsCxxInterop.kt | 4 +- .../ReactNativeFeatureFlagsDefaults.kt | 4 +- .../ReactNativeFeatureFlagsLocalAccessor.kt | 13 +- .../ReactNativeFeatureFlagsProvider.kt | 4 +- .../JReactNativeFeatureFlagsCxxInterop.cpp | 16 ++- .../JReactNativeFeatureFlagsCxxInterop.h | 5 +- .../featureflags/ReactNativeFeatureFlags.cpp | 6 +- .../featureflags/ReactNativeFeatureFlags.h | 7 +- .../ReactNativeFeatureFlagsAccessor.cpp | 118 ++++++++++-------- .../ReactNativeFeatureFlagsAccessor.h | 6 +- .../ReactNativeFeatureFlagsDefaults.h | 6 +- .../ReactNativeFeatureFlagsDynamicProvider.h | 11 +- .../ReactNativeFeatureFlagsProvider.h | 3 +- .../NativeReactNativeFeatureFlags.cpp | 7 +- .../NativeReactNativeFeatureFlags.h | 4 +- .../renderer/imagemanager/ImageFetcher.cpp | 39 ++++-- .../renderer/imagemanager/ImageFetcher.h | 2 + .../ReactNativeFeatureFlags.config.js | 11 ++ .../featureflags/ReactNativeFeatureFlags.js | 7 +- .../specs/NativeReactNativeFeatureFlags.js | 3 +- 22 files changed, 214 insertions(+), 82 deletions(-) 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 bf1a86854dc722..eb07acd5df2386 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<<6b632ac7553ae149fa42a67efde5acfa>> */ /** @@ -180,6 +180,12 @@ public object ReactNativeFeatureFlags { @JvmStatic public fun enableImagePrefetchingAndroid(): Boolean = accessor.enableImagePrefetchingAndroid() + /** + * When enabled, Android will build and initiate image prefetch requests on ImageShadowNode::layout and batch them together in a single JNI call + */ + @JvmStatic + public fun enableImagePrefetchingJNIBatchingAndroid(): Boolean = accessor.enableImagePrefetchingJNIBatchingAndroid() + /** * When enabled, Android will initiate image prefetch requested on ImageShadowNode::layout on the UI thread */ 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 61b4b60513a54e..91a6990138d11f 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<> + * @generated SignedSource<> */ /** @@ -45,6 +45,7 @@ internal class ReactNativeFeatureFlagsCxxAccessor : ReactNativeFeatureFlagsAcces private var enableIOSTextBaselineOffsetPerLineCache: Boolean? = null private var enableIOSViewClipToPaddingBoxCache: Boolean? = null private var enableImagePrefetchingAndroidCache: Boolean? = null + private var enableImagePrefetchingJNIBatchingAndroidCache: Boolean? = null private var enableImagePrefetchingOnUiThreadAndroidCache: Boolean? = null private var enableImmediateUpdateModeForContentOffsetChangesCache: Boolean? = null private var enableImperativeFocusCache: Boolean? = null @@ -320,6 +321,15 @@ internal class ReactNativeFeatureFlagsCxxAccessor : ReactNativeFeatureFlagsAcces return cached } + override fun enableImagePrefetchingJNIBatchingAndroid(): Boolean { + var cached = enableImagePrefetchingJNIBatchingAndroidCache + if (cached == null) { + cached = ReactNativeFeatureFlagsCxxInterop.enableImagePrefetchingJNIBatchingAndroid() + enableImagePrefetchingJNIBatchingAndroidCache = cached + } + return cached + } + override fun enableImagePrefetchingOnUiThreadAndroid(): Boolean { var cached = enableImagePrefetchingOnUiThreadAndroidCache 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 5fef6dfb4b9baa..98d05d177e36dd 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsCxxInterop.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsCxxInterop.kt @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<> + * @generated SignedSource<<26dce1945df5641ab84cbca9eaf2b10f>> */ /** @@ -78,6 +78,8 @@ public object ReactNativeFeatureFlagsCxxInterop { @DoNotStrip @JvmStatic public external fun enableImagePrefetchingAndroid(): Boolean + @DoNotStrip @JvmStatic public external fun enableImagePrefetchingJNIBatchingAndroid(): Boolean + @DoNotStrip @JvmStatic public external fun enableImagePrefetchingOnUiThreadAndroid(): Boolean @DoNotStrip @JvmStatic public external fun enableImmediateUpdateModeForContentOffsetChanges(): 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 3f98049620be92..d08bd81b443420 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<<4fa15189361a6c92bdd44bafc9356167>> + * @generated SignedSource<<11051ece1b61fd4bf4ca003a3b7fc4f9>> */ /** @@ -73,6 +73,8 @@ public open class ReactNativeFeatureFlagsDefaults : ReactNativeFeatureFlagsProvi override fun enableImagePrefetchingAndroid(): Boolean = false + override fun enableImagePrefetchingJNIBatchingAndroid(): Boolean = false + override fun enableImagePrefetchingOnUiThreadAndroid(): Boolean = false override fun enableImmediateUpdateModeForContentOffsetChanges(): 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 b418295b880f2c..eb575e5159bc20 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<<962c60d1761b0a1f4945b44767aa7b02>> + * @generated SignedSource<<66fa583c37021750123a483ab0ccb030>> */ /** @@ -49,6 +49,7 @@ internal class ReactNativeFeatureFlagsLocalAccessor : ReactNativeFeatureFlagsAcc private var enableIOSTextBaselineOffsetPerLineCache: Boolean? = null private var enableIOSViewClipToPaddingBoxCache: Boolean? = null private var enableImagePrefetchingAndroidCache: Boolean? = null + private var enableImagePrefetchingJNIBatchingAndroidCache: Boolean? = null private var enableImagePrefetchingOnUiThreadAndroidCache: Boolean? = null private var enableImmediateUpdateModeForContentOffsetChangesCache: Boolean? = null private var enableImperativeFocusCache: Boolean? = null @@ -349,6 +350,16 @@ internal class ReactNativeFeatureFlagsLocalAccessor : ReactNativeFeatureFlagsAcc return cached } + override fun enableImagePrefetchingJNIBatchingAndroid(): Boolean { + var cached = enableImagePrefetchingJNIBatchingAndroidCache + if (cached == null) { + cached = currentProvider.enableImagePrefetchingJNIBatchingAndroid() + accessedFeatureFlags.add("enableImagePrefetchingJNIBatchingAndroid") + enableImagePrefetchingJNIBatchingAndroidCache = cached + } + return cached + } + override fun enableImagePrefetchingOnUiThreadAndroid(): Boolean { var cached = enableImagePrefetchingOnUiThreadAndroidCache 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 b38c61d3a6d46e..06e0872a916529 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<> */ /** @@ -73,6 +73,8 @@ public interface ReactNativeFeatureFlagsProvider { @DoNotStrip public fun enableImagePrefetchingAndroid(): Boolean + @DoNotStrip public fun enableImagePrefetchingJNIBatchingAndroid(): Boolean + @DoNotStrip public fun enableImagePrefetchingOnUiThreadAndroid(): Boolean @DoNotStrip public fun enableImmediateUpdateModeForContentOffsetChanges(): Boolean diff --git a/packages/react-native/ReactAndroid/src/main/jni/react/featureflags/JReactNativeFeatureFlagsCxxInterop.cpp b/packages/react-native/ReactAndroid/src/main/jni/react/featureflags/JReactNativeFeatureFlagsCxxInterop.cpp index 8fe12981ba24bd..8170bb1fcd34c6 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<> + * @generated SignedSource<<8e1821e9a153a4b725890cb4a7f262ee>> */ /** @@ -189,6 +189,12 @@ class ReactNativeFeatureFlagsJavaProvider return method(javaProvider_); } + bool enableImagePrefetchingJNIBatchingAndroid() override { + static const auto method = + getReactNativeFeatureFlagsProviderJavaClass()->getMethod("enableImagePrefetchingJNIBatchingAndroid"); + return method(javaProvider_); + } + bool enableImagePrefetchingOnUiThreadAndroid() override { static const auto method = getReactNativeFeatureFlagsProviderJavaClass()->getMethod("enableImagePrefetchingOnUiThreadAndroid"); @@ -612,6 +618,11 @@ bool JReactNativeFeatureFlagsCxxInterop::enableImagePrefetchingAndroid( return ReactNativeFeatureFlags::enableImagePrefetchingAndroid(); } +bool JReactNativeFeatureFlagsCxxInterop::enableImagePrefetchingJNIBatchingAndroid( + facebook::jni::alias_ref /*unused*/) { + return ReactNativeFeatureFlags::enableImagePrefetchingJNIBatchingAndroid(); +} + bool JReactNativeFeatureFlagsCxxInterop::enableImagePrefetchingOnUiThreadAndroid( facebook::jni::alias_ref /*unused*/) { return ReactNativeFeatureFlags::enableImagePrefetchingOnUiThreadAndroid(); @@ -963,6 +974,9 @@ void JReactNativeFeatureFlagsCxxInterop::registerNatives() { makeNativeMethod( "enableImagePrefetchingAndroid", JReactNativeFeatureFlagsCxxInterop::enableImagePrefetchingAndroid), + makeNativeMethod( + "enableImagePrefetchingJNIBatchingAndroid", + JReactNativeFeatureFlagsCxxInterop::enableImagePrefetchingJNIBatchingAndroid), makeNativeMethod( "enableImagePrefetchingOnUiThreadAndroid", JReactNativeFeatureFlagsCxxInterop::enableImagePrefetchingOnUiThreadAndroid), 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 df9508488905b2..9670480569e55f 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<> + * @generated SignedSource<<5be2ec52bda638a1eac837c402149b9f>> */ /** @@ -105,6 +105,9 @@ class JReactNativeFeatureFlagsCxxInterop static bool enableImagePrefetchingAndroid( facebook::jni::alias_ref); + static bool enableImagePrefetchingJNIBatchingAndroid( + facebook::jni::alias_ref); + static bool enableImagePrefetchingOnUiThreadAndroid( facebook::jni::alias_ref); diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.cpp b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.cpp index 46024295c8c368..4473f6df66a974 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<<50f81f60d4c2d2e08fde98f4a2d841ea>> + * @generated SignedSource<> */ /** @@ -126,6 +126,10 @@ bool ReactNativeFeatureFlags::enableImagePrefetchingAndroid() { return getAccessor().enableImagePrefetchingAndroid(); } +bool ReactNativeFeatureFlags::enableImagePrefetchingJNIBatchingAndroid() { + return getAccessor().enableImagePrefetchingJNIBatchingAndroid(); +} + bool ReactNativeFeatureFlags::enableImagePrefetchingOnUiThreadAndroid() { return getAccessor().enableImagePrefetchingOnUiThreadAndroid(); } diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.h b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.h index 34b9c6329b7599..2a8d97a0b9e6db 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<<3446eebd26c1142fd78cd5413771569d>> + * @generated SignedSource<> */ /** @@ -164,6 +164,11 @@ class ReactNativeFeatureFlags { */ RN_EXPORT static bool enableImagePrefetchingAndroid(); + /** + * When enabled, Android will build and initiate image prefetch requests on ImageShadowNode::layout and batch them together in a single JNI call + */ + RN_EXPORT static bool enableImagePrefetchingJNIBatchingAndroid(); + /** * When enabled, Android will initiate image prefetch requested on ImageShadowNode::layout on the UI thread */ diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.cpp b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.cpp index cc960f7f57c2c9..8263cb0cf18306 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<<891375f88b3f48b324de29e31c8936cf>> + * @generated SignedSource<<908baa1dbc4608be4b6407ffd670c066>> */ /** @@ -479,6 +479,24 @@ bool ReactNativeFeatureFlagsAccessor::enableImagePrefetchingAndroid() { return flagValue.value(); } +bool ReactNativeFeatureFlagsAccessor::enableImagePrefetchingJNIBatchingAndroid() { + auto flagValue = enableImagePrefetchingJNIBatchingAndroid_.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(25, "enableImagePrefetchingJNIBatchingAndroid"); + + flagValue = currentProvider_->enableImagePrefetchingJNIBatchingAndroid(); + enableImagePrefetchingJNIBatchingAndroid_ = flagValue; + } + + return flagValue.value(); +} + bool ReactNativeFeatureFlagsAccessor::enableImagePrefetchingOnUiThreadAndroid() { auto flagValue = enableImagePrefetchingOnUiThreadAndroid_.load(); @@ -488,7 +506,7 @@ bool ReactNativeFeatureFlagsAccessor::enableImagePrefetchingOnUiThreadAndroid() // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(25, "enableImagePrefetchingOnUiThreadAndroid"); + markFlagAsAccessed(26, "enableImagePrefetchingOnUiThreadAndroid"); flagValue = currentProvider_->enableImagePrefetchingOnUiThreadAndroid(); enableImagePrefetchingOnUiThreadAndroid_ = flagValue; @@ -506,7 +524,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(26, "enableImmediateUpdateModeForContentOffsetChanges"); + markFlagAsAccessed(27, "enableImmediateUpdateModeForContentOffsetChanges"); flagValue = currentProvider_->enableImmediateUpdateModeForContentOffsetChanges(); enableImmediateUpdateModeForContentOffsetChanges_ = flagValue; @@ -524,7 +542,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(27, "enableImperativeFocus"); + markFlagAsAccessed(28, "enableImperativeFocus"); flagValue = currentProvider_->enableImperativeFocus(); enableImperativeFocus_ = flagValue; @@ -542,7 +560,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(28, "enableInteropViewManagerClassLookUpOptimizationIOS"); + markFlagAsAccessed(29, "enableInteropViewManagerClassLookUpOptimizationIOS"); flagValue = currentProvider_->enableInteropViewManagerClassLookUpOptimizationIOS(); enableInteropViewManagerClassLookUpOptimizationIOS_ = flagValue; @@ -560,7 +578,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(29, "enableLayoutAnimationsOnAndroid"); + markFlagAsAccessed(30, "enableLayoutAnimationsOnAndroid"); flagValue = currentProvider_->enableLayoutAnimationsOnAndroid(); enableLayoutAnimationsOnAndroid_ = flagValue; @@ -578,7 +596,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(30, "enableLayoutAnimationsOnIOS"); + markFlagAsAccessed(31, "enableLayoutAnimationsOnIOS"); flagValue = currentProvider_->enableLayoutAnimationsOnIOS(); enableLayoutAnimationsOnIOS_ = flagValue; @@ -596,7 +614,7 @@ bool ReactNativeFeatureFlagsAccessor::enableMainQueueCoordinatorOnIOS() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(31, "enableMainQueueCoordinatorOnIOS"); + markFlagAsAccessed(32, "enableMainQueueCoordinatorOnIOS"); flagValue = currentProvider_->enableMainQueueCoordinatorOnIOS(); enableMainQueueCoordinatorOnIOS_ = flagValue; @@ -614,7 +632,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(32, "enableModuleArgumentNSNullConversionIOS"); + markFlagAsAccessed(33, "enableModuleArgumentNSNullConversionIOS"); flagValue = currentProvider_->enableModuleArgumentNSNullConversionIOS(); enableModuleArgumentNSNullConversionIOS_ = flagValue; @@ -632,7 +650,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(33, "enableNativeCSSParsing"); + markFlagAsAccessed(34, "enableNativeCSSParsing"); flagValue = currentProvider_->enableNativeCSSParsing(); enableNativeCSSParsing_ = flagValue; @@ -650,7 +668,7 @@ bool ReactNativeFeatureFlagsAccessor::enableNetworkEventReporting() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(34, "enableNetworkEventReporting"); + markFlagAsAccessed(35, "enableNetworkEventReporting"); flagValue = currentProvider_->enableNetworkEventReporting(); enableNetworkEventReporting_ = flagValue; @@ -668,7 +686,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(35, "enablePreparedTextLayout"); + markFlagAsAccessed(36, "enablePreparedTextLayout"); flagValue = currentProvider_->enablePreparedTextLayout(); enablePreparedTextLayout_ = flagValue; @@ -686,7 +704,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(36, "enablePropsUpdateReconciliationAndroid"); + markFlagAsAccessed(37, "enablePropsUpdateReconciliationAndroid"); flagValue = currentProvider_->enablePropsUpdateReconciliationAndroid(); enablePropsUpdateReconciliationAndroid_ = flagValue; @@ -704,7 +722,7 @@ bool ReactNativeFeatureFlagsAccessor::enableResourceTimingAPI() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(37, "enableResourceTimingAPI"); + markFlagAsAccessed(38, "enableResourceTimingAPI"); flagValue = currentProvider_->enableResourceTimingAPI(); enableResourceTimingAPI_ = flagValue; @@ -722,7 +740,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(38, "enableSwiftUIBasedFilters"); + markFlagAsAccessed(39, "enableSwiftUIBasedFilters"); flagValue = currentProvider_->enableSwiftUIBasedFilters(); enableSwiftUIBasedFilters_ = flagValue; @@ -740,7 +758,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(39, "enableViewCulling"); + markFlagAsAccessed(40, "enableViewCulling"); flagValue = currentProvider_->enableViewCulling(); enableViewCulling_ = flagValue; @@ -758,7 +776,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(40, "enableViewRecycling"); + markFlagAsAccessed(41, "enableViewRecycling"); flagValue = currentProvider_->enableViewRecycling(); enableViewRecycling_ = flagValue; @@ -776,7 +794,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(41, "enableViewRecyclingForImage"); + markFlagAsAccessed(42, "enableViewRecyclingForImage"); flagValue = currentProvider_->enableViewRecyclingForImage(); enableViewRecyclingForImage_ = flagValue; @@ -794,7 +812,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(42, "enableViewRecyclingForScrollView"); + markFlagAsAccessed(43, "enableViewRecyclingForScrollView"); flagValue = currentProvider_->enableViewRecyclingForScrollView(); enableViewRecyclingForScrollView_ = flagValue; @@ -812,7 +830,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(43, "enableViewRecyclingForText"); + markFlagAsAccessed(44, "enableViewRecyclingForText"); flagValue = currentProvider_->enableViewRecyclingForText(); enableViewRecyclingForText_ = flagValue; @@ -830,7 +848,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(44, "enableViewRecyclingForView"); + markFlagAsAccessed(45, "enableViewRecyclingForView"); flagValue = currentProvider_->enableViewRecyclingForView(); enableViewRecyclingForView_ = flagValue; @@ -848,7 +866,7 @@ bool ReactNativeFeatureFlagsAccessor::enableVirtualViewDebugFeatures() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(45, "enableVirtualViewDebugFeatures"); + markFlagAsAccessed(46, "enableVirtualViewDebugFeatures"); flagValue = currentProvider_->enableVirtualViewDebugFeatures(); enableVirtualViewDebugFeatures_ = flagValue; @@ -866,7 +884,7 @@ bool ReactNativeFeatureFlagsAccessor::enableVirtualViewRenderState() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(46, "enableVirtualViewRenderState"); + markFlagAsAccessed(47, "enableVirtualViewRenderState"); flagValue = currentProvider_->enableVirtualViewRenderState(); enableVirtualViewRenderState_ = flagValue; @@ -884,7 +902,7 @@ bool ReactNativeFeatureFlagsAccessor::enableVirtualViewWindowFocusDetection() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(47, "enableVirtualViewWindowFocusDetection"); + markFlagAsAccessed(48, "enableVirtualViewWindowFocusDetection"); flagValue = currentProvider_->enableVirtualViewWindowFocusDetection(); enableVirtualViewWindowFocusDetection_ = flagValue; @@ -902,7 +920,7 @@ bool ReactNativeFeatureFlagsAccessor::enableWebPerformanceAPIsByDefault() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(48, "enableWebPerformanceAPIsByDefault"); + markFlagAsAccessed(49, "enableWebPerformanceAPIsByDefault"); flagValue = currentProvider_->enableWebPerformanceAPIsByDefault(); enableWebPerformanceAPIsByDefault_ = flagValue; @@ -920,7 +938,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(49, "fixMappingOfEventPrioritiesBetweenFabricAndReact"); + markFlagAsAccessed(50, "fixMappingOfEventPrioritiesBetweenFabricAndReact"); flagValue = currentProvider_->fixMappingOfEventPrioritiesBetweenFabricAndReact(); fixMappingOfEventPrioritiesBetweenFabricAndReact_ = flagValue; @@ -938,7 +956,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(50, "fuseboxEnabledRelease"); + markFlagAsAccessed(51, "fuseboxEnabledRelease"); flagValue = currentProvider_->fuseboxEnabledRelease(); fuseboxEnabledRelease_ = flagValue; @@ -956,7 +974,7 @@ bool ReactNativeFeatureFlagsAccessor::fuseboxNetworkInspectionEnabled() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(51, "fuseboxNetworkInspectionEnabled"); + markFlagAsAccessed(52, "fuseboxNetworkInspectionEnabled"); flagValue = currentProvider_->fuseboxNetworkInspectionEnabled(); fuseboxNetworkInspectionEnabled_ = flagValue; @@ -974,7 +992,7 @@ bool ReactNativeFeatureFlagsAccessor::hideOffscreenVirtualViewsOnIOS() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(52, "hideOffscreenVirtualViewsOnIOS"); + markFlagAsAccessed(53, "hideOffscreenVirtualViewsOnIOS"); flagValue = currentProvider_->hideOffscreenVirtualViewsOnIOS(); hideOffscreenVirtualViewsOnIOS_ = flagValue; @@ -992,7 +1010,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(53, "overrideBySynchronousMountPropsAtMountingAndroid"); + markFlagAsAccessed(54, "overrideBySynchronousMountPropsAtMountingAndroid"); flagValue = currentProvider_->overrideBySynchronousMountPropsAtMountingAndroid(); overrideBySynchronousMountPropsAtMountingAndroid_ = flagValue; @@ -1010,7 +1028,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(54, "perfMonitorV2Enabled"); + markFlagAsAccessed(55, "perfMonitorV2Enabled"); flagValue = currentProvider_->perfMonitorV2Enabled(); perfMonitorV2Enabled_ = flagValue; @@ -1028,7 +1046,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(55, "preparedTextCacheSize"); + markFlagAsAccessed(56, "preparedTextCacheSize"); flagValue = currentProvider_->preparedTextCacheSize(); preparedTextCacheSize_ = flagValue; @@ -1046,7 +1064,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(56, "preventShadowTreeCommitExhaustion"); + markFlagAsAccessed(57, "preventShadowTreeCommitExhaustion"); flagValue = currentProvider_->preventShadowTreeCommitExhaustion(); preventShadowTreeCommitExhaustion_ = flagValue; @@ -1064,7 +1082,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(57, "shouldPressibilityUseW3CPointerEventsForHover"); + markFlagAsAccessed(58, "shouldPressibilityUseW3CPointerEventsForHover"); flagValue = currentProvider_->shouldPressibilityUseW3CPointerEventsForHover(); shouldPressibilityUseW3CPointerEventsForHover_ = flagValue; @@ -1082,7 +1100,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(58, "skipActivityIdentityAssertionOnHostPause"); + markFlagAsAccessed(59, "skipActivityIdentityAssertionOnHostPause"); flagValue = currentProvider_->skipActivityIdentityAssertionOnHostPause(); skipActivityIdentityAssertionOnHostPause_ = flagValue; @@ -1100,7 +1118,7 @@ bool ReactNativeFeatureFlagsAccessor::sweepActiveTouchOnChildNativeGesturesAndro // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(59, "sweepActiveTouchOnChildNativeGesturesAndroid"); + markFlagAsAccessed(60, "sweepActiveTouchOnChildNativeGesturesAndroid"); flagValue = currentProvider_->sweepActiveTouchOnChildNativeGesturesAndroid(); sweepActiveTouchOnChildNativeGesturesAndroid_ = flagValue; @@ -1118,7 +1136,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(60, "traceTurboModulePromiseRejectionsOnAndroid"); + markFlagAsAccessed(61, "traceTurboModulePromiseRejectionsOnAndroid"); flagValue = currentProvider_->traceTurboModulePromiseRejectionsOnAndroid(); traceTurboModulePromiseRejectionsOnAndroid_ = flagValue; @@ -1136,7 +1154,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(61, "updateRuntimeShadowNodeReferencesOnCommit"); + markFlagAsAccessed(62, "updateRuntimeShadowNodeReferencesOnCommit"); flagValue = currentProvider_->updateRuntimeShadowNodeReferencesOnCommit(); updateRuntimeShadowNodeReferencesOnCommit_ = flagValue; @@ -1154,7 +1172,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(62, "useAlwaysAvailableJSErrorHandling"); + markFlagAsAccessed(63, "useAlwaysAvailableJSErrorHandling"); flagValue = currentProvider_->useAlwaysAvailableJSErrorHandling(); useAlwaysAvailableJSErrorHandling_ = flagValue; @@ -1172,7 +1190,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(63, "useFabricInterop"); + markFlagAsAccessed(64, "useFabricInterop"); flagValue = currentProvider_->useFabricInterop(); useFabricInterop_ = flagValue; @@ -1190,7 +1208,7 @@ bool ReactNativeFeatureFlagsAccessor::useNativeEqualsInNativeReadableArrayAndroi // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(64, "useNativeEqualsInNativeReadableArrayAndroid"); + markFlagAsAccessed(65, "useNativeEqualsInNativeReadableArrayAndroid"); flagValue = currentProvider_->useNativeEqualsInNativeReadableArrayAndroid(); useNativeEqualsInNativeReadableArrayAndroid_ = flagValue; @@ -1208,7 +1226,7 @@ bool ReactNativeFeatureFlagsAccessor::useNativeTransformHelperAndroid() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(65, "useNativeTransformHelperAndroid"); + markFlagAsAccessed(66, "useNativeTransformHelperAndroid"); flagValue = currentProvider_->useNativeTransformHelperAndroid(); useNativeTransformHelperAndroid_ = flagValue; @@ -1226,7 +1244,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(66, "useNativeViewConfigsInBridgelessMode"); + markFlagAsAccessed(67, "useNativeViewConfigsInBridgelessMode"); flagValue = currentProvider_->useNativeViewConfigsInBridgelessMode(); useNativeViewConfigsInBridgelessMode_ = flagValue; @@ -1244,7 +1262,7 @@ bool ReactNativeFeatureFlagsAccessor::useOptimizedEventBatchingOnAndroid() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(67, "useOptimizedEventBatchingOnAndroid"); + markFlagAsAccessed(68, "useOptimizedEventBatchingOnAndroid"); flagValue = currentProvider_->useOptimizedEventBatchingOnAndroid(); useOptimizedEventBatchingOnAndroid_ = flagValue; @@ -1262,7 +1280,7 @@ bool ReactNativeFeatureFlagsAccessor::useRawPropsJsiValue() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(68, "useRawPropsJsiValue"); + markFlagAsAccessed(69, "useRawPropsJsiValue"); flagValue = currentProvider_->useRawPropsJsiValue(); useRawPropsJsiValue_ = flagValue; @@ -1280,7 +1298,7 @@ bool ReactNativeFeatureFlagsAccessor::useShadowNodeStateOnClone() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(69, "useShadowNodeStateOnClone"); + markFlagAsAccessed(70, "useShadowNodeStateOnClone"); flagValue = currentProvider_->useShadowNodeStateOnClone(); useShadowNodeStateOnClone_ = flagValue; @@ -1298,7 +1316,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(70, "useTurboModuleInterop"); + markFlagAsAccessed(71, "useTurboModuleInterop"); flagValue = currentProvider_->useTurboModuleInterop(); useTurboModuleInterop_ = flagValue; @@ -1316,7 +1334,7 @@ bool ReactNativeFeatureFlagsAccessor::useTurboModules() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(71, "useTurboModules"); + markFlagAsAccessed(72, "useTurboModules"); flagValue = currentProvider_->useTurboModules(); useTurboModules_ = flagValue; @@ -1334,7 +1352,7 @@ double ReactNativeFeatureFlagsAccessor::virtualViewHysteresisRatio() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(72, "virtualViewHysteresisRatio"); + markFlagAsAccessed(73, "virtualViewHysteresisRatio"); flagValue = currentProvider_->virtualViewHysteresisRatio(); virtualViewHysteresisRatio_ = flagValue; @@ -1352,7 +1370,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(73, "virtualViewPrerenderRatio"); + markFlagAsAccessed(74, "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 802d599e1e14cd..ede7746b6dc2b0 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<<53981e017d681009b240ad6b5d09d4d8>> + * @generated SignedSource<> */ /** @@ -57,6 +57,7 @@ class ReactNativeFeatureFlagsAccessor { bool enableIOSTextBaselineOffsetPerLine(); bool enableIOSViewClipToPaddingBox(); bool enableImagePrefetchingAndroid(); + bool enableImagePrefetchingJNIBatchingAndroid(); bool enableImagePrefetchingOnUiThreadAndroid(); bool enableImmediateUpdateModeForContentOffsetChanges(); bool enableImperativeFocus(); @@ -117,7 +118,7 @@ class ReactNativeFeatureFlagsAccessor { std::unique_ptr currentProvider_; bool wasOverridden_; - std::array, 74> accessedFeatureFlags_; + std::array, 75> accessedFeatureFlags_; std::atomic> commonTestFlag_; std::atomic> cdpInteractionMetricsEnabled_; @@ -144,6 +145,7 @@ class ReactNativeFeatureFlagsAccessor { std::atomic> enableIOSTextBaselineOffsetPerLine_; std::atomic> enableIOSViewClipToPaddingBox_; std::atomic> enableImagePrefetchingAndroid_; + std::atomic> enableImagePrefetchingJNIBatchingAndroid_; std::atomic> enableImagePrefetchingOnUiThreadAndroid_; std::atomic> enableImmediateUpdateModeForContentOffsetChanges_; std::atomic> enableImperativeFocus_; diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDefaults.h b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDefaults.h index d5a67e812873cc..2b407600c30d4b 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<<6c1c050029f105d0075fb83ca21aa48b>> + * @generated SignedSource<<5e6ee2118acc15edb9da813bb43978f3>> */ /** @@ -127,6 +127,10 @@ class ReactNativeFeatureFlagsDefaults : public ReactNativeFeatureFlagsProvider { return false; } + bool enableImagePrefetchingJNIBatchingAndroid() override { + return false; + } + bool enableImagePrefetchingOnUiThreadAndroid() override { return false; } diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDynamicProvider.h b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDynamicProvider.h index 76ff2d8420e340..126f07e6bd44c4 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<<0bef63a074aa6b67787c2ca30706a847>> + * @generated SignedSource<> */ /** @@ -270,6 +270,15 @@ class ReactNativeFeatureFlagsDynamicProvider : public ReactNativeFeatureFlagsDef return ReactNativeFeatureFlagsDefaults::enableImagePrefetchingAndroid(); } + bool enableImagePrefetchingJNIBatchingAndroid() override { + auto value = values_["enableImagePrefetchingJNIBatchingAndroid"]; + if (!value.isNull()) { + return value.getBool(); + } + + return ReactNativeFeatureFlagsDefaults::enableImagePrefetchingJNIBatchingAndroid(); + } + bool enableImagePrefetchingOnUiThreadAndroid() override { auto value = values_["enableImagePrefetchingOnUiThreadAndroid"]; if (!value.isNull()) { diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsProvider.h b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsProvider.h index a32ad655eea29c..b8c7ca07366f3b 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<<40ea902a1d376324f75680d651f3eff6>> + * @generated SignedSource<<4d141c52bc66225656194eeb8786f475>> */ /** @@ -50,6 +50,7 @@ class ReactNativeFeatureFlagsProvider { virtual bool enableIOSTextBaselineOffsetPerLine() = 0; virtual bool enableIOSViewClipToPaddingBox() = 0; virtual bool enableImagePrefetchingAndroid() = 0; + virtual bool enableImagePrefetchingJNIBatchingAndroid() = 0; virtual bool enableImagePrefetchingOnUiThreadAndroid() = 0; virtual bool enableImmediateUpdateModeForContentOffsetChanges() = 0; virtual bool enableImperativeFocus() = 0; diff --git a/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.cpp b/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.cpp index 35e3977b5336ac..761cb2a2ea44da 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<> */ /** @@ -169,6 +169,11 @@ bool NativeReactNativeFeatureFlags::enableImagePrefetchingAndroid( return ReactNativeFeatureFlags::enableImagePrefetchingAndroid(); } +bool NativeReactNativeFeatureFlags::enableImagePrefetchingJNIBatchingAndroid( + jsi::Runtime& /*runtime*/) { + return ReactNativeFeatureFlags::enableImagePrefetchingJNIBatchingAndroid(); +} + bool NativeReactNativeFeatureFlags::enableImagePrefetchingOnUiThreadAndroid( jsi::Runtime& /*runtime*/) { return ReactNativeFeatureFlags::enableImagePrefetchingOnUiThreadAndroid(); diff --git a/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.h b/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.h index 6f2cb0ff337ad9..5a064f078cc5f8 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<> + * @generated SignedSource<<2dcd8b3add6f1fdc1bf378714deb41d7>> */ /** @@ -86,6 +86,8 @@ class NativeReactNativeFeatureFlags bool enableImagePrefetchingAndroid(jsi::Runtime& runtime); + bool enableImagePrefetchingJNIBatchingAndroid(jsi::Runtime& runtime); + bool enableImagePrefetchingOnUiThreadAndroid(jsi::Runtime& runtime); bool enableImmediateUpdateModeForContentOffsetChanges(jsi::Runtime& runtime); diff --git a/packages/react-native/ReactCommon/react/renderer/imagemanager/platform/android/react/renderer/imagemanager/ImageFetcher.cpp b/packages/react-native/ReactCommon/react/renderer/imagemanager/platform/android/react/renderer/imagemanager/ImageFetcher.cpp index 0152c4872e3e2e..771ab980ed26a7 100644 --- a/packages/react-native/ReactCommon/react/renderer/imagemanager/platform/android/react/renderer/imagemanager/ImageFetcher.cpp +++ b/packages/react-native/ReactCommon/react/renderer/imagemanager/platform/android/react/renderer/imagemanager/ImageFetcher.cpp @@ -16,20 +16,26 @@ namespace facebook::react { ImageFetcher::ImageFetcher( std::shared_ptr contextContainer) : contextContainer_(std::move(contextContainer)) { - if (auto uiManagerCommitHookManager = - contextContainer_->find>( - std::string(UIManagerCommitHookManagerKey)); - uiManagerCommitHookManager.has_value()) { - (*uiManagerCommitHookManager)->registerCommitHook(*this); + if (ReactNativeFeatureFlags::enableImagePrefetchingJNIBatchingAndroid()) { + if (auto uiManagerCommitHookManager = + contextContainer_ + ->find>( + std::string(UIManagerCommitHookManagerKey)); + uiManagerCommitHookManager.has_value()) { + (*uiManagerCommitHookManager)->registerCommitHook(*this); + } } } ImageFetcher::~ImageFetcher() { - if (auto uiManagerCommitHookManager = - contextContainer_->find>( - std::string(UIManagerCommitHookManagerKey)); - uiManagerCommitHookManager.has_value()) { - (*uiManagerCommitHookManager)->unregisterCommitHook(*this); + if (ReactNativeFeatureFlags::enableImagePrefetchingJNIBatchingAndroid()) { + if (auto uiManagerCommitHookManager = + contextContainer_ + ->find>( + std::string(UIManagerCommitHookManagerKey)); + uiManagerCommitHookManager.has_value()) { + (*uiManagerCommitHookManager)->unregisterCommitHook(*this); + } } } @@ -45,6 +51,10 @@ ImageRequest ImageFetcher::requestImage( auto telemetry = std::make_shared(surfaceId); + if (!ReactNativeFeatureFlags::enableImagePrefetchingJNIBatchingAndroid()) { + flushImageRequests(); + } + return {imageSource, telemetry}; } @@ -53,8 +63,13 @@ RootShadowNode::Unshared ImageFetcher::shadowTreeWillCommit( const RootShadowNode::Shared& /*oldRootShadowNode*/, const RootShadowNode::Unshared& newRootShadowNode, const ShadowTree::CommitOptions& /*commitOptions*/) noexcept { + flushImageRequests(); + return newRootShadowNode; +} + +void ImageFetcher::flushImageRequests() { if (items_.empty()) { - return newRootShadowNode; + return; } auto fabricUIManager_ = @@ -73,8 +88,6 @@ RootShadowNode::Unshared ImageFetcher::shadowTreeWillCommit( } items_.clear(); - - return newRootShadowNode; } } // namespace facebook::react diff --git a/packages/react-native/ReactCommon/react/renderer/imagemanager/platform/android/react/renderer/imagemanager/ImageFetcher.h b/packages/react-native/ReactCommon/react/renderer/imagemanager/platform/android/react/renderer/imagemanager/ImageFetcher.h index 5d69620679f479..0bebb6f850151b 100644 --- a/packages/react-native/ReactCommon/react/renderer/imagemanager/platform/android/react/renderer/imagemanager/ImageFetcher.h +++ b/packages/react-native/ReactCommon/react/renderer/imagemanager/platform/android/react/renderer/imagemanager/ImageFetcher.h @@ -45,6 +45,8 @@ class ImageFetcher : public UIManagerCommitHook { const ShadowTree::CommitOptions& commitOptions) noexcept override; private: + void flushImageRequests(); + std::unordered_map> items_; std::shared_ptr contextContainer_; }; diff --git a/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js b/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js index 31069d2243a80a..31caaddc0a5fde 100644 --- a/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js +++ b/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js @@ -314,6 +314,17 @@ const definitions: FeatureFlagDefinitions = { }, ossReleaseStage: 'none', }, + enableImagePrefetchingJNIBatchingAndroid: { + defaultValue: false, + metadata: { + dateAdded: '2025-09-19', + description: + 'When enabled, Android will build and initiate image prefetch requests on ImageShadowNode::layout and batch them together in a single JNI call', + expectedReleaseValue: true, + purpose: 'experimentation', + }, + ossReleaseStage: 'none', + }, enableImagePrefetchingOnUiThreadAndroid: { defaultValue: false, metadata: { diff --git a/packages/react-native/src/private/featureflags/ReactNativeFeatureFlags.js b/packages/react-native/src/private/featureflags/ReactNativeFeatureFlags.js index eeafd333a840f8..8db688c63c65a9 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<> + * @generated SignedSource<<44c491f4078e75b8b77ee346ac802a70>> * @flow strict * @noformat */ @@ -75,6 +75,7 @@ export type ReactNativeFeatureFlags = $ReadOnly<{ enableIOSTextBaselineOffsetPerLine: Getter, enableIOSViewClipToPaddingBox: Getter, enableImagePrefetchingAndroid: Getter, + enableImagePrefetchingJNIBatchingAndroid: Getter, enableImagePrefetchingOnUiThreadAndroid: Getter, enableImmediateUpdateModeForContentOffsetChanges: Getter, enableImperativeFocus: Getter, @@ -305,6 +306,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, Android will build and initiate image prefetch requests on ImageShadowNode::layout and batch them together in a single JNI call + */ +export const enableImagePrefetchingJNIBatchingAndroid: Getter = createNativeFlagGetter('enableImagePrefetchingJNIBatchingAndroid', false); /** * When enabled, Android will initiate image prefetch requested on ImageShadowNode::layout on the UI thread */ diff --git a/packages/react-native/src/private/featureflags/specs/NativeReactNativeFeatureFlags.js b/packages/react-native/src/private/featureflags/specs/NativeReactNativeFeatureFlags.js index fc36f715b1892b..0a72c27c6a7570 100644 --- a/packages/react-native/src/private/featureflags/specs/NativeReactNativeFeatureFlags.js +++ b/packages/react-native/src/private/featureflags/specs/NativeReactNativeFeatureFlags.js @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<> + * @generated SignedSource<<18ae10cff4e5611d4774252917f57c13>> * @flow strict * @noformat */ @@ -50,6 +50,7 @@ export interface Spec extends TurboModule { +enableIOSTextBaselineOffsetPerLine?: () => boolean; +enableIOSViewClipToPaddingBox?: () => boolean; +enableImagePrefetchingAndroid?: () => boolean; + +enableImagePrefetchingJNIBatchingAndroid?: () => boolean; +enableImagePrefetchingOnUiThreadAndroid?: () => boolean; +enableImmediateUpdateModeForContentOffsetChanges?: () => boolean; +enableImperativeFocus?: () => boolean;