Title: [242997] trunk
Revision
242997
Author
[email protected]
Date
2019-03-15 09:26:09 -0700 (Fri, 15 Mar 2019)

Log Message

[Async overflow Scrolling] Update positioned node layers when overflows are scrolled
https://bugs.webkit.org/show_bug.cgi?id=195733
rdar://problem/11642295

Reviewed by Antti Koivisto.

Source/WebCore:

Make ScrollingTree positioned nodes in the two cases where we need them, as
detected by RenderLayerCompositor::computeCoordinatedPositioningForLayer().

For "Moves" layers we know that the overflow is not in the z-order ancestor chain,
so ScrollingTree needs a map of overflow node -> affected positioned nodes which
notifyRelatedNodesAfterScrollPositionChange() uses to find nodes to update after
a scroll. Computing these dependent nodes in RenderLayerCompositor() would require
correct dependency analysis between an overflow layers and "positioned" layers which
is hard. It's easier to have "positioned" layers figure out which overflow nodes
affect them, then compute the inverse relationship when the scrolling tree is updated
which happens in ScrollingTreePositionedNode::commitStateBeforeChildren().

Tests: scrollingcoordinator/ios/absolute-layer-should-not-move-with-scroll.html
       scrollingcoordinator/ios/relative-layer-should-move-with-scroll.html

* page/scrolling/ScrollingTree.cpp:
(WebCore::ScrollingTree::commitTreeState):
(WebCore::ScrollingTree::applyLayerPositions):
(WebCore::ScrollingTree::notifyRelatedNodesAfterScrollPositionChange):
(WebCore::ScrollingTree::scrollingTreeAsText):
* page/scrolling/ScrollingTree.h:
(WebCore::ScrollingTree::overflowRelatedNodes):
* page/scrolling/ScrollingTreeOverflowScrollingNode.cpp:
(WebCore::ScrollingTreeOverflowScrollingNode::dumpProperties const):
* page/scrolling/ScrollingTreeOverflowScrollingNode.h:
* page/scrolling/cocoa/ScrollingTreePositionedNode.mm:
(WebCore::ScrollingTreePositionedNode::commitStateBeforeChildren):
(WebCore::ScrollingTreePositionedNode::applyLayerPositions):
(WebCore::ScrollingTreePositionedNode::relatedNodeScrollPositionDidChange):
* rendering/RenderLayerCompositor.cpp:
(WebCore::layerContainingBlockCrossesCoordinatedScrollingBoundary):
(WebCore::layerParentedAcrossCoordinatedScrollingBoundary):
(WebCore::RenderLayerCompositor::computeCoordinatedPositioningForLayer const):
(WebCore::collectRelatedCoordinatedScrollingNodes):
(WebCore::RenderLayerCompositor::updateScrollingNodeForPositioningRole):

LayoutTests:

New tests that use uiController.scrollUpdatesDisabled, and are the two move/stationary
cases.

* platform/ios-wk2/scrollingcoordinator/scrolling-tree/positioned-nodes-expected.txt:
* scrollingcoordinator/ios/absolute-layer-should-not-move-with-scroll-expected.html: Added.
* scrollingcoordinator/ios/absolute-layer-should-not-move-with-scroll.html: Added.
* scrollingcoordinator/ios/relative-layer-should-move-with-scroll-expected.html: Added.
* scrollingcoordinator/ios/relative-layer-should-move-with-scroll.html: Added.
* scrollingcoordinator/ios/ui-scrolling-tree-expected.txt: Fixed the last paren showing up.
* scrollingcoordinator/scrolling-tree/positioned-nodes-expected.txt: We make positioned nodes now.

Modified Paths

Added Paths

Diff

Modified: trunk/LayoutTests/ChangeLog (242996 => 242997)


--- trunk/LayoutTests/ChangeLog	2019-03-15 16:20:31 UTC (rev 242996)
+++ trunk/LayoutTests/ChangeLog	2019-03-15 16:26:09 UTC (rev 242997)
@@ -1,3 +1,22 @@
+2019-03-15  Simon Fraser  <[email protected]>
+
+        [Async overflow Scrolling] Update positioned node layers when overflows are scrolled
+        https://bugs.webkit.org/show_bug.cgi?id=195733
+        rdar://problem/11642295
+
+        Reviewed by Antti Koivisto.
+
+        New tests that use uiController.scrollUpdatesDisabled, and are the two move/stationary
+        cases.
+
+        * platform/ios-wk2/scrollingcoordinator/scrolling-tree/positioned-nodes-expected.txt:
+        * scrollingcoordinator/ios/absolute-layer-should-not-move-with-scroll-expected.html: Added.
+        * scrollingcoordinator/ios/absolute-layer-should-not-move-with-scroll.html: Added.
+        * scrollingcoordinator/ios/relative-layer-should-move-with-scroll-expected.html: Added.
+        * scrollingcoordinator/ios/relative-layer-should-move-with-scroll.html: Added.
+        * scrollingcoordinator/ios/ui-scrolling-tree-expected.txt: Fixed the last paren showing up.
+        * scrollingcoordinator/scrolling-tree/positioned-nodes-expected.txt: We make positioned nodes now.
+
 2019-03-15  Fujii Hironori  <[email protected]>
 
         Unreviewed test gardening for WinCairo port

Modified: trunk/LayoutTests/platform/ios-wk2/scrollingcoordinator/scrolling-tree/positioned-nodes-expected.txt (242996 => 242997)


--- trunk/LayoutTests/platform/ios-wk2/scrollingcoordinator/scrolling-tree/positioned-nodes-expected.txt	2019-03-15 16:20:31 UTC (rev 242996)
+++ trunk/LayoutTests/platform/ios-wk2/scrollingcoordinator/scrolling-tree/positioned-nodes-expected.txt	2019-03-15 16:26:09 UTC (rev 242997)
@@ -17,7 +17,7 @@
   (min layout viewport origin (0,0))
   (max layout viewport origin (0,141))
   (behavior for fixed 0)
-  (children 4
+  (children 5
     (Overflow scrolling node
       (scrollable area size 220 170)
       (contents size 220 1020)
@@ -54,6 +54,12 @@
         )
       )
     )
+    (Positioned node
+      (layout constraints 
+        (layer-position-at-last-layout (10,30))
+        (positioning-behavior moves))
+      (related overflow nodes 1)
+    )
     (Overflow scrolling node
       (scrollable area size 220 170)
       (contents size 220 1020)

Added: trunk/LayoutTests/scrollingcoordinator/ios/absolute-layer-should-not-move-with-scroll-expected.html (0 => 242997)


--- trunk/LayoutTests/scrollingcoordinator/ios/absolute-layer-should-not-move-with-scroll-expected.html	                        (rev 0)
+++ trunk/LayoutTests/scrollingcoordinator/ios/absolute-layer-should-not-move-with-scroll-expected.html	2019-03-15 16:26:09 UTC (rev 242997)
@@ -0,0 +1,61 @@
+<!DOCTYPE html> <!-- webkit-test-runner [ useFlexibleViewport=true ] -->
+<html>
+<head>
+    <meta name="viewport" content="initial-scale=1.0">
+    <style>
+        #scroller {
+            margin: 10px;
+            height: 300px;
+            width: 300px;
+            border: 1px solid black;
+            overflow: scroll;
+            opacity: 0.999; /* Make this a stacking context */
+        }
+        
+        .box {
+            width: 200px;
+            height: 200px;
+            background-color: gray;
+        }
+        
+        .absolute {
+            position: absolute;
+            top: 200px;
+            background-color: blue;
+            transform: translateZ(0); /* Currently need to trigger compositing */
+        }
+        
+        .scroll-content {
+            height: 2000px;
+        }
+    </style>
+    <script src=""
+    <script>
+        if (window.testRunner)
+            testRunner.waitUntilDone();
+
+        if (window.internals)
+            window.internals.settings.setAsyncOverflowScrollingEnabled(true);
+
+        async function doTest()
+        {
+            await UIHelper.ensurePresentationUpdate(); // Not sure why this is necessary, but it is.
+            scroller.scrollTo(0, 100);
+            await UIHelper.ensurePresentationUpdate();
+
+            if (window.testRunner)
+                testRunner.notifyDone();
+        }
+        
+        window.addEventListener('load', doTest, false);
+    </script>
+</head>
+<body>
+    <div id="scroller">
+        <div class="scroll-content">
+            <div class="box"></div>
+            <div class="absolute box"></div>
+        </div>
+    </div>
+</body>
+</html>

Added: trunk/LayoutTests/scrollingcoordinator/ios/absolute-layer-should-not-move-with-scroll.html (0 => 242997)


--- trunk/LayoutTests/scrollingcoordinator/ios/absolute-layer-should-not-move-with-scroll.html	                        (rev 0)
+++ trunk/LayoutTests/scrollingcoordinator/ios/absolute-layer-should-not-move-with-scroll.html	2019-03-15 16:26:09 UTC (rev 242997)
@@ -0,0 +1,71 @@
+<!DOCTYPE html> <!-- webkit-test-runner [ useFlexibleViewport=true ] -->
+<html>
+<head>
+    <meta name="viewport" content="initial-scale=1.0">
+    <style>
+        #scroller {
+            margin: 10px;
+            height: 300px;
+            width: 300px;
+            border: 1px solid black;
+            overflow: scroll;
+            opacity: 0.999; /* Make this a stacking context */
+        }
+        
+        .box {
+            width: 200px;
+            height: 200px;
+            background-color: gray;
+        }
+        
+        .absolute {
+            position: absolute;
+            top: 200px;
+            background-color: blue;
+            transform: translateZ(0); /* Currently need to trigger compositing */
+        }
+        
+        .scroll-content {
+            height: 2000px;
+        }
+    </style>
+    <script>
+        if (window.testRunner)
+            testRunner.waitUntilDone();
+
+        if (window.internals)
+            window.internals.settings.setAsyncOverflowScrollingEnabled(true);
+
+        function getUnstableScrollScript(x, y, scrollX, scrollY)
+        {
+            return `(function() {
+                uiController.scrollUpdatesDisabled = true;
+                uiController.immediateScrollElementAtContentPointToOffset(${x}, ${y}, ${scrollX}, ${scrollY});
+                uiController.doAfterPresentationUpdate(function() {
+                    uiController.uiScriptComplete();
+                });
+            })();`;
+        }
+
+        function doTest()
+        {
+            if (!testRunner.runUIScript)
+                return
+
+            testRunner.runUIScript(getUnstableScrollScript(50, 50, 0, 100), () => {
+                testRunner.notifyDone();
+            });
+        }
+        
+        window.addEventListener('load', doTest, false);
+    </script>
+</head>
+<body>
+    <div id="scroller">
+        <div class="scroll-content">
+            <div class="box"></div>
+            <div class="absolute box"></div>
+        </div>
+    </div>
+</body>
+</html>

Added: trunk/LayoutTests/scrollingcoordinator/ios/relative-layer-should-move-with-scroll-expected.html (0 => 242997)


--- trunk/LayoutTests/scrollingcoordinator/ios/relative-layer-should-move-with-scroll-expected.html	                        (rev 0)
+++ trunk/LayoutTests/scrollingcoordinator/ios/relative-layer-should-move-with-scroll-expected.html	2019-03-15 16:26:09 UTC (rev 242997)
@@ -0,0 +1,55 @@
+<!DOCTYPE html> <!-- webkit-test-runner [ useFlexibleViewport=true ] -->
+<html>
+<head>
+    <meta name="viewport" content="initial-scale=1.0">
+    <style>
+        #scroller {
+            margin: 10px;
+            height: 300px;
+            width: 300px;
+            border: 1px solid black;
+            overflow: scroll;
+        }
+        
+        .box {
+            position: relative;
+            z-index: 0; /* stacking context ancestor is the root */
+            top: 250px;
+            width: 200px;
+            height: 200px;
+            background-color: blue;
+        }
+        
+        .scroll-content {
+            height: 2000px;
+        }
+    </style>
+    <script src=""
+    <script>
+        if (window.testRunner)
+            testRunner.waitUntilDone();
+
+        if (window.internals)
+            window.internals.settings.setAsyncOverflowScrollingEnabled(true);
+
+        async function doTest()
+        {
+            await UIHelper.ensurePresentationUpdate(); // Not sure why this is necessary, but it is.
+            scroller.scrollTo(0, 250);
+            await UIHelper.ensurePresentationUpdate();
+
+            if (window.testRunner)
+                testRunner.notifyDone();
+        }
+        
+        window.addEventListener('load', doTest, false);
+    </script>
+</head>
+<body>
+    <div id="scroller">
+        <div class="scroll-content">
+            <div class="box"></div>
+        </div>
+    </div>
+</body>
+</html>

Added: trunk/LayoutTests/scrollingcoordinator/ios/relative-layer-should-move-with-scroll.html (0 => 242997)


--- trunk/LayoutTests/scrollingcoordinator/ios/relative-layer-should-move-with-scroll.html	                        (rev 0)
+++ trunk/LayoutTests/scrollingcoordinator/ios/relative-layer-should-move-with-scroll.html	2019-03-15 16:26:09 UTC (rev 242997)
@@ -0,0 +1,65 @@
+<!DOCTYPE html> <!-- webkit-test-runner [ useFlexibleViewport=true ] -->
+<html>
+<head>
+    <meta name="viewport" content="initial-scale=1.0">
+    <style>
+        #scroller {
+            margin: 10px;
+            height: 300px;
+            width: 300px;
+            border: 1px solid black;
+            overflow: scroll;
+        }
+        
+        .box {
+            position: relative;
+            z-index: 0; /* stacking context ancestor is the root */
+            top: 250px;
+            width: 200px;
+            height: 200px;
+            background-color: blue;
+        }
+        
+        .scroll-content {
+            height: 2000px;
+        }
+    </style>
+    <script>
+        if (window.testRunner)
+            testRunner.waitUntilDone();
+
+        if (window.internals)
+            window.internals.settings.setAsyncOverflowScrollingEnabled(true);
+
+        function getUnstableScrollScript(x, y, scrollX, scrollY)
+        {
+            return `(function() {
+                uiController.scrollUpdatesDisabled = true;
+                uiController.immediateScrollElementAtContentPointToOffset(${x}, ${y}, ${scrollX}, ${scrollY});
+                uiController.doAfterPresentationUpdate(function() {
+                    uiController.uiScriptComplete();
+                });
+            })();`;
+        }
+
+        function doTest()
+        {
+            if (!testRunner.runUIScript)
+                return
+
+            testRunner.runUIScript(getUnstableScrollScript(50, 50, 0, 250), () => {
+                testRunner.notifyDone();
+            });
+        }
+        
+        window.addEventListener('load', doTest, false);
+    </script>
+</head>
+<body>
+    <div id="scroller">
+        <div class="scroll-content">
+            <div class="box"></div>
+        </div>
+    </div>
+</body>
+</html>

Modified: trunk/LayoutTests/scrollingcoordinator/ios/ui-scrolling-tree-expected.txt (242996 => 242997)


--- trunk/LayoutTests/scrollingcoordinator/ios/ui-scrolling-tree-expected.txt	2019-03-15 16:20:31 UTC (rev 242996)
+++ trunk/LayoutTests/scrollingcoordinator/ios/ui-scrolling-tree-expected.txt	2019-03-15 16:26:09 UTC (rev 242997)
@@ -17,4 +17,4 @@
       (fixed constraints 
         (viewport-rect-at-last-layout (0,0) width=320 height=548)
         (layer-position-at-last-layout (12,10)))
-      (layer top left (12,10))))
+      (layer top left (12,10)))))

Modified: trunk/LayoutTests/scrollingcoordinator/scrolling-tree/positioned-nodes-expected.txt (242996 => 242997)


--- trunk/LayoutTests/scrollingcoordinator/scrolling-tree/positioned-nodes-expected.txt	2019-03-15 16:20:31 UTC (rev 242996)
+++ trunk/LayoutTests/scrollingcoordinator/scrolling-tree/positioned-nodes-expected.txt	2019-03-15 16:26:09 UTC (rev 242997)
@@ -18,7 +18,7 @@
   (min layout viewport origin (0,0))
   (max layout viewport origin (0,141))
   (behavior for fixed 0)
-  (children 4
+  (children 5
     (Overflow scrolling node
       (scrollable area size 205 155)
       (contents size 205 1020)
@@ -55,6 +55,12 @@
         )
       )
     )
+    (Positioned node
+      (layout constraints 
+        (layer-position-at-last-layout (10,28))
+        (positioning-behavior moves))
+      (related overflow nodes 1)
+    )
     (Overflow scrolling node
       (scrollable area size 205 155)
       (contents size 205 1020)

Modified: trunk/Source/WebCore/ChangeLog (242996 => 242997)


--- trunk/Source/WebCore/ChangeLog	2019-03-15 16:20:31 UTC (rev 242996)
+++ trunk/Source/WebCore/ChangeLog	2019-03-15 16:26:09 UTC (rev 242997)
@@ -1,3 +1,47 @@
+2019-03-15  Simon Fraser  <[email protected]>
+
+        [Async overflow Scrolling] Update positioned node layers when overflows are scrolled
+        https://bugs.webkit.org/show_bug.cgi?id=195733
+        rdar://problem/11642295
+
+        Reviewed by Antti Koivisto.
+
+        Make ScrollingTree positioned nodes in the two cases where we need them, as
+        detected by RenderLayerCompositor::computeCoordinatedPositioningForLayer().
+
+        For "Moves" layers we know that the overflow is not in the z-order ancestor chain,
+        so ScrollingTree needs a map of overflow node -> affected positioned nodes which
+        notifyRelatedNodesAfterScrollPositionChange() uses to find nodes to update after
+        a scroll. Computing these dependent nodes in RenderLayerCompositor() would require
+        correct dependency analysis between an overflow layers and "positioned" layers which
+        is hard. It's easier to have "positioned" layers figure out which overflow nodes
+        affect them, then compute the inverse relationship when the scrolling tree is updated
+        which happens in ScrollingTreePositionedNode::commitStateBeforeChildren().
+
+        Tests: scrollingcoordinator/ios/absolute-layer-should-not-move-with-scroll.html
+               scrollingcoordinator/ios/relative-layer-should-move-with-scroll.html
+
+        * page/scrolling/ScrollingTree.cpp:
+        (WebCore::ScrollingTree::commitTreeState):
+        (WebCore::ScrollingTree::applyLayerPositions):
+        (WebCore::ScrollingTree::notifyRelatedNodesAfterScrollPositionChange):
+        (WebCore::ScrollingTree::scrollingTreeAsText):
+        * page/scrolling/ScrollingTree.h:
+        (WebCore::ScrollingTree::overflowRelatedNodes):
+        * page/scrolling/ScrollingTreeOverflowScrollingNode.cpp:
+        (WebCore::ScrollingTreeOverflowScrollingNode::dumpProperties const):
+        * page/scrolling/ScrollingTreeOverflowScrollingNode.h:
+        * page/scrolling/cocoa/ScrollingTreePositionedNode.mm:
+        (WebCore::ScrollingTreePositionedNode::commitStateBeforeChildren):
+        (WebCore::ScrollingTreePositionedNode::applyLayerPositions):
+        (WebCore::ScrollingTreePositionedNode::relatedNodeScrollPositionDidChange):
+        * rendering/RenderLayerCompositor.cpp:
+        (WebCore::layerContainingBlockCrossesCoordinatedScrollingBoundary):
+        (WebCore::layerParentedAcrossCoordinatedScrollingBoundary):
+        (WebCore::RenderLayerCompositor::computeCoordinatedPositioningForLayer const):
+        (WebCore::collectRelatedCoordinatedScrollingNodes):
+        (WebCore::RenderLayerCompositor::updateScrollingNodeForPositioningRole):
+
 2019-03-15  Antti Koivisto  <[email protected]>
 
         Optimize Region for single rectangle case

Modified: trunk/Source/WebCore/page/scrolling/ScrollingTree.cpp (242996 => 242997)


--- trunk/Source/WebCore/page/scrolling/ScrollingTree.cpp	2019-03-15 16:20:31 UTC (rev 242996)
+++ trunk/Source/WebCore/page/scrolling/ScrollingTree.cpp	2019-03-15 16:26:09 UTC (rev 242997)
@@ -174,6 +174,8 @@
     for (auto nodeID : m_nodeMap.keys())
         unvisitedNodes.add(nodeID);
 
+    m_overflowRelatedNodesMap.clear();
+
     // orphanNodes keeps child nodes alive while we rebuild child lists.
     OrphanScrollingNodeMap orphanNodes;
     updateTreeFromStateNode(rootNode, orphanNodes, unvisitedNodes);
@@ -185,6 +187,8 @@
         LOG(Scrolling, "ScrollingTree::commitTreeState - removing unvisited node %" PRIu64, nodeID);
         m_nodeMap.remove(nodeID);
     }
+
+    LOG(Scrolling, "committed ScrollingTree\n%s", scrollingTreeAsText(ScrollingStateTreeAsTextBehaviorDebug).utf8().data());
 }
 
 void ScrollingTree::updateTreeFromStateNode(const ScrollingStateNode* stateNode, OrphanScrollingNodeMap& orphanNodes, HashSet<ScrollingNodeID>& unvisitedNodes)
@@ -263,7 +267,11 @@
     if (!m_rootNode)
         return;
 
+    LOG(Scrolling, "\nScrollingTree %p applyLayerPositions", this);
+
     applyLayerPositionsRecursive(*m_rootNode, { }, { });
+
+    LOG(Scrolling, "ScrollingTree %p applyLayerPositions - done\n", this);
 }
 
 void ScrollingTree::applyLayerPositionsRecursive(ScrollingTreeNode& currNode, FloatRect layoutViewport, FloatSize cumulativeDelta)
@@ -291,6 +299,8 @@
 
 void ScrollingTree::notifyRelatedNodesAfterScrollPositionChange(ScrollingTreeScrollingNode& changedNode)
 {
+    Vector<ScrollingNodeID> additionalUpdateRoots;
+    
     FloatSize deltaFromLastCommittedScrollPosition;
     FloatRect currentFrameLayoutViewport;
     if (is<ScrollingTreeFrameScrollingNode>(changedNode))
@@ -300,9 +310,17 @@
 
         if (auto* frameScrollingNode = changedNode.enclosingFrameNodeIncludingSelf())
             currentFrameLayoutViewport = frameScrollingNode->layoutViewport();
+        
+        additionalUpdateRoots = overflowRelatedNodes().get(changedNode.scrollingNodeID());
     }
 
     notifyRelatedNodesRecursive(changedNode, changedNode, currentFrameLayoutViewport, deltaFromLastCommittedScrollPosition);
+    
+    for (auto positionedNodeID : additionalUpdateRoots) {
+        auto* positionedNode = nodeForID(positionedNodeID);
+        if (positionedNode)
+            notifyRelatedNodesRecursive(changedNode, *positionedNode, currentFrameLayoutViewport, deltaFromLastCommittedScrollPosition);
+    }
 }
 
 void ScrollingTree::notifyRelatedNodesRecursive(ScrollingTreeScrollingNode& changedNode, ScrollingTreeNode& currNode, const FloatRect& layoutViewport, FloatSize cumulativeDelta)
@@ -451,26 +469,37 @@
     m_treeState.latchedNodeID = 0;
 }
 
-String ScrollingTree::scrollingTreeAsText()
+String ScrollingTree::scrollingTreeAsText(ScrollingStateTreeAsTextBehavior behavior)
 {
     TextStream ts(TextStream::LineMode::MultipleLine);
 
-    TextStream::GroupScope scope(ts);
-    ts << "scrolling tree";
+    {
+        TextStream::GroupScope scope(ts);
+        ts << "scrolling tree";
 
-    LockHolder locker(m_treeStateMutex);
+        LockHolder locker(m_treeStateMutex);
 
-    if (m_treeState.latchedNodeID)
-        ts.dumpProperty("latched node", m_treeState.latchedNodeID);
+        if (m_treeState.latchedNodeID)
+            ts.dumpProperty("latched node", m_treeState.latchedNodeID);
 
-    if (!m_treeState.mainFrameScrollPosition.isZero())
-        ts.dumpProperty("main frame scroll position", m_treeState.mainFrameScrollPosition);
-    
-    if (m_rootNode) {
-        TextStream::GroupScope scope(ts);
-        m_rootNode->dump(ts, ScrollingStateTreeAsTextBehaviorIncludeLayerPositions);
+        if (!m_treeState.mainFrameScrollPosition.isZero())
+            ts.dumpProperty("main frame scroll position", m_treeState.mainFrameScrollPosition);
+        
+        if (m_rootNode) {
+            TextStream::GroupScope scope(ts);
+            m_rootNode->dump(ts, behavior | ScrollingStateTreeAsTextBehaviorIncludeLayerPositions);
+        }
+        
+        if (behavior & ScrollingStateTreeAsTextBehaviorIncludeNodeIDs && !m_overflowRelatedNodesMap.isEmpty()) {
+            TextStream::GroupScope scope(ts);
+            ts << "overflow related nodes";
+            {
+                TextStream::IndentScope indentScope(ts);
+                for (auto& it : m_overflowRelatedNodesMap)
+                    ts << "\n" << indent << it.key << " -> " << it.value;
+            }
+        }
     }
-
     return ts.release();
 }
 

Modified: trunk/Source/WebCore/page/scrolling/ScrollingTree.h (242996 => 242997)


--- trunk/Source/WebCore/page/scrolling/ScrollingTree.h	2019-03-15 16:20:31 UTC (rev 242996)
+++ trunk/Source/WebCore/page/scrolling/ScrollingTree.h	2019-03-15 16:26:09 UTC (rev 242997)
@@ -144,9 +144,14 @@
         ASSERT(m_fixedOrStickyNodeCount);
         --m_fixedOrStickyNodeCount;
     }
+
+    // A map of overflow scrolling nodes to positioned nodes which need to be updated
+    // when the scroller changes, but are not descendants.
+    using RelatedNodesMap = HashMap<ScrollingNodeID, Vector<ScrollingNodeID>>;
+    RelatedNodesMap& overflowRelatedNodes() { return m_overflowRelatedNodesMap; }
+
+    WEBCORE_EXPORT String scrollingTreeAsText(ScrollingStateTreeAsTextBehavior = ScrollingStateTreeAsTextBehaviorNormal);
     
-    WEBCORE_EXPORT String scrollingTreeAsText();
-    
 protected:
     void setMainFrameScrollPosition(FloatPoint);
 
@@ -167,6 +172,8 @@
     using ScrollingTreeNodeMap = HashMap<ScrollingNodeID, ScrollingTreeNode*>;
     ScrollingTreeNodeMap m_nodeMap;
 
+    RelatedNodesMap m_overflowRelatedNodesMap;
+
     struct TreeState {
         ScrollingNodeID latchedNodeID { 0 };
         EventTrackingRegions eventTrackingRegions;

Modified: trunk/Source/WebCore/page/scrolling/ScrollingTreeOverflowScrollingNode.cpp (242996 => 242997)


--- trunk/Source/WebCore/page/scrolling/ScrollingTreeOverflowScrollingNode.cpp	2019-03-15 16:20:31 UTC (rev 242996)
+++ trunk/Source/WebCore/page/scrolling/ScrollingTreeOverflowScrollingNode.cpp	2019-03-15 16:26:09 UTC (rev 242997)
@@ -40,6 +40,12 @@
 
 ScrollingTreeOverflowScrollingNode::~ScrollingTreeOverflowScrollingNode() = default;
 
+void ScrollingTreeOverflowScrollingNode::dumpProperties(TextStream& ts, ScrollingStateTreeAsTextBehavior behavior) const
+{
+    ts << "overflow scrolling node";
+    ScrollingTreeNode::dumpProperties(ts, behavior);
+}
+
 } // namespace WebCore
 
 #endif // ENABLE(ASYNC_SCROLLING)

Modified: trunk/Source/WebCore/page/scrolling/ScrollingTreeOverflowScrollingNode.h (242996 => 242997)


--- trunk/Source/WebCore/page/scrolling/ScrollingTreeOverflowScrollingNode.h	2019-03-15 16:20:31 UTC (rev 242996)
+++ trunk/Source/WebCore/page/scrolling/ScrollingTreeOverflowScrollingNode.h	2019-03-15 16:26:09 UTC (rev 242997)
@@ -37,6 +37,8 @@
 
 protected:
     WEBCORE_EXPORT ScrollingTreeOverflowScrollingNode(ScrollingTree&, ScrollingNodeID);
+    
+    WEBCORE_EXPORT void dumpProperties(TextStream&, ScrollingStateTreeAsTextBehavior) const;
 };
 
 } // namespace WebCore

Modified: trunk/Source/WebCore/page/scrolling/cocoa/ScrollingTreePositionedNode.h (242996 => 242997)


--- trunk/Source/WebCore/page/scrolling/cocoa/ScrollingTreePositionedNode.h	2019-03-15 16:20:31 UTC (rev 242996)
+++ trunk/Source/WebCore/page/scrolling/cocoa/ScrollingTreePositionedNode.h	2019-03-15 16:26:09 UTC (rev 242997)
@@ -49,7 +49,7 @@
 
     void applyLayerPositions(const FloatRect& layoutViewport, FloatSize& cumulativeDelta) override;
 
-    void dumpProperties(WTF::TextStream&, ScrollingStateTreeAsTextBehavior) const override;
+    WEBCORE_EXPORT void dumpProperties(WTF::TextStream&, ScrollingStateTreeAsTextBehavior) const override;
 
     Vector<ScrollingNodeID> m_relatedOverflowScrollingNodes;
     LayoutConstraints m_constraints;

Modified: trunk/Source/WebCore/page/scrolling/cocoa/ScrollingTreePositionedNode.mm (242996 => 242997)


--- trunk/Source/WebCore/page/scrolling/cocoa/ScrollingTreePositionedNode.mm	2019-03-15 16:20:31 UTC (rev 242996)
+++ trunk/Source/WebCore/page/scrolling/cocoa/ScrollingTreePositionedNode.mm	2019-03-15 16:26:09 UTC (rev 242997)
@@ -31,6 +31,7 @@
 #import "Logging.h"
 #import "ScrollingStatePositionedNode.h"
 #import "ScrollingTree.h"
+#import "ScrollingTreeOverflowScrollingNode.h"
 #import "ScrollingTreeScrollingNode.h"
 #import <QuartzCore/CALayer.h>
 #import <wtf/text/TextStream.h>
@@ -61,30 +62,51 @@
 
     if (positionedStateNode.hasChangedProperty(ScrollingStatePositionedNode::LayoutConstraintData))
         m_constraints = positionedStateNode.layoutConstraints();
+
+    // Tell the ScrollingTree about non-ancestor overflow nodes which affect this node.
+    if (m_constraints.scrollPositioningBehavior() == ScrollPositioningBehavior::Moves) {
+        auto& relatedNodes = scrollingTree().overflowRelatedNodes();
+        for (auto overflowNodeID : m_relatedOverflowScrollingNodes) {
+            relatedNodes.ensure(overflowNodeID, [] {
+                return Vector<ScrollingNodeID>();
+            }).iterator->value.append(scrollingNodeID());
+        }
+    }
 }
 
 void ScrollingTreePositionedNode::applyLayerPositions(const FloatRect&, FloatSize& cumulativeDelta)
 {
-    FloatSize layerOffset; // FIXME: layerOffset needs to be computed by looking at scrolling tree deltas
-    // in the overflow nodes that affect this node. Some of that may come in via cumulativeDelta.
+    // Note that we ignore cumulativeDelta because it will contain the delta for ancestor scrollers,
+    // but not non-ancestor ones, so it's simpler to just recompute from the scrollers we know about here.
+    FloatSize scrollOffsetSinceLastCommit;
+    for (auto nodeID : m_relatedOverflowScrollingNodes) {
+        if (auto* node = scrollingTree().nodeForID(nodeID)) {
+            if (is<ScrollingTreeOverflowScrollingNode>(node)) {
+                auto& overflowNode = downcast<ScrollingTreeOverflowScrollingNode>(*node);
+                scrollOffsetSinceLastCommit += overflowNode.lastCommittedScrollPosition() - overflowNode.currentScrollPosition();
+            }
+        }
+    }
+    LOG_WITH_STREAM(Scrolling, stream << "ScrollingTreePositionedNode " << scrollingNodeID() << " applyLayerPositions: overflow delta " << scrollOffsetSinceLastCommit);
 
-    LOG_WITH_STREAM(Scrolling, stream << "ScrollingTreePositionedNode " << scrollingNodeID() << " applyLayerPositions: total overflow delta " << layerOffset);
-
-    layerOffset += cumulativeDelta;
-    // Stationary nodes move in the opposite direction.
-    if (m_constraints.scrollPositioningBehavior() == ScrollPositioningBehavior::Stationary)
+    auto layerOffset = -scrollOffsetSinceLastCommit;
+    if (m_constraints.scrollPositioningBehavior() == ScrollPositioningBehavior::Stationary) {
+        // Stationary nodes move in the opposite direction.
         layerOffset = -layerOffset;
+    }
 
     FloatPoint layerPosition = m_constraints.layerPositionAtLastLayout() - layerOffset;
     [m_layer _web_setLayerTopLeftPosition:layerPosition - m_constraints.alignmentOffset()];
 
-    cumulativeDelta += layerPosition - m_constraints.layerPositionAtLastLayout();
+    // FIXME: Should our scroller deltas propagate to descendants?
+    cumulativeDelta = layerPosition - m_constraints.layerPositionAtLastLayout();
 }
 
 void ScrollingTreePositionedNode::relatedNodeScrollPositionDidChange(const ScrollingTreeScrollingNode& changedNode, const FloatRect& layoutViewport, FloatSize& cumulativeDelta)
 {
-    UNUSED_PARAM(changedNode);
-    // FIXME: This will avoid doing work if we can determine that changedNode doesn't affect this positioned node.
+    if (!m_relatedOverflowScrollingNodes.contains(changedNode.scrollingNodeID()))
+        return;
+
     applyLayerPositions(layoutViewport, cumulativeDelta);
 }
 

Modified: trunk/Source/WebCore/rendering/RenderLayerCompositor.cpp (242996 => 242997)


--- trunk/Source/WebCore/rendering/RenderLayerCompositor.cpp	2019-03-15 16:20:31 UTC (rev 242996)
+++ trunk/Source/WebCore/rendering/RenderLayerCompositor.cpp	2019-03-15 16:26:09 UTC (rev 242997)
@@ -2869,17 +2869,117 @@
     return false;
 }
 
+// Is this layer's containingBlock an ancestor of scrollable overflow, and is the layer's compositing ancestor inside that overflow?
+static bool layerContainingBlockCrossesCoordinatedScrollingBoundary(const RenderLayer& layer, const RenderLayer& compositedAncestor)
+{
+    ASSERT(layer.isComposited());
+    ASSERT(layer.renderer().style().position() == PositionType::Absolute);
+
+    bool sawCompositingAncestor = false;
+    for (const auto* currLayer = layer.parent(); currLayer; currLayer = currLayer->parent()) {
+        if (currLayer->renderer().canContainAbsolutelyPositionedObjects())
+            return false;
+
+        if (currLayer == &compositedAncestor)
+            sawCompositingAncestor = true;
+
+        if (currLayer->hasCompositedScrollableOverflow())
+            return sawCompositingAncestor;
+    }
+
+    return false;
+}
+
+// Is there scrollable overflow between this layer and its composited ancestor?
+static bool layerParentedAcrossCoordinatedScrollingBoundary(const RenderLayer& layer, const RenderLayer& compositedAncestor)
+{
+    ASSERT(layer.isComposited());
+
+    for (const auto* currLayer = layer.parent(); currLayer != &compositedAncestor; currLayer = currLayer->parent()) {
+        if (currLayer->hasCompositedScrollableOverflow())
+            return true;
+    }
+
+    return false;
+}
+
 ScrollPositioningBehavior RenderLayerCompositor::computeCoordinatedPositioningForLayer(const RenderLayer& layer) const
 {
     if (layer.isRenderViewLayer())
         return ScrollPositioningBehavior::None;
 
-    // FIXME: This will look at the containing block and stacking context ancestor chains and determine
-    // whether this layer needs to be repositioned when a composited overflow scroll scrolls.
+    auto* scrollingCoordinator = this->scrollingCoordinator();
+    if (!scrollingCoordinator)
+        return ScrollPositioningBehavior::None;
 
+    // There are two cases we have to deal with here:
+    // 1. There's a composited overflow:scroll in the parent chain between the renderer and its containing block, and the layer's
+    //    composited (z-order) ancestor is inside the scroller or is the scroller. In this case, we have to compensate for scroll position
+    //    changes to make the positioned layer stay in the same place. This only applies to position:absolute (since we handle fixed elsewhere).
+    auto* compositedAncestor = layer.ancestorCompositingLayer();
+
+    auto& renderer = layer.renderer();
+    if (renderer.isOutOfFlowPositioned() && renderer.style().position() == PositionType::Absolute) {
+        if (layerContainingBlockCrossesCoordinatedScrollingBoundary(layer, *compositedAncestor))
+            return ScrollPositioningBehavior::Stationary;
+
+        return ScrollPositioningBehavior::None;
+    }
+
+    // 2. The layer's containing block is the overflow or inside the overflow:scroll, but its z-order ancestor is
+    //    outside the overflow:scroll. In that case, we have to move the layer via the scrolling tree to make
+    //    it move along with the overflow scrolling.
+    if (layerParentedAcrossCoordinatedScrollingBoundary(layer, *compositedAncestor))
+        return ScrollPositioningBehavior::Moves;
+
     return ScrollPositioningBehavior::None;
 }
 
+static Vector<ScrollingNodeID> collectRelatedCoordinatedScrollingNodes(const RenderLayer& layer, ScrollPositioningBehavior positioningBehavior)
+{
+    Vector<ScrollingNodeID> overflowNodeData;
+
+    switch (positioningBehavior) {
+    case ScrollPositioningBehavior::Moves: {
+        // Collect all the composited scrollers between this layer and its composited ancestor.
+        auto* compositedAncestor = layer.ancestorCompositingLayer();
+        for (const auto* currLayer = layer.parent(); currLayer != compositedAncestor; currLayer = currLayer->parent()) {
+            if (currLayer->hasCompositedScrollableOverflow()) {
+                auto scrollingNodeID = currLayer->backing()->scrollingNodeIDForRole(ScrollCoordinationRole::Scrolling);
+                if (scrollingNodeID)
+                    overflowNodeData.append(scrollingNodeID);
+                else
+                    LOG(Scrolling, "Layer %p doesn't have scrolling node ID yet", &layer);
+            }
+        }
+        break;
+    }
+    case ScrollPositioningBehavior::Stationary: {
+        // Collect all the composited scrollers between this layer and its containing block.
+        ASSERT(layer.renderer().style().position() == PositionType::Absolute);
+        for (const auto* currLayer = layer.parent(); currLayer; currLayer = currLayer->parent()) {
+            if (currLayer->renderer().canContainAbsolutelyPositionedObjects())
+                break;
+
+            if (currLayer->hasCompositedScrollableOverflow()) {
+                auto scrollingNodeID = currLayer->backing()->scrollingNodeIDForRole(ScrollCoordinationRole::Scrolling);
+                if (scrollingNodeID)
+                    overflowNodeData.append(scrollingNodeID);
+                else
+                    LOG(Scrolling, "Layer %p doesn't have scrolling node ID yet", &layer);
+            }
+        }
+        // Don't need to do anything because the layer is a descendant of the overflow in stacking.
+        break;
+    }
+    case ScrollPositioningBehavior::None:
+        ASSERT_NOT_REACHED();
+        break;
+    }
+
+    return overflowNodeData;
+}
+
 bool RenderLayerCompositor::isLayerForIFrameWithScrollCoordinatedContents(const RenderLayer& layer) const
 {
     if (!is<RenderWidget>(layer.renderer()))
@@ -4078,7 +4178,9 @@
     }
 
     if (changes & ScrollingNodeChangeFlags::LayerGeometry && treeState.parentNodeID) {
-        Vector<ScrollingNodeID> relatedNodeIDs; // FIXME: This will do a tree walk to figure out which composited overflows affect this positioned node.
+        // Would be nice to avoid calling computeCoordinatedPositioningForLayer() again.
+        auto positioningBehavior = computeCoordinatedPositioningForLayer(layer);
+        auto relatedNodeIDs = collectRelatedCoordinatedScrollingNodes(layer, positioningBehavior);
         scrollingCoordinator->setRelatedOverflowScrollingNodes(newNodeID, WTFMove(relatedNodeIDs));
 
         auto* graphicsLayer = layer.backing()->graphicsLayer();
@@ -4085,8 +4187,7 @@
         LayoutConstraints constraints;
         constraints.setAlignmentOffset(graphicsLayer->pixelAlignmentOffset());
         constraints.setLayerPositionAtLastLayout(graphicsLayer->position());
-        // Would be nice to avoid calling computeCoordinatedPositioningForLayer() again.
-        constraints.setScrollPositioningBehavior(computeCoordinatedPositioningForLayer(layer));
+        constraints.setScrollPositioningBehavior(positioningBehavior);
         scrollingCoordinator->setPositionedNodeGeometry(newNodeID, constraints);
     }
 
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to