From 603721a72e20874bec41d7dbef4b133bfa223300 Mon Sep 17 00:00:00 2001 From: Samuel Susla Date: Wed, 11 Jun 2025 03:26:13 -0700 Subject: [PATCH] Disable view culling in ScrollView with overflow visible Summary: changelog: [internal] disable view culling if ScrollView has overflow set to visible Reviewed By: rubennorte Differential Revision: D76419519 --- .../__tests__/ScrollView-viewCulling-itest.js | 22 +++++++++++++++++++ .../mounting/internal/CullingContext.cpp | 17 +++++++++----- 2 files changed, 33 insertions(+), 6 deletions(-) diff --git a/packages/react-native/Libraries/Components/ScrollView/__tests__/ScrollView-viewCulling-itest.js b/packages/react-native/Libraries/Components/ScrollView/__tests__/ScrollView-viewCulling-itest.js index 43be9bac9899..3c33c659e72d 100644 --- a/packages/react-native/Libraries/Components/ScrollView/__tests__/ScrollView-viewCulling-itest.js +++ b/packages/react-native/Libraries/Components/ScrollView/__tests__/ScrollView-viewCulling-itest.js @@ -2534,3 +2534,25 @@ describe('opt out mechanism - Unstable_uncullableView & Unstable_uncullableTrace ); }); }); + +describe('culling inside ScrollView with overflow visible', () => { + it('shows view outside of bounds', () => { + const root = Fantom.createRoot({viewportWidth: 100, viewportHeight: 100}); + + Fantom.runTask(() => { + root.render( + + + , + ); + }); + + // Child is not culled because overflow:visible. + expect(root.takeMountingManagerLogs()).toContain( + 'Create {type: "View", nativeID: "child"}', + ); + }); +}); diff --git a/packages/react-native/ReactCommon/react/renderer/mounting/internal/CullingContext.cpp b/packages/react-native/ReactCommon/react/renderer/mounting/internal/CullingContext.cpp index b90414be6dda..b061451ac0fd 100644 --- a/packages/react-native/ReactCommon/react/renderer/mounting/internal/CullingContext.cpp +++ b/packages/react-native/ReactCommon/react/renderer/mounting/internal/CullingContext.cpp @@ -23,12 +23,17 @@ CullingContext CullingContext::adjustCullingContextIfNeeded( if (ReactNativeFeatureFlags::enableViewCulling()) { if (auto scrollViewShadowNode = dynamic_cast(pair.shadowNode)) { - cullingContext.frame.origin = - -scrollViewShadowNode->getContentOriginOffset( - /* includeTransform */ true); - cullingContext.frame.size = - scrollViewShadowNode->getLayoutMetrics().frame.size; - cullingContext.transform = Transform::Identity(); + if (scrollViewShadowNode->getConcreteProps().yogaStyle.overflow() != + yoga::Overflow::Visible) { + cullingContext.frame.origin = + -scrollViewShadowNode->getContentOriginOffset( + /* includeTransform */ true); + cullingContext.frame.size = + scrollViewShadowNode->getLayoutMetrics().frame.size; + cullingContext.transform = Transform::Identity(); + } else { + cullingContext = {}; + } } else if (pair.shadowView.traits.check( ShadowNodeTraits::Trait::RootNodeKind)) { cullingContext = {};