From 28c8eab9c2d8783c4798da1f0138ae7f60cb6f21 Mon Sep 17 00:00:00 2001 From: David Vacca <515103+mdvacca@users.noreply.github.com> Date: Wed, 28 May 2025 14:05:00 -0700 Subject: [PATCH] Introduce feature flag to greacefuly fail when an unregistered component is rendered in Android (#51175) Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/51175 Introduce feature flag to greacefuly fail when an unregistered component is rendered in Android changelog: [internal] internal Reviewed By: mlord93 Differential Revision: D74333595 --- .../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 | 86 +++++++++++-------- .../ReactNativeFeatureFlagsAccessor.h | 6 +- .../ReactNativeFeatureFlagsDefaults.h | 6 +- .../ReactNativeFeatureFlagsDynamicProvider.h | 11 ++- .../ReactNativeFeatureFlagsProvider.h | 3 +- .../NativeReactNativeFeatureFlags.cpp | 7 +- .../NativeReactNativeFeatureFlags.h | 4 +- .../ReactNativeFeatureFlags.config.js | 11 +++ .../featureflags/ReactNativeFeatureFlags.js | 7 +- .../specs/NativeReactNativeFeatureFlags.js | 3 +- 20 files changed, 170 insertions(+), 53 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 e7014329ab2f..0bf5c233b7be 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<> */ /** @@ -144,6 +144,12 @@ public object ReactNativeFeatureFlags { @JvmStatic public fun enableIOSTextBaselineOffsetPerLine(): Boolean = accessor.enableIOSTextBaselineOffsetPerLine() + /** + * Enables gracefuly failure when an unregistered component is rendered in Android. + */ + @JvmStatic + public fun enableGracefulUnregisteredComponentFailureAndroid(): Boolean = accessor.enableGracefulUnregisteredComponentFailureAndroid() + /** * iOS Views will clip to their padding box vs border box */ 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 091deb94efd4..303695262d07 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<<62a722030e9166b231e5368f193f6f0c>> + * @generated SignedSource<> */ /** @@ -39,6 +39,7 @@ internal class ReactNativeFeatureFlagsCxxAccessor : ReactNativeFeatureFlagsAcces private var enableFixForParentTagDuringReparentingCache: Boolean? = null private var enableFontScaleChangesUpdatingLayoutCache: Boolean? = null private var enableIOSTextBaselineOffsetPerLineCache: Boolean? = null + private var enableGracefulUnregisteredComponentFailureAndroidCache: Boolean? = null private var enableIOSViewClipToPaddingBoxCache: Boolean? = null private var enableIntersectionObserverEventLoopIntegrationCache: Boolean? = null private var enableLayoutAnimationsOnAndroidCache: Boolean? = null @@ -244,6 +245,15 @@ internal class ReactNativeFeatureFlagsCxxAccessor : ReactNativeFeatureFlagsAcces return cached } + override fun enableGracefulUnregisteredComponentFailureAndroid(): Boolean { + var cached = enableGracefulUnregisteredComponentFailureAndroidCache + if (cached == null) { + cached = ReactNativeFeatureFlagsCxxInterop.enableGracefulUnregisteredComponentFailureAndroid() + enableGracefulUnregisteredComponentFailureAndroidCache = cached + } + return cached + } + override fun enableIOSViewClipToPaddingBox(): Boolean { var cached = enableIOSViewClipToPaddingBoxCache 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 8003822ddd71..4c081a7a12b4 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<<39d78493b17f7abfb4220d25e663ae55>> + * @generated SignedSource<> */ /** @@ -66,6 +66,8 @@ public object ReactNativeFeatureFlagsCxxInterop { @DoNotStrip @JvmStatic public external fun enableIOSTextBaselineOffsetPerLine(): Boolean + @DoNotStrip @JvmStatic public external fun enableGracefulUnregisteredComponentFailureAndroid(): Boolean + @DoNotStrip @JvmStatic public external fun enableIOSViewClipToPaddingBox(): Boolean @DoNotStrip @JvmStatic public external fun enableIntersectionObserverEventLoopIntegration(): 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 b0b39369151b..e4ad5a570317 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<<830cbb41b3886b715b57c2f0c29f4623>> + * @generated SignedSource<<1b936df7f2b86affb273a0054d9938c4>> */ /** @@ -61,6 +61,8 @@ public open class ReactNativeFeatureFlagsDefaults : ReactNativeFeatureFlagsProvi override fun enableIOSTextBaselineOffsetPerLine(): Boolean = false + override fun enableGracefulUnregisteredComponentFailureAndroid(): Boolean = false + override fun enableIOSViewClipToPaddingBox(): Boolean = false override fun enableIntersectionObserverEventLoopIntegration(): Boolean = true 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 f8e79cd1fa68..d4608553f784 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<> + * @generated SignedSource<<646cf101688413ddb4677526742d6677>> */ /** @@ -43,6 +43,7 @@ internal class ReactNativeFeatureFlagsLocalAccessor : ReactNativeFeatureFlagsAcc private var enableFixForParentTagDuringReparentingCache: Boolean? = null private var enableFontScaleChangesUpdatingLayoutCache: Boolean? = null private var enableIOSTextBaselineOffsetPerLineCache: Boolean? = null + private var enableGracefulUnregisteredComponentFailureAndroidCache: Boolean? = null private var enableIOSViewClipToPaddingBoxCache: Boolean? = null private var enableIntersectionObserverEventLoopIntegrationCache: Boolean? = null private var enableLayoutAnimationsOnAndroidCache: Boolean? = null @@ -267,6 +268,16 @@ internal class ReactNativeFeatureFlagsLocalAccessor : ReactNativeFeatureFlagsAcc return cached } + override fun enableGracefulUnregisteredComponentFailureAndroid(): Boolean { + var cached = enableGracefulUnregisteredComponentFailureAndroidCache + if (cached == null) { + cached = currentProvider.enableGracefulUnregisteredComponentFailureAndroid() + accessedFeatureFlags.add("enableGracefulUnregisteredComponentFailureAndroid") + enableGracefulUnregisteredComponentFailureAndroidCache = cached + } + return cached + } + override fun enableIOSViewClipToPaddingBox(): Boolean { var cached = enableIOSViewClipToPaddingBoxCache 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 cdfa7660d614..98c0c05e165e 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<<25acda9d60691c0ca5ea4776bfae594a>> + * @generated SignedSource<<7848d98b460497d1c322158f89cec7b4>> */ /** @@ -61,6 +61,8 @@ public interface ReactNativeFeatureFlagsProvider { @DoNotStrip public fun enableIOSTextBaselineOffsetPerLine(): Boolean + @DoNotStrip public fun enableGracefulUnregisteredComponentFailureAndroid(): Boolean + @DoNotStrip public fun enableIOSViewClipToPaddingBox(): Boolean @DoNotStrip public fun enableIntersectionObserverEventLoopIntegration(): 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 b30dc6a7bad2..5b6cd5f84067 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<> */ /** @@ -153,6 +153,12 @@ class ReactNativeFeatureFlagsJavaProvider return method(javaProvider_); } + bool enableGracefulUnregisteredComponentFailureAndroid() override { + static const auto method = + getReactNativeFeatureFlagsProviderJavaClass()->getMethod("enableGracefulUnregisteredComponentFailureAndroid"); + return method(javaProvider_); + } + bool enableIOSViewClipToPaddingBox() override { static const auto method = getReactNativeFeatureFlagsProviderJavaClass()->getMethod("enableIOSViewClipToPaddingBox"); @@ -450,6 +456,11 @@ bool JReactNativeFeatureFlagsCxxInterop::enableIOSTextBaselineOffsetPerLine( return ReactNativeFeatureFlags::enableIOSTextBaselineOffsetPerLine(); } +bool JReactNativeFeatureFlagsCxxInterop::enableGracefulUnregisteredComponentFailureAndroid( + facebook::jni::alias_ref /*unused*/) { + return ReactNativeFeatureFlags::enableGracefulUnregisteredComponentFailureAndroid(); +} + bool JReactNativeFeatureFlagsCxxInterop::enableIOSViewClipToPaddingBox( facebook::jni::alias_ref /*unused*/) { return ReactNativeFeatureFlags::enableIOSViewClipToPaddingBox(); @@ -703,6 +714,9 @@ void JReactNativeFeatureFlagsCxxInterop::registerNatives() { makeNativeMethod( "enableIOSTextBaselineOffsetPerLine", JReactNativeFeatureFlagsCxxInterop::enableIOSTextBaselineOffsetPerLine), + makeNativeMethod( + "enableGracefulUnregisteredComponentFailureAndroid", + JReactNativeFeatureFlagsCxxInterop::enableGracefulUnregisteredComponentFailureAndroid), makeNativeMethod( "enableIOSViewClipToPaddingBox", JReactNativeFeatureFlagsCxxInterop::enableIOSViewClipToPaddingBox), 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 c0d2b716b373..dfc8792d990a 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<<3a00be0b706f3513e1aca741c55a0747>> + * @generated SignedSource<> */ /** @@ -87,6 +87,9 @@ class JReactNativeFeatureFlagsCxxInterop static bool enableIOSTextBaselineOffsetPerLine( facebook::jni::alias_ref); + static bool enableGracefulUnregisteredComponentFailureAndroid( + facebook::jni::alias_ref); + static bool enableIOSViewClipToPaddingBox( 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 ef7389534f05..37bdc68cee93 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<<05405c3bb41e2511ac66792eb641db5e>> + * @generated SignedSource<<5415b718e13b2b4e1daee3bb6faba776>> */ /** @@ -102,6 +102,10 @@ bool ReactNativeFeatureFlags::enableIOSTextBaselineOffsetPerLine() { return getAccessor().enableIOSTextBaselineOffsetPerLine(); } +bool ReactNativeFeatureFlags::enableGracefulUnregisteredComponentFailureAndroid() { + return getAccessor().enableGracefulUnregisteredComponentFailureAndroid(); +} + bool ReactNativeFeatureFlags::enableIOSViewClipToPaddingBox() { return getAccessor().enableIOSViewClipToPaddingBox(); } diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.h b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.h index 90aef2fa729b..1dd94eb65e7d 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<> */ /** @@ -134,6 +134,11 @@ class ReactNativeFeatureFlags { */ RN_EXPORT static bool enableIOSTextBaselineOffsetPerLine(); + /** + * Enables gracefuly failure when an unregistered component is rendered in Android. + */ + RN_EXPORT static bool enableGracefulUnregisteredComponentFailureAndroid(); + /** * iOS Views will clip to their padding box vs border box */ diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.cpp b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.cpp index f09250be2ea6..f9be927933ca 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<<5a2deb42cf254a1f8705d7d012394fad>> + * @generated SignedSource<> */ /** @@ -371,6 +371,24 @@ bool ReactNativeFeatureFlagsAccessor::enableIOSTextBaselineOffsetPerLine() { return flagValue.value(); } +bool ReactNativeFeatureFlagsAccessor::enableGracefulUnregisteredComponentFailureAndroid() { + auto flagValue = enableGracefulUnregisteredComponentFailureAndroid_.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(19, "enableGracefulUnregisteredComponentFailureAndroid"); + + flagValue = currentProvider_->enableGracefulUnregisteredComponentFailureAndroid(); + enableGracefulUnregisteredComponentFailureAndroid_ = flagValue; + } + + return flagValue.value(); +} + bool ReactNativeFeatureFlagsAccessor::enableIOSViewClipToPaddingBox() { auto flagValue = enableIOSViewClipToPaddingBox_.load(); @@ -380,7 +398,7 @@ bool ReactNativeFeatureFlagsAccessor::enableIOSViewClipToPaddingBox() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(19, "enableIOSViewClipToPaddingBox"); + markFlagAsAccessed(20, "enableIOSViewClipToPaddingBox"); flagValue = currentProvider_->enableIOSViewClipToPaddingBox(); enableIOSViewClipToPaddingBox_ = flagValue; @@ -398,7 +416,7 @@ bool ReactNativeFeatureFlagsAccessor::enableIntersectionObserverEventLoopIntegra // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(20, "enableIntersectionObserverEventLoopIntegration"); + markFlagAsAccessed(21, "enableIntersectionObserverEventLoopIntegration"); flagValue = currentProvider_->enableIntersectionObserverEventLoopIntegration(); enableIntersectionObserverEventLoopIntegration_ = flagValue; @@ -416,7 +434,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(21, "enableLayoutAnimationsOnAndroid"); + markFlagAsAccessed(22, "enableLayoutAnimationsOnAndroid"); flagValue = currentProvider_->enableLayoutAnimationsOnAndroid(); enableLayoutAnimationsOnAndroid_ = flagValue; @@ -434,7 +452,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(22, "enableLayoutAnimationsOnIOS"); + markFlagAsAccessed(23, "enableLayoutAnimationsOnIOS"); flagValue = currentProvider_->enableLayoutAnimationsOnIOS(); enableLayoutAnimationsOnIOS_ = flagValue; @@ -452,7 +470,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(23, "enableMainQueueCoordinatorOnIOS"); + markFlagAsAccessed(24, "enableMainQueueCoordinatorOnIOS"); flagValue = currentProvider_->enableMainQueueCoordinatorOnIOS(); enableMainQueueCoordinatorOnIOS_ = flagValue; @@ -470,7 +488,7 @@ bool ReactNativeFeatureFlagsAccessor::enableMainQueueModulesOnIOS() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(24, "enableMainQueueModulesOnIOS"); + markFlagAsAccessed(25, "enableMainQueueModulesOnIOS"); flagValue = currentProvider_->enableMainQueueModulesOnIOS(); enableMainQueueModulesOnIOS_ = flagValue; @@ -488,7 +506,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(25, "enableModuleArgumentNSNullConversionIOS"); + markFlagAsAccessed(26, "enableModuleArgumentNSNullConversionIOS"); flagValue = currentProvider_->enableModuleArgumentNSNullConversionIOS(); enableModuleArgumentNSNullConversionIOS_ = flagValue; @@ -506,7 +524,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(26, "enableNativeCSSParsing"); + markFlagAsAccessed(27, "enableNativeCSSParsing"); flagValue = currentProvider_->enableNativeCSSParsing(); enableNativeCSSParsing_ = flagValue; @@ -524,7 +542,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(27, "enableNetworkEventReporting"); + markFlagAsAccessed(28, "enableNetworkEventReporting"); flagValue = currentProvider_->enableNetworkEventReporting(); enableNetworkEventReporting_ = flagValue; @@ -542,7 +560,7 @@ bool ReactNativeFeatureFlagsAccessor::enableNewBackgroundAndBorderDrawables() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(28, "enableNewBackgroundAndBorderDrawables"); + markFlagAsAccessed(29, "enableNewBackgroundAndBorderDrawables"); flagValue = currentProvider_->enableNewBackgroundAndBorderDrawables(); enableNewBackgroundAndBorderDrawables_ = flagValue; @@ -560,7 +578,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(29, "enablePreparedTextLayout"); + markFlagAsAccessed(30, "enablePreparedTextLayout"); flagValue = currentProvider_->enablePreparedTextLayout(); enablePreparedTextLayout_ = flagValue; @@ -578,7 +596,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(30, "enablePropsUpdateReconciliationAndroid"); + markFlagAsAccessed(31, "enablePropsUpdateReconciliationAndroid"); flagValue = currentProvider_->enablePropsUpdateReconciliationAndroid(); enablePropsUpdateReconciliationAndroid_ = flagValue; @@ -596,7 +614,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(31, "enableResourceTimingAPI"); + markFlagAsAccessed(32, "enableResourceTimingAPI"); flagValue = currentProvider_->enableResourceTimingAPI(); enableResourceTimingAPI_ = flagValue; @@ -614,7 +632,7 @@ bool ReactNativeFeatureFlagsAccessor::enableSynchronousStateUpdates() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(32, "enableSynchronousStateUpdates"); + markFlagAsAccessed(33, "enableSynchronousStateUpdates"); flagValue = currentProvider_->enableSynchronousStateUpdates(); enableSynchronousStateUpdates_ = flagValue; @@ -632,7 +650,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(33, "enableViewCulling"); + markFlagAsAccessed(34, "enableViewCulling"); flagValue = currentProvider_->enableViewCulling(); enableViewCulling_ = flagValue; @@ -650,7 +668,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(34, "enableViewRecycling"); + markFlagAsAccessed(35, "enableViewRecycling"); flagValue = currentProvider_->enableViewRecycling(); enableViewRecycling_ = flagValue; @@ -668,7 +686,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(35, "enableViewRecyclingForText"); + markFlagAsAccessed(36, "enableViewRecyclingForText"); flagValue = currentProvider_->enableViewRecyclingForText(); enableViewRecyclingForText_ = flagValue; @@ -686,7 +704,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(36, "enableViewRecyclingForView"); + markFlagAsAccessed(37, "enableViewRecyclingForView"); flagValue = currentProvider_->enableViewRecyclingForView(); enableViewRecyclingForView_ = flagValue; @@ -704,7 +722,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(37, "fixMappingOfEventPrioritiesBetweenFabricAndReact"); + markFlagAsAccessed(38, "fixMappingOfEventPrioritiesBetweenFabricAndReact"); flagValue = currentProvider_->fixMappingOfEventPrioritiesBetweenFabricAndReact(); fixMappingOfEventPrioritiesBetweenFabricAndReact_ = flagValue; @@ -722,7 +740,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(38, "fuseboxEnabledRelease"); + markFlagAsAccessed(39, "fuseboxEnabledRelease"); flagValue = currentProvider_->fuseboxEnabledRelease(); fuseboxEnabledRelease_ = flagValue; @@ -740,7 +758,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(39, "fuseboxNetworkInspectionEnabled"); + markFlagAsAccessed(40, "fuseboxNetworkInspectionEnabled"); flagValue = currentProvider_->fuseboxNetworkInspectionEnabled(); fuseboxNetworkInspectionEnabled_ = flagValue; @@ -758,7 +776,7 @@ bool ReactNativeFeatureFlagsAccessor::incorporateMaxLinesDuringAndroidLayout() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(40, "incorporateMaxLinesDuringAndroidLayout"); + markFlagAsAccessed(41, "incorporateMaxLinesDuringAndroidLayout"); flagValue = currentProvider_->incorporateMaxLinesDuringAndroidLayout(); incorporateMaxLinesDuringAndroidLayout_ = flagValue; @@ -776,7 +794,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(41, "traceTurboModulePromiseRejectionsOnAndroid"); + markFlagAsAccessed(42, "traceTurboModulePromiseRejectionsOnAndroid"); flagValue = currentProvider_->traceTurboModulePromiseRejectionsOnAndroid(); traceTurboModulePromiseRejectionsOnAndroid_ = flagValue; @@ -794,7 +812,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(42, "updateRuntimeShadowNodeReferencesOnCommit"); + markFlagAsAccessed(43, "updateRuntimeShadowNodeReferencesOnCommit"); flagValue = currentProvider_->updateRuntimeShadowNodeReferencesOnCommit(); updateRuntimeShadowNodeReferencesOnCommit_ = flagValue; @@ -812,7 +830,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(43, "useAlwaysAvailableJSErrorHandling"); + markFlagAsAccessed(44, "useAlwaysAvailableJSErrorHandling"); flagValue = currentProvider_->useAlwaysAvailableJSErrorHandling(); useAlwaysAvailableJSErrorHandling_ = flagValue; @@ -830,7 +848,7 @@ bool ReactNativeFeatureFlagsAccessor::useAndroidTextLayoutWidthDirectly() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(44, "useAndroidTextLayoutWidthDirectly"); + markFlagAsAccessed(45, "useAndroidTextLayoutWidthDirectly"); flagValue = currentProvider_->useAndroidTextLayoutWidthDirectly(); useAndroidTextLayoutWidthDirectly_ = flagValue; @@ -848,7 +866,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(45, "useFabricInterop"); + markFlagAsAccessed(46, "useFabricInterop"); flagValue = currentProvider_->useFabricInterop(); useFabricInterop_ = flagValue; @@ -866,7 +884,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(46, "useNativeViewConfigsInBridgelessMode"); + markFlagAsAccessed(47, "useNativeViewConfigsInBridgelessMode"); flagValue = currentProvider_->useNativeViewConfigsInBridgelessMode(); useNativeViewConfigsInBridgelessMode_ = flagValue; @@ -884,7 +902,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(47, "useOptimizedEventBatchingOnAndroid"); + markFlagAsAccessed(48, "useOptimizedEventBatchingOnAndroid"); flagValue = currentProvider_->useOptimizedEventBatchingOnAndroid(); useOptimizedEventBatchingOnAndroid_ = flagValue; @@ -902,7 +920,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(48, "useRawPropsJsiValue"); + markFlagAsAccessed(49, "useRawPropsJsiValue"); flagValue = currentProvider_->useRawPropsJsiValue(); useRawPropsJsiValue_ = flagValue; @@ -920,7 +938,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(49, "useShadowNodeStateOnClone"); + markFlagAsAccessed(50, "useShadowNodeStateOnClone"); flagValue = currentProvider_->useShadowNodeStateOnClone(); useShadowNodeStateOnClone_ = flagValue; @@ -938,7 +956,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(50, "useTurboModuleInterop"); + markFlagAsAccessed(51, "useTurboModuleInterop"); flagValue = currentProvider_->useTurboModuleInterop(); useTurboModuleInterop_ = flagValue; @@ -956,7 +974,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(51, "useTurboModules"); + markFlagAsAccessed(52, "useTurboModules"); flagValue = currentProvider_->useTurboModules(); useTurboModules_ = flagValue; diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.h b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.h index 9d823b8eeb48..8a2cb730fb9d 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<<83db564764ee7cd15de7d0ed647ddde2>> + * @generated SignedSource<> */ /** @@ -51,6 +51,7 @@ class ReactNativeFeatureFlagsAccessor { bool enableFixForParentTagDuringReparenting(); bool enableFontScaleChangesUpdatingLayout(); bool enableIOSTextBaselineOffsetPerLine(); + bool enableGracefulUnregisteredComponentFailureAndroid(); bool enableIOSViewClipToPaddingBox(); bool enableIntersectionObserverEventLoopIntegration(); bool enableLayoutAnimationsOnAndroid(); @@ -95,7 +96,7 @@ class ReactNativeFeatureFlagsAccessor { std::unique_ptr currentProvider_; bool wasOverridden_; - std::array, 52> accessedFeatureFlags_; + std::array, 53> accessedFeatureFlags_; std::atomic> commonTestFlag_; std::atomic> animatedShouldSignalBatch_; @@ -116,6 +117,7 @@ class ReactNativeFeatureFlagsAccessor { std::atomic> enableFixForParentTagDuringReparenting_; std::atomic> enableFontScaleChangesUpdatingLayout_; std::atomic> enableIOSTextBaselineOffsetPerLine_; + std::atomic> enableGracefulUnregisteredComponentFailureAndroid_; std::atomic> enableIOSViewClipToPaddingBox_; std::atomic> enableIntersectionObserverEventLoopIntegration_; std::atomic> enableLayoutAnimationsOnAndroid_; diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDefaults.h b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDefaults.h index a06b7edec83c..76555fa83491 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<<3a3e4a601f680f792ebe624215e7f97f>> + * @generated SignedSource<<6ffc5cd7ee5a6fbfabf7f68e877c38e7>> */ /** @@ -103,6 +103,10 @@ class ReactNativeFeatureFlagsDefaults : public ReactNativeFeatureFlagsProvider { return false; } + bool enableGracefulUnregisteredComponentFailureAndroid() override { + return false; + } + bool enableIOSViewClipToPaddingBox() override { return false; } diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDynamicProvider.h b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDynamicProvider.h index 0633d1d177bc..fb1eb8c6df39 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<<440403abd700123f4edee6f49b1d9886>> + * @generated SignedSource<<6f13be05a9658c73ccfa951f6c95faf3>> */ /** @@ -216,6 +216,15 @@ class ReactNativeFeatureFlagsDynamicProvider : public ReactNativeFeatureFlagsDef return ReactNativeFeatureFlagsDefaults::enableIOSTextBaselineOffsetPerLine(); } + bool enableGracefulUnregisteredComponentFailureAndroid() override { + auto value = values_["enableGracefulUnregisteredComponentFailureAndroid"]; + if (!value.isNull()) { + return value.getBool(); + } + + return ReactNativeFeatureFlagsDefaults::enableGracefulUnregisteredComponentFailureAndroid(); + } + bool enableIOSViewClipToPaddingBox() override { auto value = values_["enableIOSViewClipToPaddingBox"]; if (!value.isNull()) { diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsProvider.h b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsProvider.h index 16dec1cc085c..16c73f0e6d67 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<> + * @generated SignedSource<> */ /** @@ -44,6 +44,7 @@ class ReactNativeFeatureFlagsProvider { virtual bool enableFixForParentTagDuringReparenting() = 0; virtual bool enableFontScaleChangesUpdatingLayout() = 0; virtual bool enableIOSTextBaselineOffsetPerLine() = 0; + virtual bool enableGracefulUnregisteredComponentFailureAndroid() = 0; virtual bool enableIOSViewClipToPaddingBox() = 0; virtual bool enableIntersectionObserverEventLoopIntegration() = 0; virtual bool enableLayoutAnimationsOnAndroid() = 0; diff --git a/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.cpp b/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.cpp index 2f1f4ccd522a..4412d9aa8fae 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<<48970fb0e5b9304cc54934791ba97d98>> + * @generated SignedSource<<50a76c49345d2f70c62f20c4a88deea6>> */ /** @@ -139,6 +139,11 @@ bool NativeReactNativeFeatureFlags::enableIOSTextBaselineOffsetPerLine( return ReactNativeFeatureFlags::enableIOSTextBaselineOffsetPerLine(); } +bool NativeReactNativeFeatureFlags::enableGracefulUnregisteredComponentFailureAndroid( + jsi::Runtime& /*runtime*/) { + return ReactNativeFeatureFlags::enableGracefulUnregisteredComponentFailureAndroid(); +} + bool NativeReactNativeFeatureFlags::enableIOSViewClipToPaddingBox( jsi::Runtime& /*runtime*/) { return ReactNativeFeatureFlags::enableIOSViewClipToPaddingBox(); diff --git a/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.h b/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.h index ee299f847beb..96d9d12f54b6 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<<2b1f3f390fee81f7add6a556ee6faf8b>> + * @generated SignedSource<> */ /** @@ -75,6 +75,8 @@ class NativeReactNativeFeatureFlags bool enableIOSTextBaselineOffsetPerLine(jsi::Runtime& runtime); + bool enableGracefulUnregisteredComponentFailureAndroid(jsi::Runtime& runtime); + bool enableIOSViewClipToPaddingBox(jsi::Runtime& runtime); bool enableIntersectionObserverEventLoopIntegration(jsi::Runtime& runtime); diff --git a/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js b/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js index 5d949559a246..085dac333518 100644 --- a/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js +++ b/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js @@ -243,6 +243,17 @@ const definitions: FeatureFlagDefinitions = { dateAdded: '2025-05-21', description: 'Applies base offset for each line of text separately on iOS.', + expectedReleaseValue: true, + purpose: 'experimentation', + }, + ossReleaseStage: 'none', + }, + enableGracefulUnregisteredComponentFailureAndroid: { + defaultValue: false, + metadata: { + dateAdded: '2025-05-07', + description: + 'Enables gracefuly failure when an unregistered component is rendered in Android.', expectedReleaseValue: true, purpose: 'experimentation', }, diff --git a/packages/react-native/src/private/featureflags/ReactNativeFeatureFlags.js b/packages/react-native/src/private/featureflags/ReactNativeFeatureFlags.js index 82a28ac6a742..26ff3f8e626d 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<<288c2d4e34ec2cf4597fd4e77f5198ca>> + * @generated SignedSource<<7ce4a155645f3ce28a0a90a83b59a074>> * @flow strict * @noformat */ @@ -69,6 +69,7 @@ export type ReactNativeFeatureFlags = $ReadOnly<{ enableFixForParentTagDuringReparenting: Getter, enableFontScaleChangesUpdatingLayout: Getter, enableIOSTextBaselineOffsetPerLine: Getter, + enableGracefulUnregisteredComponentFailureAndroid: Getter, enableIOSViewClipToPaddingBox: Getter, enableIntersectionObserverEventLoopIntegration: Getter, enableLayoutAnimationsOnAndroid: Getter, @@ -259,6 +260,10 @@ export const enableFontScaleChangesUpdatingLayout: Getter = createNativ * Applies base offset for each line of text separately on iOS. */ export const enableIOSTextBaselineOffsetPerLine: Getter = createNativeFlagGetter('enableIOSTextBaselineOffsetPerLine', false); +/** + * Enables gracefuly failure when an unregistered component is rendered in Android. + */ +export const enableGracefulUnregisteredComponentFailureAndroid: Getter = createNativeFlagGetter('enableGracefulUnregisteredComponentFailureAndroid', false); /** * iOS Views will clip to their padding box vs border box */ diff --git a/packages/react-native/src/private/featureflags/specs/NativeReactNativeFeatureFlags.js b/packages/react-native/src/private/featureflags/specs/NativeReactNativeFeatureFlags.js index a117b441c92a..08fd643c2f65 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<<273456c231b69ff9c24a532aaaa796eb>> + * @generated SignedSource<<5ca20c96f04d07175bd27904405cbd62>> * @flow strict * @noformat */ @@ -44,6 +44,7 @@ export interface Spec extends TurboModule { +enableFixForParentTagDuringReparenting?: () => boolean; +enableFontScaleChangesUpdatingLayout?: () => boolean; +enableIOSTextBaselineOffsetPerLine?: () => boolean; + +enableGracefulUnregisteredComponentFailureAndroid?: () => boolean; +enableIOSViewClipToPaddingBox?: () => boolean; +enableIntersectionObserverEventLoopIntegration?: () => boolean; +enableLayoutAnimationsOnAndroid?: () => boolean;