Skip to content

Fix crash in view culling when view is unflattened/flattened in deep hierarchy where each node has top offset #51639

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -1244,6 +1244,122 @@ describe('reparenting', () => {
]);
});

test('flattening grandparent ', () => {
const root = Fantom.createRoot({viewportWidth: 100, viewportHeight: 100});

Fantom.runTask(() => {
root.render(
<ScrollView style={{height: 100, width: 100}}>
<View // grandparent
style={{
marginTop: 70,
opacity: 0, // opacity 0 - can't be flattened
}}>
<View // parent
nativeID="parent"
style={{height: 10, width: 10, marginTop: 10}}>
<View // child
nativeID="child"
style={{height: 5, width: 5, marginTop: 5}}
/>
</View>
</View>
</ScrollView>,
);
});

expect(root.takeMountingManagerLogs()).toContain(
'Insert {type: "View", parentNativeID: "parent", index: 0, nativeID: "child"}',
);

// Flatten grandparent by changing opacity to default value.
Fantom.runTask(() => {
root.render(
<ScrollView style={{height: 100, width: 100}}>
<View // grandparent
style={{
marginTop: 70,
}}>
<View // parent
nativeID="parent"
style={{height: 10, width: 11, marginTop: 10}}>
<View // child
nativeID="child"
style={{height: 5, width: 5, marginTop: 5}}
/>
</View>
</View>
</ScrollView>,
);
});

expect(root.takeMountingManagerLogs()).toEqual([
'Update {type: "View", nativeID: "parent"}',
'Remove {type: "View", parentNativeID: (N/A), index: 0, nativeID: "parent"}',
'Remove {type: "View", parentNativeID: (N/A), index: 0, nativeID: (N/A)}',
'Delete {type: "View", nativeID: (N/A)}',
'Insert {type: "View", parentNativeID: (N/A), index: 0, nativeID: "parent"}',
]);
});

test('unflattening grandparent', () => {
const root = Fantom.createRoot({viewportWidth: 100, viewportHeight: 100});

Fantom.runTask(() => {
root.render(
<ScrollView style={{height: 100, width: 100}}>
<View // grandparent
style={{
marginTop: 70,
}}>
<View // parent
nativeID={'parent'}
style={{height: 10, width: 10, marginTop: 10}}>
<View // child
nativeID="child"
style={{height: 5, width: 5, marginTop: 5}}
/>
</View>
</View>
</ScrollView>,
);
});

expect(root.takeMountingManagerLogs()).toContain(
'Insert {type: "View", parentNativeID: "parent", index: 0, nativeID: "child"}',
);

// Unflatten grandparent by setting opacity to 0.
Fantom.runTask(() => {
root.render(
<ScrollView style={{height: 100, width: 100}}>
<View // grandparent
style={{
marginTop: 70,
opacity: 0, // opacity 0 - can't be flattened
}}>
<View // parent
nativeID={'parent'}
style={{height: 10, width: 11, marginTop: 10}}>
<View // child
nativeID="child"
style={{height: 5, width: 5, marginTop: 5}}
/>
</View>
</View>
</ScrollView>,
);
});

expect(root.takeMountingManagerLogs()).toEqual([
'Update {type: "View", nativeID: "parent"}',
'Remove {type: "View", parentNativeID: (N/A), index: 0, nativeID: "parent"}',
'Create {type: "View", nativeID: (N/A)}',
'Insert {type: "View", parentNativeID: (N/A), index: 0, nativeID: (N/A)}',
'Insert {type: "View", parentNativeID: (N/A), index: 0, nativeID: "parent"}',
]);
});

test('parent-child flattening with child culled', () => {
const root = Fantom.createRoot({viewportWidth: 100, viewportHeight: 100});
const nodeRef = createRef<HostInstance>();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,7 @@ static void calculateShadowViewMutationsFlattener(
Tag parentTagForUpdate,
TinyMap<Tag, ShadowViewNodePair*>* parentSubVisitedOtherNewNodes,
TinyMap<Tag, ShadowViewNodePair*>* parentSubVisitedOtherOldNodes,
const CullingContext& cullingContextForUnvisitedOtherNodes,
const CullingContext& cullingContext);

/**
Expand Down Expand Up @@ -221,6 +222,7 @@ static void updateMatchedPairSubtrees(
oldPair.shadowView.tag,
nullptr,
nullptr,
oldCullingContext,
oldCullingContextCopy);
}
// Unflattening
Expand Down Expand Up @@ -255,6 +257,7 @@ static void updateMatchedPairSubtrees(
parentTag,
nullptr,
nullptr,
newCullingContext,
newCullingContextCopy);

// If old nodes were not visited, we know that we can delete
Expand Down Expand Up @@ -420,6 +423,7 @@ static void calculateShadowViewMutationsFlattener(
Tag parentTagForUpdate,
TinyMap<Tag, ShadowViewNodePair*>* parentSubVisitedOtherNewNodes,
TinyMap<Tag, ShadowViewNodePair*>* parentSubVisitedOtherOldNodes,
const CullingContext& cullingContextForUnvisitedOtherNodes,
const CullingContext& cullingContext) {
// Step 1: iterate through entire tree
std::vector<ShadowViewNodePair*> treeChildren =
Expand Down Expand Up @@ -615,10 +619,14 @@ static void calculateShadowViewMutationsFlattener(
parentTagForUpdate));
}

auto adjustedOldCullingContext =
cullingContext.adjustCullingContextIfNeeded(oldTreeNodePair);
auto adjustedNewCullingContext =
cullingContext.adjustCullingContextIfNeeded(newTreeNodePair);
auto adjustedOldCullingContext = reparentMode == ReparentMode::Flatten
? cullingContext.adjustCullingContextIfNeeded(oldTreeNodePair)
: cullingContextForUnvisitedOtherNodes.adjustCullingContextIfNeeded(
oldTreeNodePair);
auto adjustedNewCullingContext = reparentMode == ReparentMode::Flatten
? cullingContextForUnvisitedOtherNodes.adjustCullingContextIfNeeded(
newTreeNodePair)
: cullingContext.adjustCullingContextIfNeeded(newTreeNodePair);

// Update children if appropriate.
if (!oldTreeNodePair.flattened && !newTreeNodePair.flattened) {
Expand Down Expand Up @@ -670,6 +678,7 @@ static void calculateShadowViewMutationsFlattener(
: parentTag),
subVisitedNewMap,
subVisitedOldMap,
cullingContext,
cullingContext.adjustCullingContextIfNeeded(treeChildPair));
} else {
// Get flattened nodes from either new or old tree
Expand Down Expand Up @@ -720,6 +729,7 @@ static void calculateShadowViewMutationsFlattener(
fixedParentTagForUpdate,
subVisitedNewMap,
subVisitedOldMap,
adjustedNewCullingContext,
adjustedNewCullingContext);
} else {
// Flatten parent, unflatten child
Expand All @@ -740,6 +750,8 @@ static void calculateShadowViewMutationsFlattener(
/* parentTagForUpdate */ fixedParentTagForUpdate,
/* parentSubVisitedOtherNewNodes */ subVisitedNewMap,
/* parentSubVisitedOtherOldNodes */ subVisitedOldMap,
/* cullingContextForUnvisitedOtherNodes */
adjustedOldCullingContext,
/* cullingContext */ adjustedOldCullingContext);

// If old nodes were not visited, we know that we can delete them
Expand Down
Loading