Branch: refs/heads/main
  Home:   https://github.com/WebKit/WebKit
  Commit: 7799f34a1444dcbf7d2635b7cc591a70a2794c6d
      
https://github.com/WebKit/WebKit/commit/7799f34a1444dcbf7d2635b7cc591a70a2794c6d
  Author: Matt Woodrow <[email protected]>
  Date:   2026-09-24 (Thu, 24 Sep 2026)

  Changed paths:
    A 
LayoutTests/scrollingcoordinator/scrolling-tree/overflow-node-reattached-after-ancestor-proxy-destroyed-expected.txt
    A 
LayoutTests/scrollingcoordinator/scrolling-tree/overflow-node-reattached-after-ancestor-proxy-destroyed.html
    M Source/WebCore/page/scrolling/ScrollingTree.cpp
    M Source/WebCore/rendering/LayerAncestorClippingStack.cpp
    M Source/WebCore/rendering/LayerAncestorClippingStack.h
    M Source/WebCore/rendering/RenderLayerBacking.cpp
    M Source/WebCore/rendering/RenderLayerCompositor.cpp
    M Source/WebCore/rendering/RenderLayerCompositor.h
    M 
Source/WebKit/UIProcess/RemoteLayerTree/RemoteScrollingCoordinatorProxy.cpp

  Log Message:
  -----------
  MESSAGE_CHECK in establishLayerTreeScrollingRelations because scrolling nodes 
can be orphaned when an overflow scroll proxy node is destroyed.
https://bugs.webkit.org/show_bug.cgi?id=325098
rdar://184655581

Reviewed by Simon Fraser.

When a composited layer stops needing an ancestor clip, 
LayerAncestorClippingStack
destroys its overflow scroll proxy nodes with unparentChildrenAndDestroyNode(), 
which
moves their child nodes into the unparented set. Those nodes belong to 
paint-order
descendants of the layer, and they are only reattached if the compositing 
traversal
visits their layers again. It does not when a clean composited layer lies in 
between:
that layer gets an empty update level, so updateBackingAndHierarchy() does not 
descend
into it.

The orphaned node is then left out of every commit, while a node elsewhere in 
the tree
can still refer to it: an overflow scroll proxy node refers to the overflow 
scrolling
node of the scroller that clips its layer, and it is often parented outside the
destroyed subtree, because a stacking context escapes a non-stacking-context 
scroller
in paint order.

detachScrollCoordinatedLayerWithRole() already marks the layers of the children 
of the
node it destroys as needing a scrolling tree update, but the three
LayerAncestorClippingStack call sites did not. Route them through a new
RenderLayerCompositor::unparentChildrenAndDestroyScrollingNode() that does the 
same, and
share the marking with detachScrollCoordinatedLayerWithRole(). That reattaches 
them in
the same update: the dirty bit propagates to paint-order ancestors, and the 
layer being
updated only decides whether to traverse its descendants after the 
configuration update
that clears its clipping stack.

Overflow scroll proxy nodes were never added to m_scrollingNodeToLayerMap, even 
though
detachScrollCoordinatedLayerWithRole() removes them from it, so the layer of an 
orphaned
proxy node could not be marked. Register them when they are created, and remove 
the
entry when the node is destroyed. There is no separate test for this: an 
orphaned proxy
node's layer is clipped by a scroller inside its stacking context, and as far 
as I can
tell some node on that scroller's paint-order path is always orphaned alongside 
it and
is in the map, so marking that one already got the proxy node's layer visited.

Also stop killing the web process over a relation the UI process cannot resolve.
Neither a web process bug like this one nor stale state in the active node sets 
is
a memory safety problem.

updateTreeFromStateNodeRecursive() could also leave stale entries in the active 
node
sets. It bails out when a node's parent is not in m_nodeMap, and did a bare
m_nodeMap.remove() to do it. That strands the node: it stays in 
m_nodeMapPerFrame, so a
later commit calls removeNode() for it, but removeNode() early-returns once the 
node is
out of m_nodeMap, so removeFromActiveNodes() never runs for it. Worse,
commitStateBeforeChildren() still ran on the node afterwards, which adds proxy 
nodes to
the active sets, and every descendant hit the same bailout. Route it through 
removeNode()
instead, and skip the subtree rather than committing state into a node we just 
removed;
the descendants stay in unvisitedNodes and are removed the same way when the 
commit
finishes.

Test: 
scrollingcoordinator/scrolling-tree/overflow-node-reattached-after-ancestor-proxy-destroyed.html

* 
LayoutTests/scrollingcoordinator/scrolling-tree/overflow-node-reattached-after-ancestor-proxy-destroyed-expected.txt:
 Added.
* 
LayoutTests/scrollingcoordinator/scrolling-tree/overflow-node-reattached-after-ancestor-proxy-destroyed.html:
 Added.
* Source/WebCore/page/scrolling/ScrollingTree.cpp:
(WebCore::ScrollingTree::updateTreeFromStateNodeRecursive):
* Source/WebCore/rendering/LayerAncestorClippingStack.cpp:
(WebCore::LayerAncestorClippingStack::clear):
(WebCore::LayerAncestorClippingStack::detachFromScrollingCoordinator):
(WebCore::LayerAncestorClippingStack::updateWithClipData):
* Source/WebCore/rendering/LayerAncestorClippingStack.h:
* Source/WebCore/rendering/RenderLayerBacking.cpp:
(WebCore::RenderLayerBacking::updateAncestorClippingStack):
(WebCore::RenderLayerBacking::ensureOverflowControlsHostLayerAncestorClippingStack):
(WebCore::RenderLayerBacking::updateAncestorClipping):
(WebCore::RenderLayerBacking::detachFromScrollingCoordinator):
* Source/WebCore/rendering/RenderLayerCompositor.cpp:
(WebCore::RenderLayerCompositor::setNeedsScrollingTreeUpdateForChildrenOfNode):
(WebCore::RenderLayerCompositor::unparentChildrenAndDestroyScrollingNode):
(WebCore::RenderLayerCompositor::detachScrollCoordinatedLayerWithRole):
(WebCore::RenderLayerCompositor::updateScrollingNodeForScrollingProxyRole):
* Source/WebCore/rendering/RenderLayerCompositor.h:
* Source/WebKit/UIProcess/RemoteLayerTree/RemoteScrollingCoordinatorProxy.cpp:
(WebKit::RemoteScrollingCoordinatorProxy::establishLayerTreeScrollingRelations):

Canonical link: https://commits.webkit.org/321860@main



To unsubscribe from these emails, change your notification settings at 
https://github.com/WebKit/WebKit/settings/notifications

Reply via email to