Title: [246083] trunk
Revision
246083
Author
an...@apple.com
Date
2019-06-04 14:53:57 -0700 (Tue, 04 Jun 2019)

Log Message

Sticky positioning is jumpy in many overflow cases
https://bugs.webkit.org/show_bug.cgi?id=198532
<rdar://problem/51400532>

Reviewed by Simon Fraser.

Source/WebCore:

Tests: scrollingcoordinator/ios/sticky-overflow-no-stacking-context-no-stick-1.html
       scrollingcoordinator/ios/sticky-overflow-no-stacking-context-no-stick-2.html
       scrollingcoordinator/ios/sticky-overflow-no-stacking-context-stick-1.html
       scrollingcoordinator/ios/sticky-overflow-no-stacking-context-stick-2.html
       scrollingcoordinator/ios/sticky-overflow-stacking-context-no-stick.html
       scrollingcoordinator/ios/sticky-overflow-stacking-context-stick.html

* page/scrolling/ScrollingTree.cpp:
(WebCore::ScrollingTree::notifyRelatedNodesAfterScrollPositionChange):
(WebCore::ScrollingTree::notifyRelatedNodesRecursive):

Simplify for relatedNodeScrollPositionDidChange removal.

* page/scrolling/ScrollingTree.h:
* page/scrolling/ScrollingTreeNode.cpp:
(WebCore::ScrollingTreeNode::relatedNodeScrollPositionDidChange): Deleted.
* page/scrolling/ScrollingTreeNode.h:
* page/scrolling/cocoa/ScrollingTreeFixedNode.mm:
(WebCore::ScrollingTreeFixedNode::applyLayerPositions):
* page/scrolling/cocoa/ScrollingTreePositionedNode.h:
* page/scrolling/cocoa/ScrollingTreePositionedNode.mm:
(WebCore::ScrollingTreePositionedNode::scrollOffsetSinceLastCommit const):

Factor into a function.

(WebCore::ScrollingTreePositionedNode::applyLayerPositions):
(WebCore::ScrollingTreePositionedNode::relatedNodeScrollPositionDidChange): Deleted.

We can't bail out based on changed node as that makes us compute different positions based on what the change root is.
Since all relatedNodeScrollPositionDidChange functions now always simply call applyLayerPositions we can remove the whole thing.

* page/scrolling/cocoa/ScrollingTreeStickyNode.mm:
(WebCore::ScrollingTreeStickyNode::applyLayerPositions):

Implement taking into account that the containing scroller may not be our ancestor.

LayoutTests:

* scrollingcoordinator/ios/sticky-overflow-no-stacking-context-no-stick-1-expected.html: Added.
* scrollingcoordinator/ios/sticky-overflow-no-stacking-context-no-stick-1.html: Added.
* scrollingcoordinator/ios/sticky-overflow-no-stacking-context-no-stick-2-expected.html: Added.
* scrollingcoordinator/ios/sticky-overflow-no-stacking-context-no-stick-2.html: Added.
* scrollingcoordinator/ios/sticky-overflow-no-stacking-context-stick-1-expected.html: Added.
* scrollingcoordinator/ios/sticky-overflow-no-stacking-context-stick-1.html: Added.
* scrollingcoordinator/ios/sticky-overflow-no-stacking-context-stick-2-expected.html: Added.
* scrollingcoordinator/ios/sticky-overflow-no-stacking-context-stick-2.html: Added.
* scrollingcoordinator/ios/sticky-overflow-stacking-context-no-stick-expected.html: Added.
* scrollingcoordinator/ios/sticky-overflow-stacking-context-no-stick.html: Added.
* scrollingcoordinator/ios/sticky-overflow-stacking-context-stick-expected.html: Added.
* scrollingcoordinator/ios/sticky-overflow-stacking-context-stick.html: Added.

Modified Paths

Added Paths

Diff

Modified: trunk/LayoutTests/ChangeLog (246082 => 246083)


--- trunk/LayoutTests/ChangeLog	2019-06-04 21:40:00 UTC (rev 246082)
+++ trunk/LayoutTests/ChangeLog	2019-06-04 21:53:57 UTC (rev 246083)
@@ -1,3 +1,24 @@
+2019-06-04  Antti Koivisto  <an...@apple.com>
+
+        Sticky positioning is jumpy in many overflow cases
+        https://bugs.webkit.org/show_bug.cgi?id=198532
+        <rdar://problem/51400532>
+
+        Reviewed by Simon Fraser.
+
+        * scrollingcoordinator/ios/sticky-overflow-no-stacking-context-no-stick-1-expected.html: Added.
+        * scrollingcoordinator/ios/sticky-overflow-no-stacking-context-no-stick-1.html: Added.
+        * scrollingcoordinator/ios/sticky-overflow-no-stacking-context-no-stick-2-expected.html: Added.
+        * scrollingcoordinator/ios/sticky-overflow-no-stacking-context-no-stick-2.html: Added.
+        * scrollingcoordinator/ios/sticky-overflow-no-stacking-context-stick-1-expected.html: Added.
+        * scrollingcoordinator/ios/sticky-overflow-no-stacking-context-stick-1.html: Added.
+        * scrollingcoordinator/ios/sticky-overflow-no-stacking-context-stick-2-expected.html: Added.
+        * scrollingcoordinator/ios/sticky-overflow-no-stacking-context-stick-2.html: Added.
+        * scrollingcoordinator/ios/sticky-overflow-stacking-context-no-stick-expected.html: Added.
+        * scrollingcoordinator/ios/sticky-overflow-stacking-context-no-stick.html: Added.
+        * scrollingcoordinator/ios/sticky-overflow-stacking-context-stick-expected.html: Added.
+        * scrollingcoordinator/ios/sticky-overflow-stacking-context-stick.html: Added.
+
 2019-06-04  Takashi Komori  <takashi.kom...@sony.com>
 
         [WinCairo] Implement cpu and memory measuring functions.

Added: trunk/LayoutTests/scrollingcoordinator/ios/sticky-overflow-no-stacking-context-no-stick-1-expected.html (0 => 246083)


--- trunk/LayoutTests/scrollingcoordinator/ios/sticky-overflow-no-stacking-context-no-stick-1-expected.html	                        (rev 0)
+++ trunk/LayoutTests/scrollingcoordinator/ios/sticky-overflow-no-stacking-context-no-stick-1-expected.html	2019-06-04 21:53:57 UTC (rev 246083)
@@ -0,0 +1,62 @@
+<!DOCTYPE html> <!-- webkit-test-runner [ useFlexibleViewport=true internal:AsyncOverflowScrollingEnabled=true internal:AsyncFrameScrollingEnabled=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;
+            position: relative;
+        }
+        
+        .sticky {
+            position: -webkit-sticky;
+            top: 0px;
+            width: 200px;
+            height: 200px;
+            background-color: green;
+        }
+
+        .container {
+            margin: 40px;
+            border: 2px solid red;
+            height: 5000px;
+        }
+    </style>
+    <script src=""
+    <script>
+        if (window.testRunner)
+            testRunner.waitUntilDone();
+
+        async function doTest()
+        {
+            if (!window.testRunner)
+                return;
+
+            if (!testRunner.runUIScript)
+                return;
+            
+            await UIHelper.ensurePresentationUpdate();
+            document.querySelector('.scroller').scrollTo(0, 20);
+            await UIHelper.ensurePresentationUpdate();
+
+            if (window.testRunner)
+                testRunner.notifyDone();
+        }
+        
+        window.addEventListener('load', doTest, false);
+    </script>
+</head>
+<body>
+    <div class="spacer">
+        <div class="scroller">
+            <div class="container">
+                <div class="sticky"></div>
+            </div>
+        </div>
+    </div>
+</body>
+</html>

Added: trunk/LayoutTests/scrollingcoordinator/ios/sticky-overflow-no-stacking-context-no-stick-1.html (0 => 246083)


--- trunk/LayoutTests/scrollingcoordinator/ios/sticky-overflow-no-stacking-context-no-stick-1.html	                        (rev 0)
+++ trunk/LayoutTests/scrollingcoordinator/ios/sticky-overflow-no-stacking-context-no-stick-1.html	2019-06-04 21:53:57 UTC (rev 246083)
@@ -0,0 +1,59 @@
+<!DOCTYPE html> <!-- webkit-test-runner [ useFlexibleViewport=true internal:AsyncOverflowScrollingEnabled=true internal:AsyncFrameScrollingEnabled=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;
+            position: relative;
+        }
+        
+        .sticky {
+            position: -webkit-sticky;
+            top: 0px;
+            width: 200px;
+            height: 200px;
+            background-color: green;
+        }
+
+        .container {
+            margin: 40px;
+            border: 2px solid red;
+            height: 5000px;
+        }
+    </style>
+    <script src=""
+    <script>
+        if (window.testRunner)
+            testRunner.waitUntilDone();
+
+        async function doTest()
+        {
+            if (!window.testRunner)
+                return;
+
+            if (!testRunner.runUIScript)
+                return;
+
+            const scrollUpdatesDisabled = true;
+            await UIHelper.immediateScrollElementAtContentPointToOffset(50, 50, 0, 20, scrollUpdatesDisabled);
+            testRunner.notifyDone();
+        }
+        
+        window.addEventListener('load', doTest, false);
+    </script>
+</head>
+<body>
+    <div class="spacer">
+        <div class="scroller">
+            <div class="container">
+                <div class="sticky"></div>
+            </div>
+        </div>
+    </div>
+</body>
+</html>

Added: trunk/LayoutTests/scrollingcoordinator/ios/sticky-overflow-no-stacking-context-no-stick-2-expected.html (0 => 246083)


--- trunk/LayoutTests/scrollingcoordinator/ios/sticky-overflow-no-stacking-context-no-stick-2-expected.html	                        (rev 0)
+++ trunk/LayoutTests/scrollingcoordinator/ios/sticky-overflow-no-stacking-context-no-stick-2-expected.html	2019-06-04 21:53:57 UTC (rev 246083)
@@ -0,0 +1,64 @@
+<!DOCTYPE html> <!-- webkit-test-runner [ useFlexibleViewport=true internal:AsyncOverflowScrollingEnabled=true internal:AsyncFrameScrollingEnabled=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;
+            z-index: 0;
+            position: relative;
+        }
+        
+        .sticky {
+            position: -webkit-sticky;
+            top: 0px;
+            width: 200px;
+            height: 200px;
+            background-color: green;
+        }
+
+        .container {
+            margin: 40px;
+            border: 2px solid red;
+            height: 5000px;
+            will-change: transform;
+        }
+    </style>
+    <script src=""
+    <script>
+        if (window.testRunner)
+            testRunner.waitUntilDone();
+
+        async function doTest()
+        {
+            if (!window.testRunner)
+                return;
+
+            if (!testRunner.runUIScript)
+                return;
+            
+            await UIHelper.ensurePresentationUpdate();
+            document.querySelector('.scroller').scrollTo(0, 20);
+            await UIHelper.ensurePresentationUpdate();
+
+            if (window.testRunner)
+                testRunner.notifyDone();
+        }
+        
+        window.addEventListener('load', doTest, false);
+    </script>
+</head>
+<body>
+    <div class="spacer">
+        <div class="scroller">
+            <div class="container">
+                <div class="sticky"></div>
+            </div>
+        </div>
+    </div>
+</body>
+</html>

Added: trunk/LayoutTests/scrollingcoordinator/ios/sticky-overflow-no-stacking-context-no-stick-2.html (0 => 246083)


--- trunk/LayoutTests/scrollingcoordinator/ios/sticky-overflow-no-stacking-context-no-stick-2.html	                        (rev 0)
+++ trunk/LayoutTests/scrollingcoordinator/ios/sticky-overflow-no-stacking-context-no-stick-2.html	2019-06-04 21:53:57 UTC (rev 246083)
@@ -0,0 +1,60 @@
+<!DOCTYPE html> <!-- webkit-test-runner [ useFlexibleViewport=true internal:AsyncOverflowScrollingEnabled=true internal:AsyncFrameScrollingEnabled=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;
+            position: relative;
+        }
+        
+        .sticky {
+            position: -webkit-sticky;
+            top: 0px;
+            width: 200px;
+            height: 200px;
+            background-color: green;
+        }
+
+        .container {
+            margin: 40px;
+            border: 2px solid red;
+            height: 5000px;
+            will-change: transform;
+        }
+    </style>
+    <script src=""
+    <script>
+        if (window.testRunner)
+            testRunner.waitUntilDone();
+
+        async function doTest()
+        {
+            if (!window.testRunner)
+                return;
+
+            if (!testRunner.runUIScript)
+                return;
+
+            const scrollUpdatesDisabled = true;
+            await UIHelper.immediateScrollElementAtContentPointToOffset(50, 50, 0, 20, scrollUpdatesDisabled);
+            testRunner.notifyDone();
+        }
+        
+        window.addEventListener('load', doTest, false);
+    </script>
+</head>
+<body>
+    <div class="spacer">
+        <div class="scroller">
+            <div class="container">
+                <div class="sticky"></div>
+            </div>
+        </div>
+    </div>
+</body>
+</html>

Added: trunk/LayoutTests/scrollingcoordinator/ios/sticky-overflow-no-stacking-context-stick-1-expected.html (0 => 246083)


--- trunk/LayoutTests/scrollingcoordinator/ios/sticky-overflow-no-stacking-context-stick-1-expected.html	                        (rev 0)
+++ trunk/LayoutTests/scrollingcoordinator/ios/sticky-overflow-no-stacking-context-stick-1-expected.html	2019-06-04 21:53:57 UTC (rev 246083)
@@ -0,0 +1,62 @@
+<!DOCTYPE html> <!-- webkit-test-runner [ useFlexibleViewport=true internal:AsyncOverflowScrollingEnabled=true internal:AsyncFrameScrollingEnabled=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;
+            position: relative;
+        }
+        
+        .sticky {
+            position: -webkit-sticky;
+            top: 0px;
+            width: 200px;
+            height: 200px;
+            background-color: green;
+        }
+
+        .container {
+            margin: 40px;
+            border: 2px solid red;
+            height: 5000px;
+        }
+    </style>
+    <script src=""
+    <script>
+        if (window.testRunner)
+            testRunner.waitUntilDone();
+
+        async function doTest()
+        {
+            if (!window.testRunner)
+                return;
+
+            if (!testRunner.runUIScript)
+                return;
+            
+            await UIHelper.ensurePresentationUpdate();
+            document.querySelector('.scroller').scrollTo(0, 200);
+            await UIHelper.ensurePresentationUpdate();
+
+            if (window.testRunner)
+                testRunner.notifyDone();
+        }
+        
+        window.addEventListener('load', doTest, false);
+    </script>
+</head>
+<body>
+    <div class="spacer">
+        <div class="scroller">
+            <div class="container">
+                <div class="sticky"></div>
+            </div>
+        </div>
+    </div>
+</body>
+</html>

Added: trunk/LayoutTests/scrollingcoordinator/ios/sticky-overflow-no-stacking-context-stick-1.html (0 => 246083)


--- trunk/LayoutTests/scrollingcoordinator/ios/sticky-overflow-no-stacking-context-stick-1.html	                        (rev 0)
+++ trunk/LayoutTests/scrollingcoordinator/ios/sticky-overflow-no-stacking-context-stick-1.html	2019-06-04 21:53:57 UTC (rev 246083)
@@ -0,0 +1,59 @@
+<!DOCTYPE html> <!-- webkit-test-runner [ useFlexibleViewport=true internal:AsyncOverflowScrollingEnabled=true internal:AsyncFrameScrollingEnabled=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;
+            position: relative;
+        }
+        
+        .sticky {
+            position: -webkit-sticky;
+            top: 0px;
+            width: 200px;
+            height: 200px;
+            background-color: green;
+        }
+
+        .container {
+            margin: 40px;
+            border: 2px solid red;
+            height: 5000px;
+        }
+    </style>
+    <script src=""
+    <script>
+        if (window.testRunner)
+            testRunner.waitUntilDone();
+
+        async function doTest()
+        {
+            if (!window.testRunner)
+                return;
+
+            if (!testRunner.runUIScript)
+                return;
+
+            const scrollUpdatesDisabled = true;
+            await UIHelper.immediateScrollElementAtContentPointToOffset(50, 50, 0, 200, scrollUpdatesDisabled);
+            testRunner.notifyDone();
+        }
+        
+        window.addEventListener('load', doTest, false);
+    </script>
+</head>
+<body>
+    <div class="spacer">
+        <div class="scroller">
+            <div class="container">
+                <div class="sticky"></div>
+            </div>
+        </div>
+    </div>
+</body>
+</html>

Added: trunk/LayoutTests/scrollingcoordinator/ios/sticky-overflow-no-stacking-context-stick-2-expected.html (0 => 246083)


--- trunk/LayoutTests/scrollingcoordinator/ios/sticky-overflow-no-stacking-context-stick-2-expected.html	                        (rev 0)
+++ trunk/LayoutTests/scrollingcoordinator/ios/sticky-overflow-no-stacking-context-stick-2-expected.html	2019-06-04 21:53:57 UTC (rev 246083)
@@ -0,0 +1,64 @@
+<!DOCTYPE html> <!-- webkit-test-runner [ useFlexibleViewport=true internal:AsyncOverflowScrollingEnabled=true internal:AsyncFrameScrollingEnabled=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;
+            z-index: 0;
+            position: relative;
+        }
+        
+        .sticky {
+            position: -webkit-sticky;
+            top: 0px;
+            width: 200px;
+            height: 200px;
+            background-color: green;
+        }
+
+        .container {
+            margin: 40px;
+            border: 2px solid red;
+            height: 5000px;
+            will-change: transform;
+        }
+    </style>
+    <script src=""
+    <script>
+        if (window.testRunner)
+            testRunner.waitUntilDone();
+
+        async function doTest()
+        {
+            if (!window.testRunner)
+                return;
+
+            if (!testRunner.runUIScript)
+                return;
+            
+            await UIHelper.ensurePresentationUpdate();
+            document.querySelector('.scroller').scrollTo(0, 200);
+            await UIHelper.ensurePresentationUpdate();
+
+            if (window.testRunner)
+                testRunner.notifyDone();
+        }
+        
+        window.addEventListener('load', doTest, false);
+    </script>
+</head>
+<body>
+    <div class="spacer">
+        <div class="scroller">
+            <div class="container">
+                <div class="sticky"></div>
+            </div>
+        </div>
+    </div>
+</body>
+</html>

Added: trunk/LayoutTests/scrollingcoordinator/ios/sticky-overflow-no-stacking-context-stick-2.html (0 => 246083)


--- trunk/LayoutTests/scrollingcoordinator/ios/sticky-overflow-no-stacking-context-stick-2.html	                        (rev 0)
+++ trunk/LayoutTests/scrollingcoordinator/ios/sticky-overflow-no-stacking-context-stick-2.html	2019-06-04 21:53:57 UTC (rev 246083)
@@ -0,0 +1,60 @@
+<!DOCTYPE html> <!-- webkit-test-runner [ useFlexibleViewport=true internal:AsyncOverflowScrollingEnabled=true internal:AsyncFrameScrollingEnabled=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;
+            position: relative;
+        }
+        
+        .sticky {
+            position: -webkit-sticky;
+            top: 0px;
+            width: 200px;
+            height: 200px;
+            background-color: green;
+        }
+
+        .container {
+            margin: 40px;
+            border: 2px solid red;
+            height: 5000px;
+            will-change: transform;
+        }
+    </style>
+    <script src=""
+    <script>
+        if (window.testRunner)
+            testRunner.waitUntilDone();
+
+        async function doTest()
+        {
+            if (!window.testRunner)
+                return;
+
+            if (!testRunner.runUIScript)
+                return;
+
+            const scrollUpdatesDisabled = true;
+            await UIHelper.immediateScrollElementAtContentPointToOffset(50, 50, 0, 200, scrollUpdatesDisabled);
+            testRunner.notifyDone();
+        }
+        
+        window.addEventListener('load', doTest, false);
+    </script>
+</head>
+<body>
+    <div class="spacer">
+        <div class="scroller">
+            <div class="container">
+                <div class="sticky"></div>
+            </div>
+        </div>
+    </div>
+</body>
+</html>

Added: trunk/LayoutTests/scrollingcoordinator/ios/sticky-overflow-stacking-context-no-stick-expected.html (0 => 246083)


--- trunk/LayoutTests/scrollingcoordinator/ios/sticky-overflow-stacking-context-no-stick-expected.html	                        (rev 0)
+++ trunk/LayoutTests/scrollingcoordinator/ios/sticky-overflow-stacking-context-no-stick-expected.html	2019-06-04 21:53:57 UTC (rev 246083)
@@ -0,0 +1,63 @@
+<!DOCTYPE html> <!-- webkit-test-runner [ useFlexibleViewport=true internal:AsyncOverflowScrollingEnabled=true internal:AsyncFrameScrollingEnabled=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;
+            z-index: 0;
+            position: relative;
+        }
+        
+        .sticky {
+            position: -webkit-sticky;
+            top: 0px;
+            width: 200px;
+            height: 200px;
+            background-color: green;
+        }
+
+        .container {
+            margin: 40px;
+            border: 2px solid red;
+            height: 5000px;
+        }
+    </style>
+    <script src=""
+    <script>
+        if (window.testRunner)
+            testRunner.waitUntilDone();
+
+        async function doTest()
+        {
+            if (!window.testRunner)
+                return;
+
+            if (!testRunner.runUIScript)
+                return;
+            
+            await UIHelper.ensurePresentationUpdate();
+            document.querySelector('.scroller').scrollTo(0, 20);
+            await UIHelper.ensurePresentationUpdate();
+
+            if (window.testRunner)
+                testRunner.notifyDone();
+        }
+        
+        window.addEventListener('load', doTest, false);
+    </script>
+</head>
+<body>
+    <div class="spacer">
+        <div class="scroller">
+            <div class="container">
+                <div class="sticky"></div>
+            </div>
+        </div>
+    </div>
+</body>
+</html>

Added: trunk/LayoutTests/scrollingcoordinator/ios/sticky-overflow-stacking-context-no-stick.html (0 => 246083)


--- trunk/LayoutTests/scrollingcoordinator/ios/sticky-overflow-stacking-context-no-stick.html	                        (rev 0)
+++ trunk/LayoutTests/scrollingcoordinator/ios/sticky-overflow-stacking-context-no-stick.html	2019-06-04 21:53:57 UTC (rev 246083)
@@ -0,0 +1,60 @@
+<!DOCTYPE html> <!-- webkit-test-runner [ useFlexibleViewport=true internal:AsyncOverflowScrollingEnabled=true internal:AsyncFrameScrollingEnabled=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;
+            z-index: 0;
+            position: relative;
+        }
+        
+        .sticky {
+            position: -webkit-sticky;
+            top: 0px;
+            width: 200px;
+            height: 200px;
+            background-color: green;
+        }
+
+        .container {
+            margin: 40px;
+            border: 2px solid red;
+            height: 5000px;
+        }
+    </style>
+    <script src=""
+    <script>
+        if (window.testRunner)
+            testRunner.waitUntilDone();
+
+        async function doTest()
+        {
+            if (!window.testRunner)
+                return;
+
+            if (!testRunner.runUIScript)
+                return;
+
+            const scrollUpdatesDisabled = true;
+            await UIHelper.immediateScrollElementAtContentPointToOffset(50, 50, 0, 20, scrollUpdatesDisabled);
+            testRunner.notifyDone();
+        }
+        
+        window.addEventListener('load', doTest, false);
+    </script>
+</head>
+<body>
+    <div class="spacer">
+        <div class="scroller">
+            <div class="container">
+                <div class="sticky"></div>
+            </div>
+        </div>
+    </div>
+</body>
+</html>

Added: trunk/LayoutTests/scrollingcoordinator/ios/sticky-overflow-stacking-context-stick-expected.html (0 => 246083)


--- trunk/LayoutTests/scrollingcoordinator/ios/sticky-overflow-stacking-context-stick-expected.html	                        (rev 0)
+++ trunk/LayoutTests/scrollingcoordinator/ios/sticky-overflow-stacking-context-stick-expected.html	2019-06-04 21:53:57 UTC (rev 246083)
@@ -0,0 +1,63 @@
+<!DOCTYPE html> <!-- webkit-test-runner [ useFlexibleViewport=true internal:AsyncOverflowScrollingEnabled=true internal:AsyncFrameScrollingEnabled=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;
+            z-index: 0;
+            position: relative;
+        }
+        
+        .sticky {
+            position: -webkit-sticky;
+            top: 0px;
+            width: 200px;
+            height: 200px;
+            background-color: green;
+        }
+
+        .container {
+            margin: 40px;
+            border: 2px solid red;
+            height: 5000px;
+        }
+    </style>
+    <script src=""
+    <script>
+        if (window.testRunner)
+            testRunner.waitUntilDone();
+
+        async function doTest()
+        {
+            if (!window.testRunner)
+                return;
+
+            if (!testRunner.runUIScript)
+                return;
+            
+            await UIHelper.ensurePresentationUpdate();
+            document.querySelector('.scroller').scrollTo(0, 200);
+            await UIHelper.ensurePresentationUpdate();
+
+            if (window.testRunner)
+                testRunner.notifyDone();
+        }
+        
+        window.addEventListener('load', doTest, false);
+    </script>
+</head>
+<body>
+    <div class="spacer">
+        <div class="scroller">
+            <div class="container">
+                <div class="sticky"></div>
+            </div>
+        </div>
+    </div>
+</body>
+</html>

Added: trunk/LayoutTests/scrollingcoordinator/ios/sticky-overflow-stacking-context-stick.html (0 => 246083)


--- trunk/LayoutTests/scrollingcoordinator/ios/sticky-overflow-stacking-context-stick.html	                        (rev 0)
+++ trunk/LayoutTests/scrollingcoordinator/ios/sticky-overflow-stacking-context-stick.html	2019-06-04 21:53:57 UTC (rev 246083)
@@ -0,0 +1,60 @@
+<!DOCTYPE html> <!-- webkit-test-runner [ useFlexibleViewport=true internal:AsyncOverflowScrollingEnabled=true internal:AsyncFrameScrollingEnabled=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;
+            z-index: 0;
+            position: relative;
+        }
+        
+        .sticky {
+            position: -webkit-sticky;
+            top: 0px;
+            width: 200px;
+            height: 200px;
+            background-color: green;
+        }
+
+        .container {
+            margin: 40px;
+            border: 2px solid red;
+            height: 5000px;
+        }
+    </style>
+    <script src=""
+    <script>
+        if (window.testRunner)
+            testRunner.waitUntilDone();
+
+        async function doTest()
+        {
+            if (!window.testRunner)
+                return;
+
+            if (!testRunner.runUIScript)
+                return;
+
+            const scrollUpdatesDisabled = true;
+            await UIHelper.immediateScrollElementAtContentPointToOffset(50, 50, 0, 200, scrollUpdatesDisabled);
+            testRunner.notifyDone();
+        }
+        
+        window.addEventListener('load', doTest, false);
+    </script>
+</head>
+<body>
+    <div class="spacer">
+        <div class="scroller">
+            <div class="container">
+                <div class="sticky"></div>
+            </div>
+        </div>
+    </div>
+</body>
+</html>

Modified: trunk/Source/WebCore/ChangeLog (246082 => 246083)


--- trunk/Source/WebCore/ChangeLog	2019-06-04 21:40:00 UTC (rev 246082)
+++ trunk/Source/WebCore/ChangeLog	2019-06-04 21:53:57 UTC (rev 246083)
@@ -1,3 +1,47 @@
+2019-06-04  Antti Koivisto  <an...@apple.com>
+
+        Sticky positioning is jumpy in many overflow cases
+        https://bugs.webkit.org/show_bug.cgi?id=198532
+        <rdar://problem/51400532>
+
+        Reviewed by Simon Fraser.
+
+        Tests: scrollingcoordinator/ios/sticky-overflow-no-stacking-context-no-stick-1.html
+               scrollingcoordinator/ios/sticky-overflow-no-stacking-context-no-stick-2.html
+               scrollingcoordinator/ios/sticky-overflow-no-stacking-context-stick-1.html
+               scrollingcoordinator/ios/sticky-overflow-no-stacking-context-stick-2.html
+               scrollingcoordinator/ios/sticky-overflow-stacking-context-no-stick.html
+               scrollingcoordinator/ios/sticky-overflow-stacking-context-stick.html
+
+        * page/scrolling/ScrollingTree.cpp:
+        (WebCore::ScrollingTree::notifyRelatedNodesAfterScrollPositionChange):
+        (WebCore::ScrollingTree::notifyRelatedNodesRecursive):
+
+        Simplify for relatedNodeScrollPositionDidChange removal.
+
+        * page/scrolling/ScrollingTree.h:
+        * page/scrolling/ScrollingTreeNode.cpp:
+        (WebCore::ScrollingTreeNode::relatedNodeScrollPositionDidChange): Deleted.
+        * page/scrolling/ScrollingTreeNode.h:
+        * page/scrolling/cocoa/ScrollingTreeFixedNode.mm:
+        (WebCore::ScrollingTreeFixedNode::applyLayerPositions):
+        * page/scrolling/cocoa/ScrollingTreePositionedNode.h:
+        * page/scrolling/cocoa/ScrollingTreePositionedNode.mm:
+        (WebCore::ScrollingTreePositionedNode::scrollOffsetSinceLastCommit const):
+
+        Factor into a function.
+
+        (WebCore::ScrollingTreePositionedNode::applyLayerPositions):
+        (WebCore::ScrollingTreePositionedNode::relatedNodeScrollPositionDidChange): Deleted.
+
+        We can't bail out based on changed node as that makes us compute different positions based on what the change root is.
+        Since all relatedNodeScrollPositionDidChange functions now always simply call applyLayerPositions we can remove the whole thing.
+
+        * page/scrolling/cocoa/ScrollingTreeStickyNode.mm:
+        (WebCore::ScrollingTreeStickyNode::applyLayerPositions):
+
+        Implement taking into account that the containing scroller may not be our ancestor.
+
 2019-06-04  Takashi Komori  <takashi.kom...@sony.com>
 
         [WinCairo] Implement cpu and memory measuring functions.

Modified: trunk/Source/WebCore/page/scrolling/ScrollingTree.cpp (246082 => 246083)


--- trunk/Source/WebCore/page/scrolling/ScrollingTree.cpp	2019-06-04 21:40:00 UTC (rev 246082)
+++ trunk/Source/WebCore/page/scrolling/ScrollingTree.cpp	2019-06-04 21:53:57 UTC (rev 246083)
@@ -294,28 +294,28 @@
     if (is<ScrollingTreeOverflowScrollingNode>(changedNode))
         additionalUpdateRoots = overflowRelatedNodes().get(changedNode.scrollingNodeID());
 
-    notifyRelatedNodesRecursive(changedNode, changedNode);
+    notifyRelatedNodesRecursive(changedNode);
     
     for (auto positionedNodeID : additionalUpdateRoots) {
         auto* positionedNode = nodeForID(positionedNodeID);
         if (positionedNode)
-            notifyRelatedNodesRecursive(changedNode, *positionedNode);
+            notifyRelatedNodesRecursive(*positionedNode);
     }
 }
 
-void ScrollingTree::notifyRelatedNodesRecursive(ScrollingTreeScrollingNode& changedNode, ScrollingTreeNode& currNode)
+void ScrollingTree::notifyRelatedNodesRecursive(ScrollingTreeNode& node)
 {
-    currNode.relatedNodeScrollPositionDidChange(changedNode);
+    node.applyLayerPositions();
 
-    if (!currNode.children())
+    if (!node.children())
         return;
 
-    for (auto& child : *currNode.children()) {
+    for (auto& child : *node.children()) {
         // Never need to cross frame boundaries, since scroll layer adjustments are isolated to each document.
         if (is<ScrollingTreeFrameScrollingNode>(child))
             continue;
 
-        notifyRelatedNodesRecursive(changedNode, *child);
+        notifyRelatedNodesRecursive(*child);
     }
 }
 

Modified: trunk/Source/WebCore/page/scrolling/ScrollingTree.h (246082 => 246083)


--- trunk/Source/WebCore/page/scrolling/ScrollingTree.h	2019-06-04 21:40:00 UTC (rev 246082)
+++ trunk/Source/WebCore/page/scrolling/ScrollingTree.h	2019-06-04 21:53:57 UTC (rev 246083)
@@ -167,7 +167,7 @@
 
     void applyLayerPositionsRecursive(ScrollingTreeNode&);
 
-    void notifyRelatedNodesRecursive(ScrollingTreeScrollingNode& changedNode, ScrollingTreeNode& currNode);
+    void notifyRelatedNodesRecursive(ScrollingTreeNode&);
 
     Lock m_treeMutex; // Protects the scrolling tree.
 

Modified: trunk/Source/WebCore/page/scrolling/ScrollingTreeNode.cpp (246082 => 246083)


--- trunk/Source/WebCore/page/scrolling/ScrollingTreeNode.cpp	2019-06-04 21:40:00 UTC (rev 246082)
+++ trunk/Source/WebCore/page/scrolling/ScrollingTreeNode.cpp	2019-06-04 21:53:57 UTC (rev 246083)
@@ -77,11 +77,6 @@
     return m_scrollingTree.rootNode() == this;
 }
 
-void ScrollingTreeNode::relatedNodeScrollPositionDidChange(const ScrollingTreeScrollingNode&)
-{
-    applyLayerPositions();
-}
-
 void ScrollingTreeNode::dumpProperties(TextStream& ts, ScrollingStateTreeAsTextBehavior behavior) const
 {
     if (behavior & ScrollingStateTreeAsTextBehaviorIncludeNodeIDs)

Modified: trunk/Source/WebCore/page/scrolling/ScrollingTreeNode.h (246082 => 246083)


--- trunk/Source/WebCore/page/scrolling/ScrollingTreeNode.h	2019-06-04 21:40:00 UTC (rev 246082)
+++ trunk/Source/WebCore/page/scrolling/ScrollingTreeNode.h	2019-06-04 21:53:57 UTC (rev 246083)
@@ -84,8 +84,6 @@
     ScrollingTreeNode(ScrollingTree&, ScrollingNodeType, ScrollingNodeID);
     ScrollingTree& scrollingTree() const { return m_scrollingTree; }
 
-    WEBCORE_EXPORT virtual void relatedNodeScrollPositionDidChange(const ScrollingTreeScrollingNode& changedNode);
-
     virtual void applyLayerPositions() = 0;
 
     WEBCORE_EXPORT virtual void dumpProperties(WTF::TextStream&, ScrollingStateTreeAsTextBehavior) const;

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


--- trunk/Source/WebCore/page/scrolling/cocoa/ScrollingTreePositionedNode.h	2019-06-04 21:40:00 UTC (rev 246082)
+++ trunk/Source/WebCore/page/scrolling/cocoa/ScrollingTreePositionedNode.h	2019-06-04 21:53:57 UTC (rev 246083)
@@ -46,11 +46,12 @@
     ScrollPositioningBehavior scrollPositioningBehavior() const { return m_constraints.scrollPositioningBehavior(); }
     const Vector<ScrollingNodeID>& relatedOverflowScrollingNodes() const { return m_relatedOverflowScrollingNodes; }
 
+    FloatSize scrollOffsetSinceLastCommit() const;
+
 private:
     ScrollingTreePositionedNode(ScrollingTree&, ScrollingNodeID);
 
     void commitStateBeforeChildren(const ScrollingStateNode&) override;
-    void relatedNodeScrollPositionDidChange(const ScrollingTreeScrollingNode& changedNode) override;
 
     void applyLayerPositions() override;
 

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


--- trunk/Source/WebCore/page/scrolling/cocoa/ScrollingTreePositionedNode.mm	2019-06-04 21:40:00 UTC (rev 246082)
+++ trunk/Source/WebCore/page/scrolling/cocoa/ScrollingTreePositionedNode.mm	2019-06-04 21:53:57 UTC (rev 246083)
@@ -76,36 +76,33 @@
         scrollingTree().positionedNodesWithRelatedOverflow().add(scrollingNodeID());
 }
 
-void ScrollingTreePositionedNode::applyLayerPositions()
+FloatSize ScrollingTreePositionedNode::scrollOffsetSinceLastCommit() const
 {
-    FloatSize scrollOffsetSinceLastCommit;
+    FloatSize offset;
     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();
+                offset += overflowNode.currentScrollPosition() - overflowNode.lastCommittedScrollPosition();
             }
         }
     }
-    auto layerOffset = -scrollOffsetSinceLastCommit;
     if (m_constraints.scrollPositioningBehavior() == ScrollPositioningBehavior::Stationary) {
         // Stationary nodes move in the opposite direction.
-        layerOffset = -layerOffset;
+        return -offset;
     }
 
-    FloatPoint layerPosition = m_constraints.layerPositionAtLastLayout() - layerOffset;
-
-    LOG_WITH_STREAM(Scrolling, stream << "ScrollingTreePositionedNode " << scrollingNodeID() << " applyLayerPositions: overflow delta " << scrollOffsetSinceLastCommit << " moving layer to " << layerPosition);
-
-    [m_layer _web_setLayerTopLeftPosition:layerPosition - m_constraints.alignmentOffset()];
+    return offset;
 }
 
-void ScrollingTreePositionedNode::relatedNodeScrollPositionDidChange(const ScrollingTreeScrollingNode& changedNode)
+void ScrollingTreePositionedNode::applyLayerPositions()
 {
-    if (!m_relatedOverflowScrollingNodes.contains(changedNode.scrollingNodeID()))
-        return;
+    auto offset = scrollOffsetSinceLastCommit();
+    auto layerPosition = m_constraints.layerPositionAtLastLayout() - offset;
 
-    applyLayerPositions();
+    LOG_WITH_STREAM(Scrolling, stream << "ScrollingTreePositionedNode " << scrollingNodeID() << " applyLayerPositions: overflow delta " << offset << " moving layer to " << layerPosition);
+
+    [m_layer _web_setLayerTopLeftPosition:layerPosition - m_constraints.alignmentOffset()];
 }
 
 void ScrollingTreePositionedNode::dumpProperties(TextStream& ts, ScrollingStateTreeAsTextBehavior behavior) const

Modified: trunk/Source/WebCore/page/scrolling/cocoa/ScrollingTreeStickyNode.mm (246082 => 246083)


--- trunk/Source/WebCore/page/scrolling/cocoa/ScrollingTreeStickyNode.mm	2019-06-04 21:40:00 UTC (rev 246082)
+++ trunk/Source/WebCore/page/scrolling/cocoa/ScrollingTreeStickyNode.mm	2019-06-04 21:53:57 UTC (rev 246083)
@@ -31,6 +31,7 @@
 #import "Logging.h"
 #import "ScrollingStateStickyNode.h"
 #import "ScrollingTree.h"
+#import "ScrollingTreeFixedNode.h"
 #import "ScrollingTreeFrameScrollingNode.h"
 #import "ScrollingTreeOverflowScrollingNode.h"
 #import "WebCoreCALayerExtras.h"
@@ -67,21 +68,58 @@
 
 void ScrollingTreeStickyNode::applyLayerPositions()
 {
-    FloatRect constrainingRect;
+    auto computeLayerPositionForScrollingNode = [&](ScrollingTreeNode& scrollingNode) {
+        FloatRect constrainingRect;
+        if (is<ScrollingTreeFrameScrollingNode>(scrollingNode)) {
+            auto& frameScrollingNode = downcast<ScrollingTreeFrameScrollingNode>(scrollingNode);
+            constrainingRect = frameScrollingNode.layoutViewport();
+        } else {
+            auto& overflowScrollingNode = downcast<ScrollingTreeOverflowScrollingNode>(scrollingNode);
+            constrainingRect = FloatRect(overflowScrollingNode.currentScrollPosition(), m_constraints.constrainingRectAtLastLayout().size());
+        }
+        return m_constraints.layerPositionForConstrainingRect(constrainingRect);
+    };
 
-    auto* enclosingScrollingNode = parent();
-    if (is<ScrollingTreeOverflowScrollingNode>(enclosingScrollingNode))
-        constrainingRect = FloatRect(downcast<ScrollingTreeOverflowScrollingNode>(*enclosingScrollingNode).currentScrollPosition(), m_constraints.constrainingRectAtLastLayout().size());
-    else if (is<ScrollingTreeFrameScrollingNode>(enclosingScrollingNode))
-        constrainingRect = downcast<ScrollingTreeFrameScrollingNode>(enclosingScrollingNode)->layoutViewport();
-    else
-        return;
+    auto computeLayerPosition = [&] {
+        for (auto* ancestor = parent(); ancestor; ancestor = ancestor->parent()) {
+            if (is<ScrollingTreePositionedNode>(*ancestor)) {
+                auto& positioningAncestor = downcast<ScrollingTreePositionedNode>(*ancestor);
 
-    FloatPoint layerPosition = m_constraints.layerPositionForConstrainingRect(constrainingRect) - m_constraints.alignmentOffset();
+                // FIXME: Do we need to do anything for ScrollPositioningBehavior::Stationary?
+                if (positioningAncestor.scrollPositioningBehavior() == ScrollPositioningBehavior::Moves) {
+                    if (positioningAncestor.relatedOverflowScrollingNodes().isEmpty())
+                        break;
+                    auto overflowNode = scrollingTree().nodeForID(positioningAncestor.relatedOverflowScrollingNodes()[0]);
+                    if (!overflowNode)
+                        break;
 
-    LOG_WITH_STREAM(Scrolling, stream << "ScrollingTreeStickyNode " << scrollingNodeID() << " constrainingRect " << constrainingRect << " constrainingRectAtLastLayout " << m_constraints.constrainingRectAtLastLayout() << " last layer pos " << m_constraints.layerPositionAtLastLayout() << " layerPosition " << layerPosition);
+                    auto position = computeLayerPositionForScrollingNode(*overflowNode);
 
-    [m_layer _web_setLayerTopLeftPosition:layerPosition];
+                    if (positioningAncestor.layer() == m_layer) {
+                        // We'll also do the adjustment the positioning node would do.
+                        position -= positioningAncestor.scrollOffsetSinceLastCommit();
+                    }
+                    
+                    return position;
+                }
+            }
+            if (is<ScrollingTreeScrollingNode>(*ancestor))
+                return computeLayerPositionForScrollingNode(*ancestor);
+
+            if (is<ScrollingTreeFixedNode>(*ancestor) || is<ScrollingTreeStickyNode>(*ancestor)) {
+                // FIXME: Do we need scrolling tree nodes at all for nested cases?
+                return m_constraints.layerPositionAtLastLayout();
+            }
+        }
+        ASSERT_NOT_REACHED();
+        return m_constraints.layerPositionAtLastLayout();
+    };
+
+    auto layerPosition = computeLayerPosition();
+
+    LOG_WITH_STREAM(Scrolling, stream << "ScrollingTreeStickyNode " << scrollingNodeID() << " constrainingRectAtLastLayout " << m_constraints.constrainingRectAtLastLayout() << " last layer pos " << m_constraints.layerPositionAtLastLayout() << " layerPosition " << layerPosition);
+
+    [m_layer _web_setLayerTopLeftPosition:layerPosition - m_constraints.alignmentOffset()];
 }
 
 void ScrollingTreeStickyNode::dumpProperties(TextStream& ts, ScrollingStateTreeAsTextBehavior behavior) const
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to