[0.86] Fix display: contents nodes having hasNewLayout set incorrectly#57146
Open
j-piasecki wants to merge 1 commit into
Open
[0.86] Fix display: contents nodes having hasNewLayout set incorrectly#57146j-piasecki wants to merge 1 commit into
j-piasecki wants to merge 1 commit into
Conversation
…eact#57103) Summary: Pull Request resolved: react#57103 Changelog: [General][Fixed] Fixed `display: contents` nodes having `hasNewLayout` set incorrectly `cleanupContentsNodesRecursively` unconditionally sets `hasNewLayout=true` on `display: contents` children, including on code paths where their parent's layout was not actually performed in this pass. The stale flag can survive across layout passes and, in clone-on-write renderers (e.g. React Native Fabric), be observed by a subsequent pass whose parent was cloned but whose layout was served from cache, leaving the contents child's owner pointing at the previous parent revision. There are two paths through which the cleanup could stamp a contents child whose parent's `hasNewLayout` would end up false: 1. Measure-phase visit. Inside `calculateLayoutImpl`, the cleanup ran with no knowledge of `performLayout`. When the parent's `calculateLayoutImpl` was invoked only with `performLayout=false` (cache miss on measure, cache hit on layout), the cleanup stamped contents children even though the parent itself never had its `hasNewLayout` set. 2. Absolute-layout walk. `layoutAbsoluteDescendants` walks every static layout descendant of the containing block - including ones whose own `calculateLayoutImpl` was skipped via the layout-phase cache. The cleanup invoked along that walk unconditionally stamped contents children, but the parent's `hasNewLayout` was only updated when the recursion actually found new layout downstream. In both cases, the result is the same invariant violation: a contents node with `hasNewLayout=true` whose parent has `hasNewLayout=false`. A consumer iterating the tree via `hasNewLayout` skips the parent and never clears the stale flag. X-link: react/yoga#1970 Test Plan: Added `YGContentsNodeHasNewLayoutTest.cpp` with regression tests: - `contents_child_hasNewLayout_not_stamped_on_measure_only_visit` - pins the measure-phase fix - `absolute_descendant_through_contents_is_reachable_via_hasNewLayout` - pins the positive case for absolute-layout path - `absolute_phase_cleanup_does_not_stamp_when_parent_layout_skipped` - pins the negative case for absolute-layout path Reviewed By: javache Differential Revision: D107854528 Pulled By: j-piasecki fbshipit-source-id: cae5e889622296e8b6380a6428509b5ffea3e9ae
8121dac to
7e726e0
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Cherry-pick of #57103
Changelog: [General][Fixed] Fixed display: contents nodes having hasNewLayout set incorrectly
cleanupContentsNodesRecursively unconditionally sets hasNewLayout=true on display: contents children, including on code paths where their parent's layout was not actually performed in this pass. The stale flag can survive across layout passes and, in clone-on-write renderers (e.g. React Native Fabric), be observed by a subsequent pass whose parent was cloned but whose layout was served from cache, leaving the contents child's owner pointing at the previous parent revision.
There are two paths through which the cleanup could stamp a contents child whose parent's hasNewLayout would end up false:
Measure-phase visit. Inside calculateLayoutImpl, the cleanup ran with no knowledge of performLayout. When the parent's calculateLayoutImpl was invoked only with performLayout=false (cache miss on measure, cache hit on layout), the cleanup stamped contents children even though the parent itself never had its hasNewLayout set.
Absolute-layout walk. layoutAbsoluteDescendants walks every static layout descendant of the containing block - including ones whose own calculateLayoutImpl was skipped via the layout-phase cache. The cleanup invoked along that walk unconditionally stamped contents children, but the parent's hasNewLayout was only updated when the recursion actually found new layout downstream.
In both cases, the result is the same invariant violation: a contents node with hasNewLayout=true whose parent has hasNewLayout=false. A consumer iterating the tree via hasNewLayout skips the parent and never clears the stale flag.
Test Plan
The picked PR added unit tests in Yoga