From cb0d9638e6dbb2c1044a2be01db20243681174a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hanno=20J=2E=20G=C3=B6decke?= Date: Wed, 15 Jul 2026 08:57:38 +0200 Subject: [PATCH 1/6] feat(ios): add configurable legacy shadow paths --- .../Components/View/ViewPropTypes.d.ts | 20 ++ .../Components/View/ViewPropTypes.js | 20 ++ .../NativeComponent/BaseViewConfig.ios.js | 1 + .../View/RCTViewComponentView.mm | 49 +++- .../react-native/React/Views/RCTViewManager.m | 7 + .../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 | 112 ++++--- .../ReactNativeFeatureFlagsAccessor.h | 6 +- .../ReactNativeFeatureFlagsDefaults.h | 6 +- .../ReactNativeFeatureFlagsDynamicProvider.h | 11 +- .../ReactNativeFeatureFlagsProvider.h | 3 +- .../NativeReactNativeFeatureFlags.cpp | 7 +- .../NativeReactNativeFeatureFlags.h | 4 +- .../components/view/BaseViewProps.cpp | 10 + .../renderer/components/view/BaseViewProps.h | 1 + .../components/view/ViewShadowNode.cpp | 1 + .../renderer/components/view/conversions.h | 28 ++ .../renderer/components/view/primitives.h | 21 ++ .../components/view/tests/ConversionsTest.cpp | 32 ++ .../ReactNativeFeatureFlags.config.js | 11 + .../featureflags/ReactNativeFeatureFlags.js | 7 +- .../specs/NativeReactNativeFeatureFlags.js | 3 +- .../examples/Playground/PlaygroundExample.js | 6 +- .../RNTesterShadowPathPlayground.js | 276 ++++++++++++++++++ 33 files changed, 641 insertions(+), 80 deletions(-) create mode 100644 packages/rn-tester/js/examples/Playground/RNTesterShadowPathPlayground.js diff --git a/packages/react-native/Libraries/Components/View/ViewPropTypes.d.ts b/packages/react-native/Libraries/Components/View/ViewPropTypes.d.ts index 62803cabce7e..ae7bc6751c9e 100644 --- a/packages/react-native/Libraries/Components/View/ViewPropTypes.d.ts +++ b/packages/react-native/Libraries/Components/View/ViewPropTypes.d.ts @@ -71,6 +71,26 @@ export interface TVViewPropsIOS { } export interface ViewPropsIOS extends TVViewPropsIOS { + /** + * Controls how iOS determines the shape used by the legacy `shadow*` + * properties. This does not affect `boxShadow`. + * + * - `auto`: Uses an optimized border-box path when the view has an opaque + * background. Otherwise derives the shadow from the composited content alpha. + * - `border-box`: Always uses the view's bounds and resolved border radii. + * This avoids content-alpha offscreen rendering, but callers must guarantee + * that the intended shadow silhouette matches the view's border box. + * - `content-alpha`: Always derives the shadow from the rendered alpha of the + * view and its children. This supports irregular silhouettes but may require + * offscreen rendering. + * + * When omitted, the default is controlled by a React Native feature flag. + * The initial default remains `auto`. + * + * @platform ios + */ + shadowPathIOS?: 'auto' | 'border-box' | 'content-alpha' | undefined; + /** * Whether this view should be rendered as a bitmap before compositing. * diff --git a/packages/react-native/Libraries/Components/View/ViewPropTypes.js b/packages/react-native/Libraries/Components/View/ViewPropTypes.js index 36cb09adf1a5..3ab031fad93a 100644 --- a/packages/react-native/Libraries/Components/View/ViewPropTypes.js +++ b/packages/react-native/Libraries/Components/View/ViewPropTypes.js @@ -394,6 +394,26 @@ export type TVViewPropsIOS = $ReadOnly<{ }>; export type ViewPropsIOS = $ReadOnly<{ + /** + * Controls how iOS determines the shape used by the legacy `shadow*` + * properties. This does not affect `boxShadow`. + * + * - `auto`: Uses an optimized border-box path when the view has an opaque + * background. Otherwise derives the shadow from the composited content alpha. + * - `border-box`: Always uses the view's bounds and resolved border radii. + * This avoids content-alpha offscreen rendering, but callers must guarantee + * that the intended shadow silhouette matches the view's border box. + * - `content-alpha`: Always derives the shadow from the rendered alpha of the + * view and its children. This supports irregular silhouettes but may require + * offscreen rendering. + * + * When omitted, the default is controlled by a React Native feature flag. + * The initial default remains `auto`. + * + * @platform ios + */ + shadowPathIOS?: ?('auto' | 'border-box' | 'content-alpha'), + /** * Whether this `View` should be rendered as a bitmap before compositing. * diff --git a/packages/react-native/Libraries/NativeComponent/BaseViewConfig.ios.js b/packages/react-native/Libraries/NativeComponent/BaseViewConfig.ios.js index 5c96ddeabeff..4e7438b12e52 100644 --- a/packages/react-native/Libraries/NativeComponent/BaseViewConfig.ios.js +++ b/packages/react-native/Libraries/NativeComponent/BaseViewConfig.ios.js @@ -206,6 +206,7 @@ const validAttributesForNonEventProps = { shadowColor: {process: require('../StyleSheet/processColor').default}, shadowOffset: {diff: require('../Utilities/differ/sizesDiffer').default}, shadowOpacity: true, + shadowPathIOS: true, shadowRadius: true, needsOffscreenAlphaCompositing: true, overflow: true, diff --git a/packages/react-native/React/Fabric/Mounting/ComponentViews/View/RCTViewComponentView.mm b/packages/react-native/React/Fabric/Mounting/ComponentViews/View/RCTViewComponentView.mm index 48f497bce262..62eff72135af 100644 --- a/packages/react-native/React/Fabric/Mounting/ComponentViews/View/RCTViewComponentView.mm +++ b/packages/react-native/React/Fabric/Mounting/ComponentViews/View/RCTViewComponentView.mm @@ -22,6 +22,7 @@ #import #import #import +#import #import #import #import @@ -272,6 +273,11 @@ - (void)updateProps:(const Props::Shared &)props oldProps:(const Props::Shared & needsInvalidateLayer = YES; } + // `shadowPathIOS` + if (oldViewProps.shadowPathIOS != newViewProps.shadowPathIOS) { + needsInvalidateLayer = YES; + } + // `shadowRadius` if (oldViewProps.shadowRadius != newViewProps.shadowRadius) { self.layer.shadowRadius = (CGFloat)newViewProps.shadowRadius; @@ -829,20 +835,37 @@ - (void)invalidateLayer // Stage 1. Shadow Path BOOL const layerHasShadow = layer.shadowOpacity > 0 && CGColorGetAlpha(layer.shadowColor) > 0; - if (layerHasShadow) { - if (CGColorGetAlpha(_backgroundColor.CGColor) > 0.999) { - // If view has a solid background color, calculate shadow path from border. - const RCTCornerInsets cornerInsets = - RCTGetCornerInsets(RCTCornerRadiiFromBorderRadii(borderMetrics.borderRadii), UIEdgeInsetsZero); - CGPathRef shadowPath = RCTPathCreateWithRoundedRect(self.bounds, cornerInsets, nil, NO); - layer.shadowPath = shadowPath; - CGPathRelease(shadowPath); - } else { - // Can't accurately calculate box shadow, so fall back to pixel-based shadow. - layer.shadowPath = nil; - } - } else { + if (!layerHasShadow) { layer.shadowPath = nil; + } else { + bool borderBoxDefaultEnabled = ReactNativeFeatureFlags::enableIOSBorderBoxShadowPathByDefault(); + ShadowPathMode shadowPathMode = resolveShadowPathMode(_props->shadowPathIOS, borderBoxDefaultEnabled); + RCTCornerRadii cornerRadii = RCTCornerRadiiFromBorderRadii(borderMetrics.borderRadii); + const RCTCornerInsets cornerInsets = RCTGetCornerInsets(cornerRadii, UIEdgeInsetsZero); + + switch (shadowPathMode) { + case ShadowPathMode::BorderBox: { + CGPathRef shadowPath = RCTPathCreateWithRoundedRect(self.bounds, cornerInsets, nil, NO); + layer.shadowPath = shadowPath; + CGPathRelease(shadowPath); + break; + } + case ShadowPathMode::ContentAlpha: + layer.shadowPath = nil; + break; + case ShadowPathMode::Auto: + if (CGColorGetAlpha(_backgroundColor.CGColor) > 0.999) { + CGPathRef shadowPath = RCTPathCreateWithRoundedRect(self.bounds, cornerInsets, nil, NO); + layer.shadowPath = shadowPath; + CGPathRelease(shadowPath); + } else { + layer.shadowPath = nil; + } + break; + case ShadowPathMode::Default: + react_native_assert(false); + break; + } } #if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 170000 /* __IPHONE_17_0 */ diff --git a/packages/react-native/React/Views/RCTViewManager.m b/packages/react-native/React/Views/RCTViewManager.m index f017cb87caae..6094303e0e23 100644 --- a/packages/react-native/React/Views/RCTViewManager.m +++ b/packages/react-native/React/Views/RCTViewManager.m @@ -439,6 +439,13 @@ - (void)updateAccessibilityTraitsForRole:(RCTView *)view withDefaultView:(RCTVie // filtered by view configs. } +RCT_CUSTOM_VIEW_PROPERTY(shadowPathIOS, NSString *, RCTView) +{ + // Property is only to be used in the new renderer. + // It is necessary to add it here, otherwise it gets + // filtered by view configs. +} + RCT_CUSTOM_VIEW_PROPERTY(mixBlendMode, NSString *, RCTView) { // Property is only to be used in the new renderer. 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 18b18c8bffc6..be58190699b3 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<<2943f7c7f969e25c549ee6bfeff650c6>> */ /** @@ -150,6 +150,12 @@ public object ReactNativeFeatureFlags { @JvmStatic public fun enableFontScaleChangesUpdatingLayout(): Boolean = accessor.enableFontScaleChangesUpdatingLayout() + /** + * Use the border box for legacy iOS shadow paths when shadowPathIOS is omitted. + */ + @JvmStatic + public fun enableIOSBorderBoxShadowPathByDefault(): Boolean = accessor.enableIOSBorderBoxShadowPathByDefault() + /** * Fixes #56595 by moving the autoFocus from didMoveToWindow to viewDidAppear */ 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 c814df9eab66..d167c594bb67 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<> */ /** @@ -40,6 +40,7 @@ internal class ReactNativeFeatureFlagsCxxAccessor : ReactNativeFeatureFlagsAcces private var enableFabricRendererCache: Boolean? = null private var enableFixForParentTagDuringReparentingCache: Boolean? = null private var enableFontScaleChangesUpdatingLayoutCache: Boolean? = null + private var enableIOSBorderBoxShadowPathByDefaultCache: Boolean? = null private var enableIOSExperimentalAutoFocusImplementationCache: Boolean? = null private var enableIOSTextBaselineOffsetPerLineCache: Boolean? = null private var enableIOSViewClipToPaddingBoxCache: Boolean? = null @@ -267,6 +268,15 @@ internal class ReactNativeFeatureFlagsCxxAccessor : ReactNativeFeatureFlagsAcces return cached } + override fun enableIOSBorderBoxShadowPathByDefault(): Boolean { + var cached = enableIOSBorderBoxShadowPathByDefaultCache + if (cached == null) { + cached = ReactNativeFeatureFlagsCxxInterop.enableIOSBorderBoxShadowPathByDefault() + enableIOSBorderBoxShadowPathByDefaultCache = cached + } + return cached + } + override fun enableIOSExperimentalAutoFocusImplementation(): Boolean { var cached = enableIOSExperimentalAutoFocusImplementationCache 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 abdef81d36f0..0cb215c7fdd4 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<<1234d5c7c6bb64451618817387318d13>> + * @generated SignedSource<<42800c900774644d53216aa2c7dd624c>> */ /** @@ -68,6 +68,8 @@ public object ReactNativeFeatureFlagsCxxInterop { @DoNotStrip @JvmStatic public external fun enableFontScaleChangesUpdatingLayout(): Boolean + @DoNotStrip @JvmStatic public external fun enableIOSBorderBoxShadowPathByDefault(): Boolean + @DoNotStrip @JvmStatic public external fun enableIOSExperimentalAutoFocusImplementation(): Boolean @DoNotStrip @JvmStatic public external fun enableIOSTextBaselineOffsetPerLine(): 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 12c9c2d2a564..a9bc337db331 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<<3255db6cb7c71d866991568dc632321f>> + * @generated SignedSource<<84c5a4338deb791e07275d340c3cd419>> */ /** @@ -63,6 +63,8 @@ public open class ReactNativeFeatureFlagsDefaults : ReactNativeFeatureFlagsProvi override fun enableFontScaleChangesUpdatingLayout(): Boolean = false + override fun enableIOSBorderBoxShadowPathByDefault(): Boolean = false + override fun enableIOSExperimentalAutoFocusImplementation(): Boolean = false override fun enableIOSTextBaselineOffsetPerLine(): 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 6058c9cd3dfd..175b9500311c 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<<6f23f8fd399adf79814bb8f2edc29841>> + * @generated SignedSource<<402131230f486c9bee0e5d877419280b>> */ /** @@ -44,6 +44,7 @@ internal class ReactNativeFeatureFlagsLocalAccessor : ReactNativeFeatureFlagsAcc private var enableFabricRendererCache: Boolean? = null private var enableFixForParentTagDuringReparentingCache: Boolean? = null private var enableFontScaleChangesUpdatingLayoutCache: Boolean? = null + private var enableIOSBorderBoxShadowPathByDefaultCache: Boolean? = null private var enableIOSExperimentalAutoFocusImplementationCache: Boolean? = null private var enableIOSTextBaselineOffsetPerLineCache: Boolean? = null private var enableIOSViewClipToPaddingBoxCache: Boolean? = null @@ -291,6 +292,16 @@ internal class ReactNativeFeatureFlagsLocalAccessor : ReactNativeFeatureFlagsAcc return cached } + override fun enableIOSBorderBoxShadowPathByDefault(): Boolean { + var cached = enableIOSBorderBoxShadowPathByDefaultCache + if (cached == null) { + cached = currentProvider.enableIOSBorderBoxShadowPathByDefault() + accessedFeatureFlags.add("enableIOSBorderBoxShadowPathByDefault") + enableIOSBorderBoxShadowPathByDefaultCache = cached + } + return cached + } + override fun enableIOSExperimentalAutoFocusImplementation(): Boolean { var cached = enableIOSExperimentalAutoFocusImplementationCache 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 35726811606f..b08ab0f30299 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<<2877a4acf03951884d1d663fdf00b9f8>> + * @generated SignedSource<<752961866ff51cfccd11f8c4314bc920>> */ /** @@ -63,6 +63,8 @@ public interface ReactNativeFeatureFlagsProvider { @DoNotStrip public fun enableFontScaleChangesUpdatingLayout(): Boolean + @DoNotStrip public fun enableIOSBorderBoxShadowPathByDefault(): Boolean + @DoNotStrip public fun enableIOSExperimentalAutoFocusImplementation(): Boolean @DoNotStrip public fun enableIOSTextBaselineOffsetPerLine(): 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 479d7f71c528..df1e51ffae9f 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<<5c4c763d4b19e17b107c87f772decaa8>> */ /** @@ -159,6 +159,12 @@ class ReactNativeFeatureFlagsJavaProvider return method(javaProvider_); } + bool enableIOSBorderBoxShadowPathByDefault() override { + static const auto method = + getReactNativeFeatureFlagsProviderJavaClass()->getMethod("enableIOSBorderBoxShadowPathByDefault"); + return method(javaProvider_); + } + bool enableIOSExperimentalAutoFocusImplementation() override { static const auto method = getReactNativeFeatureFlagsProviderJavaClass()->getMethod("enableIOSExperimentalAutoFocusImplementation"); @@ -539,6 +545,11 @@ bool JReactNativeFeatureFlagsCxxInterop::enableFontScaleChangesUpdatingLayout( return ReactNativeFeatureFlags::enableFontScaleChangesUpdatingLayout(); } +bool JReactNativeFeatureFlagsCxxInterop::enableIOSBorderBoxShadowPathByDefault( + facebook::jni::alias_ref /*unused*/) { + return ReactNativeFeatureFlags::enableIOSBorderBoxShadowPathByDefault(); +} + bool JReactNativeFeatureFlagsCxxInterop::enableIOSExperimentalAutoFocusImplementation( facebook::jni::alias_ref /*unused*/) { return ReactNativeFeatureFlags::enableIOSExperimentalAutoFocusImplementation(); @@ -860,6 +871,9 @@ void JReactNativeFeatureFlagsCxxInterop::registerNatives() { makeNativeMethod( "enableFontScaleChangesUpdatingLayout", JReactNativeFeatureFlagsCxxInterop::enableFontScaleChangesUpdatingLayout), + makeNativeMethod( + "enableIOSBorderBoxShadowPathByDefault", + JReactNativeFeatureFlagsCxxInterop::enableIOSBorderBoxShadowPathByDefault), makeNativeMethod( "enableIOSExperimentalAutoFocusImplementation", JReactNativeFeatureFlagsCxxInterop::enableIOSExperimentalAutoFocusImplementation), 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 10457edc1088..483fac726126 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<> */ /** @@ -90,6 +90,9 @@ class JReactNativeFeatureFlagsCxxInterop static bool enableFontScaleChangesUpdatingLayout( facebook::jni::alias_ref); + static bool enableIOSBorderBoxShadowPathByDefault( + facebook::jni::alias_ref); + static bool enableIOSExperimentalAutoFocusImplementation( 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 123729aac840..caad94b541bb 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<> + * @generated SignedSource<<0fb2c41e81fff95999861a345052d99f>> */ /** @@ -106,6 +106,10 @@ bool ReactNativeFeatureFlags::enableFontScaleChangesUpdatingLayout() { return getAccessor().enableFontScaleChangesUpdatingLayout(); } +bool ReactNativeFeatureFlags::enableIOSBorderBoxShadowPathByDefault() { + return getAccessor().enableIOSBorderBoxShadowPathByDefault(); +} + bool ReactNativeFeatureFlags::enableIOSExperimentalAutoFocusImplementation() { return getAccessor().enableIOSExperimentalAutoFocusImplementation(); } diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.h b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.h index 63855748befe..3ca0da4039ec 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<<8dc801da5ad5fd085dcedb245f7dee08>> + * @generated SignedSource<> */ /** @@ -139,6 +139,11 @@ class ReactNativeFeatureFlags { */ RN_EXPORT static bool enableFontScaleChangesUpdatingLayout(); + /** + * Use the border box for legacy iOS shadow paths when shadowPathIOS is omitted. + */ + RN_EXPORT static bool enableIOSBorderBoxShadowPathByDefault(); + /** * Fixes #56595 by moving the autoFocus from didMoveToWindow to viewDidAppear */ diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.cpp b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.cpp index 6e90d149e633..3f879a4cf3d5 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<<10aa043b91c4189bd7772066085e168d>> + * @generated SignedSource<<676605110414d92058485a2f290d532b>> */ /** @@ -389,6 +389,24 @@ bool ReactNativeFeatureFlagsAccessor::enableFontScaleChangesUpdatingLayout() { return flagValue.value(); } +bool ReactNativeFeatureFlagsAccessor::enableIOSBorderBoxShadowPathByDefault() { + auto flagValue = enableIOSBorderBoxShadowPathByDefault_.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(20, "enableIOSBorderBoxShadowPathByDefault"); + + flagValue = currentProvider_->enableIOSBorderBoxShadowPathByDefault(); + enableIOSBorderBoxShadowPathByDefault_ = flagValue; + } + + return flagValue.value(); +} + bool ReactNativeFeatureFlagsAccessor::enableIOSExperimentalAutoFocusImplementation() { auto flagValue = enableIOSExperimentalAutoFocusImplementation_.load(); @@ -398,7 +416,7 @@ bool ReactNativeFeatureFlagsAccessor::enableIOSExperimentalAutoFocusImplementati // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(20, "enableIOSExperimentalAutoFocusImplementation"); + markFlagAsAccessed(21, "enableIOSExperimentalAutoFocusImplementation"); flagValue = currentProvider_->enableIOSExperimentalAutoFocusImplementation(); enableIOSExperimentalAutoFocusImplementation_ = flagValue; @@ -416,7 +434,7 @@ bool ReactNativeFeatureFlagsAccessor::enableIOSTextBaselineOffsetPerLine() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(21, "enableIOSTextBaselineOffsetPerLine"); + markFlagAsAccessed(22, "enableIOSTextBaselineOffsetPerLine"); flagValue = currentProvider_->enableIOSTextBaselineOffsetPerLine(); enableIOSTextBaselineOffsetPerLine_ = flagValue; @@ -434,7 +452,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(22, "enableIOSViewClipToPaddingBox"); + markFlagAsAccessed(23, "enableIOSViewClipToPaddingBox"); flagValue = currentProvider_->enableIOSViewClipToPaddingBox(); enableIOSViewClipToPaddingBox_ = flagValue; @@ -452,7 +470,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(23, "enableInteropViewManagerClassLookUpOptimizationIOS"); + markFlagAsAccessed(24, "enableInteropViewManagerClassLookUpOptimizationIOS"); flagValue = currentProvider_->enableInteropViewManagerClassLookUpOptimizationIOS(); enableInteropViewManagerClassLookUpOptimizationIOS_ = flagValue; @@ -470,7 +488,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(24, "enableLayoutAnimationsOnAndroid"); + markFlagAsAccessed(25, "enableLayoutAnimationsOnAndroid"); flagValue = currentProvider_->enableLayoutAnimationsOnAndroid(); enableLayoutAnimationsOnAndroid_ = flagValue; @@ -488,7 +506,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(25, "enableLayoutAnimationsOnIOS"); + markFlagAsAccessed(26, "enableLayoutAnimationsOnIOS"); flagValue = currentProvider_->enableLayoutAnimationsOnIOS(); enableLayoutAnimationsOnIOS_ = flagValue; @@ -506,7 +524,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(26, "enableMainQueueCoordinatorOnIOS"); + markFlagAsAccessed(27, "enableMainQueueCoordinatorOnIOS"); flagValue = currentProvider_->enableMainQueueCoordinatorOnIOS(); enableMainQueueCoordinatorOnIOS_ = flagValue; @@ -524,7 +542,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(27, "enableMainQueueModulesOnIOS"); + markFlagAsAccessed(28, "enableMainQueueModulesOnIOS"); flagValue = currentProvider_->enableMainQueueModulesOnIOS(); enableMainQueueModulesOnIOS_ = flagValue; @@ -542,7 +560,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(28, "enableModuleArgumentNSNullConversionIOS"); + markFlagAsAccessed(29, "enableModuleArgumentNSNullConversionIOS"); flagValue = currentProvider_->enableModuleArgumentNSNullConversionIOS(); enableModuleArgumentNSNullConversionIOS_ = flagValue; @@ -560,7 +578,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(29, "enableNativeCSSParsing"); + markFlagAsAccessed(30, "enableNativeCSSParsing"); flagValue = currentProvider_->enableNativeCSSParsing(); enableNativeCSSParsing_ = flagValue; @@ -578,7 +596,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(30, "enableNetworkEventReporting"); + markFlagAsAccessed(31, "enableNetworkEventReporting"); flagValue = currentProvider_->enableNetworkEventReporting(); enableNetworkEventReporting_ = flagValue; @@ -596,7 +614,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(31, "enableNewBackgroundAndBorderDrawables"); + markFlagAsAccessed(32, "enableNewBackgroundAndBorderDrawables"); flagValue = currentProvider_->enableNewBackgroundAndBorderDrawables(); enableNewBackgroundAndBorderDrawables_ = flagValue; @@ -614,7 +632,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(32, "enablePreparedTextLayout"); + markFlagAsAccessed(33, "enablePreparedTextLayout"); flagValue = currentProvider_->enablePreparedTextLayout(); enablePreparedTextLayout_ = flagValue; @@ -632,7 +650,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(33, "enablePropsUpdateReconciliationAndroid"); + markFlagAsAccessed(34, "enablePropsUpdateReconciliationAndroid"); flagValue = currentProvider_->enablePropsUpdateReconciliationAndroid(); enablePropsUpdateReconciliationAndroid_ = flagValue; @@ -650,7 +668,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(34, "enableResourceTimingAPI"); + markFlagAsAccessed(35, "enableResourceTimingAPI"); flagValue = currentProvider_->enableResourceTimingAPI(); enableResourceTimingAPI_ = flagValue; @@ -668,7 +686,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(35, "enableSynchronousStateUpdates"); + markFlagAsAccessed(36, "enableSynchronousStateUpdates"); flagValue = currentProvider_->enableSynchronousStateUpdates(); enableSynchronousStateUpdates_ = flagValue; @@ -686,7 +704,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(36, "enableViewCulling"); + markFlagAsAccessed(37, "enableViewCulling"); flagValue = currentProvider_->enableViewCulling(); enableViewCulling_ = flagValue; @@ -704,7 +722,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(37, "enableViewRecycling"); + markFlagAsAccessed(38, "enableViewRecycling"); flagValue = currentProvider_->enableViewRecycling(); enableViewRecycling_ = flagValue; @@ -722,7 +740,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(38, "enableViewRecyclingForText"); + markFlagAsAccessed(39, "enableViewRecyclingForText"); flagValue = currentProvider_->enableViewRecyclingForText(); enableViewRecyclingForText_ = flagValue; @@ -740,7 +758,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(39, "enableViewRecyclingForView"); + markFlagAsAccessed(40, "enableViewRecyclingForView"); flagValue = currentProvider_->enableViewRecyclingForView(); enableViewRecyclingForView_ = flagValue; @@ -758,7 +776,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(40, "enableVirtualViewDebugFeatures"); + markFlagAsAccessed(41, "enableVirtualViewDebugFeatures"); flagValue = currentProvider_->enableVirtualViewDebugFeatures(); enableVirtualViewDebugFeatures_ = flagValue; @@ -776,7 +794,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(41, "enableVirtualViewRenderState"); + markFlagAsAccessed(42, "enableVirtualViewRenderState"); flagValue = currentProvider_->enableVirtualViewRenderState(); enableVirtualViewRenderState_ = flagValue; @@ -794,7 +812,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(42, "enableVirtualViewWindowFocusDetection"); + markFlagAsAccessed(43, "enableVirtualViewWindowFocusDetection"); flagValue = currentProvider_->enableVirtualViewWindowFocusDetection(); enableVirtualViewWindowFocusDetection_ = flagValue; @@ -812,7 +830,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(43, "fixMappingOfEventPrioritiesBetweenFabricAndReact"); + markFlagAsAccessed(44, "fixMappingOfEventPrioritiesBetweenFabricAndReact"); flagValue = currentProvider_->fixMappingOfEventPrioritiesBetweenFabricAndReact(); fixMappingOfEventPrioritiesBetweenFabricAndReact_ = flagValue; @@ -830,7 +848,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(44, "fuseboxEnabledRelease"); + markFlagAsAccessed(45, "fuseboxEnabledRelease"); flagValue = currentProvider_->fuseboxEnabledRelease(); fuseboxEnabledRelease_ = flagValue; @@ -848,7 +866,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(45, "fuseboxNetworkInspectionEnabled"); + markFlagAsAccessed(46, "fuseboxNetworkInspectionEnabled"); flagValue = currentProvider_->fuseboxNetworkInspectionEnabled(); fuseboxNetworkInspectionEnabled_ = flagValue; @@ -866,7 +884,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(46, "hideOffscreenVirtualViewsOnIOS"); + markFlagAsAccessed(47, "hideOffscreenVirtualViewsOnIOS"); flagValue = currentProvider_->hideOffscreenVirtualViewsOnIOS(); hideOffscreenVirtualViewsOnIOS_ = flagValue; @@ -884,7 +902,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(47, "preparedTextCacheSize"); + markFlagAsAccessed(48, "preparedTextCacheSize"); flagValue = currentProvider_->preparedTextCacheSize(); preparedTextCacheSize_ = flagValue; @@ -902,7 +920,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(48, "preventShadowTreeCommitExhaustion"); + markFlagAsAccessed(49, "preventShadowTreeCommitExhaustion"); flagValue = currentProvider_->preventShadowTreeCommitExhaustion(); preventShadowTreeCommitExhaustion_ = flagValue; @@ -920,7 +938,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(49, "traceTurboModulePromiseRejectionsOnAndroid"); + markFlagAsAccessed(50, "traceTurboModulePromiseRejectionsOnAndroid"); flagValue = currentProvider_->traceTurboModulePromiseRejectionsOnAndroid(); traceTurboModulePromiseRejectionsOnAndroid_ = flagValue; @@ -938,7 +956,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(50, "updateRuntimeShadowNodeReferencesOnCommit"); + markFlagAsAccessed(51, "updateRuntimeShadowNodeReferencesOnCommit"); flagValue = currentProvider_->updateRuntimeShadowNodeReferencesOnCommit(); updateRuntimeShadowNodeReferencesOnCommit_ = flagValue; @@ -956,7 +974,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(51, "useAlwaysAvailableJSErrorHandling"); + markFlagAsAccessed(52, "useAlwaysAvailableJSErrorHandling"); flagValue = currentProvider_->useAlwaysAvailableJSErrorHandling(); useAlwaysAvailableJSErrorHandling_ = flagValue; @@ -974,7 +992,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(52, "useFabricInterop"); + markFlagAsAccessed(53, "useFabricInterop"); flagValue = currentProvider_->useFabricInterop(); useFabricInterop_ = flagValue; @@ -992,7 +1010,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(53, "useNativeEqualsInNativeReadableArrayAndroid"); + markFlagAsAccessed(54, "useNativeEqualsInNativeReadableArrayAndroid"); flagValue = currentProvider_->useNativeEqualsInNativeReadableArrayAndroid(); useNativeEqualsInNativeReadableArrayAndroid_ = flagValue; @@ -1010,7 +1028,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(54, "useNativeTransformHelperAndroid"); + markFlagAsAccessed(55, "useNativeTransformHelperAndroid"); flagValue = currentProvider_->useNativeTransformHelperAndroid(); useNativeTransformHelperAndroid_ = flagValue; @@ -1028,7 +1046,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(55, "useNativeViewConfigsInBridgelessMode"); + markFlagAsAccessed(56, "useNativeViewConfigsInBridgelessMode"); flagValue = currentProvider_->useNativeViewConfigsInBridgelessMode(); useNativeViewConfigsInBridgelessMode_ = flagValue; @@ -1046,7 +1064,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(56, "useOptimizedEventBatchingOnAndroid"); + markFlagAsAccessed(57, "useOptimizedEventBatchingOnAndroid"); flagValue = currentProvider_->useOptimizedEventBatchingOnAndroid(); useOptimizedEventBatchingOnAndroid_ = flagValue; @@ -1064,7 +1082,7 @@ bool ReactNativeFeatureFlagsAccessor::usePullModelOnAndroid() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(57, "usePullModelOnAndroid"); + markFlagAsAccessed(58, "usePullModelOnAndroid"); flagValue = currentProvider_->usePullModelOnAndroid(); usePullModelOnAndroid_ = flagValue; @@ -1082,7 +1100,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(58, "useRawPropsJsiValue"); + markFlagAsAccessed(59, "useRawPropsJsiValue"); flagValue = currentProvider_->useRawPropsJsiValue(); useRawPropsJsiValue_ = flagValue; @@ -1100,7 +1118,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(59, "useShadowNodeStateOnClone"); + markFlagAsAccessed(60, "useShadowNodeStateOnClone"); flagValue = currentProvider_->useShadowNodeStateOnClone(); useShadowNodeStateOnClone_ = flagValue; @@ -1118,7 +1136,7 @@ bool ReactNativeFeatureFlagsAccessor::useSilenceErrorSMMViewNotFound() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(60, "useSilenceErrorSMMViewNotFound"); + markFlagAsAccessed(61, "useSilenceErrorSMMViewNotFound"); flagValue = currentProvider_->useSilenceErrorSMMViewNotFound(); useSilenceErrorSMMViewNotFound_ = flagValue; @@ -1136,7 +1154,7 @@ bool ReactNativeFeatureFlagsAccessor::useTraitHiddenOnIOS() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(61, "useTraitHiddenOnIOS"); + markFlagAsAccessed(62, "useTraitHiddenOnIOS"); flagValue = currentProvider_->useTraitHiddenOnIOS(); useTraitHiddenOnIOS_ = flagValue; @@ -1154,7 +1172,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(62, "useTurboModuleInterop"); + markFlagAsAccessed(63, "useTurboModuleInterop"); flagValue = currentProvider_->useTurboModuleInterop(); useTurboModuleInterop_ = flagValue; @@ -1172,7 +1190,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(63, "useTurboModules"); + markFlagAsAccessed(64, "useTurboModules"); flagValue = currentProvider_->useTurboModules(); useTurboModules_ = flagValue; @@ -1190,7 +1208,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(64, "virtualViewPrerenderRatio"); + markFlagAsAccessed(65, "virtualViewPrerenderRatio"); flagValue = currentProvider_->virtualViewPrerenderRatio(); virtualViewPrerenderRatio_ = flagValue; @@ -1208,7 +1226,7 @@ bool ReactNativeFeatureFlagsAccessor::runtimeCrashUiThreadUtils() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(65, "runtimeCrashUiThreadUtils"); + markFlagAsAccessed(66, "runtimeCrashUiThreadUtils"); flagValue = currentProvider_->runtimeCrashUiThreadUtils(); runtimeCrashUiThreadUtils_ = flagValue; diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.h b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.h index 860aea749315..aef569c64182 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<<98d2d767bf370439a9efc44a745ae802>> + * @generated SignedSource<> */ /** @@ -52,6 +52,7 @@ class ReactNativeFeatureFlagsAccessor { bool enableFabricRenderer(); bool enableFixForParentTagDuringReparenting(); bool enableFontScaleChangesUpdatingLayout(); + bool enableIOSBorderBoxShadowPathByDefault(); bool enableIOSExperimentalAutoFocusImplementation(); bool enableIOSTextBaselineOffsetPerLine(); bool enableIOSViewClipToPaddingBox(); @@ -109,7 +110,7 @@ class ReactNativeFeatureFlagsAccessor { std::unique_ptr currentProvider_; bool wasOverridden_; - std::array, 66> accessedFeatureFlags_; + std::array, 67> accessedFeatureFlags_; std::atomic> commonTestFlag_; std::atomic> animatedShouldSignalBatch_; @@ -131,6 +132,7 @@ class ReactNativeFeatureFlagsAccessor { std::atomic> enableFabricRenderer_; std::atomic> enableFixForParentTagDuringReparenting_; std::atomic> enableFontScaleChangesUpdatingLayout_; + std::atomic> enableIOSBorderBoxShadowPathByDefault_; std::atomic> enableIOSExperimentalAutoFocusImplementation_; std::atomic> enableIOSTextBaselineOffsetPerLine_; std::atomic> enableIOSViewClipToPaddingBox_; diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDefaults.h b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDefaults.h index 873efaebfcb2..f72e13144897 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<<5cb395cf45a846c4e93b4eb5ca2d99e3>> + * @generated SignedSource<> */ /** @@ -107,6 +107,10 @@ class ReactNativeFeatureFlagsDefaults : public ReactNativeFeatureFlagsProvider { return false; } + bool enableIOSBorderBoxShadowPathByDefault() override { + return false; + } + bool enableIOSExperimentalAutoFocusImplementation() override { return false; } diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDynamicProvider.h b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDynamicProvider.h index 9566d80657b3..3f950c1e4956 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<<627e0e191716b63f3a5dd7fd343506b9>> + * @generated SignedSource<<8a75aa937ab50bb2ea4e920c046e2d1e>> */ /** @@ -225,6 +225,15 @@ class ReactNativeFeatureFlagsDynamicProvider : public ReactNativeFeatureFlagsDef return ReactNativeFeatureFlagsDefaults::enableFontScaleChangesUpdatingLayout(); } + bool enableIOSBorderBoxShadowPathByDefault() override { + auto value = values_["enableIOSBorderBoxShadowPathByDefault"]; + if (!value.isNull()) { + return value.getBool(); + } + + return ReactNativeFeatureFlagsDefaults::enableIOSBorderBoxShadowPathByDefault(); + } + bool enableIOSExperimentalAutoFocusImplementation() override { auto value = values_["enableIOSExperimentalAutoFocusImplementation"]; if (!value.isNull()) { diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsProvider.h b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsProvider.h index 8c6d60c9646c..657ea5aa42e5 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<> */ /** @@ -45,6 +45,7 @@ class ReactNativeFeatureFlagsProvider { virtual bool enableFabricRenderer() = 0; virtual bool enableFixForParentTagDuringReparenting() = 0; virtual bool enableFontScaleChangesUpdatingLayout() = 0; + virtual bool enableIOSBorderBoxShadowPathByDefault() = 0; virtual bool enableIOSExperimentalAutoFocusImplementation() = 0; virtual bool enableIOSTextBaselineOffsetPerLine() = 0; virtual bool enableIOSViewClipToPaddingBox() = 0; diff --git a/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.cpp b/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.cpp index 8f3742fca812..391a81d8afc7 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<<4f37b3bec59e6d9cd6e61e4ecbbe7b6d>> + * @generated SignedSource<> */ /** @@ -144,6 +144,11 @@ bool NativeReactNativeFeatureFlags::enableFontScaleChangesUpdatingLayout( return ReactNativeFeatureFlags::enableFontScaleChangesUpdatingLayout(); } +bool NativeReactNativeFeatureFlags::enableIOSBorderBoxShadowPathByDefault( + jsi::Runtime& /*runtime*/) { + return ReactNativeFeatureFlags::enableIOSBorderBoxShadowPathByDefault(); +} + bool NativeReactNativeFeatureFlags::enableIOSExperimentalAutoFocusImplementation( jsi::Runtime& /*runtime*/) { return ReactNativeFeatureFlags::enableIOSExperimentalAutoFocusImplementation(); diff --git a/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.h b/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.h index 9218683bd9b8..b70c1b43c6d4 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<<267e09d240d84ae5613aa58e2ed1deb8>> + * @generated SignedSource<<3a4e198b27be6df0f2d707c60b635278>> */ /** @@ -76,6 +76,8 @@ class NativeReactNativeFeatureFlags bool enableFontScaleChangesUpdatingLayout(jsi::Runtime& runtime); + bool enableIOSBorderBoxShadowPathByDefault(jsi::Runtime& runtime); + bool enableIOSExperimentalAutoFocusImplementation(jsi::Runtime& runtime); bool enableIOSTextBaselineOffsetPerLine(jsi::Runtime& runtime); diff --git a/packages/react-native/ReactCommon/react/renderer/components/view/BaseViewProps.cpp b/packages/react-native/ReactCommon/react/renderer/components/view/BaseViewProps.cpp index 5bf3c6e4179e..0ae053486178 100644 --- a/packages/react-native/ReactCommon/react/renderer/components/view/BaseViewProps.cpp +++ b/packages/react-native/ReactCommon/react/renderer/components/view/BaseViewProps.cpp @@ -180,6 +180,15 @@ BaseViewProps::BaseViewProps( "shadowOpacity", sourceProps.shadowOpacity, {})), + shadowPathIOS( + ReactNativeFeatureFlags::enableCppPropsIteratorSetter() + ? sourceProps.shadowPathIOS + : convertRawProp( + context, + rawProps, + "shadowPathIOS", + sourceProps.shadowPathIOS, + ShadowPathMode::Default)), shadowRadius( ReactNativeFeatureFlags::enableCppPropsIteratorSetter() ? sourceProps.shadowRadius @@ -379,6 +388,7 @@ void BaseViewProps::setProp( RAW_SET_PROP_SWITCH_CASE_BASIC(shadowColor); RAW_SET_PROP_SWITCH_CASE_BASIC(shadowOffset); RAW_SET_PROP_SWITCH_CASE_BASIC(shadowOpacity); + RAW_SET_PROP_SWITCH_CASE_BASIC(shadowPathIOS); RAW_SET_PROP_SWITCH_CASE_BASIC(shadowRadius); RAW_SET_PROP_SWITCH_CASE_BASIC(transform); RAW_SET_PROP_SWITCH_CASE_BASIC(backfaceVisibility); diff --git a/packages/react-native/ReactCommon/react/renderer/components/view/BaseViewProps.h b/packages/react-native/ReactCommon/react/renderer/components/view/BaseViewProps.h index 6258d1375dae..e59eeda50f54 100644 --- a/packages/react-native/ReactCommon/react/renderer/components/view/BaseViewProps.h +++ b/packages/react-native/ReactCommon/react/renderer/components/view/BaseViewProps.h @@ -63,6 +63,7 @@ class BaseViewProps : public YogaStylableProps, public AccessibilityProps { SharedColor shadowColor{}; Size shadowOffset{0, -3}; Float shadowOpacity{}; + ShadowPathMode shadowPathIOS{ShadowPathMode::Default}; Float shadowRadius{3}; Cursor cursor{}; diff --git a/packages/react-native/ReactCommon/react/renderer/components/view/ViewShadowNode.cpp b/packages/react-native/ReactCommon/react/renderer/components/view/ViewShadowNode.cpp index 19e6f2106e00..0d2e0be786a0 100644 --- a/packages/react-native/ReactCommon/react/renderer/components/view/ViewShadowNode.cpp +++ b/packages/react-native/ReactCommon/react/renderer/components/view/ViewShadowNode.cpp @@ -69,6 +69,7 @@ void ViewShadowNode::initialize() noexcept { isColorMeaningful(viewProps.backgroundColor) || hasBorder() || !viewProps.testId.empty() || !viewProps.boxShadow.empty() || !viewProps.backgroundImage.empty() || + viewProps.shadowPathIOS != ShadowPathMode::Default || HostPlatformViewTraitsInitializer::formsView(viewProps) || viewProps.outlineWidth > 0; diff --git a/packages/react-native/ReactCommon/react/renderer/components/view/conversions.h b/packages/react-native/ReactCommon/react/renderer/components/view/conversions.h index 349b1e332e03..39301d055ea7 100644 --- a/packages/react-native/ReactCommon/react/renderer/components/view/conversions.h +++ b/packages/react-native/ReactCommon/react/renderer/components/view/conversions.h @@ -961,6 +961,34 @@ inline void fromRawValue( react_native_expect(false); } +inline void fromRawValue( + const PropsParserContext& context, + const RawValue& value, + ShadowPathMode& result) { + result = ShadowPathMode::Default; + react_native_expect(value.hasType()); + if (!value.hasType()) { + return; + } + + auto stringValue = (std::string)value; + if (stringValue == "auto") { + result = ShadowPathMode::Auto; + return; + } + if (stringValue == "border-box") { + result = ShadowPathMode::BorderBox; + return; + } + if (stringValue == "content-alpha") { + result = ShadowPathMode::ContentAlpha; + return; + } + + LOG(ERROR) << "Could not parse ShadowPathMode:" << stringValue; + react_native_expect(false); +} + inline void fromRawValue( const PropsParserContext& context, const RawValue& value, diff --git a/packages/react-native/ReactCommon/react/renderer/components/view/primitives.h b/packages/react-native/ReactCommon/react/renderer/components/view/primitives.h index 96d379b6c4bf..d6f3ddab1d2c 100644 --- a/packages/react-native/ReactCommon/react/renderer/components/view/primitives.h +++ b/packages/react-native/ReactCommon/react/renderer/components/view/primitives.h @@ -94,6 +94,27 @@ enum class BorderStyle : uint8_t { Solid, Dotted, Dashed }; enum class OutlineStyle : uint8_t { Solid, Dotted, Dashed }; +enum class ShadowPathMode : uint8_t { + Default, + Auto, + BorderBox, + ContentAlpha, +}; + +inline ShadowPathMode resolveShadowPathMode( + ShadowPathMode propMode, + bool borderBoxDefaultEnabled) { + if (propMode != ShadowPathMode::Default) { + return propMode; + } + + if (borderBoxDefaultEnabled) { + return ShadowPathMode::BorderBox; + } + + return ShadowPathMode::Auto; +} + struct CornerRadii { float vertical{0.0f}; float horizontal{0.0f}; diff --git a/packages/react-native/ReactCommon/react/renderer/components/view/tests/ConversionsTest.cpp b/packages/react-native/ReactCommon/react/renderer/components/view/tests/ConversionsTest.cpp index a21bade8c222..7caf32441c44 100644 --- a/packages/react-native/ReactCommon/react/renderer/components/view/tests/ConversionsTest.cpp +++ b/packages/react-native/ReactCommon/react/renderer/components/view/tests/ConversionsTest.cpp @@ -9,9 +9,41 @@ #include #include +#include namespace facebook::react { +TEST(ConversionsTest, shadow_path_modes) { + PropsParserContext context{-1, ContextContainer{}}; + ShadowPathMode result = ShadowPathMode::Default; + RawValue autoValue{folly::dynamic("auto")}; + RawValue borderBoxValue{folly::dynamic("border-box")}; + RawValue contentAlphaValue{folly::dynamic("content-alpha")}; + + fromRawValue(context, autoValue, result); + EXPECT_EQ(result, ShadowPathMode::Auto); + + fromRawValue(context, borderBoxValue, result); + EXPECT_EQ(result, ShadowPathMode::BorderBox); + + fromRawValue(context, contentAlphaValue, result); + EXPECT_EQ(result, ShadowPathMode::ContentAlpha); +} + +TEST(ConversionsTest, resolves_default_shadow_path_mode) { + EXPECT_EQ( + resolveShadowPathMode(ShadowPathMode::Default, false), + ShadowPathMode::Auto); + EXPECT_EQ( + resolveShadowPathMode(ShadowPathMode::Default, true), + ShadowPathMode::BorderBox); + EXPECT_EQ( + resolveShadowPathMode(ShadowPathMode::Auto, true), ShadowPathMode::Auto); + EXPECT_EQ( + resolveShadowPathMode(ShadowPathMode::ContentAlpha, true), + ShadowPathMode::ContentAlpha); +} + TEST(ConversionsTest, unprocessed_box_shadow_string) { RawValue value{ folly::dynamic("10px 2px 0 5px #fff, inset 20px 10px 5px 0 #000")}; diff --git a/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js b/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js index 924e620aadb2..79fa981d73dc 100644 --- a/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js +++ b/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js @@ -259,6 +259,17 @@ const definitions: FeatureFlagDefinitions = { }, ossReleaseStage: 'none', }, + enableIOSBorderBoxShadowPathByDefault: { + defaultValue: false, + metadata: { + dateAdded: '2026-07-14', + description: + 'Use the border box for legacy iOS shadow paths when shadowPathIOS is omitted.', + expectedReleaseValue: true, + purpose: 'experimentation', + }, + ossReleaseStage: 'none', + }, enableIOSExperimentalAutoFocusImplementation: { defaultValue: false, metadata: { diff --git a/packages/react-native/src/private/featureflags/ReactNativeFeatureFlags.js b/packages/react-native/src/private/featureflags/ReactNativeFeatureFlags.js index c1bf13b3bc58..028318b0630f 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<<2d214619ebe2d8c61230de56ebfd95f6>> + * @generated SignedSource<> * @flow strict * @noformat */ @@ -71,6 +71,7 @@ export type ReactNativeFeatureFlags = $ReadOnly<{ enableFabricRenderer: Getter, enableFixForParentTagDuringReparenting: Getter, enableFontScaleChangesUpdatingLayout: Getter, + enableIOSBorderBoxShadowPathByDefault: Getter, enableIOSExperimentalAutoFocusImplementation: Getter, enableIOSTextBaselineOffsetPerLine: Getter, enableIOSViewClipToPaddingBox: Getter, @@ -283,6 +284,10 @@ export const enableFixForParentTagDuringReparenting: Getter = createNat * Enables font scale changes updating layout for measurable nodes. */ export const enableFontScaleChangesUpdatingLayout: Getter = createNativeFlagGetter('enableFontScaleChangesUpdatingLayout', false); +/** + * Use the border box for legacy iOS shadow paths when shadowPathIOS is omitted. + */ +export const enableIOSBorderBoxShadowPathByDefault: Getter = createNativeFlagGetter('enableIOSBorderBoxShadowPathByDefault', false); /** * Fixes #56595 by moving the autoFocus from didMoveToWindow to viewDidAppear */ diff --git a/packages/react-native/src/private/featureflags/specs/NativeReactNativeFeatureFlags.js b/packages/react-native/src/private/featureflags/specs/NativeReactNativeFeatureFlags.js index c6d69e5004b5..702327785608 100644 --- a/packages/react-native/src/private/featureflags/specs/NativeReactNativeFeatureFlags.js +++ b/packages/react-native/src/private/featureflags/specs/NativeReactNativeFeatureFlags.js @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<> + * @generated SignedSource<> * @flow strict * @noformat */ @@ -45,6 +45,7 @@ export interface Spec extends TurboModule { +enableFabricRenderer?: () => boolean; +enableFixForParentTagDuringReparenting?: () => boolean; +enableFontScaleChangesUpdatingLayout?: () => boolean; + +enableIOSBorderBoxShadowPathByDefault?: () => boolean; +enableIOSExperimentalAutoFocusImplementation?: () => boolean; +enableIOSTextBaselineOffsetPerLine?: () => boolean; +enableIOSViewClipToPaddingBox?: () => boolean; diff --git a/packages/rn-tester/js/examples/Playground/PlaygroundExample.js b/packages/rn-tester/js/examples/Playground/PlaygroundExample.js index b622cf822f8d..8b1d2ea730d0 100644 --- a/packages/rn-tester/js/examples/Playground/PlaygroundExample.js +++ b/packages/rn-tester/js/examples/Playground/PlaygroundExample.js @@ -11,8 +11,12 @@ import type {RNTesterModuleExample} from '../../types/RNTesterTypes'; import Playground from './RNTesterPlayground'; +import ShadowPathPlayground from './RNTesterShadowPathPlayground'; export const title = Playground.title; export const framework = 'React'; export const description = 'Test out new features and ideas.'; -export const examples: Array = [Playground]; +export const examples: Array = [ + Playground, + ShadowPathPlayground, +]; diff --git a/packages/rn-tester/js/examples/Playground/RNTesterShadowPathPlayground.js b/packages/rn-tester/js/examples/Playground/RNTesterShadowPathPlayground.js new file mode 100644 index 000000000000..8f1826954623 --- /dev/null +++ b/packages/rn-tester/js/examples/Playground/RNTesterShadowPathPlayground.js @@ -0,0 +1,276 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @flow strict-local + * @format + */ + +import type {RNTesterModuleExample} from '../../types/RNTesterTypes'; + +import * as React from 'react'; +import {useEffect, useRef, useState} from 'react'; +import { + Animated, + Pressable, + StyleSheet, + Switch, + Text, + View, +} from 'react-native'; +import {enableIOSBorderBoxShadowPathByDefault} from 'react-native/src/private/featureflags/ReactNativeFeatureFlags'; + +type ShadowPathMode = 'auto' | 'border-box' | 'content-alpha'; +type ShadowPathSelection = 'default' | ShadowPathMode; + +const SHADOW_PATH_OPTIONS: $ReadOnlyArray<{ + label: string, + value: ShadowPathSelection, +}> = [ + {label: 'Default', value: 'default'}, + {label: 'Auto', value: 'auto'}, + {label: 'Border Box', value: 'border-box'}, + {label: 'Content Alpha', value: 'content-alpha'}, +]; + +function ShadowPathPlayground(): React.Node { + const [selectedMode, setSelectedMode] = + useState('default'); + const [animationEnabled, setAnimationEnabled] = useState(false); + const animationProgressRef = useRef(null); + if (animationProgressRef.current == null) { + animationProgressRef.current = new Animated.Value(0); + } + const animationProgress = animationProgressRef.current; + + useEffect(() => { + if (!animationEnabled) { + animationProgress.stopAnimation(); + animationProgress.setValue(0); + return; + } + + const growAnimation = Animated.timing(animationProgress, { + duration: 700, + toValue: 1, + useNativeDriver: true, + }); + const shrinkAnimation = Animated.timing(animationProgress, { + duration: 700, + toValue: 0, + useNativeDriver: true, + }); + const animationSequence = Animated.sequence([ + growAnimation, + shrinkAnimation, + ]); + const animation = Animated.loop(animationSequence); + animation.start(); + + return () => { + animation.stop(); + }; + }, [animationEnabled, animationProgress]); + + const scale = animationProgress.interpolate({ + inputRange: [0, 1], + outputRange: [1, 1.12], + }); + const animatedStyle = {transform: [{scale}]}; + const shadowPathProps: {shadowPathIOS?: ShadowPathMode} = + selectedMode === 'default' ? {} : {shadowPathIOS: selectedMode}; + const flagEnabled = enableIOSBorderBoxShadowPathByDefault(); + const flagDefault = flagEnabled ? 'Border Box' : 'Auto'; + const activeMode = SHADOW_PATH_OPTIONS.find( + option => option.value === selectedMode, + ); + + if (activeMode == null) { + throw new Error('Selected shadow path mode must have a label.'); + } + + return ( + + + {SHADOW_PATH_OPTIONS.map(option => { + const selected = option.value === selectedMode; + return ( + setSelectedMode(option.value)} + style={[ + styles.segment, + selected ? styles.selectedSegment : null, + ]}> + + {option.label} + + + ); + })} + + + + Scale animation + + + + Active mode: {activeMode.label} + Feature-flag default: {flagDefault} + + Translucent button hierarchy + + + + Translucent button + + + + + Irregular composited content + + Content Alpha follows the circles. Border Box intentionally shadows the + host bounds instead. + + + + + + + + + + ); +} + +const styles = StyleSheet.create({ + container: { + gap: 12, + padding: 16, + }, + segmentedControl: { + borderColor: '#8e8e93', + borderRadius: 8, + borderWidth: StyleSheet.hairlineWidth, + flexDirection: 'row', + overflow: 'hidden', + }, + segment: { + alignItems: 'center', + borderColor: '#8e8e93', + borderRightWidth: StyleSheet.hairlineWidth, + flex: 1, + justifyContent: 'center', + minHeight: 38, + paddingHorizontal: 4, + }, + selectedSegment: { + backgroundColor: '#0a84ff', + }, + segmentLabel: { + color: '#3c3c43', + fontSize: 12, + textAlign: 'center', + }, + selectedSegmentLabel: { + color: 'white', + fontWeight: '600', + }, + animationRow: { + alignItems: 'center', + flexDirection: 'row', + justifyContent: 'space-between', + }, + label: { + fontSize: 16, + }, + status: { + color: '#636366', + fontVariant: ['tabular-nums'], + }, + heading: { + fontSize: 17, + fontWeight: '600', + marginTop: 8, + }, + explanation: { + color: '#636366', + lineHeight: 19, + }, + sampleStage: { + alignItems: 'center', + backgroundColor: '#d1d1d6', + justifyContent: 'center', + minHeight: 140, + overflow: 'visible', + }, + buttonShadowHost: { + borderRadius: 8, + shadowColor: 'black', + shadowOffset: {width: 0, height: 1}, + shadowOpacity: 0.14, + shadowRadius: 4, + }, + translucentButton: { + alignItems: 'center', + backgroundColor: 'rgba(255, 255, 255, 0.12)', + borderRadius: 8, + justifyContent: 'center', + minHeight: 40, + minWidth: 160, + overflow: 'hidden', + }, + buttonLabel: { + color: '#1c1c1e', + fontWeight: '600', + }, + irregularShadowHost: { + height: 72, + position: 'relative', + shadowColor: 'black', + shadowOffset: {width: 0, height: 1}, + shadowOpacity: 0.25, + shadowRadius: 4, + width: 160, + }, + circle: { + backgroundColor: '#ff9f0a', + borderRadius: 28, + height: 56, + position: 'absolute', + top: 8, + width: 56, + }, + leftCircle: { + left: 8, + }, + centerCircle: { + backgroundColor: '#ff375f', + left: 52, + }, + rightCircle: { + backgroundColor: '#bf5af2', + left: 96, + }, +}); + +export default ({ + title: 'iOS Shadow Path', + name: 'ios-shadow-path', + description: + 'Compares legacy iOS shadow path modes and their dynamic-shadow behavior.', + platform: 'ios', + render: (): React.Node => , +}: RNTesterModuleExample); From d213795881b62a4d5970f7257040497e0aadc081 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hanno=20J=2E=20G=C3=B6decke?= Date: Wed, 15 Jul 2026 10:11:44 +0200 Subject: [PATCH 2/6] pod install --- Gemfile.lock | 18 ++++++++++-------- packages/rn-tester/Gemfile | 1 + packages/rn-tester/Podfile.lock | 4 ++-- 3 files changed, 13 insertions(+), 10 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index e304f8287968..dce502adf470 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -3,11 +3,12 @@ GEM specs: CFPropertyList (3.0.6) rexml - activesupport (7.0.8) + activesupport (6.1.7.10) concurrent-ruby (~> 1.0, >= 1.0.2) i18n (>= 1.6, < 2) minitest (>= 5.1) tzinfo (~> 2.0) + zeitwerk (~> 2.3) addressable (2.8.5) public_suffix (>= 2.0.2, < 6.0) algoliasearch (1.27.5) @@ -17,12 +18,12 @@ GEM benchmark (0.4.0) bigdecimal (3.1.9) claide (1.1.0) - cocoapods (1.14.2) + cocoapods (1.15.2) addressable (~> 2.8) claide (>= 1.0.2, < 2.0) - cocoapods-core (= 1.14.2) + cocoapods-core (= 1.15.2) cocoapods-deintegrate (>= 1.0.3, < 2.0) - cocoapods-downloader (>= 2.0) + cocoapods-downloader (>= 2.1, < 3.0) cocoapods-plugins (>= 1.0.0, < 2.0) cocoapods-search (>= 1.0.0, < 2.0) cocoapods-trunk (>= 1.6.0, < 2.0) @@ -35,7 +36,7 @@ GEM nap (~> 1.0) ruby-macho (>= 2.3.0, < 3.0) xcodeproj (>= 1.23.0, < 2.0) - cocoapods-core (1.14.2) + cocoapods-core (1.15.2) activesupport (>= 5.0, < 8) addressable (~> 2.8) algoliasearch (~> 1.0) @@ -46,7 +47,7 @@ GEM public_suffix (~> 4.0) typhoeus (~> 1.0) cocoapods-deintegrate (1.0.5) - cocoapods-downloader (2.0) + cocoapods-downloader (2.1) cocoapods-plugins (1.0.0) nap cocoapods-search (1.0.1) @@ -81,13 +82,14 @@ GEM ethon (>= 0.9.0) tzinfo (2.0.6) concurrent-ruby (~> 1.0) - xcodeproj (1.23.0) + xcodeproj (1.24.0) CFPropertyList (>= 2.3.3, < 4.0) atomos (~> 0.1.3) claide (>= 1.0.2, < 2.0) colored2 (~> 3.1) nanaimo (~> 0.3.0) rexml (~> 3.2.4) + zeitwerk (2.6.18) PLATFORMS ruby @@ -106,4 +108,4 @@ RUBY VERSION ruby 3.2.0p0 BUNDLED WITH - 2.4.12 + 2.4.6 diff --git a/packages/rn-tester/Gemfile b/packages/rn-tester/Gemfile index 03087ee0ccd9..bc9f6785d6e7 100644 --- a/packages/rn-tester/Gemfile +++ b/packages/rn-tester/Gemfile @@ -16,3 +16,4 @@ gem 'bigdecimal' gem 'logger' gem 'benchmark' gem 'mutex_m' +gem 'nkf' diff --git a/packages/rn-tester/Podfile.lock b/packages/rn-tester/Podfile.lock index c140a0d4d73b..55750f568f1e 100644 --- a/packages/rn-tester/Podfile.lock +++ b/packages/rn-tester/Podfile.lock @@ -2752,7 +2752,7 @@ SPEC CHECKSUMS: React-timing: 1e6a8acb66e2b7ac9d418956617fd1fdb19322fd React-utils: 52bbb03f130319ef82e4c3bc7a85eaacdb1fec87 ReactAppDependencyProvider: 433ddfb4536948630aadd5bd925aff8a632d2fe3 - ReactCodegen: cb2a3dea7ad220568d9453e17d275dbd9f9bc044 + ReactCodegen: f9a16823d5e01de57cc449c9451d6ddc2a424f64 ReactCommon: 394c6b92765cf6d211c2c3f7f6bc601dffb316a6 ReactCommon-Samples: d083c8092a5ad0df33b1ca589cb2d909fbd6914e ScreenshotManager: cd9e43b0c749ba38101effc22be58f4a69440d5f @@ -2761,4 +2761,4 @@ SPEC CHECKSUMS: PODFILE CHECKSUM: b0abc972ac4c3a335250548a31500196b1a2c163 -COCOAPODS: 1.16.2 +COCOAPODS: 1.15.2 From da4ae8ebd8fe0b907ea948eba9664f4a560a3644 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hanno=20J=2E=20G=C3=B6decke?= Date: Wed, 15 Jul 2026 10:46:01 +0200 Subject: [PATCH 3/6] this component is android only --- packages/rn-tester/package.json | 3 --- 1 file changed, 3 deletions(-) diff --git a/packages/rn-tester/package.json b/packages/rn-tester/package.json index 9078a93920cc..d4f5e7208a5a 100644 --- a/packages/rn-tester/package.json +++ b/packages/rn-tester/package.json @@ -53,9 +53,6 @@ "components": { "RNTMyNativeView": { "className": "RNTMyNativeViewComponentView" - }, - "CustomView": { - "className": "CustomViewComponentView" } } } From 0b85e54a7acacdf7545136338dcee843c9e17db8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hanno=20J=2E=20G=C3=B6decke?= Date: Wed, 15 Jul 2026 17:30:44 +0200 Subject: [PATCH 4/6] feat(ios): prototype opaque shadow backdrops --- CONTEXT.md | 39 ++ .../0001-ios-shadow-backplate-propagation.md | 3 + .../View/RCTViewComponentView.h | 3 + .../View/RCTViewComponentView.mm | 183 +++++++- .../Mounting/RCTComponentViewProtocol.h | 15 +- .../Fabric/Mounting/RCTMountingManager.mm | 8 + .../Mounting/UIView+ComponentViewProtocol.h | 3 + .../Mounting/UIView+ComponentViewProtocol.mm | 6 + .../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 | 112 +++-- .../ReactNativeFeatureFlagsAccessor.h | 6 +- .../ReactNativeFeatureFlagsDefaults.h | 6 +- .../ReactNativeFeatureFlagsDynamicProvider.h | 11 +- .../ReactNativeFeatureFlagsProvider.h | 3 +- .../NativeReactNativeFeatureFlags.cpp | 7 +- .../NativeReactNativeFeatureFlags.h | 4 +- .../renderer/mounting/Differentiator.cpp | 23 +- .../react/renderer/mounting/ShadowView.cpp | 6 +- .../react/renderer/mounting/ShadowView.h | 162 ++++++- .../mounting/internal/ShadowViewNodePair.h | 8 + .../sliceChildShadowNodeViewPairs.cpp | 196 ++++++++- .../internal/sliceChildShadowNodeViewPairs.h | 10 +- .../react/renderer/mounting/stubs/stubs.cpp | 15 +- .../mounting/tests/ShadowBackdropTest.cpp | 254 +++++++++++ .../ReactNativeFeatureFlags.config.js | 11 + .../featureflags/ReactNativeFeatureFlags.js | 7 +- .../specs/NativeReactNativeFeatureFlags.js | 3 +- .../RNTesterShadowPathPlayground.js | 412 ++++++++++++++++-- 37 files changed, 1470 insertions(+), 125 deletions(-) create mode 100644 CONTEXT.md create mode 100644 docs/adr/0001-ios-shadow-backplate-propagation.md create mode 100644 packages/react-native/ReactCommon/react/renderer/mounting/tests/ShadowBackdropTest.cpp diff --git a/CONTEXT.md b/CONTEXT.md new file mode 100644 index 000000000000..ba278d1fa347 --- /dev/null +++ b/CONTEXT.md @@ -0,0 +1,39 @@ +# iOS Shadow Rendering + +This context defines the silhouettes and compositing behavior of iOS shadows rendered by React Native views. + +## Language + +**Border-box shadow**: +A legacy iOS shadow whose silhouette is the view's border box and resolved corner radii, independent of rendered content alpha. The caller is responsible for ensuring that this silhouette remains appropriate for its content and backdrop. + +**Content-alpha shadow**: +A legacy iOS shadow whose silhouette is derived from the composited alpha of the view and its descendants. + +**Backdrop precomposition**: +Rendering an opaque backplate beneath a border-box shadow host so the shadow cannot show through otherwise translucent pixels. It is a best-effort optimization applied only after border-box behavior has already been selected. + +**Backdrop provider**: +A canonical view or root with a concrete opaque solid background that supplies an inherited backdrop color. Unknown or content-bearing component types terminate Fabric backdrop propagation. + +**Inherited backdrop color**: +A concrete opaque color supplied by a backdrop provider and passed unchanged through transparent wrappers. + +**Translucent ancestor barrier**: +A non-opaque background between a backdrop provider and a shadow host. It terminates backdrop propagation because using one unblended backplate would change the rendered pixels. + +**Concrete color**: +A fixed RGBA color whose value does not depend on native appearance traits. Native dynamic and semantic system colors are not concrete colors. + +**Opaque backplate**: +A border-box-shaped fill beneath a shadow host's normal background and content. It uses the uniform opaque backdrop color to prevent a border-box shadow from showing through translucent pixels. + +**Uniform opaque backdrop**: +A single fully opaque color behind every point of a view's border box. Previously painted content that makes the color vary means the backdrop is not uniform. +_Avoid_: Parent background color + +**Propagation source**: +Backdrop eligibility is derived once for each complete, laid-out old and new +Fabric tree at mounting-diff time. Later flattening slices read the derived +environment from a tag-indexed sidecar map. The mounted UIKit hierarchy cannot +be used because view flattening can erase logical provider boundaries. diff --git a/docs/adr/0001-ios-shadow-backplate-propagation.md b/docs/adr/0001-ios-shadow-backplate-propagation.md new file mode 100644 index 000000000000..a919f36ab96f --- /dev/null +++ b/docs/adr/0001-ios-shadow-backplate-propagation.md @@ -0,0 +1,3 @@ +# Use opaque backplates for eligible legacy border-box shadows + +For legacy iOS `shadow*` props resolved to `BorderBox`, React Native may render an opaque, fill-only backplate matching the host's border path beneath its existing background and content. This prevents the shadow from showing through translucent host content without adding a mask. A concrete opaque color is supplied by a canonical Root or View and relayed unchanged only through transparent canonical Views; translucent ancestors, unknown components, complex effects, rounded providers without clipping, and potentially intersecting earlier siblings are barriers. When eligibility is unknown, retain ordinary unmasked border-box behavior. Eligibility is propagated through the Fabric tree because view flattening can remove logical provider boundaries from the mounted UIKit hierarchy. The mounting Differentiator computes one tag-indexed environment map for each complete old and new laid-out tree, and all nested flattening slices reuse those maps. diff --git a/packages/react-native/React/Fabric/Mounting/ComponentViews/View/RCTViewComponentView.h b/packages/react-native/React/Fabric/Mounting/ComponentViews/View/RCTViewComponentView.h index a3c58cb16f57..6578e825dd0a 100644 --- a/packages/react-native/React/Fabric/Mounting/ComponentViews/View/RCTViewComponentView.h +++ b/packages/react-native/React/Fabric/Mounting/ComponentViews/View/RCTViewComponentView.h @@ -16,6 +16,7 @@ #import #import #import +#import NS_ASSUME_NONNULL_BEGIN @@ -74,6 +75,8 @@ NS_ASSUME_NONNULL_BEGIN oldLayoutMetrics:(const facebook::react::LayoutMetrics &)oldLayoutMetrics NS_REQUIRES_SUPER; - (void)finalizeUpdates:(RNComponentViewUpdateMask)updateMask NS_REQUIRES_SUPER; - (void)prepareForRecycle NS_REQUIRES_SUPER; +- (void)updateEnvironment:(const facebook::react::ShadowViewEnvironment &)environment + oldEnvironment:(const facebook::react::ShadowViewEnvironment &)oldEnvironment; - (UIView *)betterHitTest:(CGPoint)point withEvent:(UIEvent *)event; /* diff --git a/packages/react-native/React/Fabric/Mounting/ComponentViews/View/RCTViewComponentView.mm b/packages/react-native/React/Fabric/Mounting/ComponentViews/View/RCTViewComponentView.mm index 62eff72135af..795b2c64d91e 100644 --- a/packages/react-native/React/Fabric/Mounting/ComponentViews/View/RCTViewComponentView.mm +++ b/packages/react-native/React/Fabric/Mounting/ComponentViews/View/RCTViewComponentView.mm @@ -21,6 +21,7 @@ #import #import #import +#import #import #import #import @@ -37,10 +38,77 @@ using namespace facebook::react; const CGFloat BACKGROUND_COLOR_ZPOSITION = -1024.0f; +// The backplate must sit below the normal background so translucent content +// blends over it while the backplate still occludes the shadow interior. +const CGFloat SHADOW_BACKDROP_ZPOSITION = BACKGROUND_COLOR_ZPOSITION - 1.0f; + +static NSString *RCTShadowBackdropReasonString(ShadowBackdropReason reason) +{ + switch (reason) { + case ShadowBackdropReason::None: + return @"none"; + case ShadowBackdropReason::EarlierSibling: + return @"earlier-sibling"; + case ShadowBackdropReason::TranslucentAncestor: + return @"translucent-ancestor"; + case ShadowBackdropReason::UnknownAncestor: + return @"unknown-ancestor"; + case ShadowBackdropReason::VisualEffect: + return @"visual-effect"; + case ShadowBackdropReason::RoundedAncestor: + return @"rounded-ancestor"; + case ShadowBackdropReason::OutsideProvider: + return @"outside-provider"; + case ShadowBackdropReason::DynamicColor: + return @"dynamic-color"; + } + + react_native_assert(false); + return @"invalid"; +} + +static BOOL RCTShadowBackdropColorIsConcreteOpaque(UIColor *color) +{ + UITraitCollection *lightTraits = + [UITraitCollection traitCollectionWithUserInterfaceStyle:UIUserInterfaceStyleLight]; + UITraitCollection *darkTraits = + [UITraitCollection traitCollectionWithUserInterfaceStyle:UIUserInterfaceStyleDark]; + UITraitCollection *highContrastTraits = + [UITraitCollection traitCollectionWithAccessibilityContrast:UIAccessibilityContrastHigh]; + UITraitCollection *highContrastLightTraits = + [UITraitCollection traitCollectionWithTraitsFromCollections:@[ lightTraits, highContrastTraits ]]; + UITraitCollection *highContrastDarkTraits = + [UITraitCollection traitCollectionWithTraitsFromCollections:@[ darkTraits, highContrastTraits ]]; + + UIColor *lightColor = [color resolvedColorWithTraitCollection:lightTraits]; + UIColor *darkColor = [color resolvedColorWithTraitCollection:darkTraits]; + UIColor *highContrastLightColor = [color resolvedColorWithTraitCollection:highContrastLightTraits]; + UIColor *highContrastDarkColor = [color resolvedColorWithTraitCollection:highContrastDarkTraits]; + CGColorRef lightCGColor = lightColor.CGColor; + CGColorRef darkCGColor = darkColor.CGColor; + CGColorRef highContrastLightCGColor = highContrastLightColor.CGColor; + CGColorRef highContrastDarkCGColor = highContrastDarkColor.CGColor; + + // A provider has to remain the same opaque color for every supported trait. + // Otherwise the backplate could diverge from its ancestor after a trait change. + if (CGColorGetAlpha(lightCGColor) < 0.999 || CGColorGetAlpha(darkCGColor) < 0.999 || + CGColorGetAlpha(highContrastLightCGColor) < 0.999 || CGColorGetAlpha(highContrastDarkCGColor) < 0.999) { + return NO; + } + + return CGColorEqualToColor(lightCGColor, darkCGColor) && + CGColorEqualToColor(lightCGColor, highContrastLightCGColor) && + CGColorEqualToColor(lightCGColor, highContrastDarkCGColor); +} + +@interface RCTViewComponentView () +- (void)invalidateLayer; +@end @implementation RCTViewComponentView { UIColor *_backgroundColor; CALayer *_backgroundColorLayer; + CAShapeLayer *_shadowBackdropLayer; __weak CALayer *_borderLayer; CALayer *_outlineLayer; NSMutableArray *_boxShadowLayers; @@ -56,6 +124,11 @@ @implementation RCTViewComponentView { NSMutableSet *_accessibilityOrderNativeIDs; NSMutableArray *_accessibilityElements; RCTViewAccessibilityElement *_axElementDescribingSelf; + ShadowBackdrop _shadowBackdrop; + ShadowBackdropKind _lastShadowBackdropKind; + ShadowBackdropReason _lastShadowBackdropReason; + NSInteger _lastShadowBackdropProviderTag; + NSInteger _lastShadowBackdropBarrierTag; } #ifdef RCT_DYNAMIC_FRAMEWORKS @@ -73,6 +146,10 @@ - (instancetype)initWithFrame:(CGRect)frame self.multipleTouchEnabled = YES; _useCustomContainerView = NO; _removeClippedSubviews = NO; + _lastShadowBackdropKind = ShadowBackdropKind::None; + _lastShadowBackdropReason = ShadowBackdropReason::None; + _lastShadowBackdropProviderTag = -1; + _lastShadowBackdropBarrierTag = -1; } return self; } @@ -150,6 +227,7 @@ - (void)mountChildComponentView:(UIView *)childCompone } else { [self.currentContainerView insertSubview:childComponentView atIndex:index]; } + } - (void)unmountChildComponentView:(UIView *)childComponentView index:(NSInteger)index @@ -554,6 +632,17 @@ - (void)updateEventEmitter:(const EventEmitter::Shared &)eventEmitter _eventEmitter = std::static_pointer_cast(eventEmitter); } +- (void)updateEnvironment:(const ShadowViewEnvironment &)environment + oldEnvironment:(const ShadowViewEnvironment &)oldEnvironment +{ + if (oldEnvironment == environment) { + return; + } + + _shadowBackdrop = environment.shadowBackdrop; + _needsInvalidateLayer = YES; +} + - (void)updateLayoutMetrics:(const LayoutMetrics &)layoutMetrics oldLayoutMetrics:(const LayoutMetrics &)oldLayoutMetrics { @@ -599,12 +688,11 @@ - (void)finalizeUpdates:(RNComponentViewUpdateMask)updateMask { [super finalizeUpdates:updateMask]; _useCustomContainerView = [self styleWouldClipOverflowInk]; - if (!_needsInvalidateLayer) { - return; + if (_needsInvalidateLayer) { + _needsInvalidateLayer = NO; + [self invalidateLayer]; } - _needsInvalidateLayer = NO; - [self invalidateLayer]; } - (void)prepareForRecycle @@ -626,6 +714,13 @@ - (void)prepareForRecycle _removeClippedSubviews = NO; _reactSubviews = [NSMutableArray new]; _accessibilityElements = [NSMutableArray new]; + [_shadowBackdropLayer removeFromSuperlayer]; + _shadowBackdropLayer = nil; + _shadowBackdrop = {}; + _lastShadowBackdropKind = ShadowBackdropKind::None; + _lastShadowBackdropReason = ShadowBackdropReason::None; + _lastShadowBackdropProviderTag = -1; + _lastShadowBackdropBarrierTag = -1; } - (void)setPropKeysManagedByAnimated_DO_NOT_USE_THIS_IS_BROKEN:(NSSet *_Nullable)props @@ -823,6 +918,55 @@ - (UIView *)currentContainerView } } +- (void)logShadowBackdrop:(const ShadowBackdrop &)shadowBackdrop +{ + if (_lastShadowBackdropKind == shadowBackdrop.kind && + _lastShadowBackdropReason == shadowBackdrop.reason && + _lastShadowBackdropProviderTag == shadowBackdrop.providerTag && + _lastShadowBackdropBarrierTag == shadowBackdrop.barrierTag) { + return; + } + + _lastShadowBackdropKind = shadowBackdrop.kind; + _lastShadowBackdropReason = shadowBackdrop.reason; + _lastShadowBackdropProviderTag = shadowBackdrop.providerTag; + _lastShadowBackdropBarrierTag = shadowBackdrop.barrierTag; + + if (shadowBackdrop.kind != ShadowBackdropKind::Barrier) { + return; + } + + NSString *reason = RCTShadowBackdropReasonString(shadowBackdrop.reason); + RCTLogInfo( + @"[HannoDebug][ShadowBackdrop] host=%ld result=barrier reason=%@ provider=%ld barrier=%ld", + (long)self.tag, + reason, + (long)shadowBackdrop.providerTag, + (long)shadowBackdrop.barrierTag); +} + +- (void)updateShadowBackdropLayerWithColor:(UIColor *)color cornerInsets:(RCTCornerInsets)cornerInsets +{ + if (color == nil) { + [_shadowBackdropLayer removeFromSuperlayer]; + _shadowBackdropLayer = nil; + return; + } + + if (_shadowBackdropLayer == nil) { + _shadowBackdropLayer = [CAShapeLayer layer]; + _shadowBackdropLayer.zPosition = SHADOW_BACKDROP_ZPOSITION; + [self.layer addSublayer:_shadowBackdropLayer]; + } + + CGPathRef shadowBackdropPath = RCTPathCreateWithRoundedRect(self.bounds, cornerInsets, nil, NO); + _shadowBackdropLayer.frame = self.bounds; + _shadowBackdropLayer.path = shadowBackdropPath; + CGPathRelease(shadowBackdropPath); + _shadowBackdropLayer.fillColor = color.CGColor; + [_shadowBackdropLayer removeAllAnimations]; +} + - (void)invalidateLayer { CALayer *layer = self.layer; @@ -832,6 +976,13 @@ - (void)invalidateLayer } const auto borderMetrics = _props->resolveBorderMetrics(_layoutMetrics); + RCTCornerRadii cornerRadii = RCTCornerRadiiFromBorderRadii(borderMetrics.borderRadii); + const RCTCornerInsets cornerInsets = RCTGetCornerInsets(cornerRadii, UIEdgeInsetsZero); + UIColor *shadowBackdropColor = nil; + const bool shadowBackdropEnabled = + ReactNativeFeatureFlags::enableIOSBorderBoxShadowBackdrop(); + ShadowBackdrop shadowBackdrop{}; + const bool shouldLogShadowBackdrop = shadowBackdropEnabled; // Stage 1. Shadow Path BOOL const layerHasShadow = layer.shadowOpacity > 0 && CGColorGetAlpha(layer.shadowColor) > 0; @@ -840,8 +991,6 @@ - (void)invalidateLayer } else { bool borderBoxDefaultEnabled = ReactNativeFeatureFlags::enableIOSBorderBoxShadowPathByDefault(); ShadowPathMode shadowPathMode = resolveShadowPathMode(_props->shadowPathIOS, borderBoxDefaultEnabled); - RCTCornerRadii cornerRadii = RCTCornerRadiiFromBorderRadii(borderMetrics.borderRadii); - const RCTCornerInsets cornerInsets = RCTGetCornerInsets(cornerRadii, UIEdgeInsetsZero); switch (shadowPathMode) { case ShadowPathMode::BorderBox: { @@ -866,8 +1015,28 @@ - (void)invalidateLayer react_native_assert(false); break; } + + if (shadowPathMode == ShadowPathMode::BorderBox && shadowBackdropEnabled) { + shadowBackdrop = _shadowBackdrop; + + if (shadowBackdrop.kind == ShadowBackdropKind::Provider) { + UIColor *providerColor = RCTUIColorFromSharedColor(shadowBackdrop.color); + if (RCTShadowBackdropColorIsConcreteOpaque(providerColor)) { + shadowBackdropColor = [providerColor resolvedColorWithTraitCollection:self.traitCollection]; + } else { + shadowBackdrop = shadowBackdropBarrier(ShadowBackdropReason::DynamicColor); + shadowBackdrop.barrierTag = (Tag)self.tag; + } + } + } + } + + if (shouldLogShadowBackdrop) { + [self logShadowBackdrop:shadowBackdrop]; } + [self updateShadowBackdropLayerWithColor:shadowBackdropColor cornerInsets:cornerInsets]; + #if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 170000 /* __IPHONE_17_0 */ // Stage 1.5. Cursor / Hover Effects if (@available(iOS 17.0, *)) { @@ -919,7 +1088,7 @@ - (void)invalidateLayer // we want. If we mask self.layer to this path, we would be clipping subviews // which we may not want to do. The generalized solution in this case is just // create a new layer - if (useCoreAnimationBorderRendering) { + if (useCoreAnimationBorderRendering && shadowBackdropColor == nil) { [_backgroundColorLayer removeFromSuperlayer]; _backgroundColorLayer = nil; layer.backgroundColor = backgroundColor.CGColor; diff --git a/packages/react-native/React/Fabric/Mounting/RCTComponentViewProtocol.h b/packages/react-native/React/Fabric/Mounting/RCTComponentViewProtocol.h index 1b802b5cfde3..26befe0c9170 100644 --- a/packages/react-native/React/Fabric/Mounting/RCTComponentViewProtocol.h +++ b/packages/react-native/React/Fabric/Mounting/RCTComponentViewProtocol.h @@ -13,6 +13,10 @@ #import #import +namespace facebook::react { +struct ShadowViewEnvironment; +} + NS_ASSUME_NONNULL_BEGIN /* @@ -24,9 +28,11 @@ typedef NS_OPTIONS(NSInteger, RNComponentViewUpdateMask) { RNComponentViewUpdateMaskEventEmitter = 1 << 1, RNComponentViewUpdateMaskState = 1 << 2, RNComponentViewUpdateMaskLayoutMetrics = 1 << 3, + RNComponentViewUpdateMaskEnvironment = 1 << 4, RNComponentViewUpdateMaskAll = RNComponentViewUpdateMaskProps | RNComponentViewUpdateMaskEventEmitter | - RNComponentViewUpdateMaskState | RNComponentViewUpdateMaskLayoutMetrics + RNComponentViewUpdateMaskState | RNComponentViewUpdateMaskLayoutMetrics | + RNComponentViewUpdateMaskEnvironment }; /* @@ -92,6 +98,13 @@ typedef NS_OPTIONS(NSInteger, RNComponentViewUpdateMask) { - (void)updateLayoutMetrics:(const facebook::react::LayoutMetrics &)layoutMetrics oldLayoutMetrics:(const facebook::react::LayoutMetrics &)oldLayoutMetrics; +/* + * Called when rendering context derived from the mounted Fabric tree changes. + * The default implementation intentionally does nothing. + */ +- (void)updateEnvironment:(const facebook::react::ShadowViewEnvironment &)environment + oldEnvironment:(const facebook::react::ShadowViewEnvironment &)oldEnvironment; + /* * Called when receiving a command */ diff --git a/packages/react-native/React/Fabric/Mounting/RCTMountingManager.mm b/packages/react-native/React/Fabric/Mounting/RCTMountingManager.mm index f43750313128..c0f6d3110d24 100644 --- a/packages/react-native/React/Fabric/Mounting/RCTMountingManager.mm +++ b/packages/react-native/React/Fabric/Mounting/RCTMountingManager.mm @@ -84,6 +84,8 @@ static void RCTPerformMountInstructions( [newChildComponentView updateState:newChildShadowView.state oldState:nullptr]; [newChildComponentView updateLayoutMetrics:newChildShadowView.layoutMetrics oldLayoutMetrics:EmptyLayoutMetrics]; + ShadowViewEnvironment emptyEnvironment{}; + [newChildComponentView updateEnvironment:newChildShadowView.environment oldEnvironment:emptyEnvironment]; [newChildComponentView finalizeUpdates:RNComponentViewUpdateMaskAll]; [parentViewDescriptor.view mountChildComponentView:newChildComponentView index:mutation.index]; @@ -129,6 +131,12 @@ static void RCTPerformMountInstructions( mask |= RNComponentViewUpdateMaskLayoutMetrics; } + if (oldChildShadowView.environment != newChildShadowView.environment) { + [newChildComponentView updateEnvironment:newChildShadowView.environment + oldEnvironment:oldChildShadowView.environment]; + mask |= RNComponentViewUpdateMaskEnvironment; + } + if (mask != RNComponentViewUpdateMaskNone) { [newChildComponentView finalizeUpdates:mask]; } diff --git a/packages/react-native/React/Fabric/Mounting/UIView+ComponentViewProtocol.h b/packages/react-native/React/Fabric/Mounting/UIView+ComponentViewProtocol.h index e385fb33225c..5793c131d738 100644 --- a/packages/react-native/React/Fabric/Mounting/UIView+ComponentViewProtocol.h +++ b/packages/react-native/React/Fabric/Mounting/UIView+ComponentViewProtocol.h @@ -33,6 +33,9 @@ NS_ASSUME_NONNULL_BEGIN - (void)updateLayoutMetrics:(const facebook::react::LayoutMetrics &)layoutMetrics oldLayoutMetrics:(const facebook::react::LayoutMetrics &)oldLayoutMetrics; +- (void)updateEnvironment:(const facebook::react::ShadowViewEnvironment &)environment + oldEnvironment:(const facebook::react::ShadowViewEnvironment &)oldEnvironment; + - (void)finalizeUpdates:(RNComponentViewUpdateMask)updateMask; - (void)prepareForRecycle; diff --git a/packages/react-native/React/Fabric/Mounting/UIView+ComponentViewProtocol.mm b/packages/react-native/React/Fabric/Mounting/UIView+ComponentViewProtocol.mm index 9f50554560e5..b41c25bc7915 100644 --- a/packages/react-native/React/Fabric/Mounting/UIView+ComponentViewProtocol.mm +++ b/packages/react-native/React/Fabric/Mounting/UIView+ComponentViewProtocol.mm @@ -119,6 +119,12 @@ - (void)updateLayoutMetrics:(const LayoutMetrics &)layoutMetrics } } +- (void)updateEnvironment:(const ShadowViewEnvironment &)environment + oldEnvironment:(const ShadowViewEnvironment &)oldEnvironment +{ + // Default implementation does nothing. +} + - (void)finalizeUpdates:(RNComponentViewUpdateMask)updateMask { // Default implementation does nothing. 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 be58190699b3..5c320e6ef549 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<<2943f7c7f969e25c549ee6bfeff650c6>> + * @generated SignedSource<<12f3afcae7d63e99fe0bb07abaf8bd72>> */ /** @@ -156,6 +156,12 @@ public object ReactNativeFeatureFlags { @JvmStatic public fun enableIOSBorderBoxShadowPathByDefault(): Boolean = accessor.enableIOSBorderBoxShadowPathByDefault() + /** + * Add opaque backplates behind eligible legacy iOS border-box shadows. + */ + @JvmStatic + public fun enableIOSBorderBoxShadowBackdrop(): Boolean = accessor.enableIOSBorderBoxShadowBackdrop() + /** * Fixes #56595 by moving the autoFocus from didMoveToWindow to viewDidAppear */ 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 d167c594bb67..ef3cbef759c3 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<<21afc90d69f573614425f55c40ec5ebc>> */ /** @@ -41,6 +41,7 @@ internal class ReactNativeFeatureFlagsCxxAccessor : ReactNativeFeatureFlagsAcces private var enableFixForParentTagDuringReparentingCache: Boolean? = null private var enableFontScaleChangesUpdatingLayoutCache: Boolean? = null private var enableIOSBorderBoxShadowPathByDefaultCache: Boolean? = null + private var enableIOSBorderBoxShadowBackdropCache: Boolean? = null private var enableIOSExperimentalAutoFocusImplementationCache: Boolean? = null private var enableIOSTextBaselineOffsetPerLineCache: Boolean? = null private var enableIOSViewClipToPaddingBoxCache: Boolean? = null @@ -277,6 +278,15 @@ internal class ReactNativeFeatureFlagsCxxAccessor : ReactNativeFeatureFlagsAcces return cached } + override fun enableIOSBorderBoxShadowBackdrop(): Boolean { + var cached = enableIOSBorderBoxShadowBackdropCache + if (cached == null) { + cached = ReactNativeFeatureFlagsCxxInterop.enableIOSBorderBoxShadowBackdrop() + enableIOSBorderBoxShadowBackdropCache = cached + } + return cached + } + override fun enableIOSExperimentalAutoFocusImplementation(): Boolean { var cached = enableIOSExperimentalAutoFocusImplementationCache 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 0cb215c7fdd4..a231dbf817ce 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<<42800c900774644d53216aa2c7dd624c>> + * @generated SignedSource<<40b102e535b74e9e42fb22d82bbb2b02>> */ /** @@ -70,6 +70,8 @@ public object ReactNativeFeatureFlagsCxxInterop { @DoNotStrip @JvmStatic public external fun enableIOSBorderBoxShadowPathByDefault(): Boolean + @DoNotStrip @JvmStatic public external fun enableIOSBorderBoxShadowBackdrop(): Boolean + @DoNotStrip @JvmStatic public external fun enableIOSExperimentalAutoFocusImplementation(): Boolean @DoNotStrip @JvmStatic public external fun enableIOSTextBaselineOffsetPerLine(): 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 a9bc337db331..79b497289de2 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<<84c5a4338deb791e07275d340c3cd419>> + * @generated SignedSource<<0c58eb48dc4a6a5f0b971328c3d9cba8>> */ /** @@ -65,6 +65,8 @@ public open class ReactNativeFeatureFlagsDefaults : ReactNativeFeatureFlagsProvi override fun enableIOSBorderBoxShadowPathByDefault(): Boolean = false + override fun enableIOSBorderBoxShadowBackdrop(): Boolean = false + override fun enableIOSExperimentalAutoFocusImplementation(): Boolean = false override fun enableIOSTextBaselineOffsetPerLine(): 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 175b9500311c..53f332271bee 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<<402131230f486c9bee0e5d877419280b>> + * @generated SignedSource<> */ /** @@ -45,6 +45,7 @@ internal class ReactNativeFeatureFlagsLocalAccessor : ReactNativeFeatureFlagsAcc private var enableFixForParentTagDuringReparentingCache: Boolean? = null private var enableFontScaleChangesUpdatingLayoutCache: Boolean? = null private var enableIOSBorderBoxShadowPathByDefaultCache: Boolean? = null + private var enableIOSBorderBoxShadowBackdropCache: Boolean? = null private var enableIOSExperimentalAutoFocusImplementationCache: Boolean? = null private var enableIOSTextBaselineOffsetPerLineCache: Boolean? = null private var enableIOSViewClipToPaddingBoxCache: Boolean? = null @@ -302,6 +303,16 @@ internal class ReactNativeFeatureFlagsLocalAccessor : ReactNativeFeatureFlagsAcc return cached } + override fun enableIOSBorderBoxShadowBackdrop(): Boolean { + var cached = enableIOSBorderBoxShadowBackdropCache + if (cached == null) { + cached = currentProvider.enableIOSBorderBoxShadowBackdrop() + accessedFeatureFlags.add("enableIOSBorderBoxShadowBackdrop") + enableIOSBorderBoxShadowBackdropCache = cached + } + return cached + } + override fun enableIOSExperimentalAutoFocusImplementation(): Boolean { var cached = enableIOSExperimentalAutoFocusImplementationCache 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 b08ab0f30299..8fd31b750e49 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<<752961866ff51cfccd11f8c4314bc920>> + * @generated SignedSource<<39c4e466c465ce9e3f36147df862a057>> */ /** @@ -65,6 +65,8 @@ public interface ReactNativeFeatureFlagsProvider { @DoNotStrip public fun enableIOSBorderBoxShadowPathByDefault(): Boolean + @DoNotStrip public fun enableIOSBorderBoxShadowBackdrop(): Boolean + @DoNotStrip public fun enableIOSExperimentalAutoFocusImplementation(): Boolean @DoNotStrip public fun enableIOSTextBaselineOffsetPerLine(): 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 df1e51ffae9f..5f1546122f00 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<<5c4c763d4b19e17b107c87f772decaa8>> + * @generated SignedSource<<26a284920b9060963af1bac00838faba>> */ /** @@ -165,6 +165,12 @@ class ReactNativeFeatureFlagsJavaProvider return method(javaProvider_); } + bool enableIOSBorderBoxShadowBackdrop() override { + static const auto method = + getReactNativeFeatureFlagsProviderJavaClass()->getMethod("enableIOSBorderBoxShadowBackdrop"); + return method(javaProvider_); + } + bool enableIOSExperimentalAutoFocusImplementation() override { static const auto method = getReactNativeFeatureFlagsProviderJavaClass()->getMethod("enableIOSExperimentalAutoFocusImplementation"); @@ -550,6 +556,11 @@ bool JReactNativeFeatureFlagsCxxInterop::enableIOSBorderBoxShadowPathByDefault( return ReactNativeFeatureFlags::enableIOSBorderBoxShadowPathByDefault(); } +bool JReactNativeFeatureFlagsCxxInterop::enableIOSBorderBoxShadowBackdrop( + facebook::jni::alias_ref /*unused*/) { + return ReactNativeFeatureFlags::enableIOSBorderBoxShadowBackdrop(); +} + bool JReactNativeFeatureFlagsCxxInterop::enableIOSExperimentalAutoFocusImplementation( facebook::jni::alias_ref /*unused*/) { return ReactNativeFeatureFlags::enableIOSExperimentalAutoFocusImplementation(); @@ -874,6 +885,9 @@ void JReactNativeFeatureFlagsCxxInterop::registerNatives() { makeNativeMethod( "enableIOSBorderBoxShadowPathByDefault", JReactNativeFeatureFlagsCxxInterop::enableIOSBorderBoxShadowPathByDefault), + makeNativeMethod( + "enableIOSBorderBoxShadowBackdrop", + JReactNativeFeatureFlagsCxxInterop::enableIOSBorderBoxShadowBackdrop), makeNativeMethod( "enableIOSExperimentalAutoFocusImplementation", JReactNativeFeatureFlagsCxxInterop::enableIOSExperimentalAutoFocusImplementation), 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 483fac726126..beb97785fad8 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<> */ /** @@ -93,6 +93,9 @@ class JReactNativeFeatureFlagsCxxInterop static bool enableIOSBorderBoxShadowPathByDefault( facebook::jni::alias_ref); + static bool enableIOSBorderBoxShadowBackdrop( + facebook::jni::alias_ref); + static bool enableIOSExperimentalAutoFocusImplementation( 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 caad94b541bb..1b0ff0c12193 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<<0fb2c41e81fff95999861a345052d99f>> + * @generated SignedSource<<7ec3bbd2bc74c9717c211b9d19fe74cb>> */ /** @@ -110,6 +110,10 @@ bool ReactNativeFeatureFlags::enableIOSBorderBoxShadowPathByDefault() { return getAccessor().enableIOSBorderBoxShadowPathByDefault(); } +bool ReactNativeFeatureFlags::enableIOSBorderBoxShadowBackdrop() { + return getAccessor().enableIOSBorderBoxShadowBackdrop(); +} + bool ReactNativeFeatureFlags::enableIOSExperimentalAutoFocusImplementation() { return getAccessor().enableIOSExperimentalAutoFocusImplementation(); } diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.h b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.h index 3ca0da4039ec..5a6da2a78f64 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<<8016498b9ad36b7c418df4d05de17b1a>> */ /** @@ -144,6 +144,11 @@ class ReactNativeFeatureFlags { */ RN_EXPORT static bool enableIOSBorderBoxShadowPathByDefault(); + /** + * Add opaque backplates behind eligible legacy iOS border-box shadows. + */ + RN_EXPORT static bool enableIOSBorderBoxShadowBackdrop(); + /** * Fixes #56595 by moving the autoFocus from didMoveToWindow to viewDidAppear */ diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.cpp b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.cpp index 3f879a4cf3d5..b17f9ec57171 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<<676605110414d92058485a2f290d532b>> + * @generated SignedSource<> */ /** @@ -407,6 +407,24 @@ bool ReactNativeFeatureFlagsAccessor::enableIOSBorderBoxShadowPathByDefault() { return flagValue.value(); } +bool ReactNativeFeatureFlagsAccessor::enableIOSBorderBoxShadowBackdrop() { + auto flagValue = enableIOSBorderBoxShadowBackdrop_.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(21, "enableIOSBorderBoxShadowBackdrop"); + + flagValue = currentProvider_->enableIOSBorderBoxShadowBackdrop(); + enableIOSBorderBoxShadowBackdrop_ = flagValue; + } + + return flagValue.value(); +} + bool ReactNativeFeatureFlagsAccessor::enableIOSExperimentalAutoFocusImplementation() { auto flagValue = enableIOSExperimentalAutoFocusImplementation_.load(); @@ -416,7 +434,7 @@ bool ReactNativeFeatureFlagsAccessor::enableIOSExperimentalAutoFocusImplementati // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(21, "enableIOSExperimentalAutoFocusImplementation"); + markFlagAsAccessed(22, "enableIOSExperimentalAutoFocusImplementation"); flagValue = currentProvider_->enableIOSExperimentalAutoFocusImplementation(); enableIOSExperimentalAutoFocusImplementation_ = flagValue; @@ -434,7 +452,7 @@ bool ReactNativeFeatureFlagsAccessor::enableIOSTextBaselineOffsetPerLine() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(22, "enableIOSTextBaselineOffsetPerLine"); + markFlagAsAccessed(23, "enableIOSTextBaselineOffsetPerLine"); flagValue = currentProvider_->enableIOSTextBaselineOffsetPerLine(); enableIOSTextBaselineOffsetPerLine_ = flagValue; @@ -452,7 +470,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(23, "enableIOSViewClipToPaddingBox"); + markFlagAsAccessed(24, "enableIOSViewClipToPaddingBox"); flagValue = currentProvider_->enableIOSViewClipToPaddingBox(); enableIOSViewClipToPaddingBox_ = flagValue; @@ -470,7 +488,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(24, "enableInteropViewManagerClassLookUpOptimizationIOS"); + markFlagAsAccessed(25, "enableInteropViewManagerClassLookUpOptimizationIOS"); flagValue = currentProvider_->enableInteropViewManagerClassLookUpOptimizationIOS(); enableInteropViewManagerClassLookUpOptimizationIOS_ = flagValue; @@ -488,7 +506,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(25, "enableLayoutAnimationsOnAndroid"); + markFlagAsAccessed(26, "enableLayoutAnimationsOnAndroid"); flagValue = currentProvider_->enableLayoutAnimationsOnAndroid(); enableLayoutAnimationsOnAndroid_ = flagValue; @@ -506,7 +524,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(26, "enableLayoutAnimationsOnIOS"); + markFlagAsAccessed(27, "enableLayoutAnimationsOnIOS"); flagValue = currentProvider_->enableLayoutAnimationsOnIOS(); enableLayoutAnimationsOnIOS_ = flagValue; @@ -524,7 +542,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(27, "enableMainQueueCoordinatorOnIOS"); + markFlagAsAccessed(28, "enableMainQueueCoordinatorOnIOS"); flagValue = currentProvider_->enableMainQueueCoordinatorOnIOS(); enableMainQueueCoordinatorOnIOS_ = flagValue; @@ -542,7 +560,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(28, "enableMainQueueModulesOnIOS"); + markFlagAsAccessed(29, "enableMainQueueModulesOnIOS"); flagValue = currentProvider_->enableMainQueueModulesOnIOS(); enableMainQueueModulesOnIOS_ = flagValue; @@ -560,7 +578,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(29, "enableModuleArgumentNSNullConversionIOS"); + markFlagAsAccessed(30, "enableModuleArgumentNSNullConversionIOS"); flagValue = currentProvider_->enableModuleArgumentNSNullConversionIOS(); enableModuleArgumentNSNullConversionIOS_ = flagValue; @@ -578,7 +596,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(30, "enableNativeCSSParsing"); + markFlagAsAccessed(31, "enableNativeCSSParsing"); flagValue = currentProvider_->enableNativeCSSParsing(); enableNativeCSSParsing_ = flagValue; @@ -596,7 +614,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(31, "enableNetworkEventReporting"); + markFlagAsAccessed(32, "enableNetworkEventReporting"); flagValue = currentProvider_->enableNetworkEventReporting(); enableNetworkEventReporting_ = flagValue; @@ -614,7 +632,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(32, "enableNewBackgroundAndBorderDrawables"); + markFlagAsAccessed(33, "enableNewBackgroundAndBorderDrawables"); flagValue = currentProvider_->enableNewBackgroundAndBorderDrawables(); enableNewBackgroundAndBorderDrawables_ = flagValue; @@ -632,7 +650,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(33, "enablePreparedTextLayout"); + markFlagAsAccessed(34, "enablePreparedTextLayout"); flagValue = currentProvider_->enablePreparedTextLayout(); enablePreparedTextLayout_ = flagValue; @@ -650,7 +668,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(34, "enablePropsUpdateReconciliationAndroid"); + markFlagAsAccessed(35, "enablePropsUpdateReconciliationAndroid"); flagValue = currentProvider_->enablePropsUpdateReconciliationAndroid(); enablePropsUpdateReconciliationAndroid_ = flagValue; @@ -668,7 +686,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(35, "enableResourceTimingAPI"); + markFlagAsAccessed(36, "enableResourceTimingAPI"); flagValue = currentProvider_->enableResourceTimingAPI(); enableResourceTimingAPI_ = flagValue; @@ -686,7 +704,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(36, "enableSynchronousStateUpdates"); + markFlagAsAccessed(37, "enableSynchronousStateUpdates"); flagValue = currentProvider_->enableSynchronousStateUpdates(); enableSynchronousStateUpdates_ = flagValue; @@ -704,7 +722,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(37, "enableViewCulling"); + markFlagAsAccessed(38, "enableViewCulling"); flagValue = currentProvider_->enableViewCulling(); enableViewCulling_ = flagValue; @@ -722,7 +740,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(38, "enableViewRecycling"); + markFlagAsAccessed(39, "enableViewRecycling"); flagValue = currentProvider_->enableViewRecycling(); enableViewRecycling_ = flagValue; @@ -740,7 +758,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(39, "enableViewRecyclingForText"); + markFlagAsAccessed(40, "enableViewRecyclingForText"); flagValue = currentProvider_->enableViewRecyclingForText(); enableViewRecyclingForText_ = flagValue; @@ -758,7 +776,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(40, "enableViewRecyclingForView"); + markFlagAsAccessed(41, "enableViewRecyclingForView"); flagValue = currentProvider_->enableViewRecyclingForView(); enableViewRecyclingForView_ = flagValue; @@ -776,7 +794,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(41, "enableVirtualViewDebugFeatures"); + markFlagAsAccessed(42, "enableVirtualViewDebugFeatures"); flagValue = currentProvider_->enableVirtualViewDebugFeatures(); enableVirtualViewDebugFeatures_ = flagValue; @@ -794,7 +812,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(42, "enableVirtualViewRenderState"); + markFlagAsAccessed(43, "enableVirtualViewRenderState"); flagValue = currentProvider_->enableVirtualViewRenderState(); enableVirtualViewRenderState_ = flagValue; @@ -812,7 +830,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(43, "enableVirtualViewWindowFocusDetection"); + markFlagAsAccessed(44, "enableVirtualViewWindowFocusDetection"); flagValue = currentProvider_->enableVirtualViewWindowFocusDetection(); enableVirtualViewWindowFocusDetection_ = flagValue; @@ -830,7 +848,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(44, "fixMappingOfEventPrioritiesBetweenFabricAndReact"); + markFlagAsAccessed(45, "fixMappingOfEventPrioritiesBetweenFabricAndReact"); flagValue = currentProvider_->fixMappingOfEventPrioritiesBetweenFabricAndReact(); fixMappingOfEventPrioritiesBetweenFabricAndReact_ = flagValue; @@ -848,7 +866,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(45, "fuseboxEnabledRelease"); + markFlagAsAccessed(46, "fuseboxEnabledRelease"); flagValue = currentProvider_->fuseboxEnabledRelease(); fuseboxEnabledRelease_ = flagValue; @@ -866,7 +884,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(46, "fuseboxNetworkInspectionEnabled"); + markFlagAsAccessed(47, "fuseboxNetworkInspectionEnabled"); flagValue = currentProvider_->fuseboxNetworkInspectionEnabled(); fuseboxNetworkInspectionEnabled_ = flagValue; @@ -884,7 +902,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(47, "hideOffscreenVirtualViewsOnIOS"); + markFlagAsAccessed(48, "hideOffscreenVirtualViewsOnIOS"); flagValue = currentProvider_->hideOffscreenVirtualViewsOnIOS(); hideOffscreenVirtualViewsOnIOS_ = flagValue; @@ -902,7 +920,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(48, "preparedTextCacheSize"); + markFlagAsAccessed(49, "preparedTextCacheSize"); flagValue = currentProvider_->preparedTextCacheSize(); preparedTextCacheSize_ = flagValue; @@ -920,7 +938,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(49, "preventShadowTreeCommitExhaustion"); + markFlagAsAccessed(50, "preventShadowTreeCommitExhaustion"); flagValue = currentProvider_->preventShadowTreeCommitExhaustion(); preventShadowTreeCommitExhaustion_ = flagValue; @@ -938,7 +956,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(50, "traceTurboModulePromiseRejectionsOnAndroid"); + markFlagAsAccessed(51, "traceTurboModulePromiseRejectionsOnAndroid"); flagValue = currentProvider_->traceTurboModulePromiseRejectionsOnAndroid(); traceTurboModulePromiseRejectionsOnAndroid_ = flagValue; @@ -956,7 +974,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(51, "updateRuntimeShadowNodeReferencesOnCommit"); + markFlagAsAccessed(52, "updateRuntimeShadowNodeReferencesOnCommit"); flagValue = currentProvider_->updateRuntimeShadowNodeReferencesOnCommit(); updateRuntimeShadowNodeReferencesOnCommit_ = flagValue; @@ -974,7 +992,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(52, "useAlwaysAvailableJSErrorHandling"); + markFlagAsAccessed(53, "useAlwaysAvailableJSErrorHandling"); flagValue = currentProvider_->useAlwaysAvailableJSErrorHandling(); useAlwaysAvailableJSErrorHandling_ = flagValue; @@ -992,7 +1010,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(53, "useFabricInterop"); + markFlagAsAccessed(54, "useFabricInterop"); flagValue = currentProvider_->useFabricInterop(); useFabricInterop_ = flagValue; @@ -1010,7 +1028,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(54, "useNativeEqualsInNativeReadableArrayAndroid"); + markFlagAsAccessed(55, "useNativeEqualsInNativeReadableArrayAndroid"); flagValue = currentProvider_->useNativeEqualsInNativeReadableArrayAndroid(); useNativeEqualsInNativeReadableArrayAndroid_ = flagValue; @@ -1028,7 +1046,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(55, "useNativeTransformHelperAndroid"); + markFlagAsAccessed(56, "useNativeTransformHelperAndroid"); flagValue = currentProvider_->useNativeTransformHelperAndroid(); useNativeTransformHelperAndroid_ = flagValue; @@ -1046,7 +1064,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(56, "useNativeViewConfigsInBridgelessMode"); + markFlagAsAccessed(57, "useNativeViewConfigsInBridgelessMode"); flagValue = currentProvider_->useNativeViewConfigsInBridgelessMode(); useNativeViewConfigsInBridgelessMode_ = flagValue; @@ -1064,7 +1082,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(57, "useOptimizedEventBatchingOnAndroid"); + markFlagAsAccessed(58, "useOptimizedEventBatchingOnAndroid"); flagValue = currentProvider_->useOptimizedEventBatchingOnAndroid(); useOptimizedEventBatchingOnAndroid_ = flagValue; @@ -1082,7 +1100,7 @@ bool ReactNativeFeatureFlagsAccessor::usePullModelOnAndroid() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(58, "usePullModelOnAndroid"); + markFlagAsAccessed(59, "usePullModelOnAndroid"); flagValue = currentProvider_->usePullModelOnAndroid(); usePullModelOnAndroid_ = flagValue; @@ -1100,7 +1118,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(59, "useRawPropsJsiValue"); + markFlagAsAccessed(60, "useRawPropsJsiValue"); flagValue = currentProvider_->useRawPropsJsiValue(); useRawPropsJsiValue_ = flagValue; @@ -1118,7 +1136,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(60, "useShadowNodeStateOnClone"); + markFlagAsAccessed(61, "useShadowNodeStateOnClone"); flagValue = currentProvider_->useShadowNodeStateOnClone(); useShadowNodeStateOnClone_ = flagValue; @@ -1136,7 +1154,7 @@ bool ReactNativeFeatureFlagsAccessor::useSilenceErrorSMMViewNotFound() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(61, "useSilenceErrorSMMViewNotFound"); + markFlagAsAccessed(62, "useSilenceErrorSMMViewNotFound"); flagValue = currentProvider_->useSilenceErrorSMMViewNotFound(); useSilenceErrorSMMViewNotFound_ = flagValue; @@ -1154,7 +1172,7 @@ bool ReactNativeFeatureFlagsAccessor::useTraitHiddenOnIOS() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(62, "useTraitHiddenOnIOS"); + markFlagAsAccessed(63, "useTraitHiddenOnIOS"); flagValue = currentProvider_->useTraitHiddenOnIOS(); useTraitHiddenOnIOS_ = flagValue; @@ -1172,7 +1190,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(63, "useTurboModuleInterop"); + markFlagAsAccessed(64, "useTurboModuleInterop"); flagValue = currentProvider_->useTurboModuleInterop(); useTurboModuleInterop_ = flagValue; @@ -1190,7 +1208,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(64, "useTurboModules"); + markFlagAsAccessed(65, "useTurboModules"); flagValue = currentProvider_->useTurboModules(); useTurboModules_ = flagValue; @@ -1208,7 +1226,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(65, "virtualViewPrerenderRatio"); + markFlagAsAccessed(66, "virtualViewPrerenderRatio"); flagValue = currentProvider_->virtualViewPrerenderRatio(); virtualViewPrerenderRatio_ = flagValue; @@ -1226,7 +1244,7 @@ bool ReactNativeFeatureFlagsAccessor::runtimeCrashUiThreadUtils() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(66, "runtimeCrashUiThreadUtils"); + markFlagAsAccessed(67, "runtimeCrashUiThreadUtils"); flagValue = currentProvider_->runtimeCrashUiThreadUtils(); runtimeCrashUiThreadUtils_ = flagValue; diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.h b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.h index aef569c64182..13a2e053697b 100644 --- a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.h +++ b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.h @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<> + * @generated SignedSource<<8b2400f41b0cf3d64e89a97ef1c08f9c>> */ /** @@ -53,6 +53,7 @@ class ReactNativeFeatureFlagsAccessor { bool enableFixForParentTagDuringReparenting(); bool enableFontScaleChangesUpdatingLayout(); bool enableIOSBorderBoxShadowPathByDefault(); + bool enableIOSBorderBoxShadowBackdrop(); bool enableIOSExperimentalAutoFocusImplementation(); bool enableIOSTextBaselineOffsetPerLine(); bool enableIOSViewClipToPaddingBox(); @@ -110,7 +111,7 @@ class ReactNativeFeatureFlagsAccessor { std::unique_ptr currentProvider_; bool wasOverridden_; - std::array, 67> accessedFeatureFlags_; + std::array, 68> accessedFeatureFlags_; std::atomic> commonTestFlag_; std::atomic> animatedShouldSignalBatch_; @@ -133,6 +134,7 @@ class ReactNativeFeatureFlagsAccessor { std::atomic> enableFixForParentTagDuringReparenting_; std::atomic> enableFontScaleChangesUpdatingLayout_; std::atomic> enableIOSBorderBoxShadowPathByDefault_; + std::atomic> enableIOSBorderBoxShadowBackdrop_; std::atomic> enableIOSExperimentalAutoFocusImplementation_; std::atomic> enableIOSTextBaselineOffsetPerLine_; std::atomic> enableIOSViewClipToPaddingBox_; diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDefaults.h b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDefaults.h index f72e13144897..7fda80fe89c5 100644 --- a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDefaults.h +++ b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDefaults.h @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<> + * @generated SignedSource<> */ /** @@ -111,6 +111,10 @@ class ReactNativeFeatureFlagsDefaults : public ReactNativeFeatureFlagsProvider { return false; } + bool enableIOSBorderBoxShadowBackdrop() override { + return false; + } + bool enableIOSExperimentalAutoFocusImplementation() override { return false; } diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDynamicProvider.h b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDynamicProvider.h index 3f950c1e4956..08d8c2890cb1 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<<8a75aa937ab50bb2ea4e920c046e2d1e>> + * @generated SignedSource<<8018fdfb715556c9af4c5a696e62cca4>> */ /** @@ -234,6 +234,15 @@ class ReactNativeFeatureFlagsDynamicProvider : public ReactNativeFeatureFlagsDef return ReactNativeFeatureFlagsDefaults::enableIOSBorderBoxShadowPathByDefault(); } + bool enableIOSBorderBoxShadowBackdrop() override { + auto value = values_["enableIOSBorderBoxShadowBackdrop"]; + if (!value.isNull()) { + return value.getBool(); + } + + return ReactNativeFeatureFlagsDefaults::enableIOSBorderBoxShadowBackdrop(); + } + bool enableIOSExperimentalAutoFocusImplementation() override { auto value = values_["enableIOSExperimentalAutoFocusImplementation"]; if (!value.isNull()) { diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsProvider.h b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsProvider.h index 657ea5aa42e5..a009a1b6b4a7 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<<4a1153f2a9e598520f7008ebdddc98eb>> */ /** @@ -46,6 +46,7 @@ class ReactNativeFeatureFlagsProvider { virtual bool enableFixForParentTagDuringReparenting() = 0; virtual bool enableFontScaleChangesUpdatingLayout() = 0; virtual bool enableIOSBorderBoxShadowPathByDefault() = 0; + virtual bool enableIOSBorderBoxShadowBackdrop() = 0; virtual bool enableIOSExperimentalAutoFocusImplementation() = 0; virtual bool enableIOSTextBaselineOffsetPerLine() = 0; virtual bool enableIOSViewClipToPaddingBox() = 0; diff --git a/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.cpp b/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.cpp index 391a81d8afc7..0325eafd768e 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<<35759e0c6596d969ed6cfbf393ff4908>> */ /** @@ -149,6 +149,11 @@ bool NativeReactNativeFeatureFlags::enableIOSBorderBoxShadowPathByDefault( return ReactNativeFeatureFlags::enableIOSBorderBoxShadowPathByDefault(); } +bool NativeReactNativeFeatureFlags::enableIOSBorderBoxShadowBackdrop( + jsi::Runtime& /*runtime*/) { + return ReactNativeFeatureFlags::enableIOSBorderBoxShadowBackdrop(); +} + bool NativeReactNativeFeatureFlags::enableIOSExperimentalAutoFocusImplementation( jsi::Runtime& /*runtime*/) { return ReactNativeFeatureFlags::enableIOSExperimentalAutoFocusImplementation(); diff --git a/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.h b/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.h index b70c1b43c6d4..6320ceea39b5 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<<3a4e198b27be6df0f2d707c60b635278>> + * @generated SignedSource<> */ /** @@ -78,6 +78,8 @@ class NativeReactNativeFeatureFlags bool enableIOSBorderBoxShadowPathByDefault(jsi::Runtime& runtime); + bool enableIOSBorderBoxShadowBackdrop(jsi::Runtime& runtime); + bool enableIOSExperimentalAutoFocusImplementation(jsi::Runtime& runtime); bool enableIOSTextBaselineOffsetPerLine(jsi::Runtime& runtime); diff --git a/packages/react-native/ReactCommon/react/renderer/mounting/Differentiator.cpp b/packages/react-native/ReactCommon/react/renderer/mounting/Differentiator.cpp index 7b8682dff3b1..62cfef680a31 100644 --- a/packages/react-native/ReactCommon/react/renderer/mounting/Differentiator.cpp +++ b/packages/react-native/ReactCommon/react/renderer/mounting/Differentiator.cpp @@ -1384,19 +1384,38 @@ ShadowViewMutation::List calculateShadowViewMutations( auto oldRootShadowView = ShadowView(oldRootShadowNode); auto newRootShadowView = ShadowView(newRootShadowNode); + ShadowViewEnvironmentMap oldEnvironments{}; + ShadowViewEnvironmentMap newEnvironments{}; + const ShadowViewEnvironmentMap* oldEnvironmentsPointer = nullptr; + const ShadowViewEnvironmentMap* newEnvironmentsPointer = nullptr; + const bool shadowBackdropEnabled = + ReactNativeFeatureFlags::enableIOSBorderBoxShadowBackdrop(); + if (shadowBackdropEnabled) { + oldEnvironments = collectShadowViewEnvironments(oldRootShadowNode); + newEnvironments = collectShadowViewEnvironments(newRootShadowNode); + oldEnvironmentsPointer = &oldEnvironments; + newEnvironmentsPointer = &newEnvironments; + } + if (oldRootShadowView != newRootShadowView) { mutations.push_back(ShadowViewMutation::UpdateMutation( oldRootShadowView, newRootShadowView, {})); } auto sliceOne = sliceChildShadowNodeViewPairs( - ShadowViewNodePair{.shadowNode = &oldRootShadowNode}, + ShadowViewNodePair{ + .shadowView = oldRootShadowView, + .shadowNode = &oldRootShadowNode, + .environments = oldEnvironmentsPointer}, viewNodePairScope, false /* allowFlattened */, {} /* layoutOffset */, {} /* cullingContext */); auto sliceTwo = sliceChildShadowNodeViewPairs( - ShadowViewNodePair{.shadowNode = &newRootShadowNode}, + ShadowViewNodePair{ + .shadowView = newRootShadowView, + .shadowNode = &newRootShadowNode, + .environments = newEnvironmentsPointer}, viewNodePairScope, false /* allowFlattened */, {} /* layoutOffset */, diff --git a/packages/react-native/ReactCommon/react/renderer/mounting/ShadowView.cpp b/packages/react-native/ReactCommon/react/renderer/mounting/ShadowView.cpp index d681e942a167..ebc72ca49ee4 100644 --- a/packages/react-native/ReactCommon/react/renderer/mounting/ShadowView.cpp +++ b/packages/react-native/ReactCommon/react/renderer/mounting/ShadowView.cpp @@ -39,7 +39,8 @@ bool ShadowView::operator==(const ShadowView& rhs) const { this->props, this->eventEmitter, this->layoutMetrics, - this->state) == + this->state, + this->environment) == std::tie( rhs.surfaceId, rhs.tag, @@ -47,7 +48,8 @@ bool ShadowView::operator==(const ShadowView& rhs) const { rhs.props, rhs.eventEmitter, rhs.layoutMetrics, - rhs.state); + rhs.state, + rhs.environment); } bool ShadowView::operator!=(const ShadowView& rhs) const { diff --git a/packages/react-native/ReactCommon/react/renderer/mounting/ShadowView.h b/packages/react-native/ReactCommon/react/renderer/mounting/ShadowView.h index 36dd55d37a58..bf51f609f9c9 100644 --- a/packages/react-native/ReactCommon/react/renderer/mounting/ShadowView.h +++ b/packages/react-native/ReactCommon/react/renderer/mounting/ShadowView.h @@ -7,6 +7,7 @@ #pragma once +#include #include #include #include @@ -17,6 +18,142 @@ namespace facebook::react { +enum class ShadowBackdropHostKind : uint8_t { + Root, + View, + Unknown, +}; + +enum class ShadowBackdropKind : uint8_t { + None, + Provider, + Barrier, +}; + +enum class ShadowBackdropReason : uint8_t { + None, + EarlierSibling, + TranslucentAncestor, + UnknownAncestor, + VisualEffect, + RoundedAncestor, + OutsideProvider, + DynamicColor, +}; + +struct ShadowBackdrop final { + ShadowBackdropKind kind{ShadowBackdropKind::None}; + ShadowBackdropReason reason{ShadowBackdropReason::None}; + SharedColor color{}; + Rect coverage{}; + Tag providerTag{-1}; + Tag barrierTag{-1}; + + bool operator==(const ShadowBackdrop& other) const = default; +}; + +/* + * Rendering context derived from the complete, laid-out Fabric tree before + * diffing. Component views do not receive it as a prop because it depends on + * their mounted surroundings. + */ +struct ShadowViewEnvironment final { + ShadowBackdrop shadowBackdrop{}; + + bool operator==(const ShadowViewEnvironment& other) const = default; +}; + +inline bool shadowBackdropContainsRect(const Rect& outer, const Rect& inner) { + return inner.getMinX() >= outer.getMinX() && + inner.getMaxX() <= outer.getMaxX() && + inner.getMinY() >= outer.getMinY() && + inner.getMaxY() <= outer.getMaxY(); +} + +inline bool shadowBackdropHasRoundedCorners(const BorderMetrics& borderMetrics) { + const auto& radii = borderMetrics.borderRadii; + return radii.topLeft.vertical != 0 || radii.topLeft.horizontal != 0 || + radii.topRight.vertical != 0 || radii.topRight.horizontal != 0 || + radii.bottomLeft.vertical != 0 || radii.bottomLeft.horizontal != 0 || + radii.bottomRight.vertical != 0 || radii.bottomRight.horizontal != 0; +} + +inline bool shadowBackdropHasVisualEffect(const ViewProps& props) { + return !props.boxShadow.empty() || !props.filter.empty() || + !props.backgroundImage.empty() || props.mixBlendMode != BlendMode::Normal || + props.outlineWidth != 0 || isColorMeaningful(props.shadowColor); +} + +inline ShadowBackdrop shadowBackdropBarrier(ShadowBackdropReason reason) { + return { + .kind = ShadowBackdropKind::Barrier, + .reason = reason, + }; +} + +inline ShadowBackdrop shadowBackdropForChild( + const ShadowBackdrop& backdrop, + const LayoutMetrics& childLayoutMetrics) { + if (backdrop.kind != ShadowBackdropKind::Provider) { + return backdrop; + } + + const auto& childFrame = childLayoutMetrics.frame; + if (!shadowBackdropContainsRect(backdrop.coverage, childFrame)) { + return shadowBackdropBarrier(ShadowBackdropReason::OutsideProvider); + } + + ShadowBackdrop childBackdrop = backdrop; + childBackdrop.coverage.origin.x -= childFrame.origin.x; + childBackdrop.coverage.origin.y -= childFrame.origin.y; + return childBackdrop; +} + +inline ShadowBackdrop resolveShadowBackdropForChild( + const ShadowBackdrop& inheritedBackdrop, + ShadowBackdropHostKind parentHostKind, + const ViewProps& parentProps, + const LayoutMetrics& parentLayoutMetrics, + const LayoutMetrics& childLayoutMetrics, + bool hasEarlierSiblingBarrier) { + if (parentHostKind == ShadowBackdropHostKind::Unknown) { + return shadowBackdropBarrier(ShadowBackdropReason::UnknownAncestor); + } + + if (hasEarlierSiblingBarrier) { + return shadowBackdropBarrier(ShadowBackdropReason::EarlierSibling); + } + + if (parentProps.opacity < 0.999 || shadowBackdropHasVisualEffect(parentProps)) { + return shadowBackdropBarrier(ShadowBackdropReason::VisualEffect); + } + + const auto borderMetrics = parentProps.resolveBorderMetrics(parentLayoutMetrics); + const bool hasRoundedCorners = shadowBackdropHasRoundedCorners(borderMetrics); + if (hasRoundedCorners && !parentProps.getClipsContentToBounds()) { + return shadowBackdropBarrier(ShadowBackdropReason::RoundedAncestor); + } + + if (parentProps.backgroundColor) { + const auto backgroundAlpha = alphaFromColor(parentProps.backgroundColor); + if (backgroundAlpha == 255) { + const auto& parentSize = parentLayoutMetrics.frame.size; + ShadowBackdrop parentBackdrop{ + .kind = ShadowBackdropKind::Provider, + .color = parentProps.backgroundColor, + .coverage = {{0, 0}, parentSize}, + }; + return shadowBackdropForChild(parentBackdrop, childLayoutMetrics); + } + + if (backgroundAlpha > 0) { + return shadowBackdropBarrier(ShadowBackdropReason::TranslucentAncestor); + } + } + + return shadowBackdropForChild(inheritedBackdrop, childLayoutMetrics); +} + /* * Describes a view that can be mounted. * This is exposed to the mounting layer. @@ -46,6 +183,7 @@ struct ShadowView final { EventEmitter::Shared eventEmitter{}; LayoutMetrics layoutMetrics{EmptyLayoutMetrics}; State::Shared state{}; + ShadowViewEnvironment environment{}; }; #if RN_DEBUG_STRING_CONVERTIBLE @@ -61,6 +199,27 @@ std::vector getDebugProps( namespace std { +template <> +struct hash { + size_t operator()(const facebook::react::ShadowBackdrop& backdrop) const { + return facebook::react::hash_combine( + 0, + backdrop.kind, + backdrop.reason, + backdrop.color, + backdrop.coverage, + backdrop.providerTag, + backdrop.barrierTag); + } +}; + +template <> +struct hash { + size_t operator()(const facebook::react::ShadowViewEnvironment& environment) const { + return facebook::react::hash_combine(0, environment.shadowBackdrop); + } +}; + template <> struct hash { size_t operator()(const facebook::react::ShadowView& shadowView) const { @@ -72,7 +231,8 @@ struct hash { shadowView.props, shadowView.eventEmitter, shadowView.layoutMetrics, - shadowView.state); + shadowView.state, + shadowView.environment); } }; diff --git a/packages/react-native/ReactCommon/react/renderer/mounting/internal/ShadowViewNodePair.h b/packages/react-native/ReactCommon/react/renderer/mounting/internal/ShadowViewNodePair.h index 7f64b8e9a8b4..32b4bf1e75a2 100644 --- a/packages/react-native/ReactCommon/react/renderer/mounting/internal/ShadowViewNodePair.h +++ b/packages/react-native/ReactCommon/react/renderer/mounting/internal/ShadowViewNodePair.h @@ -7,12 +7,16 @@ #pragma once +#include + #include #include #include namespace facebook::react { +using ShadowViewEnvironmentMap = std::unordered_map; + /* * Describes pair of a `ShadowView` and a `ShadowNode`. * This is not exposed to the mounting layer. @@ -33,6 +37,10 @@ struct ShadowViewNodePair final { bool isConcreteView{true}; Point contextOrigin{0, 0}; + // The top-level differ owns this map. Keeping the pointer on each pair lets + // nested slicing reuse the same derived environments without another walk. + const ShadowViewEnvironmentMap* environments{nullptr}; + size_t mountIndex{0}; /** diff --git a/packages/react-native/ReactCommon/react/renderer/mounting/internal/sliceChildShadowNodeViewPairs.cpp b/packages/react-native/ReactCommon/react/renderer/mounting/internal/sliceChildShadowNodeViewPairs.cpp index cda18e4121c6..1ca5f3f55465 100644 --- a/packages/react-native/ReactCommon/react/renderer/mounting/internal/sliceChildShadowNodeViewPairs.cpp +++ b/packages/react-native/ReactCommon/react/renderer/mounting/internal/sliceChildShadowNodeViewPairs.cpp @@ -6,13 +6,188 @@ */ #include "sliceChildShadowNodeViewPairs.h" +#include #include +#include +#include #include #include "ShadowViewNodePair.h" namespace facebook::react { +#ifndef NDEBUG +struct ShadowBackdropPropagationStats { + size_t nodesVisited{}; + size_t environmentsComputed{}; + size_t providers{}; + size_t barriers{}; + + void didVisitNode() { + nodesVisited++; + } + + void didComputeEnvironment(const ShadowBackdrop& shadowBackdrop) { + environmentsComputed++; + if (shadowBackdrop.kind == ShadowBackdropKind::Provider) { + providers++; + } else if (shadowBackdrop.kind == ShadowBackdropKind::Barrier) { + barriers++; + } + } +}; +#else +struct ShadowBackdropPropagationStats { + void didVisitNode() {} + void didComputeEnvironment(const ShadowBackdrop&) {} +}; +#endif + +static ShadowBackdropHostKind shadowBackdropHostKind( + const ShadowNode& shadowNode) { + const auto componentName = shadowNode.getComponentName(); + if (componentName == RootComponentName) { + return ShadowBackdropHostKind::Root; + } + + if (componentName == ViewComponentName) { + return ShadowBackdropHostKind::View; + } + + return ShadowBackdropHostKind::Unknown; +} + +static const ViewProps& shadowBackdropViewProps( + const ShadowNode& shadowNode, + ShadowBackdropHostKind hostKind) { + static const ViewProps emptyViewProps{}; + if (hostKind == ShadowBackdropHostKind::Unknown) { + return emptyViewProps; + } + + const auto& props = *shadowNode.getProps(); + return static_cast(props); +} + +static bool shadowBackdropSiblingHasUnboundedEffect( + const ShadowNode& shadowNode) { + const auto hostKind = shadowBackdropHostKind(shadowNode); + if (hostKind == ShadowBackdropHostKind::Unknown) { + return true; + } + + const auto& props = shadowBackdropViewProps(shadowNode, hostKind); + const Transform identityTransform{}; + return shadowBackdropHasVisualEffect(props) || props.transform != identityTransform; +} + +static bool shadowBackdropRectsIntersect(const Rect& first, const Rect& second) { + const auto intersection = Rect::intersect(first, second); + return intersection.size.width > 0 && intersection.size.height > 0; +} + +static void collectShadowBackdropsForChildren( + const ShadowNode& parentShadowNode, + const ShadowBackdrop& inheritedBackdrop, + ShadowViewEnvironmentMap& environments, + ShadowBackdropPropagationStats& stats) { + const auto parentHostKind = shadowBackdropHostKind(parentShadowNode); + const auto& parentProps = + shadowBackdropViewProps(parentShadowNode, parentHostKind); + const auto parentShadowView = ShadowView(parentShadowNode); + const auto parentLayoutMetrics = parentShadowView.layoutMetrics; + + std::vector childShadowNodes; + for (const auto& childShadowNode : parentShadowNode.getChildren()) { + const auto childShadowNodePointer = childShadowNode.get(); + childShadowNodes.push_back(childShadowNodePointer); + } + + std::stable_sort( + childShadowNodes.begin(), + childShadowNodes.end(), + [](const ShadowNode* first, const ShadowNode* second) { + return first->getOrderIndex() < second->getOrderIndex(); + }); + + Rect earlierSiblingBounds{}; + bool hasEarlierSiblingBounds = false; + bool hasEarlierSiblingUnboundedEffect = false; + + for (const auto* childShadowNode : childShadowNodes) { + stats.didVisitNode(); + const auto childShadowView = ShadowView(*childShadowNode); + const auto childLayoutMetrics = childShadowView.layoutMetrics; + const auto childBounds = childLayoutMetrics.getOverflowInsetFrame(); + bool hasEarlierSiblingBarrier = hasEarlierSiblingUnboundedEffect; + if (hasEarlierSiblingBounds && + shadowBackdropRectsIntersect(earlierSiblingBounds, childBounds)) { + hasEarlierSiblingBarrier = true; + } + + auto childBackdrop = resolveShadowBackdropForChild( + inheritedBackdrop, + parentHostKind, + parentProps, + parentLayoutMetrics, + childLayoutMetrics, + hasEarlierSiblingBarrier); + if (childBackdrop.kind == ShadowBackdropKind::Provider && + childBackdrop.providerTag == -1) { + childBackdrop.providerTag = parentShadowNode.getTag(); + } + if (childBackdrop.kind == ShadowBackdropKind::Barrier && + childBackdrop.barrierTag == -1) { + childBackdrop.barrierTag = parentShadowNode.getTag(); + } + const auto childTag = childShadowNode->getTag(); + ShadowViewEnvironment childEnvironment{}; + childEnvironment.shadowBackdrop = childBackdrop; + const auto insertion = environments.emplace(childTag, childEnvironment); + react_native_assert(insertion.second); + stats.didComputeEnvironment(childBackdrop); + + collectShadowBackdropsForChildren( + *childShadowNode, childBackdrop, environments, stats); + + if (childLayoutMetrics != EmptyLayoutMetrics) { + if (hasEarlierSiblingBounds) { + earlierSiblingBounds.unionInPlace(childBounds); + } else { + earlierSiblingBounds = childBounds; + hasEarlierSiblingBounds = true; + } + } + + if (shadowBackdropSiblingHasUnboundedEffect(*childShadowNode)) { + hasEarlierSiblingUnboundedEffect = true; + } + } +} + +ShadowViewEnvironmentMap collectShadowViewEnvironments( + const ShadowNode& rootShadowNode) { + ShadowViewEnvironmentMap environments{}; + ShadowBackdropPropagationStats stats{}; +#ifndef NDEBUG + TraceSection propagationSection("ShadowBackdrop::propagate"); +#endif + collectShadowBackdropsForChildren(rootShadowNode, {}, environments, stats); +#ifndef NDEBUG + TraceSection statsSection( + "ShadowBackdrop::propagateStats", + "nodes", + stats.nodesVisited, + "environments", + stats.environmentsComputed, + "providers", + stats.providers, + "barriers", + stats.barriers); +#endif + return environments; +} + /* * Sorting comparator for `reorderInPlaceIfNeeded`. */ @@ -53,7 +228,8 @@ static void sliceChildShadowNodeViewPairsRecursively( ViewNodePairScope& scope, Point layoutOffset, const ShadowNode& shadowNode, - const CullingContext& cullingContext) { + const CullingContext& cullingContext, + const ShadowViewEnvironmentMap* environments) { for (const auto& sharedChildShadowNode : shadowNode.getChildren()) { auto& childShadowNode = *sharedChildShadowNode; #ifndef ANDROID @@ -68,6 +244,12 @@ static void sliceChildShadowNodeViewPairsRecursively( } #endif auto shadowView = ShadowView(childShadowNode); + if (environments) { + const auto childTag = childShadowNode.getTag(); + const auto environment = environments->find(childTag); + react_native_assert(environment != environments->end()); + shadowView.environment = environment->second; + } if (ReactNativeFeatureFlags::enableViewCulling()) { auto isViewCullable = @@ -127,7 +309,8 @@ static void sliceChildShadowNodeViewPairsRecursively( &childShadowNode, areChildrenFlattened, isConcreteView, - storedOrigin}); + storedOrigin, + environments}); if (shadowView.layoutMetrics.positionType == PositionType::Static) { auto it = pairList.begin(); @@ -141,7 +324,8 @@ static void sliceChildShadowNodeViewPairsRecursively( scope, origin, childShadowNode, - cullingContextCopy); + cullingContextCopy, + environments); } } else { pairList.push_back(&scope.back()); @@ -153,7 +337,8 @@ static void sliceChildShadowNodeViewPairsRecursively( scope, origin, childShadowNode, - cullingContextCopy); + cullingContextCopy, + environments); } } } @@ -181,7 +366,8 @@ std::vector sliceChildShadowNodeViewPairs( scope, layoutOffset, shadowNode, - cullingContext); + cullingContext, + shadowNodePair.environments); // Sorting pairs based on `orderIndex` if needed. reorderInPlaceIfNeeded(pairList); diff --git a/packages/react-native/ReactCommon/react/renderer/mounting/internal/sliceChildShadowNodeViewPairs.h b/packages/react-native/ReactCommon/react/renderer/mounting/internal/sliceChildShadowNodeViewPairs.h index 278be0114465..05e2a5063b50 100644 --- a/packages/react-native/ReactCommon/react/renderer/mounting/internal/sliceChildShadowNodeViewPairs.h +++ b/packages/react-native/ReactCommon/react/renderer/mounting/internal/sliceChildShadowNodeViewPairs.h @@ -10,11 +10,10 @@ #include #include "CullingContext.h" +#include "ShadowViewNodePair.h" namespace facebook::react { -struct ShadowViewNodePair; - /** * During differ, we need to keep some `ShadowViewNodePair`s in memory. * Some `ShadowViewNodePair`s are referenced from std::vectors returned @@ -38,6 +37,13 @@ struct ShadowViewNodePair; */ using ViewNodePairScope = std::deque; +/* + * Computes the mounting environments for a complete, laid-out Shadow Tree. + * The Differentiator owns the result for the duration of one old/new diff. + */ +ShadowViewEnvironmentMap collectShadowViewEnvironments( + const ShadowNode& rootShadowNode); + /** * Generates a list of `ShadowViewNodePair`s that represents a layer of a * flattened view hierarchy. The V2 version preserves nodes even if they do diff --git a/packages/react-native/ReactCommon/react/renderer/mounting/stubs/stubs.cpp b/packages/react-native/ReactCommon/react/renderer/mounting/stubs/stubs.cpp index 22e7b6c07f6f..d2a59503e28a 100644 --- a/packages/react-native/ReactCommon/react/renderer/mounting/stubs/stubs.cpp +++ b/packages/react-native/ReactCommon/react/renderer/mounting/stubs/stubs.cpp @@ -7,6 +7,7 @@ #include "stubs.h" +#include #include #include #include @@ -74,8 +75,20 @@ StubViewTree buildStubViewTreeWithoutUsingDifferentiator( auto mutations = ShadowViewMutation::List{}; mutations.reserve(256); + ShadowViewEnvironmentMap environments{}; + const ShadowViewEnvironmentMap* environmentsPointer = nullptr; + const bool shadowBackdropEnabled = + ReactNativeFeatureFlags::enableIOSBorderBoxShadowBackdrop(); + if (shadowBackdropEnabled) { + environments = collectShadowViewEnvironments(rootShadowNode); + environmentsPointer = &environments; + } + ViewNodePairScope scope; - ShadowViewNodePair rootShadowNodePair{.shadowNode = &rootShadowNode}; + ShadowViewNodePair rootShadowNodePair{ + .shadowNode = &rootShadowNode, + .environments = environmentsPointer, + }; calculateShadowViewMutationsForNewTree( mutations, scope, diff --git a/packages/react-native/ReactCommon/react/renderer/mounting/tests/ShadowBackdropTest.cpp b/packages/react-native/ReactCommon/react/renderer/mounting/tests/ShadowBackdropTest.cpp new file mode 100644 index 000000000000..744e212e418a --- /dev/null +++ b/packages/react-native/ReactCommon/react/renderer/mounting/tests/ShadowBackdropTest.cpp @@ -0,0 +1,254 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace facebook::react { + +namespace { + +LayoutMetrics layoutMetrics(Size size, Point origin = {}) { + LayoutMetrics result{}; + result.frame = {origin, size}; + return result; +} + +} // namespace + +class ShadowBackdropTestFeatureFlags : public ReactNativeFeatureFlagsDefaults { + public: + bool enableIOSBorderBoxShadowBackdrop() override { + return true; + } +}; + +class ShadowBackdropDifferentiatorTest : public ::testing::Test { + protected: + void SetUp() override { + ReactNativeFeatureFlags::dangerouslyReset(); + auto featureFlags = std::make_unique(); + ReactNativeFeatureFlags::override(std::move(featureFlags)); + } + + void TearDown() override { + ReactNativeFeatureFlags::dangerouslyReset(); + } +}; + +TEST(ShadowBackdropTest, opaque_view_provides_backdrop_to_child) { + ViewProps parentProps{}; + parentProps.backgroundColor = blackColor(); + + auto parentLayout = layoutMetrics({100, 100}); + auto childLayout = layoutMetrics({40, 40}, {20, 20}); + auto backdrop = resolveShadowBackdropForChild( + {}, + ShadowBackdropHostKind::View, + parentProps, + parentLayout, + childLayout, + false); + + EXPECT_EQ(backdrop.kind, ShadowBackdropKind::Provider); + EXPECT_EQ(backdrop.color, blackColor()); +} + +TEST(ShadowBackdropTest, transparent_wrapper_relays_opaque_backdrop) { + ViewProps providerProps{}; + providerProps.backgroundColor = blackColor(); + auto providerLayout = layoutMetrics({100, 100}); + auto wrapperLayout = layoutMetrics({60, 60}, {20, 20}); + auto inheritedBackdrop = resolveShadowBackdropForChild( + {}, + ShadowBackdropHostKind::View, + providerProps, + providerLayout, + wrapperLayout, + false); + + ViewProps wrapperProps{}; + auto childLayout = layoutMetrics({40, 40}, {10, 10}); + auto backdrop = resolveShadowBackdropForChild( + inheritedBackdrop, + ShadowBackdropHostKind::View, + wrapperProps, + wrapperLayout, + childLayout, + false); + + EXPECT_EQ(backdrop.kind, ShadowBackdropKind::Provider); + EXPECT_EQ(backdrop.color, blackColor()); +} + +TEST(ShadowBackdropTest, transparent_wrapper_does_not_extend_provider_coverage) { + ViewProps providerProps{}; + providerProps.backgroundColor = blackColor(); + auto providerLayout = layoutMetrics({100, 100}); + auto wrapperLayout = layoutMetrics({40, 40}, {60, 0}); + auto inheritedBackdrop = resolveShadowBackdropForChild( + {}, + ShadowBackdropHostKind::View, + providerProps, + providerLayout, + wrapperLayout, + false); + + ViewProps wrapperProps{}; + auto childLayout = layoutMetrics({40, 40}, {20, 0}); + auto backdrop = resolveShadowBackdropForChild( + inheritedBackdrop, + ShadowBackdropHostKind::View, + wrapperProps, + wrapperLayout, + childLayout, + false); + + EXPECT_EQ(backdrop.kind, ShadowBackdropKind::Barrier); + EXPECT_EQ(backdrop.reason, ShadowBackdropReason::OutsideProvider); +} + +TEST(ShadowBackdropTest, translucent_ancestor_is_a_barrier) { + ViewProps parentProps{}; + parentProps.backgroundColor = colorFromRGBA(255, 255, 255, 31); + + auto parentLayout = layoutMetrics({100, 100}); + auto childLayout = layoutMetrics({40, 40}, {20, 20}); + auto backdrop = resolveShadowBackdropForChild( + {}, + ShadowBackdropHostKind::View, + parentProps, + parentLayout, + childLayout, + false); + + EXPECT_EQ(backdrop.kind, ShadowBackdropKind::Barrier); + EXPECT_EQ(backdrop.reason, ShadowBackdropReason::TranslucentAncestor); +} + +TEST(ShadowBackdropTest, overlapping_preceding_sibling_is_a_barrier) { + ViewProps parentProps{}; + parentProps.backgroundColor = blackColor(); + + auto parentLayout = layoutMetrics({100, 100}); + auto childLayout = layoutMetrics({40, 40}, {20, 20}); + auto backdrop = resolveShadowBackdropForChild( + {}, + ShadowBackdropHostKind::View, + parentProps, + parentLayout, + childLayout, + true); + + EXPECT_EQ(backdrop.kind, ShadowBackdropKind::Barrier); + EXPECT_EQ(backdrop.reason, ShadowBackdropReason::EarlierSibling); +} + +TEST(ShadowBackdropTest, unknown_ancestor_is_a_barrier) { + ViewProps parentProps{}; + auto parentLayout = layoutMetrics({100, 100}); + auto childLayout = layoutMetrics({40, 40}, {20, 20}); + auto backdrop = resolveShadowBackdropForChild( + {}, + ShadowBackdropHostKind::Unknown, + parentProps, + parentLayout, + childLayout, + false); + + EXPECT_EQ(backdrop.kind, ShadowBackdropKind::Barrier); + EXPECT_EQ(backdrop.reason, ShadowBackdropReason::UnknownAncestor); +} + +TEST(ShadowBackdropTest, provider_does_not_escape_its_own_bounds) { + ViewProps parentProps{}; + parentProps.backgroundColor = blackColor(); + + auto parentLayout = layoutMetrics({100, 100}); + auto childLayout = layoutMetrics({40, 40}, {80, 80}); + auto backdrop = resolveShadowBackdropForChild( + {}, + ShadowBackdropHostKind::View, + parentProps, + parentLayout, + childLayout, + false); + + EXPECT_EQ(backdrop.kind, ShadowBackdropKind::Barrier); + EXPECT_EQ(backdrop.reason, ShadowBackdropReason::OutsideProvider); +} + +TEST_F( + ShadowBackdropDifferentiatorTest, + root_environment_pass_reaches_nested_created_view) { + std::shared_ptr rootShadowNode; + + auto providerProps = std::make_shared(); + providerProps->backgroundColor = blackColor(); + + auto shadowHostProps = std::make_shared(); + shadowHostProps->shadowPathIOS = ShadowPathMode::BorderBox; + + // clang-format off + auto element = + Element() + .reference(rootShadowNode) + .tag(1) + .children({ + Element() + .tag(2) + .props(providerProps) + .children({ + Element() + .tag(3) + .props(shadowHostProps) + }) + }); + // clang-format on + + auto builder = simpleComponentBuilder(); + builder.build(element); + rootShadowNode->layoutIfNeeded(); + + auto emptyRootFragment = ShadowNodeFragment{ + ShadowNodeFragment::propsPlaceholder(), + ShadowNode::emptySharedShadowNodeSharedList(), + }; + auto emptyRootShadowNode = rootShadowNode->clone(emptyRootFragment); + auto mutations = + calculateShadowViewMutations(*emptyRootShadowNode, *rootShadowNode); + + const ShadowView* createdShadowHost = nullptr; + for (const auto& mutation : mutations) { + if (mutation.type == ShadowViewMutation::Create && + mutation.newChildShadowView.tag == 3) { + createdShadowHost = &mutation.newChildShadowView; + break; + } + } + + ASSERT_NE(createdShadowHost, nullptr); + const auto& backdrop = createdShadowHost->environment.shadowBackdrop; + EXPECT_EQ(backdrop.kind, ShadowBackdropKind::Provider); + EXPECT_EQ(backdrop.color, blackColor()); + EXPECT_EQ(backdrop.providerTag, 2); +} + +} // namespace facebook::react diff --git a/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js b/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js index 79fa981d73dc..03cd91085be6 100644 --- a/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js +++ b/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js @@ -270,6 +270,17 @@ const definitions: FeatureFlagDefinitions = { }, ossReleaseStage: 'none', }, + enableIOSBorderBoxShadowBackdrop: { + defaultValue: false, + metadata: { + dateAdded: '2026-07-15', + description: + 'Add opaque backplates behind eligible legacy iOS border-box shadows.', + expectedReleaseValue: true, + purpose: 'experimentation', + }, + ossReleaseStage: 'none', + }, enableIOSExperimentalAutoFocusImplementation: { defaultValue: false, metadata: { diff --git a/packages/react-native/src/private/featureflags/ReactNativeFeatureFlags.js b/packages/react-native/src/private/featureflags/ReactNativeFeatureFlags.js index 028318b0630f..4301f154976a 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<<1a49394fcdbb433c3f94597bb32827c9>> * @flow strict * @noformat */ @@ -72,6 +72,7 @@ export type ReactNativeFeatureFlags = $ReadOnly<{ enableFixForParentTagDuringReparenting: Getter, enableFontScaleChangesUpdatingLayout: Getter, enableIOSBorderBoxShadowPathByDefault: Getter, + enableIOSBorderBoxShadowBackdrop: Getter, enableIOSExperimentalAutoFocusImplementation: Getter, enableIOSTextBaselineOffsetPerLine: Getter, enableIOSViewClipToPaddingBox: Getter, @@ -288,6 +289,10 @@ export const enableFontScaleChangesUpdatingLayout: Getter = createNativ * Use the border box for legacy iOS shadow paths when shadowPathIOS is omitted. */ export const enableIOSBorderBoxShadowPathByDefault: Getter = createNativeFlagGetter('enableIOSBorderBoxShadowPathByDefault', false); +/** + * Add opaque backplates behind eligible legacy iOS border-box shadows. + */ +export const enableIOSBorderBoxShadowBackdrop: Getter = createNativeFlagGetter('enableIOSBorderBoxShadowBackdrop', false); /** * Fixes #56595 by moving the autoFocus from didMoveToWindow to viewDidAppear */ diff --git a/packages/react-native/src/private/featureflags/specs/NativeReactNativeFeatureFlags.js b/packages/react-native/src/private/featureflags/specs/NativeReactNativeFeatureFlags.js index 702327785608..737010f9026f 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<<1a88c0eb715d767b15fe668e6e60129c>> * @flow strict * @noformat */ @@ -46,6 +46,7 @@ export interface Spec extends TurboModule { +enableFixForParentTagDuringReparenting?: () => boolean; +enableFontScaleChangesUpdatingLayout?: () => boolean; +enableIOSBorderBoxShadowPathByDefault?: () => boolean; + +enableIOSBorderBoxShadowBackdrop?: () => boolean; +enableIOSExperimentalAutoFocusImplementation?: () => boolean; +enableIOSTextBaselineOffsetPerLine?: () => boolean; +enableIOSViewClipToPaddingBox?: () => boolean; diff --git a/packages/rn-tester/js/examples/Playground/RNTesterShadowPathPlayground.js b/packages/rn-tester/js/examples/Playground/RNTesterShadowPathPlayground.js index 8f1826954623..5441558fb22c 100644 --- a/packages/rn-tester/js/examples/Playground/RNTesterShadowPathPlayground.js +++ b/packages/rn-tester/js/examples/Playground/RNTesterShadowPathPlayground.js @@ -15,15 +15,26 @@ import {useEffect, useRef, useState} from 'react'; import { Animated, Pressable, + ScrollView, StyleSheet, - Switch, Text, View, } from 'react-native'; -import {enableIOSBorderBoxShadowPathByDefault} from 'react-native/src/private/featureflags/ReactNativeFeatureFlags'; +import { + enableIOSBorderBoxShadowBackdrop, + enableIOSBorderBoxShadowPathByDefault, +} from 'react-native/src/private/featureflags/ReactNativeFeatureFlags'; type ShadowPathMode = 'auto' | 'border-box' | 'content-alpha'; -type ShadowPathSelection = 'default' | ShadowPathMode; +type ShadowPathSelection = 'default' | 'box-shadow' | ShadowPathMode; +type AnimationMode = 'off' | 'scale' | 'bounds' | 'content'; +type BackdropScenario = + | 'eligible' + | 'translucent-ancestor' + | 'earlier-sibling' + | 'unknown-ancestor' + | 'rounded-visible' + | 'rounded-clipped'; const SHADOW_PATH_OPTIONS: $ReadOnlyArray<{ label: string, @@ -33,34 +44,78 @@ const SHADOW_PATH_OPTIONS: $ReadOnlyArray<{ {label: 'Auto', value: 'auto'}, {label: 'Border Box', value: 'border-box'}, {label: 'Content Alpha', value: 'content-alpha'}, + {label: 'Box Shadow', value: 'box-shadow'}, +]; + +const ANIMATION_OPTIONS: $ReadOnlyArray<{ + label: string, + value: AnimationMode, +}> = [ + {label: 'Off', value: 'off'}, + {label: 'Scale', value: 'scale'}, + {label: 'Bounds', value: 'bounds'}, + {label: 'Content', value: 'content'}, +]; + +const BACKDROP_SCENARIOS: $ReadOnlyArray<{ + label: string, + value: BackdropScenario, +}> = [ + {label: 'Eligible', value: 'eligible'}, + {label: 'Translucent', value: 'translucent-ancestor'}, + {label: 'Sibling', value: 'earlier-sibling'}, + {label: 'Unknown', value: 'unknown-ancestor'}, + {label: 'Rounded', value: 'rounded-visible'}, + {label: 'Clipped', value: 'rounded-clipped'}, ]; function ShadowPathPlayground(): React.Node { const [selectedMode, setSelectedMode] = - useState('default'); - const [animationEnabled, setAnimationEnabled] = useState(false); - const animationProgressRef = useRef(null); - if (animationProgressRef.current == null) { - animationProgressRef.current = new Animated.Value(0); + useState('box-shadow'); + const [animationMode, setAnimationMode] = useState('off'); + const [backdropScenario, setBackdropScenario] = + useState('eligible'); + // Keep layout and native-driven animations on separate values. Animated + // values cannot safely switch between the JS and native drivers. + const nativeAnimationProgressRef = useRef(null); + if (nativeAnimationProgressRef.current == null) { + nativeAnimationProgressRef.current = new Animated.Value(0); } - const animationProgress = animationProgressRef.current; + const nativeAnimationProgress = nativeAnimationProgressRef.current; + const boundsAnimationProgressRef = useRef(null); + if (boundsAnimationProgressRef.current == null) { + boundsAnimationProgressRef.current = new Animated.Value(0); + } + const boundsAnimationProgress = boundsAnimationProgressRef.current; useEffect(() => { - if (!animationEnabled) { - animationProgress.stopAnimation(); - animationProgress.setValue(0); + nativeAnimationProgress.stopAnimation(); + nativeAnimationProgress.setValue(0); + boundsAnimationProgress.stopAnimation(); + boundsAnimationProgress.setValue(0); + + if (animationMode === 'off') { return; } + // Bounds cannot use the native driver because it intentionally runs + // layout on every frame to invalidate the shadow host's silhouette. + let animationProgress = nativeAnimationProgress; + let useNativeDriver = true; + if (animationMode === 'bounds') { + animationProgress = boundsAnimationProgress; + useNativeDriver = false; + } + const growAnimation = Animated.timing(animationProgress, { duration: 700, toValue: 1, - useNativeDriver: true, + useNativeDriver, }); const shrinkAnimation = Animated.timing(animationProgress, { duration: 700, toValue: 0, - useNativeDriver: true, + useNativeDriver, }); const animationSequence = Animated.sequence([ growAnimation, @@ -72,24 +127,64 @@ function ShadowPathPlayground(): React.Node { return () => { animation.stop(); }; - }, [animationEnabled, animationProgress]); + }, [animationMode, boundsAnimationProgress, nativeAnimationProgress]); - const scale = animationProgress.interpolate({ + const scale = nativeAnimationProgress.interpolate({ inputRange: [0, 1], outputRange: [1, 1.12], }); - const animatedStyle = {transform: [{scale}]}; - const shadowPathProps: {shadowPathIOS?: ShadowPathMode} = - selectedMode === 'default' ? {} : {shadowPathIOS: selectedMode}; + const animatedWidth = boundsAnimationProgress.interpolate({ + inputRange: [0, 1], + outputRange: [160, 220], + }); + const contentTranslation = nativeAnimationProgress.interpolate({ + inputRange: [0, 1], + outputRange: [0, 24], + }); + // Keep the two shadow APIs mutually exclusive so profiling the Box Shadow + // mode cannot also measure legacy shadow work. + const usesBoxShadow = selectedMode === 'box-shadow'; + let shadowPathProps: {shadowPathIOS?: ShadowPathMode} = {}; + if (selectedMode !== 'default' && selectedMode !== 'box-shadow') { + shadowPathProps = {shadowPathIOS: selectedMode}; + } const flagEnabled = enableIOSBorderBoxShadowPathByDefault(); const flagDefault = flagEnabled ? 'Border Box' : 'Auto'; + const backdropEnabled = enableIOSBorderBoxShadowBackdrop(); const activeMode = SHADOW_PATH_OPTIONS.find( option => option.value === selectedMode, ); + const activeAnimation = ANIMATION_OPTIONS.find( + option => option.value === animationMode, + ); + const activeBackdropScenario = BACKDROP_SCENARIOS.find( + option => option.value === backdropScenario, + ); if (activeMode == null) { throw new Error('Selected shadow path mode must have a label.'); } + if (activeAnimation == null) { + throw new Error('Selected animation mode must have a label.'); + } + if (activeBackdropScenario == null) { + throw new Error('Selected backdrop scenario must have a label.'); + } + + const renderButtonShadowHost = (): React.Node => ( + + + Translucent button + + + ); return ( @@ -118,23 +213,113 @@ function ShadowPathPlayground(): React.Node { })} - - Scale animation - + Animation workload + + {ANIMATION_OPTIONS.map(option => { + const selected = option.value === animationMode; + return ( + setAnimationMode(option.value)} + style={[ + styles.segment, + selected ? styles.selectedSegment : null, + ]}> + + {option.label} + + + ); + })} Active mode: {activeMode.label} Feature-flag default: {flagDefault} + Animation: {activeAnimation.label} + + Backplate: {backdropEnabled ? 'Enabled' : 'Disabled'} + + + In Instruments, inspect the ShadowBackdrop::propagate trace for + visited-node and eligibility counts. + + + Scale exercises cached compositing. Bounds repeatedly changes the + button's silhouette. Content moves a child within the irregular shadow + host. + - Translucent button hierarchy + Backdrop proof scenario + + {BACKDROP_SCENARIOS.map(option => { + const selected = option.value === backdropScenario; + return ( + setBackdropScenario(option.value)} + style={[ + styles.segment, + selected ? styles.selectedSegment : null, + ]}> + + {option.label} + + + ); + })} + + + Scenario: {activeBackdropScenario.label} + - - - Translucent button + {backdropScenario === 'eligible' ? ( + + {renderButtonShadowHost()} - + ) : null} + {backdropScenario === 'translucent-ancestor' ? ( + + + {renderButtonShadowHost()} + + + ) : null} + {backdropScenario === 'earlier-sibling' ? ( + + + {renderButtonShadowHost()} + + ) : null} + {backdropScenario === 'unknown-ancestor' ? ( + + {renderButtonShadowHost()} + + ) : null} + {backdropScenario === 'rounded-visible' ? ( + + {renderButtonShadowHost()} + + ) : null} + {backdropScenario === 'rounded-clipped' ? ( + + {renderButtonShadowHost()} + + ) : null} Irregular composited content @@ -145,12 +330,69 @@ function ShadowPathPlayground(): React.Node { + style={[ + styles.irregularShadowHost, + usesBoxShadow + ? styles.irregularBoxShadow + : styles.irregularLegacyShadow, + animationMode === 'scale' ? {transform: [{scale}]} : null, + ]}> - + + + Structural stress cases + + The left column has deep transparent wrappers. The right column has a + dense list where every item is a shadow host. + + + + + + + + Deep wrapper + + + + + + + {[0, 1, 2, 3].map(index => ( + + + List item {index + 1} + + + ))} + + ); } @@ -165,7 +407,6 @@ const styles = StyleSheet.create({ borderRadius: 8, borderWidth: StyleSheet.hairlineWidth, flexDirection: 'row', - overflow: 'hidden', }, segment: { alignItems: 'center', @@ -188,11 +429,6 @@ const styles = StyleSheet.create({ color: 'white', fontWeight: '600', }, - animationRow: { - alignItems: 'center', - flexDirection: 'row', - justifyContent: 'space-between', - }, label: { fontSize: 16, }, @@ -216,13 +452,66 @@ const styles = StyleSheet.create({ minHeight: 140, overflow: 'visible', }, + opaqueBackdropProvider: { + alignItems: 'center', + backgroundColor: '#6750a4', + justifyContent: 'center', + minHeight: 100, + minWidth: 260, + }, + translucentAncestorBarrier: { + alignItems: 'center', + backgroundColor: 'rgba(255, 255, 255, 0.12)', + justifyContent: 'center', + minHeight: 100, + minWidth: 260, + }, + earlierSiblingBarrier: { + backgroundColor: '#ff453a', + height: 64, + position: 'absolute', + width: 190, + }, + unknownAncestorBarrier: { + backgroundColor: '#6750a4', + maxHeight: 100, + minWidth: 260, + }, + unknownAncestorContent: { + alignItems: 'center', + justifyContent: 'center', + minHeight: 100, + }, + roundedVisibleBarrier: { + alignItems: 'center', + backgroundColor: '#6750a4', + borderRadius: 24, + justifyContent: 'center', + minHeight: 100, + minWidth: 260, + }, + roundedClippedProvider: { + alignItems: 'center', + backgroundColor: '#6750a4', + borderRadius: 24, + justifyContent: 'center', + minHeight: 100, + minWidth: 260, + overflow: 'hidden', + }, buttonShadowHost: { borderRadius: 8, + width: 160, + }, + buttonLegacyShadow: { shadowColor: 'black', shadowOffset: {width: 0, height: 1}, shadowOpacity: 0.14, shadowRadius: 4, }, + buttonBoxShadow: { + boxShadow: '0px 1px 8px rgba(0, 0, 0, 0.14)', + }, translucentButton: { alignItems: 'center', backgroundColor: 'rgba(255, 255, 255, 0.12)', @@ -231,6 +520,7 @@ const styles = StyleSheet.create({ minHeight: 40, minWidth: 160, overflow: 'hidden', + width: '100%', }, buttonLabel: { color: '#1c1c1e', @@ -239,11 +529,16 @@ const styles = StyleSheet.create({ irregularShadowHost: { height: 72, position: 'relative', + width: 160, + }, + irregularLegacyShadow: { shadowColor: 'black', shadowOffset: {width: 0, height: 1}, shadowOpacity: 0.25, shadowRadius: 4, - width: 160, + }, + irregularBoxShadow: { + boxShadow: '0px 1px 8px rgba(0, 0, 0, 0.25)', }, circle: { backgroundColor: '#ff9f0a', @@ -264,6 +559,47 @@ const styles = StyleSheet.create({ backgroundColor: '#bf5af2', left: 96, }, + stressStage: { + backgroundColor: '#6750a4', + gap: 12, + padding: 16, + }, + deepWrapperOne: { + padding: 2, + }, + deepWrapperTwo: { + padding: 2, + }, + deepWrapperThree: { + padding: 2, + }, + denseList: { + gap: 8, + }, + stressShadowHost: { + borderRadius: 8, + width: 160, + }, + stressLegacyShadow: { + shadowColor: 'black', + shadowOffset: {width: 0, height: 1}, + shadowOpacity: 0.14, + shadowRadius: 4, + }, + stressBoxShadow: { + boxShadow: '0px 1px 8px rgba(0, 0, 0, 0.14)', + }, + stressTranslucentContent: { + alignItems: 'center', + backgroundColor: 'rgba(255, 255, 255, 0.12)', + borderRadius: 8, + justifyContent: 'center', + minHeight: 32, + }, + stressLabel: { + color: '#1c1c1e', + fontSize: 12, + }, }); export default ({ From be0d705f905b153d1363c25269b5d5f2a53f2576 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hanno=20J=2E=20G=C3=B6decke?= Date: Thu, 16 Jul 2026 15:18:08 +0200 Subject: [PATCH 5/6] revert(ios): remove opaque shadow backdrop prototype --- CONTEXT.md | 39 -- .../0001-ios-shadow-backplate-propagation.md | 3 - .../View/RCTViewComponentView.h | 3 - .../View/RCTViewComponentView.mm | 183 +------- .../Mounting/RCTComponentViewProtocol.h | 15 +- .../Fabric/Mounting/RCTMountingManager.mm | 8 - .../Mounting/UIView+ComponentViewProtocol.h | 3 - .../Mounting/UIView+ComponentViewProtocol.mm | 6 - .../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 | 112 ++--- .../ReactNativeFeatureFlagsAccessor.h | 6 +- .../ReactNativeFeatureFlagsDefaults.h | 6 +- .../ReactNativeFeatureFlagsDynamicProvider.h | 11 +- .../ReactNativeFeatureFlagsProvider.h | 3 +- .../NativeReactNativeFeatureFlags.cpp | 7 +- .../NativeReactNativeFeatureFlags.h | 4 +- .../renderer/mounting/Differentiator.cpp | 23 +- .../react/renderer/mounting/ShadowView.cpp | 6 +- .../react/renderer/mounting/ShadowView.h | 162 +------ .../mounting/internal/ShadowViewNodePair.h | 8 - .../sliceChildShadowNodeViewPairs.cpp | 196 +-------- .../internal/sliceChildShadowNodeViewPairs.h | 10 +- .../react/renderer/mounting/stubs/stubs.cpp | 15 +- .../mounting/tests/ShadowBackdropTest.cpp | 254 ----------- .../ReactNativeFeatureFlags.config.js | 11 - .../featureflags/ReactNativeFeatureFlags.js | 7 +- .../specs/NativeReactNativeFeatureFlags.js | 3 +- .../RNTesterShadowPathPlayground.js | 412 ++---------------- 37 files changed, 125 insertions(+), 1470 deletions(-) delete mode 100644 CONTEXT.md delete mode 100644 docs/adr/0001-ios-shadow-backplate-propagation.md delete mode 100644 packages/react-native/ReactCommon/react/renderer/mounting/tests/ShadowBackdropTest.cpp diff --git a/CONTEXT.md b/CONTEXT.md deleted file mode 100644 index ba278d1fa347..000000000000 --- a/CONTEXT.md +++ /dev/null @@ -1,39 +0,0 @@ -# iOS Shadow Rendering - -This context defines the silhouettes and compositing behavior of iOS shadows rendered by React Native views. - -## Language - -**Border-box shadow**: -A legacy iOS shadow whose silhouette is the view's border box and resolved corner radii, independent of rendered content alpha. The caller is responsible for ensuring that this silhouette remains appropriate for its content and backdrop. - -**Content-alpha shadow**: -A legacy iOS shadow whose silhouette is derived from the composited alpha of the view and its descendants. - -**Backdrop precomposition**: -Rendering an opaque backplate beneath a border-box shadow host so the shadow cannot show through otherwise translucent pixels. It is a best-effort optimization applied only after border-box behavior has already been selected. - -**Backdrop provider**: -A canonical view or root with a concrete opaque solid background that supplies an inherited backdrop color. Unknown or content-bearing component types terminate Fabric backdrop propagation. - -**Inherited backdrop color**: -A concrete opaque color supplied by a backdrop provider and passed unchanged through transparent wrappers. - -**Translucent ancestor barrier**: -A non-opaque background between a backdrop provider and a shadow host. It terminates backdrop propagation because using one unblended backplate would change the rendered pixels. - -**Concrete color**: -A fixed RGBA color whose value does not depend on native appearance traits. Native dynamic and semantic system colors are not concrete colors. - -**Opaque backplate**: -A border-box-shaped fill beneath a shadow host's normal background and content. It uses the uniform opaque backdrop color to prevent a border-box shadow from showing through translucent pixels. - -**Uniform opaque backdrop**: -A single fully opaque color behind every point of a view's border box. Previously painted content that makes the color vary means the backdrop is not uniform. -_Avoid_: Parent background color - -**Propagation source**: -Backdrop eligibility is derived once for each complete, laid-out old and new -Fabric tree at mounting-diff time. Later flattening slices read the derived -environment from a tag-indexed sidecar map. The mounted UIKit hierarchy cannot -be used because view flattening can erase logical provider boundaries. diff --git a/docs/adr/0001-ios-shadow-backplate-propagation.md b/docs/adr/0001-ios-shadow-backplate-propagation.md deleted file mode 100644 index a919f36ab96f..000000000000 --- a/docs/adr/0001-ios-shadow-backplate-propagation.md +++ /dev/null @@ -1,3 +0,0 @@ -# Use opaque backplates for eligible legacy border-box shadows - -For legacy iOS `shadow*` props resolved to `BorderBox`, React Native may render an opaque, fill-only backplate matching the host's border path beneath its existing background and content. This prevents the shadow from showing through translucent host content without adding a mask. A concrete opaque color is supplied by a canonical Root or View and relayed unchanged only through transparent canonical Views; translucent ancestors, unknown components, complex effects, rounded providers without clipping, and potentially intersecting earlier siblings are barriers. When eligibility is unknown, retain ordinary unmasked border-box behavior. Eligibility is propagated through the Fabric tree because view flattening can remove logical provider boundaries from the mounted UIKit hierarchy. The mounting Differentiator computes one tag-indexed environment map for each complete old and new laid-out tree, and all nested flattening slices reuse those maps. diff --git a/packages/react-native/React/Fabric/Mounting/ComponentViews/View/RCTViewComponentView.h b/packages/react-native/React/Fabric/Mounting/ComponentViews/View/RCTViewComponentView.h index 6578e825dd0a..a3c58cb16f57 100644 --- a/packages/react-native/React/Fabric/Mounting/ComponentViews/View/RCTViewComponentView.h +++ b/packages/react-native/React/Fabric/Mounting/ComponentViews/View/RCTViewComponentView.h @@ -16,7 +16,6 @@ #import #import #import -#import NS_ASSUME_NONNULL_BEGIN @@ -75,8 +74,6 @@ NS_ASSUME_NONNULL_BEGIN oldLayoutMetrics:(const facebook::react::LayoutMetrics &)oldLayoutMetrics NS_REQUIRES_SUPER; - (void)finalizeUpdates:(RNComponentViewUpdateMask)updateMask NS_REQUIRES_SUPER; - (void)prepareForRecycle NS_REQUIRES_SUPER; -- (void)updateEnvironment:(const facebook::react::ShadowViewEnvironment &)environment - oldEnvironment:(const facebook::react::ShadowViewEnvironment &)oldEnvironment; - (UIView *)betterHitTest:(CGPoint)point withEvent:(UIEvent *)event; /* diff --git a/packages/react-native/React/Fabric/Mounting/ComponentViews/View/RCTViewComponentView.mm b/packages/react-native/React/Fabric/Mounting/ComponentViews/View/RCTViewComponentView.mm index 795b2c64d91e..62eff72135af 100644 --- a/packages/react-native/React/Fabric/Mounting/ComponentViews/View/RCTViewComponentView.mm +++ b/packages/react-native/React/Fabric/Mounting/ComponentViews/View/RCTViewComponentView.mm @@ -21,7 +21,6 @@ #import #import #import -#import #import #import #import @@ -38,77 +37,10 @@ using namespace facebook::react; const CGFloat BACKGROUND_COLOR_ZPOSITION = -1024.0f; -// The backplate must sit below the normal background so translucent content -// blends over it while the backplate still occludes the shadow interior. -const CGFloat SHADOW_BACKDROP_ZPOSITION = BACKGROUND_COLOR_ZPOSITION - 1.0f; - -static NSString *RCTShadowBackdropReasonString(ShadowBackdropReason reason) -{ - switch (reason) { - case ShadowBackdropReason::None: - return @"none"; - case ShadowBackdropReason::EarlierSibling: - return @"earlier-sibling"; - case ShadowBackdropReason::TranslucentAncestor: - return @"translucent-ancestor"; - case ShadowBackdropReason::UnknownAncestor: - return @"unknown-ancestor"; - case ShadowBackdropReason::VisualEffect: - return @"visual-effect"; - case ShadowBackdropReason::RoundedAncestor: - return @"rounded-ancestor"; - case ShadowBackdropReason::OutsideProvider: - return @"outside-provider"; - case ShadowBackdropReason::DynamicColor: - return @"dynamic-color"; - } - - react_native_assert(false); - return @"invalid"; -} - -static BOOL RCTShadowBackdropColorIsConcreteOpaque(UIColor *color) -{ - UITraitCollection *lightTraits = - [UITraitCollection traitCollectionWithUserInterfaceStyle:UIUserInterfaceStyleLight]; - UITraitCollection *darkTraits = - [UITraitCollection traitCollectionWithUserInterfaceStyle:UIUserInterfaceStyleDark]; - UITraitCollection *highContrastTraits = - [UITraitCollection traitCollectionWithAccessibilityContrast:UIAccessibilityContrastHigh]; - UITraitCollection *highContrastLightTraits = - [UITraitCollection traitCollectionWithTraitsFromCollections:@[ lightTraits, highContrastTraits ]]; - UITraitCollection *highContrastDarkTraits = - [UITraitCollection traitCollectionWithTraitsFromCollections:@[ darkTraits, highContrastTraits ]]; - - UIColor *lightColor = [color resolvedColorWithTraitCollection:lightTraits]; - UIColor *darkColor = [color resolvedColorWithTraitCollection:darkTraits]; - UIColor *highContrastLightColor = [color resolvedColorWithTraitCollection:highContrastLightTraits]; - UIColor *highContrastDarkColor = [color resolvedColorWithTraitCollection:highContrastDarkTraits]; - CGColorRef lightCGColor = lightColor.CGColor; - CGColorRef darkCGColor = darkColor.CGColor; - CGColorRef highContrastLightCGColor = highContrastLightColor.CGColor; - CGColorRef highContrastDarkCGColor = highContrastDarkColor.CGColor; - - // A provider has to remain the same opaque color for every supported trait. - // Otherwise the backplate could diverge from its ancestor after a trait change. - if (CGColorGetAlpha(lightCGColor) < 0.999 || CGColorGetAlpha(darkCGColor) < 0.999 || - CGColorGetAlpha(highContrastLightCGColor) < 0.999 || CGColorGetAlpha(highContrastDarkCGColor) < 0.999) { - return NO; - } - - return CGColorEqualToColor(lightCGColor, darkCGColor) && - CGColorEqualToColor(lightCGColor, highContrastLightCGColor) && - CGColorEqualToColor(lightCGColor, highContrastDarkCGColor); -} - -@interface RCTViewComponentView () -- (void)invalidateLayer; -@end @implementation RCTViewComponentView { UIColor *_backgroundColor; CALayer *_backgroundColorLayer; - CAShapeLayer *_shadowBackdropLayer; __weak CALayer *_borderLayer; CALayer *_outlineLayer; NSMutableArray *_boxShadowLayers; @@ -124,11 +56,6 @@ @implementation RCTViewComponentView { NSMutableSet *_accessibilityOrderNativeIDs; NSMutableArray *_accessibilityElements; RCTViewAccessibilityElement *_axElementDescribingSelf; - ShadowBackdrop _shadowBackdrop; - ShadowBackdropKind _lastShadowBackdropKind; - ShadowBackdropReason _lastShadowBackdropReason; - NSInteger _lastShadowBackdropProviderTag; - NSInteger _lastShadowBackdropBarrierTag; } #ifdef RCT_DYNAMIC_FRAMEWORKS @@ -146,10 +73,6 @@ - (instancetype)initWithFrame:(CGRect)frame self.multipleTouchEnabled = YES; _useCustomContainerView = NO; _removeClippedSubviews = NO; - _lastShadowBackdropKind = ShadowBackdropKind::None; - _lastShadowBackdropReason = ShadowBackdropReason::None; - _lastShadowBackdropProviderTag = -1; - _lastShadowBackdropBarrierTag = -1; } return self; } @@ -227,7 +150,6 @@ - (void)mountChildComponentView:(UIView *)childCompone } else { [self.currentContainerView insertSubview:childComponentView atIndex:index]; } - } - (void)unmountChildComponentView:(UIView *)childComponentView index:(NSInteger)index @@ -632,17 +554,6 @@ - (void)updateEventEmitter:(const EventEmitter::Shared &)eventEmitter _eventEmitter = std::static_pointer_cast(eventEmitter); } -- (void)updateEnvironment:(const ShadowViewEnvironment &)environment - oldEnvironment:(const ShadowViewEnvironment &)oldEnvironment -{ - if (oldEnvironment == environment) { - return; - } - - _shadowBackdrop = environment.shadowBackdrop; - _needsInvalidateLayer = YES; -} - - (void)updateLayoutMetrics:(const LayoutMetrics &)layoutMetrics oldLayoutMetrics:(const LayoutMetrics &)oldLayoutMetrics { @@ -688,11 +599,12 @@ - (void)finalizeUpdates:(RNComponentViewUpdateMask)updateMask { [super finalizeUpdates:updateMask]; _useCustomContainerView = [self styleWouldClipOverflowInk]; - if (_needsInvalidateLayer) { - _needsInvalidateLayer = NO; - [self invalidateLayer]; + if (!_needsInvalidateLayer) { + return; } + _needsInvalidateLayer = NO; + [self invalidateLayer]; } - (void)prepareForRecycle @@ -714,13 +626,6 @@ - (void)prepareForRecycle _removeClippedSubviews = NO; _reactSubviews = [NSMutableArray new]; _accessibilityElements = [NSMutableArray new]; - [_shadowBackdropLayer removeFromSuperlayer]; - _shadowBackdropLayer = nil; - _shadowBackdrop = {}; - _lastShadowBackdropKind = ShadowBackdropKind::None; - _lastShadowBackdropReason = ShadowBackdropReason::None; - _lastShadowBackdropProviderTag = -1; - _lastShadowBackdropBarrierTag = -1; } - (void)setPropKeysManagedByAnimated_DO_NOT_USE_THIS_IS_BROKEN:(NSSet *_Nullable)props @@ -918,55 +823,6 @@ - (UIView *)currentContainerView } } -- (void)logShadowBackdrop:(const ShadowBackdrop &)shadowBackdrop -{ - if (_lastShadowBackdropKind == shadowBackdrop.kind && - _lastShadowBackdropReason == shadowBackdrop.reason && - _lastShadowBackdropProviderTag == shadowBackdrop.providerTag && - _lastShadowBackdropBarrierTag == shadowBackdrop.barrierTag) { - return; - } - - _lastShadowBackdropKind = shadowBackdrop.kind; - _lastShadowBackdropReason = shadowBackdrop.reason; - _lastShadowBackdropProviderTag = shadowBackdrop.providerTag; - _lastShadowBackdropBarrierTag = shadowBackdrop.barrierTag; - - if (shadowBackdrop.kind != ShadowBackdropKind::Barrier) { - return; - } - - NSString *reason = RCTShadowBackdropReasonString(shadowBackdrop.reason); - RCTLogInfo( - @"[HannoDebug][ShadowBackdrop] host=%ld result=barrier reason=%@ provider=%ld barrier=%ld", - (long)self.tag, - reason, - (long)shadowBackdrop.providerTag, - (long)shadowBackdrop.barrierTag); -} - -- (void)updateShadowBackdropLayerWithColor:(UIColor *)color cornerInsets:(RCTCornerInsets)cornerInsets -{ - if (color == nil) { - [_shadowBackdropLayer removeFromSuperlayer]; - _shadowBackdropLayer = nil; - return; - } - - if (_shadowBackdropLayer == nil) { - _shadowBackdropLayer = [CAShapeLayer layer]; - _shadowBackdropLayer.zPosition = SHADOW_BACKDROP_ZPOSITION; - [self.layer addSublayer:_shadowBackdropLayer]; - } - - CGPathRef shadowBackdropPath = RCTPathCreateWithRoundedRect(self.bounds, cornerInsets, nil, NO); - _shadowBackdropLayer.frame = self.bounds; - _shadowBackdropLayer.path = shadowBackdropPath; - CGPathRelease(shadowBackdropPath); - _shadowBackdropLayer.fillColor = color.CGColor; - [_shadowBackdropLayer removeAllAnimations]; -} - - (void)invalidateLayer { CALayer *layer = self.layer; @@ -976,13 +832,6 @@ - (void)invalidateLayer } const auto borderMetrics = _props->resolveBorderMetrics(_layoutMetrics); - RCTCornerRadii cornerRadii = RCTCornerRadiiFromBorderRadii(borderMetrics.borderRadii); - const RCTCornerInsets cornerInsets = RCTGetCornerInsets(cornerRadii, UIEdgeInsetsZero); - UIColor *shadowBackdropColor = nil; - const bool shadowBackdropEnabled = - ReactNativeFeatureFlags::enableIOSBorderBoxShadowBackdrop(); - ShadowBackdrop shadowBackdrop{}; - const bool shouldLogShadowBackdrop = shadowBackdropEnabled; // Stage 1. Shadow Path BOOL const layerHasShadow = layer.shadowOpacity > 0 && CGColorGetAlpha(layer.shadowColor) > 0; @@ -991,6 +840,8 @@ - (void)invalidateLayer } else { bool borderBoxDefaultEnabled = ReactNativeFeatureFlags::enableIOSBorderBoxShadowPathByDefault(); ShadowPathMode shadowPathMode = resolveShadowPathMode(_props->shadowPathIOS, borderBoxDefaultEnabled); + RCTCornerRadii cornerRadii = RCTCornerRadiiFromBorderRadii(borderMetrics.borderRadii); + const RCTCornerInsets cornerInsets = RCTGetCornerInsets(cornerRadii, UIEdgeInsetsZero); switch (shadowPathMode) { case ShadowPathMode::BorderBox: { @@ -1015,28 +866,8 @@ - (void)invalidateLayer react_native_assert(false); break; } - - if (shadowPathMode == ShadowPathMode::BorderBox && shadowBackdropEnabled) { - shadowBackdrop = _shadowBackdrop; - - if (shadowBackdrop.kind == ShadowBackdropKind::Provider) { - UIColor *providerColor = RCTUIColorFromSharedColor(shadowBackdrop.color); - if (RCTShadowBackdropColorIsConcreteOpaque(providerColor)) { - shadowBackdropColor = [providerColor resolvedColorWithTraitCollection:self.traitCollection]; - } else { - shadowBackdrop = shadowBackdropBarrier(ShadowBackdropReason::DynamicColor); - shadowBackdrop.barrierTag = (Tag)self.tag; - } - } - } - } - - if (shouldLogShadowBackdrop) { - [self logShadowBackdrop:shadowBackdrop]; } - [self updateShadowBackdropLayerWithColor:shadowBackdropColor cornerInsets:cornerInsets]; - #if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 170000 /* __IPHONE_17_0 */ // Stage 1.5. Cursor / Hover Effects if (@available(iOS 17.0, *)) { @@ -1088,7 +919,7 @@ - (void)invalidateLayer // we want. If we mask self.layer to this path, we would be clipping subviews // which we may not want to do. The generalized solution in this case is just // create a new layer - if (useCoreAnimationBorderRendering && shadowBackdropColor == nil) { + if (useCoreAnimationBorderRendering) { [_backgroundColorLayer removeFromSuperlayer]; _backgroundColorLayer = nil; layer.backgroundColor = backgroundColor.CGColor; diff --git a/packages/react-native/React/Fabric/Mounting/RCTComponentViewProtocol.h b/packages/react-native/React/Fabric/Mounting/RCTComponentViewProtocol.h index 26befe0c9170..1b802b5cfde3 100644 --- a/packages/react-native/React/Fabric/Mounting/RCTComponentViewProtocol.h +++ b/packages/react-native/React/Fabric/Mounting/RCTComponentViewProtocol.h @@ -13,10 +13,6 @@ #import #import -namespace facebook::react { -struct ShadowViewEnvironment; -} - NS_ASSUME_NONNULL_BEGIN /* @@ -28,11 +24,9 @@ typedef NS_OPTIONS(NSInteger, RNComponentViewUpdateMask) { RNComponentViewUpdateMaskEventEmitter = 1 << 1, RNComponentViewUpdateMaskState = 1 << 2, RNComponentViewUpdateMaskLayoutMetrics = 1 << 3, - RNComponentViewUpdateMaskEnvironment = 1 << 4, RNComponentViewUpdateMaskAll = RNComponentViewUpdateMaskProps | RNComponentViewUpdateMaskEventEmitter | - RNComponentViewUpdateMaskState | RNComponentViewUpdateMaskLayoutMetrics | - RNComponentViewUpdateMaskEnvironment + RNComponentViewUpdateMaskState | RNComponentViewUpdateMaskLayoutMetrics }; /* @@ -98,13 +92,6 @@ typedef NS_OPTIONS(NSInteger, RNComponentViewUpdateMask) { - (void)updateLayoutMetrics:(const facebook::react::LayoutMetrics &)layoutMetrics oldLayoutMetrics:(const facebook::react::LayoutMetrics &)oldLayoutMetrics; -/* - * Called when rendering context derived from the mounted Fabric tree changes. - * The default implementation intentionally does nothing. - */ -- (void)updateEnvironment:(const facebook::react::ShadowViewEnvironment &)environment - oldEnvironment:(const facebook::react::ShadowViewEnvironment &)oldEnvironment; - /* * Called when receiving a command */ diff --git a/packages/react-native/React/Fabric/Mounting/RCTMountingManager.mm b/packages/react-native/React/Fabric/Mounting/RCTMountingManager.mm index c0f6d3110d24..f43750313128 100644 --- a/packages/react-native/React/Fabric/Mounting/RCTMountingManager.mm +++ b/packages/react-native/React/Fabric/Mounting/RCTMountingManager.mm @@ -84,8 +84,6 @@ static void RCTPerformMountInstructions( [newChildComponentView updateState:newChildShadowView.state oldState:nullptr]; [newChildComponentView updateLayoutMetrics:newChildShadowView.layoutMetrics oldLayoutMetrics:EmptyLayoutMetrics]; - ShadowViewEnvironment emptyEnvironment{}; - [newChildComponentView updateEnvironment:newChildShadowView.environment oldEnvironment:emptyEnvironment]; [newChildComponentView finalizeUpdates:RNComponentViewUpdateMaskAll]; [parentViewDescriptor.view mountChildComponentView:newChildComponentView index:mutation.index]; @@ -131,12 +129,6 @@ static void RCTPerformMountInstructions( mask |= RNComponentViewUpdateMaskLayoutMetrics; } - if (oldChildShadowView.environment != newChildShadowView.environment) { - [newChildComponentView updateEnvironment:newChildShadowView.environment - oldEnvironment:oldChildShadowView.environment]; - mask |= RNComponentViewUpdateMaskEnvironment; - } - if (mask != RNComponentViewUpdateMaskNone) { [newChildComponentView finalizeUpdates:mask]; } diff --git a/packages/react-native/React/Fabric/Mounting/UIView+ComponentViewProtocol.h b/packages/react-native/React/Fabric/Mounting/UIView+ComponentViewProtocol.h index 5793c131d738..e385fb33225c 100644 --- a/packages/react-native/React/Fabric/Mounting/UIView+ComponentViewProtocol.h +++ b/packages/react-native/React/Fabric/Mounting/UIView+ComponentViewProtocol.h @@ -33,9 +33,6 @@ NS_ASSUME_NONNULL_BEGIN - (void)updateLayoutMetrics:(const facebook::react::LayoutMetrics &)layoutMetrics oldLayoutMetrics:(const facebook::react::LayoutMetrics &)oldLayoutMetrics; -- (void)updateEnvironment:(const facebook::react::ShadowViewEnvironment &)environment - oldEnvironment:(const facebook::react::ShadowViewEnvironment &)oldEnvironment; - - (void)finalizeUpdates:(RNComponentViewUpdateMask)updateMask; - (void)prepareForRecycle; diff --git a/packages/react-native/React/Fabric/Mounting/UIView+ComponentViewProtocol.mm b/packages/react-native/React/Fabric/Mounting/UIView+ComponentViewProtocol.mm index b41c25bc7915..9f50554560e5 100644 --- a/packages/react-native/React/Fabric/Mounting/UIView+ComponentViewProtocol.mm +++ b/packages/react-native/React/Fabric/Mounting/UIView+ComponentViewProtocol.mm @@ -119,12 +119,6 @@ - (void)updateLayoutMetrics:(const LayoutMetrics &)layoutMetrics } } -- (void)updateEnvironment:(const ShadowViewEnvironment &)environment - oldEnvironment:(const ShadowViewEnvironment &)oldEnvironment -{ - // Default implementation does nothing. -} - - (void)finalizeUpdates:(RNComponentViewUpdateMask)updateMask { // Default implementation does nothing. 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 5c320e6ef549..be58190699b3 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<<12f3afcae7d63e99fe0bb07abaf8bd72>> + * @generated SignedSource<<2943f7c7f969e25c549ee6bfeff650c6>> */ /** @@ -156,12 +156,6 @@ public object ReactNativeFeatureFlags { @JvmStatic public fun enableIOSBorderBoxShadowPathByDefault(): Boolean = accessor.enableIOSBorderBoxShadowPathByDefault() - /** - * Add opaque backplates behind eligible legacy iOS border-box shadows. - */ - @JvmStatic - public fun enableIOSBorderBoxShadowBackdrop(): Boolean = accessor.enableIOSBorderBoxShadowBackdrop() - /** * Fixes #56595 by moving the autoFocus from didMoveToWindow to viewDidAppear */ 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 ef3cbef759c3..d167c594bb67 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<<21afc90d69f573614425f55c40ec5ebc>> + * @generated SignedSource<> */ /** @@ -41,7 +41,6 @@ internal class ReactNativeFeatureFlagsCxxAccessor : ReactNativeFeatureFlagsAcces private var enableFixForParentTagDuringReparentingCache: Boolean? = null private var enableFontScaleChangesUpdatingLayoutCache: Boolean? = null private var enableIOSBorderBoxShadowPathByDefaultCache: Boolean? = null - private var enableIOSBorderBoxShadowBackdropCache: Boolean? = null private var enableIOSExperimentalAutoFocusImplementationCache: Boolean? = null private var enableIOSTextBaselineOffsetPerLineCache: Boolean? = null private var enableIOSViewClipToPaddingBoxCache: Boolean? = null @@ -278,15 +277,6 @@ internal class ReactNativeFeatureFlagsCxxAccessor : ReactNativeFeatureFlagsAcces return cached } - override fun enableIOSBorderBoxShadowBackdrop(): Boolean { - var cached = enableIOSBorderBoxShadowBackdropCache - if (cached == null) { - cached = ReactNativeFeatureFlagsCxxInterop.enableIOSBorderBoxShadowBackdrop() - enableIOSBorderBoxShadowBackdropCache = cached - } - return cached - } - override fun enableIOSExperimentalAutoFocusImplementation(): Boolean { var cached = enableIOSExperimentalAutoFocusImplementationCache 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 a231dbf817ce..0cb215c7fdd4 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<<40b102e535b74e9e42fb22d82bbb2b02>> + * @generated SignedSource<<42800c900774644d53216aa2c7dd624c>> */ /** @@ -70,8 +70,6 @@ public object ReactNativeFeatureFlagsCxxInterop { @DoNotStrip @JvmStatic public external fun enableIOSBorderBoxShadowPathByDefault(): Boolean - @DoNotStrip @JvmStatic public external fun enableIOSBorderBoxShadowBackdrop(): Boolean - @DoNotStrip @JvmStatic public external fun enableIOSExperimentalAutoFocusImplementation(): Boolean @DoNotStrip @JvmStatic public external fun enableIOSTextBaselineOffsetPerLine(): 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 79b497289de2..a9bc337db331 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<<0c58eb48dc4a6a5f0b971328c3d9cba8>> + * @generated SignedSource<<84c5a4338deb791e07275d340c3cd419>> */ /** @@ -65,8 +65,6 @@ public open class ReactNativeFeatureFlagsDefaults : ReactNativeFeatureFlagsProvi override fun enableIOSBorderBoxShadowPathByDefault(): Boolean = false - override fun enableIOSBorderBoxShadowBackdrop(): Boolean = false - override fun enableIOSExperimentalAutoFocusImplementation(): Boolean = false override fun enableIOSTextBaselineOffsetPerLine(): 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 53f332271bee..175b9500311c 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<<402131230f486c9bee0e5d877419280b>> */ /** @@ -45,7 +45,6 @@ internal class ReactNativeFeatureFlagsLocalAccessor : ReactNativeFeatureFlagsAcc private var enableFixForParentTagDuringReparentingCache: Boolean? = null private var enableFontScaleChangesUpdatingLayoutCache: Boolean? = null private var enableIOSBorderBoxShadowPathByDefaultCache: Boolean? = null - private var enableIOSBorderBoxShadowBackdropCache: Boolean? = null private var enableIOSExperimentalAutoFocusImplementationCache: Boolean? = null private var enableIOSTextBaselineOffsetPerLineCache: Boolean? = null private var enableIOSViewClipToPaddingBoxCache: Boolean? = null @@ -303,16 +302,6 @@ internal class ReactNativeFeatureFlagsLocalAccessor : ReactNativeFeatureFlagsAcc return cached } - override fun enableIOSBorderBoxShadowBackdrop(): Boolean { - var cached = enableIOSBorderBoxShadowBackdropCache - if (cached == null) { - cached = currentProvider.enableIOSBorderBoxShadowBackdrop() - accessedFeatureFlags.add("enableIOSBorderBoxShadowBackdrop") - enableIOSBorderBoxShadowBackdropCache = cached - } - return cached - } - override fun enableIOSExperimentalAutoFocusImplementation(): Boolean { var cached = enableIOSExperimentalAutoFocusImplementationCache 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 8fd31b750e49..b08ab0f30299 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<<39c4e466c465ce9e3f36147df862a057>> + * @generated SignedSource<<752961866ff51cfccd11f8c4314bc920>> */ /** @@ -65,8 +65,6 @@ public interface ReactNativeFeatureFlagsProvider { @DoNotStrip public fun enableIOSBorderBoxShadowPathByDefault(): Boolean - @DoNotStrip public fun enableIOSBorderBoxShadowBackdrop(): Boolean - @DoNotStrip public fun enableIOSExperimentalAutoFocusImplementation(): Boolean @DoNotStrip public fun enableIOSTextBaselineOffsetPerLine(): 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 5f1546122f00..df1e51ffae9f 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<<26a284920b9060963af1bac00838faba>> + * @generated SignedSource<<5c4c763d4b19e17b107c87f772decaa8>> */ /** @@ -165,12 +165,6 @@ class ReactNativeFeatureFlagsJavaProvider return method(javaProvider_); } - bool enableIOSBorderBoxShadowBackdrop() override { - static const auto method = - getReactNativeFeatureFlagsProviderJavaClass()->getMethod("enableIOSBorderBoxShadowBackdrop"); - return method(javaProvider_); - } - bool enableIOSExperimentalAutoFocusImplementation() override { static const auto method = getReactNativeFeatureFlagsProviderJavaClass()->getMethod("enableIOSExperimentalAutoFocusImplementation"); @@ -556,11 +550,6 @@ bool JReactNativeFeatureFlagsCxxInterop::enableIOSBorderBoxShadowPathByDefault( return ReactNativeFeatureFlags::enableIOSBorderBoxShadowPathByDefault(); } -bool JReactNativeFeatureFlagsCxxInterop::enableIOSBorderBoxShadowBackdrop( - facebook::jni::alias_ref /*unused*/) { - return ReactNativeFeatureFlags::enableIOSBorderBoxShadowBackdrop(); -} - bool JReactNativeFeatureFlagsCxxInterop::enableIOSExperimentalAutoFocusImplementation( facebook::jni::alias_ref /*unused*/) { return ReactNativeFeatureFlags::enableIOSExperimentalAutoFocusImplementation(); @@ -885,9 +874,6 @@ void JReactNativeFeatureFlagsCxxInterop::registerNatives() { makeNativeMethod( "enableIOSBorderBoxShadowPathByDefault", JReactNativeFeatureFlagsCxxInterop::enableIOSBorderBoxShadowPathByDefault), - makeNativeMethod( - "enableIOSBorderBoxShadowBackdrop", - JReactNativeFeatureFlagsCxxInterop::enableIOSBorderBoxShadowBackdrop), makeNativeMethod( "enableIOSExperimentalAutoFocusImplementation", JReactNativeFeatureFlagsCxxInterop::enableIOSExperimentalAutoFocusImplementation), 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 beb97785fad8..483fac726126 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<> */ /** @@ -93,9 +93,6 @@ class JReactNativeFeatureFlagsCxxInterop static bool enableIOSBorderBoxShadowPathByDefault( facebook::jni::alias_ref); - static bool enableIOSBorderBoxShadowBackdrop( - facebook::jni::alias_ref); - static bool enableIOSExperimentalAutoFocusImplementation( 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 1b0ff0c12193..caad94b541bb 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<<7ec3bbd2bc74c9717c211b9d19fe74cb>> + * @generated SignedSource<<0fb2c41e81fff95999861a345052d99f>> */ /** @@ -110,10 +110,6 @@ bool ReactNativeFeatureFlags::enableIOSBorderBoxShadowPathByDefault() { return getAccessor().enableIOSBorderBoxShadowPathByDefault(); } -bool ReactNativeFeatureFlags::enableIOSBorderBoxShadowBackdrop() { - return getAccessor().enableIOSBorderBoxShadowBackdrop(); -} - bool ReactNativeFeatureFlags::enableIOSExperimentalAutoFocusImplementation() { return getAccessor().enableIOSExperimentalAutoFocusImplementation(); } diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.h b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.h index 5a6da2a78f64..3ca0da4039ec 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<<8016498b9ad36b7c418df4d05de17b1a>> + * @generated SignedSource<> */ /** @@ -144,11 +144,6 @@ class ReactNativeFeatureFlags { */ RN_EXPORT static bool enableIOSBorderBoxShadowPathByDefault(); - /** - * Add opaque backplates behind eligible legacy iOS border-box shadows. - */ - RN_EXPORT static bool enableIOSBorderBoxShadowBackdrop(); - /** * Fixes #56595 by moving the autoFocus from didMoveToWindow to viewDidAppear */ diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.cpp b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.cpp index b17f9ec57171..3f879a4cf3d5 100644 --- a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.cpp +++ b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.cpp @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<> + * @generated SignedSource<<676605110414d92058485a2f290d532b>> */ /** @@ -407,24 +407,6 @@ bool ReactNativeFeatureFlagsAccessor::enableIOSBorderBoxShadowPathByDefault() { return flagValue.value(); } -bool ReactNativeFeatureFlagsAccessor::enableIOSBorderBoxShadowBackdrop() { - auto flagValue = enableIOSBorderBoxShadowBackdrop_.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(21, "enableIOSBorderBoxShadowBackdrop"); - - flagValue = currentProvider_->enableIOSBorderBoxShadowBackdrop(); - enableIOSBorderBoxShadowBackdrop_ = flagValue; - } - - return flagValue.value(); -} - bool ReactNativeFeatureFlagsAccessor::enableIOSExperimentalAutoFocusImplementation() { auto flagValue = enableIOSExperimentalAutoFocusImplementation_.load(); @@ -434,7 +416,7 @@ bool ReactNativeFeatureFlagsAccessor::enableIOSExperimentalAutoFocusImplementati // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(22, "enableIOSExperimentalAutoFocusImplementation"); + markFlagAsAccessed(21, "enableIOSExperimentalAutoFocusImplementation"); flagValue = currentProvider_->enableIOSExperimentalAutoFocusImplementation(); enableIOSExperimentalAutoFocusImplementation_ = flagValue; @@ -452,7 +434,7 @@ bool ReactNativeFeatureFlagsAccessor::enableIOSTextBaselineOffsetPerLine() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(23, "enableIOSTextBaselineOffsetPerLine"); + markFlagAsAccessed(22, "enableIOSTextBaselineOffsetPerLine"); flagValue = currentProvider_->enableIOSTextBaselineOffsetPerLine(); enableIOSTextBaselineOffsetPerLine_ = flagValue; @@ -470,7 +452,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(24, "enableIOSViewClipToPaddingBox"); + markFlagAsAccessed(23, "enableIOSViewClipToPaddingBox"); flagValue = currentProvider_->enableIOSViewClipToPaddingBox(); enableIOSViewClipToPaddingBox_ = flagValue; @@ -488,7 +470,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(25, "enableInteropViewManagerClassLookUpOptimizationIOS"); + markFlagAsAccessed(24, "enableInteropViewManagerClassLookUpOptimizationIOS"); flagValue = currentProvider_->enableInteropViewManagerClassLookUpOptimizationIOS(); enableInteropViewManagerClassLookUpOptimizationIOS_ = flagValue; @@ -506,7 +488,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(26, "enableLayoutAnimationsOnAndroid"); + markFlagAsAccessed(25, "enableLayoutAnimationsOnAndroid"); flagValue = currentProvider_->enableLayoutAnimationsOnAndroid(); enableLayoutAnimationsOnAndroid_ = flagValue; @@ -524,7 +506,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(27, "enableLayoutAnimationsOnIOS"); + markFlagAsAccessed(26, "enableLayoutAnimationsOnIOS"); flagValue = currentProvider_->enableLayoutAnimationsOnIOS(); enableLayoutAnimationsOnIOS_ = flagValue; @@ -542,7 +524,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(28, "enableMainQueueCoordinatorOnIOS"); + markFlagAsAccessed(27, "enableMainQueueCoordinatorOnIOS"); flagValue = currentProvider_->enableMainQueueCoordinatorOnIOS(); enableMainQueueCoordinatorOnIOS_ = flagValue; @@ -560,7 +542,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(29, "enableMainQueueModulesOnIOS"); + markFlagAsAccessed(28, "enableMainQueueModulesOnIOS"); flagValue = currentProvider_->enableMainQueueModulesOnIOS(); enableMainQueueModulesOnIOS_ = flagValue; @@ -578,7 +560,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(30, "enableModuleArgumentNSNullConversionIOS"); + markFlagAsAccessed(29, "enableModuleArgumentNSNullConversionIOS"); flagValue = currentProvider_->enableModuleArgumentNSNullConversionIOS(); enableModuleArgumentNSNullConversionIOS_ = flagValue; @@ -596,7 +578,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(31, "enableNativeCSSParsing"); + markFlagAsAccessed(30, "enableNativeCSSParsing"); flagValue = currentProvider_->enableNativeCSSParsing(); enableNativeCSSParsing_ = flagValue; @@ -614,7 +596,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(32, "enableNetworkEventReporting"); + markFlagAsAccessed(31, "enableNetworkEventReporting"); flagValue = currentProvider_->enableNetworkEventReporting(); enableNetworkEventReporting_ = flagValue; @@ -632,7 +614,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(33, "enableNewBackgroundAndBorderDrawables"); + markFlagAsAccessed(32, "enableNewBackgroundAndBorderDrawables"); flagValue = currentProvider_->enableNewBackgroundAndBorderDrawables(); enableNewBackgroundAndBorderDrawables_ = flagValue; @@ -650,7 +632,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(34, "enablePreparedTextLayout"); + markFlagAsAccessed(33, "enablePreparedTextLayout"); flagValue = currentProvider_->enablePreparedTextLayout(); enablePreparedTextLayout_ = flagValue; @@ -668,7 +650,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(35, "enablePropsUpdateReconciliationAndroid"); + markFlagAsAccessed(34, "enablePropsUpdateReconciliationAndroid"); flagValue = currentProvider_->enablePropsUpdateReconciliationAndroid(); enablePropsUpdateReconciliationAndroid_ = flagValue; @@ -686,7 +668,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(36, "enableResourceTimingAPI"); + markFlagAsAccessed(35, "enableResourceTimingAPI"); flagValue = currentProvider_->enableResourceTimingAPI(); enableResourceTimingAPI_ = flagValue; @@ -704,7 +686,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(37, "enableSynchronousStateUpdates"); + markFlagAsAccessed(36, "enableSynchronousStateUpdates"); flagValue = currentProvider_->enableSynchronousStateUpdates(); enableSynchronousStateUpdates_ = flagValue; @@ -722,7 +704,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(38, "enableViewCulling"); + markFlagAsAccessed(37, "enableViewCulling"); flagValue = currentProvider_->enableViewCulling(); enableViewCulling_ = flagValue; @@ -740,7 +722,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(39, "enableViewRecycling"); + markFlagAsAccessed(38, "enableViewRecycling"); flagValue = currentProvider_->enableViewRecycling(); enableViewRecycling_ = flagValue; @@ -758,7 +740,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(40, "enableViewRecyclingForText"); + markFlagAsAccessed(39, "enableViewRecyclingForText"); flagValue = currentProvider_->enableViewRecyclingForText(); enableViewRecyclingForText_ = flagValue; @@ -776,7 +758,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(41, "enableViewRecyclingForView"); + markFlagAsAccessed(40, "enableViewRecyclingForView"); flagValue = currentProvider_->enableViewRecyclingForView(); enableViewRecyclingForView_ = flagValue; @@ -794,7 +776,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(42, "enableVirtualViewDebugFeatures"); + markFlagAsAccessed(41, "enableVirtualViewDebugFeatures"); flagValue = currentProvider_->enableVirtualViewDebugFeatures(); enableVirtualViewDebugFeatures_ = flagValue; @@ -812,7 +794,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(43, "enableVirtualViewRenderState"); + markFlagAsAccessed(42, "enableVirtualViewRenderState"); flagValue = currentProvider_->enableVirtualViewRenderState(); enableVirtualViewRenderState_ = flagValue; @@ -830,7 +812,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(44, "enableVirtualViewWindowFocusDetection"); + markFlagAsAccessed(43, "enableVirtualViewWindowFocusDetection"); flagValue = currentProvider_->enableVirtualViewWindowFocusDetection(); enableVirtualViewWindowFocusDetection_ = flagValue; @@ -848,7 +830,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(45, "fixMappingOfEventPrioritiesBetweenFabricAndReact"); + markFlagAsAccessed(44, "fixMappingOfEventPrioritiesBetweenFabricAndReact"); flagValue = currentProvider_->fixMappingOfEventPrioritiesBetweenFabricAndReact(); fixMappingOfEventPrioritiesBetweenFabricAndReact_ = flagValue; @@ -866,7 +848,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(46, "fuseboxEnabledRelease"); + markFlagAsAccessed(45, "fuseboxEnabledRelease"); flagValue = currentProvider_->fuseboxEnabledRelease(); fuseboxEnabledRelease_ = flagValue; @@ -884,7 +866,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(47, "fuseboxNetworkInspectionEnabled"); + markFlagAsAccessed(46, "fuseboxNetworkInspectionEnabled"); flagValue = currentProvider_->fuseboxNetworkInspectionEnabled(); fuseboxNetworkInspectionEnabled_ = flagValue; @@ -902,7 +884,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(48, "hideOffscreenVirtualViewsOnIOS"); + markFlagAsAccessed(47, "hideOffscreenVirtualViewsOnIOS"); flagValue = currentProvider_->hideOffscreenVirtualViewsOnIOS(); hideOffscreenVirtualViewsOnIOS_ = flagValue; @@ -920,7 +902,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(49, "preparedTextCacheSize"); + markFlagAsAccessed(48, "preparedTextCacheSize"); flagValue = currentProvider_->preparedTextCacheSize(); preparedTextCacheSize_ = flagValue; @@ -938,7 +920,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(50, "preventShadowTreeCommitExhaustion"); + markFlagAsAccessed(49, "preventShadowTreeCommitExhaustion"); flagValue = currentProvider_->preventShadowTreeCommitExhaustion(); preventShadowTreeCommitExhaustion_ = flagValue; @@ -956,7 +938,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(51, "traceTurboModulePromiseRejectionsOnAndroid"); + markFlagAsAccessed(50, "traceTurboModulePromiseRejectionsOnAndroid"); flagValue = currentProvider_->traceTurboModulePromiseRejectionsOnAndroid(); traceTurboModulePromiseRejectionsOnAndroid_ = flagValue; @@ -974,7 +956,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(52, "updateRuntimeShadowNodeReferencesOnCommit"); + markFlagAsAccessed(51, "updateRuntimeShadowNodeReferencesOnCommit"); flagValue = currentProvider_->updateRuntimeShadowNodeReferencesOnCommit(); updateRuntimeShadowNodeReferencesOnCommit_ = flagValue; @@ -992,7 +974,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(53, "useAlwaysAvailableJSErrorHandling"); + markFlagAsAccessed(52, "useAlwaysAvailableJSErrorHandling"); flagValue = currentProvider_->useAlwaysAvailableJSErrorHandling(); useAlwaysAvailableJSErrorHandling_ = flagValue; @@ -1010,7 +992,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(54, "useFabricInterop"); + markFlagAsAccessed(53, "useFabricInterop"); flagValue = currentProvider_->useFabricInterop(); useFabricInterop_ = flagValue; @@ -1028,7 +1010,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(55, "useNativeEqualsInNativeReadableArrayAndroid"); + markFlagAsAccessed(54, "useNativeEqualsInNativeReadableArrayAndroid"); flagValue = currentProvider_->useNativeEqualsInNativeReadableArrayAndroid(); useNativeEqualsInNativeReadableArrayAndroid_ = flagValue; @@ -1046,7 +1028,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(56, "useNativeTransformHelperAndroid"); + markFlagAsAccessed(55, "useNativeTransformHelperAndroid"); flagValue = currentProvider_->useNativeTransformHelperAndroid(); useNativeTransformHelperAndroid_ = flagValue; @@ -1064,7 +1046,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(57, "useNativeViewConfigsInBridgelessMode"); + markFlagAsAccessed(56, "useNativeViewConfigsInBridgelessMode"); flagValue = currentProvider_->useNativeViewConfigsInBridgelessMode(); useNativeViewConfigsInBridgelessMode_ = flagValue; @@ -1082,7 +1064,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(58, "useOptimizedEventBatchingOnAndroid"); + markFlagAsAccessed(57, "useOptimizedEventBatchingOnAndroid"); flagValue = currentProvider_->useOptimizedEventBatchingOnAndroid(); useOptimizedEventBatchingOnAndroid_ = flagValue; @@ -1100,7 +1082,7 @@ bool ReactNativeFeatureFlagsAccessor::usePullModelOnAndroid() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(59, "usePullModelOnAndroid"); + markFlagAsAccessed(58, "usePullModelOnAndroid"); flagValue = currentProvider_->usePullModelOnAndroid(); usePullModelOnAndroid_ = flagValue; @@ -1118,7 +1100,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(60, "useRawPropsJsiValue"); + markFlagAsAccessed(59, "useRawPropsJsiValue"); flagValue = currentProvider_->useRawPropsJsiValue(); useRawPropsJsiValue_ = flagValue; @@ -1136,7 +1118,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(61, "useShadowNodeStateOnClone"); + markFlagAsAccessed(60, "useShadowNodeStateOnClone"); flagValue = currentProvider_->useShadowNodeStateOnClone(); useShadowNodeStateOnClone_ = flagValue; @@ -1154,7 +1136,7 @@ bool ReactNativeFeatureFlagsAccessor::useSilenceErrorSMMViewNotFound() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(62, "useSilenceErrorSMMViewNotFound"); + markFlagAsAccessed(61, "useSilenceErrorSMMViewNotFound"); flagValue = currentProvider_->useSilenceErrorSMMViewNotFound(); useSilenceErrorSMMViewNotFound_ = flagValue; @@ -1172,7 +1154,7 @@ bool ReactNativeFeatureFlagsAccessor::useTraitHiddenOnIOS() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(63, "useTraitHiddenOnIOS"); + markFlagAsAccessed(62, "useTraitHiddenOnIOS"); flagValue = currentProvider_->useTraitHiddenOnIOS(); useTraitHiddenOnIOS_ = flagValue; @@ -1190,7 +1172,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(64, "useTurboModuleInterop"); + markFlagAsAccessed(63, "useTurboModuleInterop"); flagValue = currentProvider_->useTurboModuleInterop(); useTurboModuleInterop_ = flagValue; @@ -1208,7 +1190,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(65, "useTurboModules"); + markFlagAsAccessed(64, "useTurboModules"); flagValue = currentProvider_->useTurboModules(); useTurboModules_ = flagValue; @@ -1226,7 +1208,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(66, "virtualViewPrerenderRatio"); + markFlagAsAccessed(65, "virtualViewPrerenderRatio"); flagValue = currentProvider_->virtualViewPrerenderRatio(); virtualViewPrerenderRatio_ = flagValue; @@ -1244,7 +1226,7 @@ bool ReactNativeFeatureFlagsAccessor::runtimeCrashUiThreadUtils() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(67, "runtimeCrashUiThreadUtils"); + markFlagAsAccessed(66, "runtimeCrashUiThreadUtils"); flagValue = currentProvider_->runtimeCrashUiThreadUtils(); runtimeCrashUiThreadUtils_ = flagValue; diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.h b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.h index 13a2e053697b..aef569c64182 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<<8b2400f41b0cf3d64e89a97ef1c08f9c>> + * @generated SignedSource<> */ /** @@ -53,7 +53,6 @@ class ReactNativeFeatureFlagsAccessor { bool enableFixForParentTagDuringReparenting(); bool enableFontScaleChangesUpdatingLayout(); bool enableIOSBorderBoxShadowPathByDefault(); - bool enableIOSBorderBoxShadowBackdrop(); bool enableIOSExperimentalAutoFocusImplementation(); bool enableIOSTextBaselineOffsetPerLine(); bool enableIOSViewClipToPaddingBox(); @@ -111,7 +110,7 @@ class ReactNativeFeatureFlagsAccessor { std::unique_ptr currentProvider_; bool wasOverridden_; - std::array, 68> accessedFeatureFlags_; + std::array, 67> accessedFeatureFlags_; std::atomic> commonTestFlag_; std::atomic> animatedShouldSignalBatch_; @@ -134,7 +133,6 @@ class ReactNativeFeatureFlagsAccessor { std::atomic> enableFixForParentTagDuringReparenting_; std::atomic> enableFontScaleChangesUpdatingLayout_; std::atomic> enableIOSBorderBoxShadowPathByDefault_; - std::atomic> enableIOSBorderBoxShadowBackdrop_; std::atomic> enableIOSExperimentalAutoFocusImplementation_; std::atomic> enableIOSTextBaselineOffsetPerLine_; std::atomic> enableIOSViewClipToPaddingBox_; diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDefaults.h b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDefaults.h index 7fda80fe89c5..f72e13144897 100644 --- a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDefaults.h +++ b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDefaults.h @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<> + * @generated SignedSource<> */ /** @@ -111,10 +111,6 @@ class ReactNativeFeatureFlagsDefaults : public ReactNativeFeatureFlagsProvider { return false; } - bool enableIOSBorderBoxShadowBackdrop() override { - return false; - } - bool enableIOSExperimentalAutoFocusImplementation() override { return false; } diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDynamicProvider.h b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDynamicProvider.h index 08d8c2890cb1..3f950c1e4956 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<<8018fdfb715556c9af4c5a696e62cca4>> + * @generated SignedSource<<8a75aa937ab50bb2ea4e920c046e2d1e>> */ /** @@ -234,15 +234,6 @@ class ReactNativeFeatureFlagsDynamicProvider : public ReactNativeFeatureFlagsDef return ReactNativeFeatureFlagsDefaults::enableIOSBorderBoxShadowPathByDefault(); } - bool enableIOSBorderBoxShadowBackdrop() override { - auto value = values_["enableIOSBorderBoxShadowBackdrop"]; - if (!value.isNull()) { - return value.getBool(); - } - - return ReactNativeFeatureFlagsDefaults::enableIOSBorderBoxShadowBackdrop(); - } - bool enableIOSExperimentalAutoFocusImplementation() override { auto value = values_["enableIOSExperimentalAutoFocusImplementation"]; if (!value.isNull()) { diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsProvider.h b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsProvider.h index a009a1b6b4a7..657ea5aa42e5 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<<4a1153f2a9e598520f7008ebdddc98eb>> + * @generated SignedSource<> */ /** @@ -46,7 +46,6 @@ class ReactNativeFeatureFlagsProvider { virtual bool enableFixForParentTagDuringReparenting() = 0; virtual bool enableFontScaleChangesUpdatingLayout() = 0; virtual bool enableIOSBorderBoxShadowPathByDefault() = 0; - virtual bool enableIOSBorderBoxShadowBackdrop() = 0; virtual bool enableIOSExperimentalAutoFocusImplementation() = 0; virtual bool enableIOSTextBaselineOffsetPerLine() = 0; virtual bool enableIOSViewClipToPaddingBox() = 0; diff --git a/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.cpp b/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.cpp index 0325eafd768e..391a81d8afc7 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<<35759e0c6596d969ed6cfbf393ff4908>> + * @generated SignedSource<> */ /** @@ -149,11 +149,6 @@ bool NativeReactNativeFeatureFlags::enableIOSBorderBoxShadowPathByDefault( return ReactNativeFeatureFlags::enableIOSBorderBoxShadowPathByDefault(); } -bool NativeReactNativeFeatureFlags::enableIOSBorderBoxShadowBackdrop( - jsi::Runtime& /*runtime*/) { - return ReactNativeFeatureFlags::enableIOSBorderBoxShadowBackdrop(); -} - bool NativeReactNativeFeatureFlags::enableIOSExperimentalAutoFocusImplementation( jsi::Runtime& /*runtime*/) { return ReactNativeFeatureFlags::enableIOSExperimentalAutoFocusImplementation(); diff --git a/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.h b/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.h index 6320ceea39b5..b70c1b43c6d4 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<<3a4e198b27be6df0f2d707c60b635278>> */ /** @@ -78,8 +78,6 @@ class NativeReactNativeFeatureFlags bool enableIOSBorderBoxShadowPathByDefault(jsi::Runtime& runtime); - bool enableIOSBorderBoxShadowBackdrop(jsi::Runtime& runtime); - bool enableIOSExperimentalAutoFocusImplementation(jsi::Runtime& runtime); bool enableIOSTextBaselineOffsetPerLine(jsi::Runtime& runtime); diff --git a/packages/react-native/ReactCommon/react/renderer/mounting/Differentiator.cpp b/packages/react-native/ReactCommon/react/renderer/mounting/Differentiator.cpp index 62cfef680a31..7b8682dff3b1 100644 --- a/packages/react-native/ReactCommon/react/renderer/mounting/Differentiator.cpp +++ b/packages/react-native/ReactCommon/react/renderer/mounting/Differentiator.cpp @@ -1384,38 +1384,19 @@ ShadowViewMutation::List calculateShadowViewMutations( auto oldRootShadowView = ShadowView(oldRootShadowNode); auto newRootShadowView = ShadowView(newRootShadowNode); - ShadowViewEnvironmentMap oldEnvironments{}; - ShadowViewEnvironmentMap newEnvironments{}; - const ShadowViewEnvironmentMap* oldEnvironmentsPointer = nullptr; - const ShadowViewEnvironmentMap* newEnvironmentsPointer = nullptr; - const bool shadowBackdropEnabled = - ReactNativeFeatureFlags::enableIOSBorderBoxShadowBackdrop(); - if (shadowBackdropEnabled) { - oldEnvironments = collectShadowViewEnvironments(oldRootShadowNode); - newEnvironments = collectShadowViewEnvironments(newRootShadowNode); - oldEnvironmentsPointer = &oldEnvironments; - newEnvironmentsPointer = &newEnvironments; - } - if (oldRootShadowView != newRootShadowView) { mutations.push_back(ShadowViewMutation::UpdateMutation( oldRootShadowView, newRootShadowView, {})); } auto sliceOne = sliceChildShadowNodeViewPairs( - ShadowViewNodePair{ - .shadowView = oldRootShadowView, - .shadowNode = &oldRootShadowNode, - .environments = oldEnvironmentsPointer}, + ShadowViewNodePair{.shadowNode = &oldRootShadowNode}, viewNodePairScope, false /* allowFlattened */, {} /* layoutOffset */, {} /* cullingContext */); auto sliceTwo = sliceChildShadowNodeViewPairs( - ShadowViewNodePair{ - .shadowView = newRootShadowView, - .shadowNode = &newRootShadowNode, - .environments = newEnvironmentsPointer}, + ShadowViewNodePair{.shadowNode = &newRootShadowNode}, viewNodePairScope, false /* allowFlattened */, {} /* layoutOffset */, diff --git a/packages/react-native/ReactCommon/react/renderer/mounting/ShadowView.cpp b/packages/react-native/ReactCommon/react/renderer/mounting/ShadowView.cpp index ebc72ca49ee4..d681e942a167 100644 --- a/packages/react-native/ReactCommon/react/renderer/mounting/ShadowView.cpp +++ b/packages/react-native/ReactCommon/react/renderer/mounting/ShadowView.cpp @@ -39,8 +39,7 @@ bool ShadowView::operator==(const ShadowView& rhs) const { this->props, this->eventEmitter, this->layoutMetrics, - this->state, - this->environment) == + this->state) == std::tie( rhs.surfaceId, rhs.tag, @@ -48,8 +47,7 @@ bool ShadowView::operator==(const ShadowView& rhs) const { rhs.props, rhs.eventEmitter, rhs.layoutMetrics, - rhs.state, - rhs.environment); + rhs.state); } bool ShadowView::operator!=(const ShadowView& rhs) const { diff --git a/packages/react-native/ReactCommon/react/renderer/mounting/ShadowView.h b/packages/react-native/ReactCommon/react/renderer/mounting/ShadowView.h index bf51f609f9c9..36dd55d37a58 100644 --- a/packages/react-native/ReactCommon/react/renderer/mounting/ShadowView.h +++ b/packages/react-native/ReactCommon/react/renderer/mounting/ShadowView.h @@ -7,7 +7,6 @@ #pragma once -#include #include #include #include @@ -18,142 +17,6 @@ namespace facebook::react { -enum class ShadowBackdropHostKind : uint8_t { - Root, - View, - Unknown, -}; - -enum class ShadowBackdropKind : uint8_t { - None, - Provider, - Barrier, -}; - -enum class ShadowBackdropReason : uint8_t { - None, - EarlierSibling, - TranslucentAncestor, - UnknownAncestor, - VisualEffect, - RoundedAncestor, - OutsideProvider, - DynamicColor, -}; - -struct ShadowBackdrop final { - ShadowBackdropKind kind{ShadowBackdropKind::None}; - ShadowBackdropReason reason{ShadowBackdropReason::None}; - SharedColor color{}; - Rect coverage{}; - Tag providerTag{-1}; - Tag barrierTag{-1}; - - bool operator==(const ShadowBackdrop& other) const = default; -}; - -/* - * Rendering context derived from the complete, laid-out Fabric tree before - * diffing. Component views do not receive it as a prop because it depends on - * their mounted surroundings. - */ -struct ShadowViewEnvironment final { - ShadowBackdrop shadowBackdrop{}; - - bool operator==(const ShadowViewEnvironment& other) const = default; -}; - -inline bool shadowBackdropContainsRect(const Rect& outer, const Rect& inner) { - return inner.getMinX() >= outer.getMinX() && - inner.getMaxX() <= outer.getMaxX() && - inner.getMinY() >= outer.getMinY() && - inner.getMaxY() <= outer.getMaxY(); -} - -inline bool shadowBackdropHasRoundedCorners(const BorderMetrics& borderMetrics) { - const auto& radii = borderMetrics.borderRadii; - return radii.topLeft.vertical != 0 || radii.topLeft.horizontal != 0 || - radii.topRight.vertical != 0 || radii.topRight.horizontal != 0 || - radii.bottomLeft.vertical != 0 || radii.bottomLeft.horizontal != 0 || - radii.bottomRight.vertical != 0 || radii.bottomRight.horizontal != 0; -} - -inline bool shadowBackdropHasVisualEffect(const ViewProps& props) { - return !props.boxShadow.empty() || !props.filter.empty() || - !props.backgroundImage.empty() || props.mixBlendMode != BlendMode::Normal || - props.outlineWidth != 0 || isColorMeaningful(props.shadowColor); -} - -inline ShadowBackdrop shadowBackdropBarrier(ShadowBackdropReason reason) { - return { - .kind = ShadowBackdropKind::Barrier, - .reason = reason, - }; -} - -inline ShadowBackdrop shadowBackdropForChild( - const ShadowBackdrop& backdrop, - const LayoutMetrics& childLayoutMetrics) { - if (backdrop.kind != ShadowBackdropKind::Provider) { - return backdrop; - } - - const auto& childFrame = childLayoutMetrics.frame; - if (!shadowBackdropContainsRect(backdrop.coverage, childFrame)) { - return shadowBackdropBarrier(ShadowBackdropReason::OutsideProvider); - } - - ShadowBackdrop childBackdrop = backdrop; - childBackdrop.coverage.origin.x -= childFrame.origin.x; - childBackdrop.coverage.origin.y -= childFrame.origin.y; - return childBackdrop; -} - -inline ShadowBackdrop resolveShadowBackdropForChild( - const ShadowBackdrop& inheritedBackdrop, - ShadowBackdropHostKind parentHostKind, - const ViewProps& parentProps, - const LayoutMetrics& parentLayoutMetrics, - const LayoutMetrics& childLayoutMetrics, - bool hasEarlierSiblingBarrier) { - if (parentHostKind == ShadowBackdropHostKind::Unknown) { - return shadowBackdropBarrier(ShadowBackdropReason::UnknownAncestor); - } - - if (hasEarlierSiblingBarrier) { - return shadowBackdropBarrier(ShadowBackdropReason::EarlierSibling); - } - - if (parentProps.opacity < 0.999 || shadowBackdropHasVisualEffect(parentProps)) { - return shadowBackdropBarrier(ShadowBackdropReason::VisualEffect); - } - - const auto borderMetrics = parentProps.resolveBorderMetrics(parentLayoutMetrics); - const bool hasRoundedCorners = shadowBackdropHasRoundedCorners(borderMetrics); - if (hasRoundedCorners && !parentProps.getClipsContentToBounds()) { - return shadowBackdropBarrier(ShadowBackdropReason::RoundedAncestor); - } - - if (parentProps.backgroundColor) { - const auto backgroundAlpha = alphaFromColor(parentProps.backgroundColor); - if (backgroundAlpha == 255) { - const auto& parentSize = parentLayoutMetrics.frame.size; - ShadowBackdrop parentBackdrop{ - .kind = ShadowBackdropKind::Provider, - .color = parentProps.backgroundColor, - .coverage = {{0, 0}, parentSize}, - }; - return shadowBackdropForChild(parentBackdrop, childLayoutMetrics); - } - - if (backgroundAlpha > 0) { - return shadowBackdropBarrier(ShadowBackdropReason::TranslucentAncestor); - } - } - - return shadowBackdropForChild(inheritedBackdrop, childLayoutMetrics); -} - /* * Describes a view that can be mounted. * This is exposed to the mounting layer. @@ -183,7 +46,6 @@ struct ShadowView final { EventEmitter::Shared eventEmitter{}; LayoutMetrics layoutMetrics{EmptyLayoutMetrics}; State::Shared state{}; - ShadowViewEnvironment environment{}; }; #if RN_DEBUG_STRING_CONVERTIBLE @@ -199,27 +61,6 @@ std::vector getDebugProps( namespace std { -template <> -struct hash { - size_t operator()(const facebook::react::ShadowBackdrop& backdrop) const { - return facebook::react::hash_combine( - 0, - backdrop.kind, - backdrop.reason, - backdrop.color, - backdrop.coverage, - backdrop.providerTag, - backdrop.barrierTag); - } -}; - -template <> -struct hash { - size_t operator()(const facebook::react::ShadowViewEnvironment& environment) const { - return facebook::react::hash_combine(0, environment.shadowBackdrop); - } -}; - template <> struct hash { size_t operator()(const facebook::react::ShadowView& shadowView) const { @@ -231,8 +72,7 @@ struct hash { shadowView.props, shadowView.eventEmitter, shadowView.layoutMetrics, - shadowView.state, - shadowView.environment); + shadowView.state); } }; diff --git a/packages/react-native/ReactCommon/react/renderer/mounting/internal/ShadowViewNodePair.h b/packages/react-native/ReactCommon/react/renderer/mounting/internal/ShadowViewNodePair.h index 32b4bf1e75a2..7f64b8e9a8b4 100644 --- a/packages/react-native/ReactCommon/react/renderer/mounting/internal/ShadowViewNodePair.h +++ b/packages/react-native/ReactCommon/react/renderer/mounting/internal/ShadowViewNodePair.h @@ -7,16 +7,12 @@ #pragma once -#include - #include #include #include namespace facebook::react { -using ShadowViewEnvironmentMap = std::unordered_map; - /* * Describes pair of a `ShadowView` and a `ShadowNode`. * This is not exposed to the mounting layer. @@ -37,10 +33,6 @@ struct ShadowViewNodePair final { bool isConcreteView{true}; Point contextOrigin{0, 0}; - // The top-level differ owns this map. Keeping the pointer on each pair lets - // nested slicing reuse the same derived environments without another walk. - const ShadowViewEnvironmentMap* environments{nullptr}; - size_t mountIndex{0}; /** diff --git a/packages/react-native/ReactCommon/react/renderer/mounting/internal/sliceChildShadowNodeViewPairs.cpp b/packages/react-native/ReactCommon/react/renderer/mounting/internal/sliceChildShadowNodeViewPairs.cpp index 1ca5f3f55465..cda18e4121c6 100644 --- a/packages/react-native/ReactCommon/react/renderer/mounting/internal/sliceChildShadowNodeViewPairs.cpp +++ b/packages/react-native/ReactCommon/react/renderer/mounting/internal/sliceChildShadowNodeViewPairs.cpp @@ -6,188 +6,13 @@ */ #include "sliceChildShadowNodeViewPairs.h" -#include #include -#include -#include #include #include "ShadowViewNodePair.h" namespace facebook::react { -#ifndef NDEBUG -struct ShadowBackdropPropagationStats { - size_t nodesVisited{}; - size_t environmentsComputed{}; - size_t providers{}; - size_t barriers{}; - - void didVisitNode() { - nodesVisited++; - } - - void didComputeEnvironment(const ShadowBackdrop& shadowBackdrop) { - environmentsComputed++; - if (shadowBackdrop.kind == ShadowBackdropKind::Provider) { - providers++; - } else if (shadowBackdrop.kind == ShadowBackdropKind::Barrier) { - barriers++; - } - } -}; -#else -struct ShadowBackdropPropagationStats { - void didVisitNode() {} - void didComputeEnvironment(const ShadowBackdrop&) {} -}; -#endif - -static ShadowBackdropHostKind shadowBackdropHostKind( - const ShadowNode& shadowNode) { - const auto componentName = shadowNode.getComponentName(); - if (componentName == RootComponentName) { - return ShadowBackdropHostKind::Root; - } - - if (componentName == ViewComponentName) { - return ShadowBackdropHostKind::View; - } - - return ShadowBackdropHostKind::Unknown; -} - -static const ViewProps& shadowBackdropViewProps( - const ShadowNode& shadowNode, - ShadowBackdropHostKind hostKind) { - static const ViewProps emptyViewProps{}; - if (hostKind == ShadowBackdropHostKind::Unknown) { - return emptyViewProps; - } - - const auto& props = *shadowNode.getProps(); - return static_cast(props); -} - -static bool shadowBackdropSiblingHasUnboundedEffect( - const ShadowNode& shadowNode) { - const auto hostKind = shadowBackdropHostKind(shadowNode); - if (hostKind == ShadowBackdropHostKind::Unknown) { - return true; - } - - const auto& props = shadowBackdropViewProps(shadowNode, hostKind); - const Transform identityTransform{}; - return shadowBackdropHasVisualEffect(props) || props.transform != identityTransform; -} - -static bool shadowBackdropRectsIntersect(const Rect& first, const Rect& second) { - const auto intersection = Rect::intersect(first, second); - return intersection.size.width > 0 && intersection.size.height > 0; -} - -static void collectShadowBackdropsForChildren( - const ShadowNode& parentShadowNode, - const ShadowBackdrop& inheritedBackdrop, - ShadowViewEnvironmentMap& environments, - ShadowBackdropPropagationStats& stats) { - const auto parentHostKind = shadowBackdropHostKind(parentShadowNode); - const auto& parentProps = - shadowBackdropViewProps(parentShadowNode, parentHostKind); - const auto parentShadowView = ShadowView(parentShadowNode); - const auto parentLayoutMetrics = parentShadowView.layoutMetrics; - - std::vector childShadowNodes; - for (const auto& childShadowNode : parentShadowNode.getChildren()) { - const auto childShadowNodePointer = childShadowNode.get(); - childShadowNodes.push_back(childShadowNodePointer); - } - - std::stable_sort( - childShadowNodes.begin(), - childShadowNodes.end(), - [](const ShadowNode* first, const ShadowNode* second) { - return first->getOrderIndex() < second->getOrderIndex(); - }); - - Rect earlierSiblingBounds{}; - bool hasEarlierSiblingBounds = false; - bool hasEarlierSiblingUnboundedEffect = false; - - for (const auto* childShadowNode : childShadowNodes) { - stats.didVisitNode(); - const auto childShadowView = ShadowView(*childShadowNode); - const auto childLayoutMetrics = childShadowView.layoutMetrics; - const auto childBounds = childLayoutMetrics.getOverflowInsetFrame(); - bool hasEarlierSiblingBarrier = hasEarlierSiblingUnboundedEffect; - if (hasEarlierSiblingBounds && - shadowBackdropRectsIntersect(earlierSiblingBounds, childBounds)) { - hasEarlierSiblingBarrier = true; - } - - auto childBackdrop = resolveShadowBackdropForChild( - inheritedBackdrop, - parentHostKind, - parentProps, - parentLayoutMetrics, - childLayoutMetrics, - hasEarlierSiblingBarrier); - if (childBackdrop.kind == ShadowBackdropKind::Provider && - childBackdrop.providerTag == -1) { - childBackdrop.providerTag = parentShadowNode.getTag(); - } - if (childBackdrop.kind == ShadowBackdropKind::Barrier && - childBackdrop.barrierTag == -1) { - childBackdrop.barrierTag = parentShadowNode.getTag(); - } - const auto childTag = childShadowNode->getTag(); - ShadowViewEnvironment childEnvironment{}; - childEnvironment.shadowBackdrop = childBackdrop; - const auto insertion = environments.emplace(childTag, childEnvironment); - react_native_assert(insertion.second); - stats.didComputeEnvironment(childBackdrop); - - collectShadowBackdropsForChildren( - *childShadowNode, childBackdrop, environments, stats); - - if (childLayoutMetrics != EmptyLayoutMetrics) { - if (hasEarlierSiblingBounds) { - earlierSiblingBounds.unionInPlace(childBounds); - } else { - earlierSiblingBounds = childBounds; - hasEarlierSiblingBounds = true; - } - } - - if (shadowBackdropSiblingHasUnboundedEffect(*childShadowNode)) { - hasEarlierSiblingUnboundedEffect = true; - } - } -} - -ShadowViewEnvironmentMap collectShadowViewEnvironments( - const ShadowNode& rootShadowNode) { - ShadowViewEnvironmentMap environments{}; - ShadowBackdropPropagationStats stats{}; -#ifndef NDEBUG - TraceSection propagationSection("ShadowBackdrop::propagate"); -#endif - collectShadowBackdropsForChildren(rootShadowNode, {}, environments, stats); -#ifndef NDEBUG - TraceSection statsSection( - "ShadowBackdrop::propagateStats", - "nodes", - stats.nodesVisited, - "environments", - stats.environmentsComputed, - "providers", - stats.providers, - "barriers", - stats.barriers); -#endif - return environments; -} - /* * Sorting comparator for `reorderInPlaceIfNeeded`. */ @@ -228,8 +53,7 @@ static void sliceChildShadowNodeViewPairsRecursively( ViewNodePairScope& scope, Point layoutOffset, const ShadowNode& shadowNode, - const CullingContext& cullingContext, - const ShadowViewEnvironmentMap* environments) { + const CullingContext& cullingContext) { for (const auto& sharedChildShadowNode : shadowNode.getChildren()) { auto& childShadowNode = *sharedChildShadowNode; #ifndef ANDROID @@ -244,12 +68,6 @@ static void sliceChildShadowNodeViewPairsRecursively( } #endif auto shadowView = ShadowView(childShadowNode); - if (environments) { - const auto childTag = childShadowNode.getTag(); - const auto environment = environments->find(childTag); - react_native_assert(environment != environments->end()); - shadowView.environment = environment->second; - } if (ReactNativeFeatureFlags::enableViewCulling()) { auto isViewCullable = @@ -309,8 +127,7 @@ static void sliceChildShadowNodeViewPairsRecursively( &childShadowNode, areChildrenFlattened, isConcreteView, - storedOrigin, - environments}); + storedOrigin}); if (shadowView.layoutMetrics.positionType == PositionType::Static) { auto it = pairList.begin(); @@ -324,8 +141,7 @@ static void sliceChildShadowNodeViewPairsRecursively( scope, origin, childShadowNode, - cullingContextCopy, - environments); + cullingContextCopy); } } else { pairList.push_back(&scope.back()); @@ -337,8 +153,7 @@ static void sliceChildShadowNodeViewPairsRecursively( scope, origin, childShadowNode, - cullingContextCopy, - environments); + cullingContextCopy); } } } @@ -366,8 +181,7 @@ std::vector sliceChildShadowNodeViewPairs( scope, layoutOffset, shadowNode, - cullingContext, - shadowNodePair.environments); + cullingContext); // Sorting pairs based on `orderIndex` if needed. reorderInPlaceIfNeeded(pairList); diff --git a/packages/react-native/ReactCommon/react/renderer/mounting/internal/sliceChildShadowNodeViewPairs.h b/packages/react-native/ReactCommon/react/renderer/mounting/internal/sliceChildShadowNodeViewPairs.h index 05e2a5063b50..278be0114465 100644 --- a/packages/react-native/ReactCommon/react/renderer/mounting/internal/sliceChildShadowNodeViewPairs.h +++ b/packages/react-native/ReactCommon/react/renderer/mounting/internal/sliceChildShadowNodeViewPairs.h @@ -10,10 +10,11 @@ #include #include "CullingContext.h" -#include "ShadowViewNodePair.h" namespace facebook::react { +struct ShadowViewNodePair; + /** * During differ, we need to keep some `ShadowViewNodePair`s in memory. * Some `ShadowViewNodePair`s are referenced from std::vectors returned @@ -37,13 +38,6 @@ namespace facebook::react { */ using ViewNodePairScope = std::deque; -/* - * Computes the mounting environments for a complete, laid-out Shadow Tree. - * The Differentiator owns the result for the duration of one old/new diff. - */ -ShadowViewEnvironmentMap collectShadowViewEnvironments( - const ShadowNode& rootShadowNode); - /** * Generates a list of `ShadowViewNodePair`s that represents a layer of a * flattened view hierarchy. The V2 version preserves nodes even if they do diff --git a/packages/react-native/ReactCommon/react/renderer/mounting/stubs/stubs.cpp b/packages/react-native/ReactCommon/react/renderer/mounting/stubs/stubs.cpp index d2a59503e28a..22e7b6c07f6f 100644 --- a/packages/react-native/ReactCommon/react/renderer/mounting/stubs/stubs.cpp +++ b/packages/react-native/ReactCommon/react/renderer/mounting/stubs/stubs.cpp @@ -7,7 +7,6 @@ #include "stubs.h" -#include #include #include #include @@ -75,20 +74,8 @@ StubViewTree buildStubViewTreeWithoutUsingDifferentiator( auto mutations = ShadowViewMutation::List{}; mutations.reserve(256); - ShadowViewEnvironmentMap environments{}; - const ShadowViewEnvironmentMap* environmentsPointer = nullptr; - const bool shadowBackdropEnabled = - ReactNativeFeatureFlags::enableIOSBorderBoxShadowBackdrop(); - if (shadowBackdropEnabled) { - environments = collectShadowViewEnvironments(rootShadowNode); - environmentsPointer = &environments; - } - ViewNodePairScope scope; - ShadowViewNodePair rootShadowNodePair{ - .shadowNode = &rootShadowNode, - .environments = environmentsPointer, - }; + ShadowViewNodePair rootShadowNodePair{.shadowNode = &rootShadowNode}; calculateShadowViewMutationsForNewTree( mutations, scope, diff --git a/packages/react-native/ReactCommon/react/renderer/mounting/tests/ShadowBackdropTest.cpp b/packages/react-native/ReactCommon/react/renderer/mounting/tests/ShadowBackdropTest.cpp deleted file mode 100644 index 744e212e418a..000000000000 --- a/packages/react-native/ReactCommon/react/renderer/mounting/tests/ShadowBackdropTest.cpp +++ /dev/null @@ -1,254 +0,0 @@ -/* - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#include - -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace facebook::react { - -namespace { - -LayoutMetrics layoutMetrics(Size size, Point origin = {}) { - LayoutMetrics result{}; - result.frame = {origin, size}; - return result; -} - -} // namespace - -class ShadowBackdropTestFeatureFlags : public ReactNativeFeatureFlagsDefaults { - public: - bool enableIOSBorderBoxShadowBackdrop() override { - return true; - } -}; - -class ShadowBackdropDifferentiatorTest : public ::testing::Test { - protected: - void SetUp() override { - ReactNativeFeatureFlags::dangerouslyReset(); - auto featureFlags = std::make_unique(); - ReactNativeFeatureFlags::override(std::move(featureFlags)); - } - - void TearDown() override { - ReactNativeFeatureFlags::dangerouslyReset(); - } -}; - -TEST(ShadowBackdropTest, opaque_view_provides_backdrop_to_child) { - ViewProps parentProps{}; - parentProps.backgroundColor = blackColor(); - - auto parentLayout = layoutMetrics({100, 100}); - auto childLayout = layoutMetrics({40, 40}, {20, 20}); - auto backdrop = resolveShadowBackdropForChild( - {}, - ShadowBackdropHostKind::View, - parentProps, - parentLayout, - childLayout, - false); - - EXPECT_EQ(backdrop.kind, ShadowBackdropKind::Provider); - EXPECT_EQ(backdrop.color, blackColor()); -} - -TEST(ShadowBackdropTest, transparent_wrapper_relays_opaque_backdrop) { - ViewProps providerProps{}; - providerProps.backgroundColor = blackColor(); - auto providerLayout = layoutMetrics({100, 100}); - auto wrapperLayout = layoutMetrics({60, 60}, {20, 20}); - auto inheritedBackdrop = resolveShadowBackdropForChild( - {}, - ShadowBackdropHostKind::View, - providerProps, - providerLayout, - wrapperLayout, - false); - - ViewProps wrapperProps{}; - auto childLayout = layoutMetrics({40, 40}, {10, 10}); - auto backdrop = resolveShadowBackdropForChild( - inheritedBackdrop, - ShadowBackdropHostKind::View, - wrapperProps, - wrapperLayout, - childLayout, - false); - - EXPECT_EQ(backdrop.kind, ShadowBackdropKind::Provider); - EXPECT_EQ(backdrop.color, blackColor()); -} - -TEST(ShadowBackdropTest, transparent_wrapper_does_not_extend_provider_coverage) { - ViewProps providerProps{}; - providerProps.backgroundColor = blackColor(); - auto providerLayout = layoutMetrics({100, 100}); - auto wrapperLayout = layoutMetrics({40, 40}, {60, 0}); - auto inheritedBackdrop = resolveShadowBackdropForChild( - {}, - ShadowBackdropHostKind::View, - providerProps, - providerLayout, - wrapperLayout, - false); - - ViewProps wrapperProps{}; - auto childLayout = layoutMetrics({40, 40}, {20, 0}); - auto backdrop = resolveShadowBackdropForChild( - inheritedBackdrop, - ShadowBackdropHostKind::View, - wrapperProps, - wrapperLayout, - childLayout, - false); - - EXPECT_EQ(backdrop.kind, ShadowBackdropKind::Barrier); - EXPECT_EQ(backdrop.reason, ShadowBackdropReason::OutsideProvider); -} - -TEST(ShadowBackdropTest, translucent_ancestor_is_a_barrier) { - ViewProps parentProps{}; - parentProps.backgroundColor = colorFromRGBA(255, 255, 255, 31); - - auto parentLayout = layoutMetrics({100, 100}); - auto childLayout = layoutMetrics({40, 40}, {20, 20}); - auto backdrop = resolveShadowBackdropForChild( - {}, - ShadowBackdropHostKind::View, - parentProps, - parentLayout, - childLayout, - false); - - EXPECT_EQ(backdrop.kind, ShadowBackdropKind::Barrier); - EXPECT_EQ(backdrop.reason, ShadowBackdropReason::TranslucentAncestor); -} - -TEST(ShadowBackdropTest, overlapping_preceding_sibling_is_a_barrier) { - ViewProps parentProps{}; - parentProps.backgroundColor = blackColor(); - - auto parentLayout = layoutMetrics({100, 100}); - auto childLayout = layoutMetrics({40, 40}, {20, 20}); - auto backdrop = resolveShadowBackdropForChild( - {}, - ShadowBackdropHostKind::View, - parentProps, - parentLayout, - childLayout, - true); - - EXPECT_EQ(backdrop.kind, ShadowBackdropKind::Barrier); - EXPECT_EQ(backdrop.reason, ShadowBackdropReason::EarlierSibling); -} - -TEST(ShadowBackdropTest, unknown_ancestor_is_a_barrier) { - ViewProps parentProps{}; - auto parentLayout = layoutMetrics({100, 100}); - auto childLayout = layoutMetrics({40, 40}, {20, 20}); - auto backdrop = resolveShadowBackdropForChild( - {}, - ShadowBackdropHostKind::Unknown, - parentProps, - parentLayout, - childLayout, - false); - - EXPECT_EQ(backdrop.kind, ShadowBackdropKind::Barrier); - EXPECT_EQ(backdrop.reason, ShadowBackdropReason::UnknownAncestor); -} - -TEST(ShadowBackdropTest, provider_does_not_escape_its_own_bounds) { - ViewProps parentProps{}; - parentProps.backgroundColor = blackColor(); - - auto parentLayout = layoutMetrics({100, 100}); - auto childLayout = layoutMetrics({40, 40}, {80, 80}); - auto backdrop = resolveShadowBackdropForChild( - {}, - ShadowBackdropHostKind::View, - parentProps, - parentLayout, - childLayout, - false); - - EXPECT_EQ(backdrop.kind, ShadowBackdropKind::Barrier); - EXPECT_EQ(backdrop.reason, ShadowBackdropReason::OutsideProvider); -} - -TEST_F( - ShadowBackdropDifferentiatorTest, - root_environment_pass_reaches_nested_created_view) { - std::shared_ptr rootShadowNode; - - auto providerProps = std::make_shared(); - providerProps->backgroundColor = blackColor(); - - auto shadowHostProps = std::make_shared(); - shadowHostProps->shadowPathIOS = ShadowPathMode::BorderBox; - - // clang-format off - auto element = - Element() - .reference(rootShadowNode) - .tag(1) - .children({ - Element() - .tag(2) - .props(providerProps) - .children({ - Element() - .tag(3) - .props(shadowHostProps) - }) - }); - // clang-format on - - auto builder = simpleComponentBuilder(); - builder.build(element); - rootShadowNode->layoutIfNeeded(); - - auto emptyRootFragment = ShadowNodeFragment{ - ShadowNodeFragment::propsPlaceholder(), - ShadowNode::emptySharedShadowNodeSharedList(), - }; - auto emptyRootShadowNode = rootShadowNode->clone(emptyRootFragment); - auto mutations = - calculateShadowViewMutations(*emptyRootShadowNode, *rootShadowNode); - - const ShadowView* createdShadowHost = nullptr; - for (const auto& mutation : mutations) { - if (mutation.type == ShadowViewMutation::Create && - mutation.newChildShadowView.tag == 3) { - createdShadowHost = &mutation.newChildShadowView; - break; - } - } - - ASSERT_NE(createdShadowHost, nullptr); - const auto& backdrop = createdShadowHost->environment.shadowBackdrop; - EXPECT_EQ(backdrop.kind, ShadowBackdropKind::Provider); - EXPECT_EQ(backdrop.color, blackColor()); - EXPECT_EQ(backdrop.providerTag, 2); -} - -} // namespace facebook::react diff --git a/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js b/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js index 03cd91085be6..79fa981d73dc 100644 --- a/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js +++ b/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js @@ -270,17 +270,6 @@ const definitions: FeatureFlagDefinitions = { }, ossReleaseStage: 'none', }, - enableIOSBorderBoxShadowBackdrop: { - defaultValue: false, - metadata: { - dateAdded: '2026-07-15', - description: - 'Add opaque backplates behind eligible legacy iOS border-box shadows.', - expectedReleaseValue: true, - purpose: 'experimentation', - }, - ossReleaseStage: 'none', - }, enableIOSExperimentalAutoFocusImplementation: { defaultValue: false, metadata: { diff --git a/packages/react-native/src/private/featureflags/ReactNativeFeatureFlags.js b/packages/react-native/src/private/featureflags/ReactNativeFeatureFlags.js index 4301f154976a..028318b0630f 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<<1a49394fcdbb433c3f94597bb32827c9>> + * @generated SignedSource<> * @flow strict * @noformat */ @@ -72,7 +72,6 @@ export type ReactNativeFeatureFlags = $ReadOnly<{ enableFixForParentTagDuringReparenting: Getter, enableFontScaleChangesUpdatingLayout: Getter, enableIOSBorderBoxShadowPathByDefault: Getter, - enableIOSBorderBoxShadowBackdrop: Getter, enableIOSExperimentalAutoFocusImplementation: Getter, enableIOSTextBaselineOffsetPerLine: Getter, enableIOSViewClipToPaddingBox: Getter, @@ -289,10 +288,6 @@ export const enableFontScaleChangesUpdatingLayout: Getter = createNativ * Use the border box for legacy iOS shadow paths when shadowPathIOS is omitted. */ export const enableIOSBorderBoxShadowPathByDefault: Getter = createNativeFlagGetter('enableIOSBorderBoxShadowPathByDefault', false); -/** - * Add opaque backplates behind eligible legacy iOS border-box shadows. - */ -export const enableIOSBorderBoxShadowBackdrop: Getter = createNativeFlagGetter('enableIOSBorderBoxShadowBackdrop', false); /** * Fixes #56595 by moving the autoFocus from didMoveToWindow to viewDidAppear */ diff --git a/packages/react-native/src/private/featureflags/specs/NativeReactNativeFeatureFlags.js b/packages/react-native/src/private/featureflags/specs/NativeReactNativeFeatureFlags.js index 737010f9026f..702327785608 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<<1a88c0eb715d767b15fe668e6e60129c>> + * @generated SignedSource<> * @flow strict * @noformat */ @@ -46,7 +46,6 @@ export interface Spec extends TurboModule { +enableFixForParentTagDuringReparenting?: () => boolean; +enableFontScaleChangesUpdatingLayout?: () => boolean; +enableIOSBorderBoxShadowPathByDefault?: () => boolean; - +enableIOSBorderBoxShadowBackdrop?: () => boolean; +enableIOSExperimentalAutoFocusImplementation?: () => boolean; +enableIOSTextBaselineOffsetPerLine?: () => boolean; +enableIOSViewClipToPaddingBox?: () => boolean; diff --git a/packages/rn-tester/js/examples/Playground/RNTesterShadowPathPlayground.js b/packages/rn-tester/js/examples/Playground/RNTesterShadowPathPlayground.js index 5441558fb22c..8f1826954623 100644 --- a/packages/rn-tester/js/examples/Playground/RNTesterShadowPathPlayground.js +++ b/packages/rn-tester/js/examples/Playground/RNTesterShadowPathPlayground.js @@ -15,26 +15,15 @@ import {useEffect, useRef, useState} from 'react'; import { Animated, Pressable, - ScrollView, StyleSheet, + Switch, Text, View, } from 'react-native'; -import { - enableIOSBorderBoxShadowBackdrop, - enableIOSBorderBoxShadowPathByDefault, -} from 'react-native/src/private/featureflags/ReactNativeFeatureFlags'; +import {enableIOSBorderBoxShadowPathByDefault} from 'react-native/src/private/featureflags/ReactNativeFeatureFlags'; type ShadowPathMode = 'auto' | 'border-box' | 'content-alpha'; -type ShadowPathSelection = 'default' | 'box-shadow' | ShadowPathMode; -type AnimationMode = 'off' | 'scale' | 'bounds' | 'content'; -type BackdropScenario = - | 'eligible' - | 'translucent-ancestor' - | 'earlier-sibling' - | 'unknown-ancestor' - | 'rounded-visible' - | 'rounded-clipped'; +type ShadowPathSelection = 'default' | ShadowPathMode; const SHADOW_PATH_OPTIONS: $ReadOnlyArray<{ label: string, @@ -44,78 +33,34 @@ const SHADOW_PATH_OPTIONS: $ReadOnlyArray<{ {label: 'Auto', value: 'auto'}, {label: 'Border Box', value: 'border-box'}, {label: 'Content Alpha', value: 'content-alpha'}, - {label: 'Box Shadow', value: 'box-shadow'}, -]; - -const ANIMATION_OPTIONS: $ReadOnlyArray<{ - label: string, - value: AnimationMode, -}> = [ - {label: 'Off', value: 'off'}, - {label: 'Scale', value: 'scale'}, - {label: 'Bounds', value: 'bounds'}, - {label: 'Content', value: 'content'}, -]; - -const BACKDROP_SCENARIOS: $ReadOnlyArray<{ - label: string, - value: BackdropScenario, -}> = [ - {label: 'Eligible', value: 'eligible'}, - {label: 'Translucent', value: 'translucent-ancestor'}, - {label: 'Sibling', value: 'earlier-sibling'}, - {label: 'Unknown', value: 'unknown-ancestor'}, - {label: 'Rounded', value: 'rounded-visible'}, - {label: 'Clipped', value: 'rounded-clipped'}, ]; function ShadowPathPlayground(): React.Node { const [selectedMode, setSelectedMode] = - useState('box-shadow'); - const [animationMode, setAnimationMode] = useState('off'); - const [backdropScenario, setBackdropScenario] = - useState('eligible'); - // Keep layout and native-driven animations on separate values. Animated - // values cannot safely switch between the JS and native drivers. - const nativeAnimationProgressRef = useRef(null); - if (nativeAnimationProgressRef.current == null) { - nativeAnimationProgressRef.current = new Animated.Value(0); + useState('default'); + const [animationEnabled, setAnimationEnabled] = useState(false); + const animationProgressRef = useRef(null); + if (animationProgressRef.current == null) { + animationProgressRef.current = new Animated.Value(0); } - const nativeAnimationProgress = nativeAnimationProgressRef.current; - const boundsAnimationProgressRef = useRef(null); - if (boundsAnimationProgressRef.current == null) { - boundsAnimationProgressRef.current = new Animated.Value(0); - } - const boundsAnimationProgress = boundsAnimationProgressRef.current; + const animationProgress = animationProgressRef.current; useEffect(() => { - nativeAnimationProgress.stopAnimation(); - nativeAnimationProgress.setValue(0); - boundsAnimationProgress.stopAnimation(); - boundsAnimationProgress.setValue(0); - - if (animationMode === 'off') { + if (!animationEnabled) { + animationProgress.stopAnimation(); + animationProgress.setValue(0); return; } - // Bounds cannot use the native driver because it intentionally runs - // layout on every frame to invalidate the shadow host's silhouette. - let animationProgress = nativeAnimationProgress; - let useNativeDriver = true; - if (animationMode === 'bounds') { - animationProgress = boundsAnimationProgress; - useNativeDriver = false; - } - const growAnimation = Animated.timing(animationProgress, { duration: 700, toValue: 1, - useNativeDriver, + useNativeDriver: true, }); const shrinkAnimation = Animated.timing(animationProgress, { duration: 700, toValue: 0, - useNativeDriver, + useNativeDriver: true, }); const animationSequence = Animated.sequence([ growAnimation, @@ -127,64 +72,24 @@ function ShadowPathPlayground(): React.Node { return () => { animation.stop(); }; - }, [animationMode, boundsAnimationProgress, nativeAnimationProgress]); + }, [animationEnabled, animationProgress]); - const scale = nativeAnimationProgress.interpolate({ + const scale = animationProgress.interpolate({ inputRange: [0, 1], outputRange: [1, 1.12], }); - const animatedWidth = boundsAnimationProgress.interpolate({ - inputRange: [0, 1], - outputRange: [160, 220], - }); - const contentTranslation = nativeAnimationProgress.interpolate({ - inputRange: [0, 1], - outputRange: [0, 24], - }); - // Keep the two shadow APIs mutually exclusive so profiling the Box Shadow - // mode cannot also measure legacy shadow work. - const usesBoxShadow = selectedMode === 'box-shadow'; - let shadowPathProps: {shadowPathIOS?: ShadowPathMode} = {}; - if (selectedMode !== 'default' && selectedMode !== 'box-shadow') { - shadowPathProps = {shadowPathIOS: selectedMode}; - } + const animatedStyle = {transform: [{scale}]}; + const shadowPathProps: {shadowPathIOS?: ShadowPathMode} = + selectedMode === 'default' ? {} : {shadowPathIOS: selectedMode}; const flagEnabled = enableIOSBorderBoxShadowPathByDefault(); const flagDefault = flagEnabled ? 'Border Box' : 'Auto'; - const backdropEnabled = enableIOSBorderBoxShadowBackdrop(); const activeMode = SHADOW_PATH_OPTIONS.find( option => option.value === selectedMode, ); - const activeAnimation = ANIMATION_OPTIONS.find( - option => option.value === animationMode, - ); - const activeBackdropScenario = BACKDROP_SCENARIOS.find( - option => option.value === backdropScenario, - ); if (activeMode == null) { throw new Error('Selected shadow path mode must have a label.'); } - if (activeAnimation == null) { - throw new Error('Selected animation mode must have a label.'); - } - if (activeBackdropScenario == null) { - throw new Error('Selected backdrop scenario must have a label.'); - } - - const renderButtonShadowHost = (): React.Node => ( - - - Translucent button - - - ); return ( @@ -213,113 +118,23 @@ function ShadowPathPlayground(): React.Node { })} - Animation workload - - {ANIMATION_OPTIONS.map(option => { - const selected = option.value === animationMode; - return ( - setAnimationMode(option.value)} - style={[ - styles.segment, - selected ? styles.selectedSegment : null, - ]}> - - {option.label} - - - ); - })} + + Scale animation + Active mode: {activeMode.label} Feature-flag default: {flagDefault} - Animation: {activeAnimation.label} - - Backplate: {backdropEnabled ? 'Enabled' : 'Disabled'} - - - In Instruments, inspect the ShadowBackdrop::propagate trace for - visited-node and eligibility counts. - - - Scale exercises cached compositing. Bounds repeatedly changes the - button's silhouette. Content moves a child within the irregular shadow - host. - - Backdrop proof scenario - - {BACKDROP_SCENARIOS.map(option => { - const selected = option.value === backdropScenario; - return ( - setBackdropScenario(option.value)} - style={[ - styles.segment, - selected ? styles.selectedSegment : null, - ]}> - - {option.label} - - - ); - })} - - - Scenario: {activeBackdropScenario.label} - + Translucent button hierarchy - {backdropScenario === 'eligible' ? ( - - {renderButtonShadowHost()} - - ) : null} - {backdropScenario === 'translucent-ancestor' ? ( - - - {renderButtonShadowHost()} - - - ) : null} - {backdropScenario === 'earlier-sibling' ? ( - - - {renderButtonShadowHost()} - - ) : null} - {backdropScenario === 'unknown-ancestor' ? ( - - {renderButtonShadowHost()} - - ) : null} - {backdropScenario === 'rounded-visible' ? ( - - {renderButtonShadowHost()} - - ) : null} - {backdropScenario === 'rounded-clipped' ? ( - - {renderButtonShadowHost()} + + + Translucent button - ) : null} + Irregular composited content @@ -330,69 +145,12 @@ function ShadowPathPlayground(): React.Node { + style={[styles.irregularShadowHost, animatedStyle]}> - + - - Structural stress cases - - The left column has deep transparent wrappers. The right column has a - dense list where every item is a shadow host. - - - - - - - - Deep wrapper - - - - - - - {[0, 1, 2, 3].map(index => ( - - - List item {index + 1} - - - ))} - - ); } @@ -407,6 +165,7 @@ const styles = StyleSheet.create({ borderRadius: 8, borderWidth: StyleSheet.hairlineWidth, flexDirection: 'row', + overflow: 'hidden', }, segment: { alignItems: 'center', @@ -429,6 +188,11 @@ const styles = StyleSheet.create({ color: 'white', fontWeight: '600', }, + animationRow: { + alignItems: 'center', + flexDirection: 'row', + justifyContent: 'space-between', + }, label: { fontSize: 16, }, @@ -452,66 +216,13 @@ const styles = StyleSheet.create({ minHeight: 140, overflow: 'visible', }, - opaqueBackdropProvider: { - alignItems: 'center', - backgroundColor: '#6750a4', - justifyContent: 'center', - minHeight: 100, - minWidth: 260, - }, - translucentAncestorBarrier: { - alignItems: 'center', - backgroundColor: 'rgba(255, 255, 255, 0.12)', - justifyContent: 'center', - minHeight: 100, - minWidth: 260, - }, - earlierSiblingBarrier: { - backgroundColor: '#ff453a', - height: 64, - position: 'absolute', - width: 190, - }, - unknownAncestorBarrier: { - backgroundColor: '#6750a4', - maxHeight: 100, - minWidth: 260, - }, - unknownAncestorContent: { - alignItems: 'center', - justifyContent: 'center', - minHeight: 100, - }, - roundedVisibleBarrier: { - alignItems: 'center', - backgroundColor: '#6750a4', - borderRadius: 24, - justifyContent: 'center', - minHeight: 100, - minWidth: 260, - }, - roundedClippedProvider: { - alignItems: 'center', - backgroundColor: '#6750a4', - borderRadius: 24, - justifyContent: 'center', - minHeight: 100, - minWidth: 260, - overflow: 'hidden', - }, buttonShadowHost: { borderRadius: 8, - width: 160, - }, - buttonLegacyShadow: { shadowColor: 'black', shadowOffset: {width: 0, height: 1}, shadowOpacity: 0.14, shadowRadius: 4, }, - buttonBoxShadow: { - boxShadow: '0px 1px 8px rgba(0, 0, 0, 0.14)', - }, translucentButton: { alignItems: 'center', backgroundColor: 'rgba(255, 255, 255, 0.12)', @@ -520,7 +231,6 @@ const styles = StyleSheet.create({ minHeight: 40, minWidth: 160, overflow: 'hidden', - width: '100%', }, buttonLabel: { color: '#1c1c1e', @@ -529,16 +239,11 @@ const styles = StyleSheet.create({ irregularShadowHost: { height: 72, position: 'relative', - width: 160, - }, - irregularLegacyShadow: { shadowColor: 'black', shadowOffset: {width: 0, height: 1}, shadowOpacity: 0.25, shadowRadius: 4, - }, - irregularBoxShadow: { - boxShadow: '0px 1px 8px rgba(0, 0, 0, 0.25)', + width: 160, }, circle: { backgroundColor: '#ff9f0a', @@ -559,47 +264,6 @@ const styles = StyleSheet.create({ backgroundColor: '#bf5af2', left: 96, }, - stressStage: { - backgroundColor: '#6750a4', - gap: 12, - padding: 16, - }, - deepWrapperOne: { - padding: 2, - }, - deepWrapperTwo: { - padding: 2, - }, - deepWrapperThree: { - padding: 2, - }, - denseList: { - gap: 8, - }, - stressShadowHost: { - borderRadius: 8, - width: 160, - }, - stressLegacyShadow: { - shadowColor: 'black', - shadowOffset: {width: 0, height: 1}, - shadowOpacity: 0.14, - shadowRadius: 4, - }, - stressBoxShadow: { - boxShadow: '0px 1px 8px rgba(0, 0, 0, 0.14)', - }, - stressTranslucentContent: { - alignItems: 'center', - backgroundColor: 'rgba(255, 255, 255, 0.12)', - borderRadius: 8, - justifyContent: 'center', - minHeight: 32, - }, - stressLabel: { - color: '#1c1c1e', - fontSize: 12, - }, }); export default ({ From 580a036913e44c4f306e08305c551f622e2175a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hanno=20J=2E=20G=C3=B6decke?= Date: Thu, 16 Jul 2026 15:24:37 +0200 Subject: [PATCH 6/6] refactor(ios): remove shadow path default feature flag --- .../Components/View/ViewPropTypes.d.ts | 3 +- .../Components/View/ViewPropTypes.js | 3 +- .../View/RCTViewComponentView.mm | 3 +- .../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 | 112 ++++++++---------- .../ReactNativeFeatureFlagsAccessor.h | 6 +- .../ReactNativeFeatureFlagsDefaults.h | 6 +- .../ReactNativeFeatureFlagsDynamicProvider.h | 11 +- .../ReactNativeFeatureFlagsProvider.h | 3 +- .../NativeReactNativeFeatureFlags.cpp | 7 +- .../NativeReactNativeFeatureFlags.h | 4 +- .../renderer/components/view/primitives.h | 14 +-- .../components/view/tests/ConversionsTest.cpp | 9 +- .../ReactNativeFeatureFlags.config.js | 11 -- .../featureflags/ReactNativeFeatureFlags.js | 7 +- .../specs/NativeReactNativeFeatureFlags.js | 3 +- .../RNTesterShadowPathPlayground.js | 5 +- 26 files changed, 77 insertions(+), 209 deletions(-) diff --git a/packages/react-native/Libraries/Components/View/ViewPropTypes.d.ts b/packages/react-native/Libraries/Components/View/ViewPropTypes.d.ts index ae7bc6751c9e..c90d2f69e94c 100644 --- a/packages/react-native/Libraries/Components/View/ViewPropTypes.d.ts +++ b/packages/react-native/Libraries/Components/View/ViewPropTypes.d.ts @@ -84,8 +84,7 @@ export interface ViewPropsIOS extends TVViewPropsIOS { * view and its children. This supports irregular silhouettes but may require * offscreen rendering. * - * When omitted, the default is controlled by a React Native feature flag. - * The initial default remains `auto`. + * When omitted, `auto` is used. * * @platform ios */ diff --git a/packages/react-native/Libraries/Components/View/ViewPropTypes.js b/packages/react-native/Libraries/Components/View/ViewPropTypes.js index 3ab031fad93a..1d8a7ee78877 100644 --- a/packages/react-native/Libraries/Components/View/ViewPropTypes.js +++ b/packages/react-native/Libraries/Components/View/ViewPropTypes.js @@ -407,8 +407,7 @@ export type ViewPropsIOS = $ReadOnly<{ * view and its children. This supports irregular silhouettes but may require * offscreen rendering. * - * When omitted, the default is controlled by a React Native feature flag. - * The initial default remains `auto`. + * When omitted, `auto` is used. * * @platform ios */ diff --git a/packages/react-native/React/Fabric/Mounting/ComponentViews/View/RCTViewComponentView.mm b/packages/react-native/React/Fabric/Mounting/ComponentViews/View/RCTViewComponentView.mm index 62eff72135af..61e9aa6e4425 100644 --- a/packages/react-native/React/Fabric/Mounting/ComponentViews/View/RCTViewComponentView.mm +++ b/packages/react-native/React/Fabric/Mounting/ComponentViews/View/RCTViewComponentView.mm @@ -838,8 +838,7 @@ - (void)invalidateLayer if (!layerHasShadow) { layer.shadowPath = nil; } else { - bool borderBoxDefaultEnabled = ReactNativeFeatureFlags::enableIOSBorderBoxShadowPathByDefault(); - ShadowPathMode shadowPathMode = resolveShadowPathMode(_props->shadowPathIOS, borderBoxDefaultEnabled); + ShadowPathMode shadowPathMode = resolveShadowPathMode(_props->shadowPathIOS); RCTCornerRadii cornerRadii = RCTCornerRadiiFromBorderRadii(borderMetrics.borderRadii); const RCTCornerInsets cornerInsets = RCTGetCornerInsets(cornerRadii, UIEdgeInsetsZero); 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 be58190699b3..18b18c8bffc6 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<<2943f7c7f969e25c549ee6bfeff650c6>> + * @generated SignedSource<> */ /** @@ -150,12 +150,6 @@ public object ReactNativeFeatureFlags { @JvmStatic public fun enableFontScaleChangesUpdatingLayout(): Boolean = accessor.enableFontScaleChangesUpdatingLayout() - /** - * Use the border box for legacy iOS shadow paths when shadowPathIOS is omitted. - */ - @JvmStatic - public fun enableIOSBorderBoxShadowPathByDefault(): Boolean = accessor.enableIOSBorderBoxShadowPathByDefault() - /** * Fixes #56595 by moving the autoFocus from didMoveToWindow to viewDidAppear */ 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 d167c594bb67..c814df9eab66 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<> */ /** @@ -40,7 +40,6 @@ internal class ReactNativeFeatureFlagsCxxAccessor : ReactNativeFeatureFlagsAcces private var enableFabricRendererCache: Boolean? = null private var enableFixForParentTagDuringReparentingCache: Boolean? = null private var enableFontScaleChangesUpdatingLayoutCache: Boolean? = null - private var enableIOSBorderBoxShadowPathByDefaultCache: Boolean? = null private var enableIOSExperimentalAutoFocusImplementationCache: Boolean? = null private var enableIOSTextBaselineOffsetPerLineCache: Boolean? = null private var enableIOSViewClipToPaddingBoxCache: Boolean? = null @@ -268,15 +267,6 @@ internal class ReactNativeFeatureFlagsCxxAccessor : ReactNativeFeatureFlagsAcces return cached } - override fun enableIOSBorderBoxShadowPathByDefault(): Boolean { - var cached = enableIOSBorderBoxShadowPathByDefaultCache - if (cached == null) { - cached = ReactNativeFeatureFlagsCxxInterop.enableIOSBorderBoxShadowPathByDefault() - enableIOSBorderBoxShadowPathByDefaultCache = cached - } - return cached - } - override fun enableIOSExperimentalAutoFocusImplementation(): Boolean { var cached = enableIOSExperimentalAutoFocusImplementationCache 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 0cb215c7fdd4..abdef81d36f0 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<<42800c900774644d53216aa2c7dd624c>> + * @generated SignedSource<<1234d5c7c6bb64451618817387318d13>> */ /** @@ -68,8 +68,6 @@ public object ReactNativeFeatureFlagsCxxInterop { @DoNotStrip @JvmStatic public external fun enableFontScaleChangesUpdatingLayout(): Boolean - @DoNotStrip @JvmStatic public external fun enableIOSBorderBoxShadowPathByDefault(): Boolean - @DoNotStrip @JvmStatic public external fun enableIOSExperimentalAutoFocusImplementation(): Boolean @DoNotStrip @JvmStatic public external fun enableIOSTextBaselineOffsetPerLine(): 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 a9bc337db331..12c9c2d2a564 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<<84c5a4338deb791e07275d340c3cd419>> + * @generated SignedSource<<3255db6cb7c71d866991568dc632321f>> */ /** @@ -63,8 +63,6 @@ public open class ReactNativeFeatureFlagsDefaults : ReactNativeFeatureFlagsProvi override fun enableFontScaleChangesUpdatingLayout(): Boolean = false - override fun enableIOSBorderBoxShadowPathByDefault(): Boolean = false - override fun enableIOSExperimentalAutoFocusImplementation(): Boolean = false override fun enableIOSTextBaselineOffsetPerLine(): 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 175b9500311c..6058c9cd3dfd 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<<402131230f486c9bee0e5d877419280b>> + * @generated SignedSource<<6f23f8fd399adf79814bb8f2edc29841>> */ /** @@ -44,7 +44,6 @@ internal class ReactNativeFeatureFlagsLocalAccessor : ReactNativeFeatureFlagsAcc private var enableFabricRendererCache: Boolean? = null private var enableFixForParentTagDuringReparentingCache: Boolean? = null private var enableFontScaleChangesUpdatingLayoutCache: Boolean? = null - private var enableIOSBorderBoxShadowPathByDefaultCache: Boolean? = null private var enableIOSExperimentalAutoFocusImplementationCache: Boolean? = null private var enableIOSTextBaselineOffsetPerLineCache: Boolean? = null private var enableIOSViewClipToPaddingBoxCache: Boolean? = null @@ -292,16 +291,6 @@ internal class ReactNativeFeatureFlagsLocalAccessor : ReactNativeFeatureFlagsAcc return cached } - override fun enableIOSBorderBoxShadowPathByDefault(): Boolean { - var cached = enableIOSBorderBoxShadowPathByDefaultCache - if (cached == null) { - cached = currentProvider.enableIOSBorderBoxShadowPathByDefault() - accessedFeatureFlags.add("enableIOSBorderBoxShadowPathByDefault") - enableIOSBorderBoxShadowPathByDefaultCache = cached - } - return cached - } - override fun enableIOSExperimentalAutoFocusImplementation(): Boolean { var cached = enableIOSExperimentalAutoFocusImplementationCache 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 b08ab0f30299..35726811606f 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<<752961866ff51cfccd11f8c4314bc920>> + * @generated SignedSource<<2877a4acf03951884d1d663fdf00b9f8>> */ /** @@ -63,8 +63,6 @@ public interface ReactNativeFeatureFlagsProvider { @DoNotStrip public fun enableFontScaleChangesUpdatingLayout(): Boolean - @DoNotStrip public fun enableIOSBorderBoxShadowPathByDefault(): Boolean - @DoNotStrip public fun enableIOSExperimentalAutoFocusImplementation(): Boolean @DoNotStrip public fun enableIOSTextBaselineOffsetPerLine(): 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 df1e51ffae9f..479d7f71c528 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<<5c4c763d4b19e17b107c87f772decaa8>> + * @generated SignedSource<> */ /** @@ -159,12 +159,6 @@ class ReactNativeFeatureFlagsJavaProvider return method(javaProvider_); } - bool enableIOSBorderBoxShadowPathByDefault() override { - static const auto method = - getReactNativeFeatureFlagsProviderJavaClass()->getMethod("enableIOSBorderBoxShadowPathByDefault"); - return method(javaProvider_); - } - bool enableIOSExperimentalAutoFocusImplementation() override { static const auto method = getReactNativeFeatureFlagsProviderJavaClass()->getMethod("enableIOSExperimentalAutoFocusImplementation"); @@ -545,11 +539,6 @@ bool JReactNativeFeatureFlagsCxxInterop::enableFontScaleChangesUpdatingLayout( return ReactNativeFeatureFlags::enableFontScaleChangesUpdatingLayout(); } -bool JReactNativeFeatureFlagsCxxInterop::enableIOSBorderBoxShadowPathByDefault( - facebook::jni::alias_ref /*unused*/) { - return ReactNativeFeatureFlags::enableIOSBorderBoxShadowPathByDefault(); -} - bool JReactNativeFeatureFlagsCxxInterop::enableIOSExperimentalAutoFocusImplementation( facebook::jni::alias_ref /*unused*/) { return ReactNativeFeatureFlags::enableIOSExperimentalAutoFocusImplementation(); @@ -871,9 +860,6 @@ void JReactNativeFeatureFlagsCxxInterop::registerNatives() { makeNativeMethod( "enableFontScaleChangesUpdatingLayout", JReactNativeFeatureFlagsCxxInterop::enableFontScaleChangesUpdatingLayout), - makeNativeMethod( - "enableIOSBorderBoxShadowPathByDefault", - JReactNativeFeatureFlagsCxxInterop::enableIOSBorderBoxShadowPathByDefault), makeNativeMethod( "enableIOSExperimentalAutoFocusImplementation", JReactNativeFeatureFlagsCxxInterop::enableIOSExperimentalAutoFocusImplementation), 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 483fac726126..10457edc1088 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<> */ /** @@ -90,9 +90,6 @@ class JReactNativeFeatureFlagsCxxInterop static bool enableFontScaleChangesUpdatingLayout( facebook::jni::alias_ref); - static bool enableIOSBorderBoxShadowPathByDefault( - facebook::jni::alias_ref); - static bool enableIOSExperimentalAutoFocusImplementation( 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 caad94b541bb..123729aac840 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<<0fb2c41e81fff95999861a345052d99f>> + * @generated SignedSource<> */ /** @@ -106,10 +106,6 @@ bool ReactNativeFeatureFlags::enableFontScaleChangesUpdatingLayout() { return getAccessor().enableFontScaleChangesUpdatingLayout(); } -bool ReactNativeFeatureFlags::enableIOSBorderBoxShadowPathByDefault() { - return getAccessor().enableIOSBorderBoxShadowPathByDefault(); -} - bool ReactNativeFeatureFlags::enableIOSExperimentalAutoFocusImplementation() { return getAccessor().enableIOSExperimentalAutoFocusImplementation(); } diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.h b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.h index 3ca0da4039ec..63855748befe 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<<8dc801da5ad5fd085dcedb245f7dee08>> */ /** @@ -139,11 +139,6 @@ class ReactNativeFeatureFlags { */ RN_EXPORT static bool enableFontScaleChangesUpdatingLayout(); - /** - * Use the border box for legacy iOS shadow paths when shadowPathIOS is omitted. - */ - RN_EXPORT static bool enableIOSBorderBoxShadowPathByDefault(); - /** * Fixes #56595 by moving the autoFocus from didMoveToWindow to viewDidAppear */ diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.cpp b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.cpp index 3f879a4cf3d5..6e90d149e633 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<<676605110414d92058485a2f290d532b>> + * @generated SignedSource<<10aa043b91c4189bd7772066085e168d>> */ /** @@ -389,24 +389,6 @@ bool ReactNativeFeatureFlagsAccessor::enableFontScaleChangesUpdatingLayout() { return flagValue.value(); } -bool ReactNativeFeatureFlagsAccessor::enableIOSBorderBoxShadowPathByDefault() { - auto flagValue = enableIOSBorderBoxShadowPathByDefault_.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(20, "enableIOSBorderBoxShadowPathByDefault"); - - flagValue = currentProvider_->enableIOSBorderBoxShadowPathByDefault(); - enableIOSBorderBoxShadowPathByDefault_ = flagValue; - } - - return flagValue.value(); -} - bool ReactNativeFeatureFlagsAccessor::enableIOSExperimentalAutoFocusImplementation() { auto flagValue = enableIOSExperimentalAutoFocusImplementation_.load(); @@ -416,7 +398,7 @@ bool ReactNativeFeatureFlagsAccessor::enableIOSExperimentalAutoFocusImplementati // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(21, "enableIOSExperimentalAutoFocusImplementation"); + markFlagAsAccessed(20, "enableIOSExperimentalAutoFocusImplementation"); flagValue = currentProvider_->enableIOSExperimentalAutoFocusImplementation(); enableIOSExperimentalAutoFocusImplementation_ = flagValue; @@ -434,7 +416,7 @@ bool ReactNativeFeatureFlagsAccessor::enableIOSTextBaselineOffsetPerLine() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(22, "enableIOSTextBaselineOffsetPerLine"); + markFlagAsAccessed(21, "enableIOSTextBaselineOffsetPerLine"); flagValue = currentProvider_->enableIOSTextBaselineOffsetPerLine(); enableIOSTextBaselineOffsetPerLine_ = flagValue; @@ -452,7 +434,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(23, "enableIOSViewClipToPaddingBox"); + markFlagAsAccessed(22, "enableIOSViewClipToPaddingBox"); flagValue = currentProvider_->enableIOSViewClipToPaddingBox(); enableIOSViewClipToPaddingBox_ = flagValue; @@ -470,7 +452,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(24, "enableInteropViewManagerClassLookUpOptimizationIOS"); + markFlagAsAccessed(23, "enableInteropViewManagerClassLookUpOptimizationIOS"); flagValue = currentProvider_->enableInteropViewManagerClassLookUpOptimizationIOS(); enableInteropViewManagerClassLookUpOptimizationIOS_ = flagValue; @@ -488,7 +470,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(25, "enableLayoutAnimationsOnAndroid"); + markFlagAsAccessed(24, "enableLayoutAnimationsOnAndroid"); flagValue = currentProvider_->enableLayoutAnimationsOnAndroid(); enableLayoutAnimationsOnAndroid_ = flagValue; @@ -506,7 +488,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(26, "enableLayoutAnimationsOnIOS"); + markFlagAsAccessed(25, "enableLayoutAnimationsOnIOS"); flagValue = currentProvider_->enableLayoutAnimationsOnIOS(); enableLayoutAnimationsOnIOS_ = flagValue; @@ -524,7 +506,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(27, "enableMainQueueCoordinatorOnIOS"); + markFlagAsAccessed(26, "enableMainQueueCoordinatorOnIOS"); flagValue = currentProvider_->enableMainQueueCoordinatorOnIOS(); enableMainQueueCoordinatorOnIOS_ = flagValue; @@ -542,7 +524,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(28, "enableMainQueueModulesOnIOS"); + markFlagAsAccessed(27, "enableMainQueueModulesOnIOS"); flagValue = currentProvider_->enableMainQueueModulesOnIOS(); enableMainQueueModulesOnIOS_ = flagValue; @@ -560,7 +542,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(29, "enableModuleArgumentNSNullConversionIOS"); + markFlagAsAccessed(28, "enableModuleArgumentNSNullConversionIOS"); flagValue = currentProvider_->enableModuleArgumentNSNullConversionIOS(); enableModuleArgumentNSNullConversionIOS_ = flagValue; @@ -578,7 +560,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(30, "enableNativeCSSParsing"); + markFlagAsAccessed(29, "enableNativeCSSParsing"); flagValue = currentProvider_->enableNativeCSSParsing(); enableNativeCSSParsing_ = flagValue; @@ -596,7 +578,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(31, "enableNetworkEventReporting"); + markFlagAsAccessed(30, "enableNetworkEventReporting"); flagValue = currentProvider_->enableNetworkEventReporting(); enableNetworkEventReporting_ = flagValue; @@ -614,7 +596,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(32, "enableNewBackgroundAndBorderDrawables"); + markFlagAsAccessed(31, "enableNewBackgroundAndBorderDrawables"); flagValue = currentProvider_->enableNewBackgroundAndBorderDrawables(); enableNewBackgroundAndBorderDrawables_ = flagValue; @@ -632,7 +614,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(33, "enablePreparedTextLayout"); + markFlagAsAccessed(32, "enablePreparedTextLayout"); flagValue = currentProvider_->enablePreparedTextLayout(); enablePreparedTextLayout_ = flagValue; @@ -650,7 +632,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(34, "enablePropsUpdateReconciliationAndroid"); + markFlagAsAccessed(33, "enablePropsUpdateReconciliationAndroid"); flagValue = currentProvider_->enablePropsUpdateReconciliationAndroid(); enablePropsUpdateReconciliationAndroid_ = flagValue; @@ -668,7 +650,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(35, "enableResourceTimingAPI"); + markFlagAsAccessed(34, "enableResourceTimingAPI"); flagValue = currentProvider_->enableResourceTimingAPI(); enableResourceTimingAPI_ = flagValue; @@ -686,7 +668,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(36, "enableSynchronousStateUpdates"); + markFlagAsAccessed(35, "enableSynchronousStateUpdates"); flagValue = currentProvider_->enableSynchronousStateUpdates(); enableSynchronousStateUpdates_ = flagValue; @@ -704,7 +686,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(37, "enableViewCulling"); + markFlagAsAccessed(36, "enableViewCulling"); flagValue = currentProvider_->enableViewCulling(); enableViewCulling_ = flagValue; @@ -722,7 +704,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(38, "enableViewRecycling"); + markFlagAsAccessed(37, "enableViewRecycling"); flagValue = currentProvider_->enableViewRecycling(); enableViewRecycling_ = flagValue; @@ -740,7 +722,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(39, "enableViewRecyclingForText"); + markFlagAsAccessed(38, "enableViewRecyclingForText"); flagValue = currentProvider_->enableViewRecyclingForText(); enableViewRecyclingForText_ = flagValue; @@ -758,7 +740,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(40, "enableViewRecyclingForView"); + markFlagAsAccessed(39, "enableViewRecyclingForView"); flagValue = currentProvider_->enableViewRecyclingForView(); enableViewRecyclingForView_ = flagValue; @@ -776,7 +758,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(41, "enableVirtualViewDebugFeatures"); + markFlagAsAccessed(40, "enableVirtualViewDebugFeatures"); flagValue = currentProvider_->enableVirtualViewDebugFeatures(); enableVirtualViewDebugFeatures_ = flagValue; @@ -794,7 +776,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(42, "enableVirtualViewRenderState"); + markFlagAsAccessed(41, "enableVirtualViewRenderState"); flagValue = currentProvider_->enableVirtualViewRenderState(); enableVirtualViewRenderState_ = flagValue; @@ -812,7 +794,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(43, "enableVirtualViewWindowFocusDetection"); + markFlagAsAccessed(42, "enableVirtualViewWindowFocusDetection"); flagValue = currentProvider_->enableVirtualViewWindowFocusDetection(); enableVirtualViewWindowFocusDetection_ = flagValue; @@ -830,7 +812,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(44, "fixMappingOfEventPrioritiesBetweenFabricAndReact"); + markFlagAsAccessed(43, "fixMappingOfEventPrioritiesBetweenFabricAndReact"); flagValue = currentProvider_->fixMappingOfEventPrioritiesBetweenFabricAndReact(); fixMappingOfEventPrioritiesBetweenFabricAndReact_ = flagValue; @@ -848,7 +830,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(45, "fuseboxEnabledRelease"); + markFlagAsAccessed(44, "fuseboxEnabledRelease"); flagValue = currentProvider_->fuseboxEnabledRelease(); fuseboxEnabledRelease_ = flagValue; @@ -866,7 +848,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(46, "fuseboxNetworkInspectionEnabled"); + markFlagAsAccessed(45, "fuseboxNetworkInspectionEnabled"); flagValue = currentProvider_->fuseboxNetworkInspectionEnabled(); fuseboxNetworkInspectionEnabled_ = flagValue; @@ -884,7 +866,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(47, "hideOffscreenVirtualViewsOnIOS"); + markFlagAsAccessed(46, "hideOffscreenVirtualViewsOnIOS"); flagValue = currentProvider_->hideOffscreenVirtualViewsOnIOS(); hideOffscreenVirtualViewsOnIOS_ = flagValue; @@ -902,7 +884,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(48, "preparedTextCacheSize"); + markFlagAsAccessed(47, "preparedTextCacheSize"); flagValue = currentProvider_->preparedTextCacheSize(); preparedTextCacheSize_ = flagValue; @@ -920,7 +902,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(49, "preventShadowTreeCommitExhaustion"); + markFlagAsAccessed(48, "preventShadowTreeCommitExhaustion"); flagValue = currentProvider_->preventShadowTreeCommitExhaustion(); preventShadowTreeCommitExhaustion_ = flagValue; @@ -938,7 +920,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(50, "traceTurboModulePromiseRejectionsOnAndroid"); + markFlagAsAccessed(49, "traceTurboModulePromiseRejectionsOnAndroid"); flagValue = currentProvider_->traceTurboModulePromiseRejectionsOnAndroid(); traceTurboModulePromiseRejectionsOnAndroid_ = flagValue; @@ -956,7 +938,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(51, "updateRuntimeShadowNodeReferencesOnCommit"); + markFlagAsAccessed(50, "updateRuntimeShadowNodeReferencesOnCommit"); flagValue = currentProvider_->updateRuntimeShadowNodeReferencesOnCommit(); updateRuntimeShadowNodeReferencesOnCommit_ = flagValue; @@ -974,7 +956,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(52, "useAlwaysAvailableJSErrorHandling"); + markFlagAsAccessed(51, "useAlwaysAvailableJSErrorHandling"); flagValue = currentProvider_->useAlwaysAvailableJSErrorHandling(); useAlwaysAvailableJSErrorHandling_ = flagValue; @@ -992,7 +974,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(53, "useFabricInterop"); + markFlagAsAccessed(52, "useFabricInterop"); flagValue = currentProvider_->useFabricInterop(); useFabricInterop_ = flagValue; @@ -1010,7 +992,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(54, "useNativeEqualsInNativeReadableArrayAndroid"); + markFlagAsAccessed(53, "useNativeEqualsInNativeReadableArrayAndroid"); flagValue = currentProvider_->useNativeEqualsInNativeReadableArrayAndroid(); useNativeEqualsInNativeReadableArrayAndroid_ = flagValue; @@ -1028,7 +1010,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(55, "useNativeTransformHelperAndroid"); + markFlagAsAccessed(54, "useNativeTransformHelperAndroid"); flagValue = currentProvider_->useNativeTransformHelperAndroid(); useNativeTransformHelperAndroid_ = flagValue; @@ -1046,7 +1028,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(56, "useNativeViewConfigsInBridgelessMode"); + markFlagAsAccessed(55, "useNativeViewConfigsInBridgelessMode"); flagValue = currentProvider_->useNativeViewConfigsInBridgelessMode(); useNativeViewConfigsInBridgelessMode_ = flagValue; @@ -1064,7 +1046,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(57, "useOptimizedEventBatchingOnAndroid"); + markFlagAsAccessed(56, "useOptimizedEventBatchingOnAndroid"); flagValue = currentProvider_->useOptimizedEventBatchingOnAndroid(); useOptimizedEventBatchingOnAndroid_ = flagValue; @@ -1082,7 +1064,7 @@ bool ReactNativeFeatureFlagsAccessor::usePullModelOnAndroid() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(58, "usePullModelOnAndroid"); + markFlagAsAccessed(57, "usePullModelOnAndroid"); flagValue = currentProvider_->usePullModelOnAndroid(); usePullModelOnAndroid_ = flagValue; @@ -1100,7 +1082,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(59, "useRawPropsJsiValue"); + markFlagAsAccessed(58, "useRawPropsJsiValue"); flagValue = currentProvider_->useRawPropsJsiValue(); useRawPropsJsiValue_ = flagValue; @@ -1118,7 +1100,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(60, "useShadowNodeStateOnClone"); + markFlagAsAccessed(59, "useShadowNodeStateOnClone"); flagValue = currentProvider_->useShadowNodeStateOnClone(); useShadowNodeStateOnClone_ = flagValue; @@ -1136,7 +1118,7 @@ bool ReactNativeFeatureFlagsAccessor::useSilenceErrorSMMViewNotFound() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(61, "useSilenceErrorSMMViewNotFound"); + markFlagAsAccessed(60, "useSilenceErrorSMMViewNotFound"); flagValue = currentProvider_->useSilenceErrorSMMViewNotFound(); useSilenceErrorSMMViewNotFound_ = flagValue; @@ -1154,7 +1136,7 @@ bool ReactNativeFeatureFlagsAccessor::useTraitHiddenOnIOS() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(62, "useTraitHiddenOnIOS"); + markFlagAsAccessed(61, "useTraitHiddenOnIOS"); flagValue = currentProvider_->useTraitHiddenOnIOS(); useTraitHiddenOnIOS_ = flagValue; @@ -1172,7 +1154,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(63, "useTurboModuleInterop"); + markFlagAsAccessed(62, "useTurboModuleInterop"); flagValue = currentProvider_->useTurboModuleInterop(); useTurboModuleInterop_ = flagValue; @@ -1190,7 +1172,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(64, "useTurboModules"); + markFlagAsAccessed(63, "useTurboModules"); flagValue = currentProvider_->useTurboModules(); useTurboModules_ = flagValue; @@ -1208,7 +1190,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(65, "virtualViewPrerenderRatio"); + markFlagAsAccessed(64, "virtualViewPrerenderRatio"); flagValue = currentProvider_->virtualViewPrerenderRatio(); virtualViewPrerenderRatio_ = flagValue; @@ -1226,7 +1208,7 @@ bool ReactNativeFeatureFlagsAccessor::runtimeCrashUiThreadUtils() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(66, "runtimeCrashUiThreadUtils"); + markFlagAsAccessed(65, "runtimeCrashUiThreadUtils"); flagValue = currentProvider_->runtimeCrashUiThreadUtils(); runtimeCrashUiThreadUtils_ = flagValue; diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.h b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.h index aef569c64182..860aea749315 100644 --- a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.h +++ b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.h @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<> + * @generated SignedSource<<98d2d767bf370439a9efc44a745ae802>> */ /** @@ -52,7 +52,6 @@ class ReactNativeFeatureFlagsAccessor { bool enableFabricRenderer(); bool enableFixForParentTagDuringReparenting(); bool enableFontScaleChangesUpdatingLayout(); - bool enableIOSBorderBoxShadowPathByDefault(); bool enableIOSExperimentalAutoFocusImplementation(); bool enableIOSTextBaselineOffsetPerLine(); bool enableIOSViewClipToPaddingBox(); @@ -110,7 +109,7 @@ class ReactNativeFeatureFlagsAccessor { std::unique_ptr currentProvider_; bool wasOverridden_; - std::array, 67> accessedFeatureFlags_; + std::array, 66> accessedFeatureFlags_; std::atomic> commonTestFlag_; std::atomic> animatedShouldSignalBatch_; @@ -132,7 +131,6 @@ class ReactNativeFeatureFlagsAccessor { std::atomic> enableFabricRenderer_; std::atomic> enableFixForParentTagDuringReparenting_; std::atomic> enableFontScaleChangesUpdatingLayout_; - std::atomic> enableIOSBorderBoxShadowPathByDefault_; std::atomic> enableIOSExperimentalAutoFocusImplementation_; std::atomic> enableIOSTextBaselineOffsetPerLine_; std::atomic> enableIOSViewClipToPaddingBox_; diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDefaults.h b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDefaults.h index f72e13144897..873efaebfcb2 100644 --- a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDefaults.h +++ b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDefaults.h @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<> + * @generated SignedSource<<5cb395cf45a846c4e93b4eb5ca2d99e3>> */ /** @@ -107,10 +107,6 @@ class ReactNativeFeatureFlagsDefaults : public ReactNativeFeatureFlagsProvider { return false; } - bool enableIOSBorderBoxShadowPathByDefault() override { - return false; - } - bool enableIOSExperimentalAutoFocusImplementation() override { return false; } diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDynamicProvider.h b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDynamicProvider.h index 3f950c1e4956..9566d80657b3 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<<8a75aa937ab50bb2ea4e920c046e2d1e>> + * @generated SignedSource<<627e0e191716b63f3a5dd7fd343506b9>> */ /** @@ -225,15 +225,6 @@ class ReactNativeFeatureFlagsDynamicProvider : public ReactNativeFeatureFlagsDef return ReactNativeFeatureFlagsDefaults::enableFontScaleChangesUpdatingLayout(); } - bool enableIOSBorderBoxShadowPathByDefault() override { - auto value = values_["enableIOSBorderBoxShadowPathByDefault"]; - if (!value.isNull()) { - return value.getBool(); - } - - return ReactNativeFeatureFlagsDefaults::enableIOSBorderBoxShadowPathByDefault(); - } - bool enableIOSExperimentalAutoFocusImplementation() override { auto value = values_["enableIOSExperimentalAutoFocusImplementation"]; if (!value.isNull()) { diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsProvider.h b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsProvider.h index 657ea5aa42e5..8c6d60c9646c 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<> */ /** @@ -45,7 +45,6 @@ class ReactNativeFeatureFlagsProvider { virtual bool enableFabricRenderer() = 0; virtual bool enableFixForParentTagDuringReparenting() = 0; virtual bool enableFontScaleChangesUpdatingLayout() = 0; - virtual bool enableIOSBorderBoxShadowPathByDefault() = 0; virtual bool enableIOSExperimentalAutoFocusImplementation() = 0; virtual bool enableIOSTextBaselineOffsetPerLine() = 0; virtual bool enableIOSViewClipToPaddingBox() = 0; diff --git a/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.cpp b/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.cpp index 391a81d8afc7..8f3742fca812 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<<4f37b3bec59e6d9cd6e61e4ecbbe7b6d>> */ /** @@ -144,11 +144,6 @@ bool NativeReactNativeFeatureFlags::enableFontScaleChangesUpdatingLayout( return ReactNativeFeatureFlags::enableFontScaleChangesUpdatingLayout(); } -bool NativeReactNativeFeatureFlags::enableIOSBorderBoxShadowPathByDefault( - jsi::Runtime& /*runtime*/) { - return ReactNativeFeatureFlags::enableIOSBorderBoxShadowPathByDefault(); -} - bool NativeReactNativeFeatureFlags::enableIOSExperimentalAutoFocusImplementation( jsi::Runtime& /*runtime*/) { return ReactNativeFeatureFlags::enableIOSExperimentalAutoFocusImplementation(); diff --git a/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.h b/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.h index b70c1b43c6d4..9218683bd9b8 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<<3a4e198b27be6df0f2d707c60b635278>> + * @generated SignedSource<<267e09d240d84ae5613aa58e2ed1deb8>> */ /** @@ -76,8 +76,6 @@ class NativeReactNativeFeatureFlags bool enableFontScaleChangesUpdatingLayout(jsi::Runtime& runtime); - bool enableIOSBorderBoxShadowPathByDefault(jsi::Runtime& runtime); - bool enableIOSExperimentalAutoFocusImplementation(jsi::Runtime& runtime); bool enableIOSTextBaselineOffsetPerLine(jsi::Runtime& runtime); diff --git a/packages/react-native/ReactCommon/react/renderer/components/view/primitives.h b/packages/react-native/ReactCommon/react/renderer/components/view/primitives.h index d6f3ddab1d2c..9b556e877284 100644 --- a/packages/react-native/ReactCommon/react/renderer/components/view/primitives.h +++ b/packages/react-native/ReactCommon/react/renderer/components/view/primitives.h @@ -101,18 +101,12 @@ enum class ShadowPathMode : uint8_t { ContentAlpha, }; -inline ShadowPathMode resolveShadowPathMode( - ShadowPathMode propMode, - bool borderBoxDefaultEnabled) { - if (propMode != ShadowPathMode::Default) { - return propMode; +inline ShadowPathMode resolveShadowPathMode(ShadowPathMode propMode) { + if (propMode == ShadowPathMode::Default) { + return ShadowPathMode::Auto; } - if (borderBoxDefaultEnabled) { - return ShadowPathMode::BorderBox; - } - - return ShadowPathMode::Auto; + return propMode; } struct CornerRadii { diff --git a/packages/react-native/ReactCommon/react/renderer/components/view/tests/ConversionsTest.cpp b/packages/react-native/ReactCommon/react/renderer/components/view/tests/ConversionsTest.cpp index 7caf32441c44..2ebd7db09be8 100644 --- a/packages/react-native/ReactCommon/react/renderer/components/view/tests/ConversionsTest.cpp +++ b/packages/react-native/ReactCommon/react/renderer/components/view/tests/ConversionsTest.cpp @@ -32,15 +32,12 @@ TEST(ConversionsTest, shadow_path_modes) { TEST(ConversionsTest, resolves_default_shadow_path_mode) { EXPECT_EQ( - resolveShadowPathMode(ShadowPathMode::Default, false), + resolveShadowPathMode(ShadowPathMode::Default), ShadowPathMode::Auto); EXPECT_EQ( - resolveShadowPathMode(ShadowPathMode::Default, true), - ShadowPathMode::BorderBox); + resolveShadowPathMode(ShadowPathMode::Auto), ShadowPathMode::Auto); EXPECT_EQ( - resolveShadowPathMode(ShadowPathMode::Auto, true), ShadowPathMode::Auto); - EXPECT_EQ( - resolveShadowPathMode(ShadowPathMode::ContentAlpha, true), + resolveShadowPathMode(ShadowPathMode::ContentAlpha), ShadowPathMode::ContentAlpha); } diff --git a/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js b/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js index 79fa981d73dc..924e620aadb2 100644 --- a/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js +++ b/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js @@ -259,17 +259,6 @@ const definitions: FeatureFlagDefinitions = { }, ossReleaseStage: 'none', }, - enableIOSBorderBoxShadowPathByDefault: { - defaultValue: false, - metadata: { - dateAdded: '2026-07-14', - description: - 'Use the border box for legacy iOS shadow paths when shadowPathIOS is omitted.', - expectedReleaseValue: true, - purpose: 'experimentation', - }, - ossReleaseStage: 'none', - }, enableIOSExperimentalAutoFocusImplementation: { defaultValue: false, metadata: { diff --git a/packages/react-native/src/private/featureflags/ReactNativeFeatureFlags.js b/packages/react-native/src/private/featureflags/ReactNativeFeatureFlags.js index 028318b0630f..c1bf13b3bc58 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<<2d214619ebe2d8c61230de56ebfd95f6>> * @flow strict * @noformat */ @@ -71,7 +71,6 @@ export type ReactNativeFeatureFlags = $ReadOnly<{ enableFabricRenderer: Getter, enableFixForParentTagDuringReparenting: Getter, enableFontScaleChangesUpdatingLayout: Getter, - enableIOSBorderBoxShadowPathByDefault: Getter, enableIOSExperimentalAutoFocusImplementation: Getter, enableIOSTextBaselineOffsetPerLine: Getter, enableIOSViewClipToPaddingBox: Getter, @@ -284,10 +283,6 @@ export const enableFixForParentTagDuringReparenting: Getter = createNat * Enables font scale changes updating layout for measurable nodes. */ export const enableFontScaleChangesUpdatingLayout: Getter = createNativeFlagGetter('enableFontScaleChangesUpdatingLayout', false); -/** - * Use the border box for legacy iOS shadow paths when shadowPathIOS is omitted. - */ -export const enableIOSBorderBoxShadowPathByDefault: Getter = createNativeFlagGetter('enableIOSBorderBoxShadowPathByDefault', false); /** * Fixes #56595 by moving the autoFocus from didMoveToWindow to viewDidAppear */ diff --git a/packages/react-native/src/private/featureflags/specs/NativeReactNativeFeatureFlags.js b/packages/react-native/src/private/featureflags/specs/NativeReactNativeFeatureFlags.js index 702327785608..c6d69e5004b5 100644 --- a/packages/react-native/src/private/featureflags/specs/NativeReactNativeFeatureFlags.js +++ b/packages/react-native/src/private/featureflags/specs/NativeReactNativeFeatureFlags.js @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<> + * @generated SignedSource<> * @flow strict * @noformat */ @@ -45,7 +45,6 @@ export interface Spec extends TurboModule { +enableFabricRenderer?: () => boolean; +enableFixForParentTagDuringReparenting?: () => boolean; +enableFontScaleChangesUpdatingLayout?: () => boolean; - +enableIOSBorderBoxShadowPathByDefault?: () => boolean; +enableIOSExperimentalAutoFocusImplementation?: () => boolean; +enableIOSTextBaselineOffsetPerLine?: () => boolean; +enableIOSViewClipToPaddingBox?: () => boolean; diff --git a/packages/rn-tester/js/examples/Playground/RNTesterShadowPathPlayground.js b/packages/rn-tester/js/examples/Playground/RNTesterShadowPathPlayground.js index 8f1826954623..c10dbf45c59d 100644 --- a/packages/rn-tester/js/examples/Playground/RNTesterShadowPathPlayground.js +++ b/packages/rn-tester/js/examples/Playground/RNTesterShadowPathPlayground.js @@ -20,7 +20,6 @@ import { Text, View, } from 'react-native'; -import {enableIOSBorderBoxShadowPathByDefault} from 'react-native/src/private/featureflags/ReactNativeFeatureFlags'; type ShadowPathMode = 'auto' | 'border-box' | 'content-alpha'; type ShadowPathSelection = 'default' | ShadowPathMode; @@ -81,8 +80,6 @@ function ShadowPathPlayground(): React.Node { const animatedStyle = {transform: [{scale}]}; const shadowPathProps: {shadowPathIOS?: ShadowPathMode} = selectedMode === 'default' ? {} : {shadowPathIOS: selectedMode}; - const flagEnabled = enableIOSBorderBoxShadowPathByDefault(); - const flagDefault = flagEnabled ? 'Border Box' : 'Auto'; const activeMode = SHADOW_PATH_OPTIONS.find( option => option.value === selectedMode, ); @@ -124,7 +121,7 @@ function ShadowPathPlayground(): React.Node { Active mode: {activeMode.label} - Feature-flag default: {flagDefault} + Omitted default: Auto Translucent button hierarchy