Title: [289074] trunk
Revision
289074
Author
nmouchta...@apple.com
Date
2022-02-03 13:32:43 -0800 (Thu, 03 Feb 2022)

Log Message

Implement CSS overscroll-behavior for synchronous scroll
https://bugs.webkit.org/show_bug.cgi?id=222968

Reviewed by Simon Fraser.

Source/WebCore:

Tests: fast/scrolling/sync-scroll-overscroll-behavior-element.html
       fast/scrolling/sync-scroll-overscroll-behavior-iframe.html
       fast/scrolling/sync-scroll-overscroll-behavior-unscrollable-element.html
       fast/scrolling/sync-scroll-overscroll-behavior-unscrollable-iframe.html

Split up patch by Cathie Chen and Frederic Wang. Add function for blocking scroll chaining
and filtering scroll delta depending on the values of overscroll behavior for a scrollable
area. This patch is for synchronous scrolling only.

* page/EventHandler.cpp:
(WebCore::EventHandler::handleWheelEventInternal):
(WebCore::scrollViaNonPlatformEvent):
(WebCore::EventHandler::handleWheelEventInAppropriateEnclosingBox):
(WebCore::EventHandler::scrollableAreaCanHandleEvent):
* page/EventHandler.h:
* page/mac/EventHandlerMac.mm:
(WebCore::findEnclosingScrollableContainer):
* page/scrolling/ScrollingTreeScrollingNode.cpp:
(WebCore::ScrollingTreeScrollingNode::eventForPropagation const):
* platform/ScrollableArea.cpp:
(WebCore::ScrollableArea::deltaForPropagation const):
(WebCore::ScrollableArea::shouldBlockScrollPropagation const):
* platform/ScrollableArea.h:
(WebCore::ScrollableArea::horizontalOverscrollBehaviorPreventsPropagation const):
(WebCore::ScrollableArea::verticalOverscrollBehaviorPreventsPropagation const):
* platform/ScrollingEffectsController.h:
* platform/mac/ScrollAnimatorMac.mm:
(WebCore::ScrollAnimatorMac::allowsVerticalStretching const):
(WebCore::ScrollAnimatorMac::allowsHorizontalStretching const):
* platform/mac/ScrollingEffectsController.mm:
(WebCore::ScrollingEffectsController::wheelDeltaBiasingTowardsVertical):

LayoutTests:

* fast/scrolling/sync-scroll-overscroll-behavior-element-expected.txt: Added.
* fast/scrolling/sync-scroll-overscroll-behavior-element.html: Added.
* fast/scrolling/sync-scroll-overscroll-behavior-iframe-expected.txt: Added.
* fast/scrolling/sync-scroll-overscroll-behavior-iframe.html: Added.
* fast/scrolling/sync-scroll-overscroll-behavior-unscrollable-element-expected.txt: Added.
* fast/scrolling/sync-scroll-overscroll-behavior-unscrollable-element.html: Added.
* fast/scrolling/sync-scroll-overscroll-behavior-unscrollable-iframe-expected.txt: Added.
* fast/scrolling/sync-scroll-overscroll-behavior-unscrollable-iframe.html: Added.
* platform/mac-wk1/TestExpectations:
* platform/win/TestExpectations:

Modified Paths

Added Paths

Diff

Modified: trunk/LayoutTests/ChangeLog (289073 => 289074)


--- trunk/LayoutTests/ChangeLog	2022-02-03 21:30:22 UTC (rev 289073)
+++ trunk/LayoutTests/ChangeLog	2022-02-03 21:32:43 UTC (rev 289074)
@@ -1,3 +1,21 @@
+2022-02-03  Nikolaos Mouchtaris  <nmouchta...@apple.com>
+
+        Implement CSS overscroll-behavior for synchronous scroll
+        https://bugs.webkit.org/show_bug.cgi?id=222968
+
+        Reviewed by Simon Fraser.
+
+        * fast/scrolling/sync-scroll-overscroll-behavior-element-expected.txt: Added.
+        * fast/scrolling/sync-scroll-overscroll-behavior-element.html: Added.
+        * fast/scrolling/sync-scroll-overscroll-behavior-iframe-expected.txt: Added.
+        * fast/scrolling/sync-scroll-overscroll-behavior-iframe.html: Added.
+        * fast/scrolling/sync-scroll-overscroll-behavior-unscrollable-element-expected.txt: Added.
+        * fast/scrolling/sync-scroll-overscroll-behavior-unscrollable-element.html: Added.
+        * fast/scrolling/sync-scroll-overscroll-behavior-unscrollable-iframe-expected.txt: Added.
+        * fast/scrolling/sync-scroll-overscroll-behavior-unscrollable-iframe.html: Added.
+        * platform/mac-wk1/TestExpectations:
+        * platform/win/TestExpectations:
+
 2022-02-03  Kimmo Kinnunen  <kkinnu...@apple.com>
 
         ANGLE Metal and ANGLE OpenGL cannot be initialised one after the other

Added: trunk/LayoutTests/fast/scrolling/sync-scroll-overscroll-behavior-element-expected.txt (0 => 289074)


--- trunk/LayoutTests/fast/scrolling/sync-scroll-overscroll-behavior-element-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/fast/scrolling/sync-scroll-overscroll-behavior-element-expected.txt	2022-02-03 21:32:43 UTC (rev 289074)
@@ -0,0 +1,20 @@
+
+PASS Element horizontal scroll test with overscroll-behavior: auto auto.
+PASS Element vertical scroll test with overscroll-behavior: auto auto.
+PASS Element horizontal scroll test with overscroll-behavior: contain auto.
+PASS Element vertical scroll test with overscroll-behavior: contain auto.
+PASS Element horizontal scroll test with overscroll-behavior: none auto.
+PASS Element vertical scroll test with overscroll-behavior: none auto.
+PASS Element horizontal scroll test with overscroll-behavior: auto contain.
+PASS Element vertical scroll test with overscroll-behavior: auto contain.
+PASS Element horizontal scroll test with overscroll-behavior: contain contain.
+PASS Element vertical scroll test with overscroll-behavior: contain contain.
+PASS Element horizontal scroll test with overscroll-behavior: none contain.
+PASS Element vertical scroll test with overscroll-behavior: none contain.
+PASS Element horizontal scroll test with overscroll-behavior: auto none.
+PASS Element vertical scroll test with overscroll-behavior: auto none.
+PASS Element horizontal scroll test with overscroll-behavior: contain none.
+PASS Element vertical scroll test with overscroll-behavior: contain none.
+PASS Element horizontal scroll test with overscroll-behavior: none none.
+PASS Element vertical scroll test with overscroll-behavior: none none.
+

Added: trunk/LayoutTests/fast/scrolling/sync-scroll-overscroll-behavior-element.html (0 => 289074)


--- trunk/LayoutTests/fast/scrolling/sync-scroll-overscroll-behavior-element.html	                        (rev 0)
+++ trunk/LayoutTests/fast/scrolling/sync-scroll-overscroll-behavior-element.html	2022-02-03 21:32:43 UTC (rev 289074)
@@ -0,0 +1,86 @@
+<!doctype html>
+<script src=""
+<script src=""
+<script src=""
+<link rel="help" href=""
+<style>
+    .scrolling {
+        overflow: scroll;
+    }
+    .scrollContent {
+        width: 300px;
+        height: 300px;
+    }
+    #root {
+        width: 200px;
+        height: 200px;
+    }
+    #scroller {
+        width: 100px;
+        height: 100px;
+    }
+</style>
+<div id='root' class="scrolling">
+    <div id='scroller' class="scrolling">
+        <div class="scrollContent"></div>
+    </div>
+    <div class="scrollContent"></div>
+</div>
+
+<script>
+const root = document.getElementById('root');
+const scroller = document.getElementById('scroller');
+var overscrollDatas = [["auto", "auto", true, true],
+                        ["contain", "auto", false, true],
+                        ["none", "auto", false, true],
+                        ["auto", "contain", true, false],
+                        ["contain", "contain", false, false],
+                        ["none", "contain", false, false],
+                        ["auto", "none", true, false],
+                        ["contain", "none", false, false],
+                        ["none", "none", false, false]];
+
+function resetTest() {
+    // Try various methods to ensure the element position is reset immediately.
+    scroller.scrollLeft = 300;
+    scroller.scrollTop = 300;
+    scroller.scrollTo(300, 300);
+
+    root.scrollLeft = 0;
+    root.scrollTop = 0;
+    root.scrollTo(0, 0);
+}
+
+function startTest() {
+    overscrollDatas.forEach(([overscrollX, overscrollY, propagateX, propagateY]) => {
+        promise_test(() => {
+            resetTest();
+            scroller.style.overscrollBehaviorX = overscrollX;
+            scroller.style.overscrollBehaviorY = overscrollY;
+            document.body.clientWidth;
+
+            var x = scroller.clientWidth / 2;
+            var y = scroller.clientHeight / 2;
+            var delta = getDeltas("right");
+            return mouseWheelScrollAndWait(x, y, delta.X, delta.Y, delta.X, delta.Y).then(() => {
+                assert_equals(root.scrollLeft > 0, propagateX, 'Propagate horizontal scroll');
+            });
+        }, 'Element horizontal scroll test with overscroll-behavior: ' + overscrollX + ' ' + overscrollY + '.');
+
+        promise_test(() => {
+            resetTest();
+            scroller.style.overscrollBehaviorX = overscrollX;
+            scroller.style.overscrollBehaviorY = overscrollY;
+
+            var x = scroller.clientWidth / 2;
+            var y = scroller.clientHeight / 2;
+            var delta = getDeltas("down");
+            return mouseWheelScrollAndWait(x, y, delta.X, delta.Y, delta.X, delta.Y).then(() => {
+                assert_equals(root.scrollTop > 0, propagateY, 'Propagate vertical scroll');
+            });
+        }, 'Element vertical scroll test with overscroll-behavior: ' + overscrollX + ' ' + overscrollY + '.');
+    });
+}
+
+addEventListener("load", startTest);
+</script>
\ No newline at end of file

Added: trunk/LayoutTests/fast/scrolling/sync-scroll-overscroll-behavior-iframe-expected.txt (0 => 289074)


--- trunk/LayoutTests/fast/scrolling/sync-scroll-overscroll-behavior-iframe-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/fast/scrolling/sync-scroll-overscroll-behavior-iframe-expected.txt	2022-02-03 21:32:43 UTC (rev 289074)
@@ -0,0 +1,21 @@
+
+
+PASS Iframe horizontal scroll test with overscroll-behavior: auto auto.
+PASS Iframe vertical scroll test with overscroll-behavior: auto auto.
+PASS Iframe horizontal scroll test with overscroll-behavior: contain auto.
+PASS Iframe vertical scroll test with overscroll-behavior: contain auto.
+PASS Iframe horizontal scroll test with overscroll-behavior: none auto.
+PASS Iframe vertical scroll test with overscroll-behavior: none auto.
+PASS Iframe horizontal scroll test with overscroll-behavior: auto contain.
+PASS Iframe vertical scroll test with overscroll-behavior: auto contain.
+PASS Iframe horizontal scroll test with overscroll-behavior: contain contain.
+PASS Iframe vertical scroll test with overscroll-behavior: contain contain.
+PASS Iframe horizontal scroll test with overscroll-behavior: none contain.
+PASS Iframe vertical scroll test with overscroll-behavior: none contain.
+PASS Iframe horizontal scroll test with overscroll-behavior: auto none.
+PASS Iframe vertical scroll test with overscroll-behavior: auto none.
+PASS Iframe horizontal scroll test with overscroll-behavior: contain none.
+PASS Iframe vertical scroll test with overscroll-behavior: contain none.
+PASS Iframe horizontal scroll test with overscroll-behavior: none none.
+PASS Iframe vertical scroll test with overscroll-behavior: none none.
+

Added: trunk/LayoutTests/fast/scrolling/sync-scroll-overscroll-behavior-iframe.html (0 => 289074)


--- trunk/LayoutTests/fast/scrolling/sync-scroll-overscroll-behavior-iframe.html	                        (rev 0)
+++ trunk/LayoutTests/fast/scrolling/sync-scroll-overscroll-behavior-iframe.html	2022-02-03 21:32:43 UTC (rev 289074)
@@ -0,0 +1,85 @@
+<!doctype html>
+<script src=""
+<script src=""
+<script src=""
+<link rel="help" href=""
+<style>
+    .scrolling {
+        overflow: scroll;
+    }
+    .scrollContent {
+        width: 300px;
+        height: 300px;
+        background: linear-gradient(135deg, red, blue);
+    }
+    #root {
+        width: 200px;
+        height: 200px;
+    }
+    #scroller {
+        width: 100px;
+        height: 100px;
+        overscroll-behavior-y: auto;
+    }
+</style>
+<div id='root' class="scrolling">
+    <iframe id='scroller' srcdoc="<html><div style='width: 300px; height: 300px;'></div></html>
+        "></iframe>
+    <div class="scrollContent"></div>
+</div>
+
+<script>
+    const root = document.getElementById('root');
+    const scroller = document.getElementById('scroller');
+    var overscrollDatas = [["auto", "auto", true, true],
+                            ["contain", "auto", false, true],
+                            ["none", "auto", false, true],
+                            ["auto", "contain", true, false],
+                            ["contain", "contain", false, false],
+                            ["none", "contain", false, false],
+                            ["auto", "none", true, false],
+                            ["contain", "none", false, false],
+                            ["none", "none", false, false]];
+    function resetTest() {
+        // Try various methods to ensure the element position is reset immediately.
+        scroller.contentWindow.scrollX = 300;
+        scroller.contentWindow.scrollY = 300;
+        scroller.contentWindow.scrollTo(300, 300);
+        root.scrollLeft = 0;
+        root.scrollTop = 0;
+        root.scrollTo(0, 0);
+    }
+
+    function overscrollBehaviorTest() {
+        overscrollDatas.forEach(([overscrollX, overscrollY, propagateX, propagateY]) => {
+            promise_test(() => {
+                resetTest();
+                var target = scroller.contentWindow.document.getElementsByTagName("html")[0];
+                target.style.overscrollBehaviorX = overscrollX;
+                target.style.overscrollBehaviorY = overscrollY;
+                var x = scroller.clientWidth / 2;
+                var y = scroller.clientHeight / 2;
+                var delta = getDeltas("right");
+                return mouseWheelScrollAndWait(x, y, delta.X, delta.Y, delta.X, delta.Y).then(() => {
+                    assert_equals(root.scrollLeft > 0, propagateX, 'Propagate horizontal scroll');
+                });
+            }, 'Iframe horizontal scroll test with overscroll-behavior: ' + overscrollX + ' ' + overscrollY + '.');
+
+            promise_test(() => {
+                resetTest();
+                var target = scroller.contentWindow.document.getElementsByTagName("html")[0];
+                target.style.overscrollBehaviorX = overscrollX;
+                target.style.overscrollBehaviorY = overscrollY;
+
+                var x = scroller.clientWidth / 2;
+                var y = scroller.clientHeight / 2;
+                var delta = getDeltas("down");
+                return mouseWheelScrollAndWait(x, y, delta.X, delta.Y, delta.X, delta.Y).then(() => {
+                    assert_equals(root.scrollTop > 0, propagateY, 'Propagate vertical scroll');
+                });
+            }, 'Iframe vertical scroll test with overscroll-behavior: ' + overscrollX + ' ' + overscrollY + '.');
+        });
+    }
+
+    scroller.addEventListener("load", overscrollBehaviorTest);
+</script>
\ No newline at end of file

Added: trunk/LayoutTests/fast/scrolling/sync-scroll-overscroll-behavior-unscrollable-element-expected.txt (0 => 289074)


--- trunk/LayoutTests/fast/scrolling/sync-scroll-overscroll-behavior-unscrollable-element-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/fast/scrolling/sync-scroll-overscroll-behavior-unscrollable-element-expected.txt	2022-02-03 21:32:43 UTC (rev 289074)
@@ -0,0 +1,4 @@
+
+PASS Unscrollable element horizontal scroll test with overscroll-behavior: none.
+PASS Unscrollable element vertical scroll test with overscroll-behavior: none.
+

Added: trunk/LayoutTests/fast/scrolling/sync-scroll-overscroll-behavior-unscrollable-element.html (0 => 289074)


--- trunk/LayoutTests/fast/scrolling/sync-scroll-overscroll-behavior-unscrollable-element.html	                        (rev 0)
+++ trunk/LayoutTests/fast/scrolling/sync-scroll-overscroll-behavior-unscrollable-element.html	2022-02-03 21:32:43 UTC (rev 289074)
@@ -0,0 +1,66 @@
+<!doctype html>
+<script src=""
+<script src=""
+<script src=""
+<link rel="help" href=""
+<style>
+    .scrolling {
+        overflow: scroll;
+    }
+    .scrollContent {
+        width: 300px;
+        height: 300px;
+    }
+    #root {
+        width: 200px;
+        height: 200px;
+    }
+    #scroller {
+        width: 100px;
+        height: 100px;
+        overscroll-behavior: none;
+        overflow: visible;
+        background: linear-gradient(135deg, red, blue);
+    }
+</style>
+<div id="root" class="scrolling">
+    <div id="scroller">
+        <div class="scrollContent"></div>
+    </div>
+    <div class="scrollContent"></div>
+</div>
+
+<script>
+const root = document.getElementById('root');
+const scroller = document.getElementById('scroller');
+
+function resetTest() {
+    root.scrollLeft = 0;
+    root.scrollTop = 0;
+    root.scrollTo(0, 0);
+}
+
+function startTest() {
+        promise_test(() => {
+            resetTest();
+            var x = scroller.clientWidth / 2;
+            var y = scroller.clientHeight / 2;
+            var delta = getDeltas("right");
+            return mouseWheelScrollAndWait(x, y, delta.X, delta.Y, delta.X, delta.Y).then(() => {
+                assert_equals(root.scrollLeft > 0, true, 'Propagate horizontal scroll');
+            });
+        }, 'Unscrollable element horizontal scroll test with overscroll-behavior: none.');
+
+        promise_test(() => {
+            resetTest();
+            var x = scroller.clientWidth / 2;
+            var y = scroller.clientHeight / 2;
+            var delta = getDeltas("down");
+            return mouseWheelScrollAndWait(x, y, delta.X, delta.Y, delta.X, delta.Y).then(() => {
+                assert_equals(root.scrollTop > 0, true, 'Propagate vertical scroll');
+            });
+        }, 'Unscrollable element vertical scroll test with overscroll-behavior: none.');
+}
+
+addEventListener("load", startTest);
+</script>
\ No newline at end of file

Added: trunk/LayoutTests/fast/scrolling/sync-scroll-overscroll-behavior-unscrollable-iframe-expected.txt (0 => 289074)


--- trunk/LayoutTests/fast/scrolling/sync-scroll-overscroll-behavior-unscrollable-iframe-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/fast/scrolling/sync-scroll-overscroll-behavior-unscrollable-iframe-expected.txt	2022-02-03 21:32:43 UTC (rev 289074)
@@ -0,0 +1,5 @@
+
+
+PASS Unscrollable iframe horizontal scroll test with overscroll-behavior: none.
+PASS Unscrollable iframe vertical scroll test with overscroll-behavior: none.
+

Added: trunk/LayoutTests/fast/scrolling/sync-scroll-overscroll-behavior-unscrollable-iframe.html (0 => 289074)


--- trunk/LayoutTests/fast/scrolling/sync-scroll-overscroll-behavior-unscrollable-iframe.html	                        (rev 0)
+++ trunk/LayoutTests/fast/scrolling/sync-scroll-overscroll-behavior-unscrollable-iframe.html	2022-02-03 21:32:43 UTC (rev 289074)
@@ -0,0 +1,65 @@
+<!doctype html>
+<script src=""
+<script src=""
+<script src=""
+<link rel="help" href=""
+<style>
+    .scrolling {
+        overflow: scroll;
+    }
+    .scrollContent {
+        width: 300px;
+        height: 300px;
+    }
+    #root {
+        width: 200px;
+        height: 200px;
+    }
+    #scroller {
+        width: 200px;
+        height: 200px;
+        overscroll-behavior: none;
+        overflow: visible;
+        background: linear-gradient(135deg, red, blue);
+    }
+</style>
+<div id='root' class="scrolling">
+    <iframe id='scroller' srcdoc="<html style='overscroll-behavior: none;'><div style='width: 100px; height: 100px;'></div></html>
+        "></iframe>
+    <div class="scrollContent"></div>
+</div>
+
+<script>
+const root = document.getElementById('root');
+const scroller = document.getElementById('scroller');
+
+function resetTest() {
+    root.scrollLeft = 0;
+    root.scrollTop = 0;
+    root.scrollTo(0, 0);
+}
+
+function startTest() {
+        promise_test(() => {
+            resetTest();
+            var x = scroller.clientWidth / 2;
+            var y = scroller.clientHeight / 2;
+            var delta = getDeltas("right");
+            return mouseWheelScrollAndWait(x, y, delta.X, delta.Y, delta.X, delta.Y).then(() => {
+                assert_equals(root.scrollLeft > 0, true, 'Propagate horizontal scroll');
+            });
+        }, 'Unscrollable iframe horizontal scroll test with overscroll-behavior: none.');
+
+        promise_test(() => {
+            resetTest();
+            var x = scroller.clientWidth / 2;
+            var y = scroller.clientHeight / 2;
+            var delta = getDeltas("down");
+            return mouseWheelScrollAndWait(x, y, delta.X, delta.Y, delta.X, delta.Y).then(() => {
+                assert_equals(root.scrollTop > 0, true, 'Propagate vertical scroll');
+            });
+        }, 'Unscrollable iframe vertical scroll test with overscroll-behavior: none.');
+}
+
+addEventListener("load", startTest);
+</script>
\ No newline at end of file

Modified: trunk/LayoutTests/platform/ios/TestExpectations (289073 => 289074)


--- trunk/LayoutTests/platform/ios/TestExpectations	2022-02-03 21:30:22 UTC (rev 289073)
+++ trunk/LayoutTests/platform/ios/TestExpectations	2022-02-03 21:32:43 UTC (rev 289074)
@@ -814,6 +814,12 @@
 svg/text/selection-tripleclick.svg [ Skip ]
 imported/w3c/web-platform-tests/css/css-shadow-parts/invalidation-part-pseudo.html [ Skip ]
 
+# Overscroll-behavior on iOS needs SkyEcho19E179/StarE21E168a: https://bugs.webkit.org/show_bug.cgi?id=235852
+fast/scrolling/sync-scroll-overscroll-behavior-element.html [ Skip ]
+fast/scrolling/sync-scroll-overscroll-behavior-iframe.html [ Skip ]
+fast/scrolling/sync-scroll-overscroll-behavior-unscrollable-element.html [ Skip ]
+fast/scrolling/sync-scroll-overscroll-behavior-unscrollable-iframe.html [ Skip ]
+
 # IOS does not implement setAutomaticLinkDetectionEnabled
 editing/inserting/typing-space-to-trigger-smart-link.html [ Skip ]
 editing/inserting/smart-link-when-caret-is-moved-before-URL.html [ Skip ]

Modified: trunk/LayoutTests/platform/mac-wk1/TestExpectations (289073 => 289074)


--- trunk/LayoutTests/platform/mac-wk1/TestExpectations	2022-02-03 21:30:22 UTC (rev 289073)
+++ trunk/LayoutTests/platform/mac-wk1/TestExpectations	2022-02-03 21:32:43 UTC (rev 289074)
@@ -684,6 +684,10 @@
 imported/w3c/web-platform-tests/css/css-scroll-snap/scroll-snap-stop-change.html [ Failure ]
 css3/scroll-snap/scroll-padding-mainframe-paging.html [ Failure ]
 
+# Lack of ways to control the cross-frame scroll chaining in WK1.
+fast/scrolling/sync-scroll-overscroll-behavior-iframe.html [ Skip ]
+fast/scrolling/sync-scroll-overscroll-behavior-unscrollable-iframe.html [ Skip ]
+
 ### END OF (2) Failures without bug reports
 ########################################
 

Modified: trunk/LayoutTests/platform/win/TestExpectations (289073 => 289074)


--- trunk/LayoutTests/platform/win/TestExpectations	2022-02-03 21:30:22 UTC (rev 289073)
+++ trunk/LayoutTests/platform/win/TestExpectations	2022-02-03 21:32:43 UTC (rev 289074)
@@ -765,6 +765,12 @@
 http/tests/pointer-lock/ [ Skip ]
 fast/js-promise/js-promise-invalid-context-access.html [ Skip ]
 
+# Overscroll-behavior is not supported on Windows
+fast/scrolling/sync-scroll-overscroll-behavior-element.html [ Skip ]
+fast/scrolling/sync-scroll-overscroll-behavior-iframe.html [ Skip ]
+fast/scrolling/sync-scroll-overscroll-behavior-unscrollable-element.html [ Skip ]
+fast/scrolling/sync-scroll-overscroll-behavior-unscrollable-iframe.html [ Skip ]
+
 # Datalist is unsupported on Windows
 accessibility/datalist.html [ WontFix ]
 fast/forms/datalist [ WontFix ]

Modified: trunk/Source/WebCore/ChangeLog (289073 => 289074)


--- trunk/Source/WebCore/ChangeLog	2022-02-03 21:30:22 UTC (rev 289073)
+++ trunk/Source/WebCore/ChangeLog	2022-02-03 21:32:43 UTC (rev 289074)
@@ -1,3 +1,42 @@
+2022-02-03  Nikolaos Mouchtaris  <nmouchta...@apple.com>
+
+        Implement CSS overscroll-behavior for synchronous scroll
+        https://bugs.webkit.org/show_bug.cgi?id=222968
+
+        Reviewed by Simon Fraser.
+
+        Tests: fast/scrolling/sync-scroll-overscroll-behavior-element.html
+               fast/scrolling/sync-scroll-overscroll-behavior-iframe.html
+               fast/scrolling/sync-scroll-overscroll-behavior-unscrollable-element.html
+               fast/scrolling/sync-scroll-overscroll-behavior-unscrollable-iframe.html
+
+        Split up patch by Cathie Chen and Frederic Wang. Add function for blocking scroll chaining
+        and filtering scroll delta depending on the values of overscroll behavior for a scrollable
+        area. This patch is for synchronous scrolling only.
+
+        * page/EventHandler.cpp:
+        (WebCore::EventHandler::handleWheelEventInternal):
+        (WebCore::scrollViaNonPlatformEvent):
+        (WebCore::EventHandler::handleWheelEventInAppropriateEnclosingBox):
+        (WebCore::EventHandler::scrollableAreaCanHandleEvent):
+        * page/EventHandler.h:
+        * page/mac/EventHandlerMac.mm:
+        (WebCore::findEnclosingScrollableContainer):
+        * page/scrolling/ScrollingTreeScrollingNode.cpp:
+        (WebCore::ScrollingTreeScrollingNode::eventForPropagation const):
+        * platform/ScrollableArea.cpp:
+        (WebCore::ScrollableArea::deltaForPropagation const):
+        (WebCore::ScrollableArea::shouldBlockScrollPropagation const):
+        * platform/ScrollableArea.h:
+        (WebCore::ScrollableArea::horizontalOverscrollBehaviorPreventsPropagation const):
+        (WebCore::ScrollableArea::verticalOverscrollBehaviorPreventsPropagation const):
+        * platform/ScrollingEffectsController.h:
+        * platform/mac/ScrollAnimatorMac.mm:
+        (WebCore::ScrollAnimatorMac::allowsVerticalStretching const):
+        (WebCore::ScrollAnimatorMac::allowsHorizontalStretching const):
+        * platform/mac/ScrollingEffectsController.mm:
+        (WebCore::ScrollingEffectsController::wheelDeltaBiasingTowardsVertical):
+
 2022-02-03  Kimmo Kinnunen  <kkinnu...@apple.com>
 
         ANGLE Metal and ANGLE OpenGL cannot be initialised one after the other

Modified: trunk/Source/WebCore/page/EventHandler.cpp (289073 => 289074)


--- trunk/Source/WebCore/page/EventHandler.cpp	2022-02-03 21:30:22 UTC (rev 289073)
+++ trunk/Source/WebCore/page/EventHandler.cpp	2022-02-03 21:32:43 UTC (rev 289074)
@@ -3027,11 +3027,17 @@
     if (allowScrolling)
         allowScrolling = m_frame.page()->scrollLatchingController().latchingAllowsScrollingInFrame(m_frame, scrollableArea);
 #endif
+    auto adjustedWheelEvent = event;
+    auto filteredDelta = adjustedWheelEvent.delta();
+    filteredDelta = view->deltaForPropagation(filteredDelta);
+    if (view->shouldBlockScrollPropagation(filteredDelta))
+        return true;
 
     if (allowScrolling) {
         // FIXME: processWheelEventForScrolling() is only called for FrameView scrolling, not overflow scrolling, which is confusing.
-        handledEvent = processWheelEventForScrolling(event, scrollableArea, handling);
-        processWheelEventForScrollSnap(event, scrollableArea);
+        adjustedWheelEvent = adjustedWheelEvent.copyWithDeltaAndVelocity(filteredDelta, adjustedWheelEvent.scrollingVelocity());
+        handledEvent = processWheelEventForScrolling(adjustedWheelEvent, scrollableArea, handling);
+        processWheelEventForScrollSnap(adjustedWheelEvent, scrollableArea);
     }
 
     return handledEvent;
@@ -3050,18 +3056,19 @@
 
 static bool scrollViaNonPlatformEvent(ScrollableArea& scrollableArea, const WheelEvent& wheelEvent)
 {
+    auto filteredDelta = FloatSize(wheelEvent.deltaX(), wheelEvent.deltaY());
+    filteredDelta = scrollableArea.deltaForPropagation(filteredDelta);
     ScrollGranularity scrollGranularity = wheelGranularityToScrollGranularity(wheelEvent.deltaMode());
     bool didHandleWheelEvent = false;
-    if (float absoluteDelta = std::abs(wheelEvent.deltaX()))
-        didHandleWheelEvent |= scrollableArea.scroll(wheelEvent.deltaX() > 0 ? ScrollRight : ScrollLeft, scrollGranularity, absoluteDelta);
+    if (float absoluteDelta = std::abs(filteredDelta.width()))
+        didHandleWheelEvent |= scrollableArea.scroll(filteredDelta.width() > 0 ? ScrollRight : ScrollLeft, scrollGranularity, absoluteDelta);
 
-    if (float absoluteDelta = std::abs(wheelEvent.deltaY()))
-        didHandleWheelEvent |= scrollableArea.scroll(wheelEvent.deltaY() > 0 ? ScrollDown : ScrollUp, scrollGranularity, absoluteDelta);
-
+    if (float absoluteDelta = std::abs(filteredDelta.height()))
+        didHandleWheelEvent |= scrollableArea.scroll(filteredDelta.height() > 0 ? ScrollDown : ScrollUp, scrollGranularity, absoluteDelta);
     return didHandleWheelEvent;
 }
 
-bool EventHandler::handleWheelEventInAppropriateEnclosingBox(Node* startNode, const WheelEvent& wheelEvent, const FloatSize& filteredPlatformDelta, const FloatSize& filteredVelocity, OptionSet<EventHandling> eventHandling)
+bool EventHandler::handleWheelEventInAppropriateEnclosingBox(Node* startNode, const WheelEvent& wheelEvent, FloatSize& filteredPlatformDelta, const FloatSize& filteredVelocity, OptionSet<EventHandling> eventHandling)
 {
     bool shouldHandleEvent = wheelEvent.deltaX() || wheelEvent.deltaY();
 #if ENABLE(WHEEL_EVENT_LATCHING)
@@ -3091,6 +3098,12 @@
     };
 
     RenderBox* currentEnclosingBox = &initialEnclosingBox;
+#if PLATFORM(MAC)
+    auto biasedDelta = ScrollingEffectsController::wheelDeltaBiasingTowardsVertical(FloatSize(wheelEvent.deltaX(), wheelEvent.deltaY()));
+#else
+    auto biasedDelta = FloatSize(wheelEvent.deltaX(), wheelEvent.deltaY());
+#endif
+    
     while (currentEnclosingBox) {
         if (auto* boxScrollableArea = scrollableAreaForBox(*currentEnclosingBox)) {
             auto platformEvent = wheelEvent.underlyingPlatformEvent();
@@ -3103,6 +3116,10 @@
 
             if (scrollingWasHandled)
                 return true;
+            
+            biasedDelta = boxScrollableArea->deltaForPropagation(biasedDelta);
+            if (boxScrollableArea->shouldBlockScrollPropagation(biasedDelta))
+                return true;
         }
 
         currentEnclosingBox = currentEnclosingBox->containingBlock();
@@ -3115,7 +3132,7 @@
 bool EventHandler::scrollableAreaCanHandleEvent(const PlatformWheelEvent& wheelEvent, ScrollableArea& scrollableArea)
 {
 #if PLATFORM(MAC)
-    auto biasedDelta = ScrollingEffectsController::wheelDeltaBiasingTowardsVertical(wheelEvent);
+    auto biasedDelta = ScrollingEffectsController::wheelDeltaBiasingTowardsVertical(wheelEvent.delta());
 #else
     auto biasedDelta = wheelEvent.delta();
 #endif
@@ -3127,6 +3144,8 @@
     auto horizontalSide = ScrollableArea::targetSideForScrollDelta(-biasedDelta, ScrollEventAxis::Horizontal);
     if (horizontalSide && !scrollableArea.isPinnedOnSide(*horizontalSide))
         return true;
+    if (scrollableArea.shouldBlockScrollPropagation(biasedDelta) && scrollableArea.overscrollBehaviorAllowsRubberBand())
+        return true;
 
     return false;
 }

Modified: trunk/Source/WebCore/page/EventHandler.h (289073 => 289074)


--- trunk/Source/WebCore/page/EventHandler.h	2022-02-03 21:30:22 UTC (rev 289073)
+++ trunk/Source/WebCore/page/EventHandler.h	2022-02-03 21:32:43 UTC (rev 289074)
@@ -475,7 +475,7 @@
     void processWheelEventForScrollSnap(const PlatformWheelEvent&, const WeakPtr<ScrollableArea>&);
     bool completeWidgetWheelEvent(const PlatformWheelEvent&, const WeakPtr<Widget>&, const WeakPtr<ScrollableArea>&);
 
-    bool handleWheelEventInAppropriateEnclosingBox(Node* startNode, const WheelEvent&, const FloatSize& filteredPlatformDelta, const FloatSize& filteredVelocity, OptionSet<EventHandling>);
+    bool handleWheelEventInAppropriateEnclosingBox(Node* startNode, const WheelEvent&, FloatSize& filteredPlatformDelta, const FloatSize& filteredVelocity, OptionSet<EventHandling>);
 
     bool handleWheelEventInScrollableArea(const PlatformWheelEvent&, ScrollableArea&, OptionSet<EventHandling>);
     std::optional<WheelScrollGestureState> updateWheelGestureState(const PlatformWheelEvent&, OptionSet<EventHandling>);

Modified: trunk/Source/WebCore/page/mac/EventHandlerMac.mm (289073 => 289074)


--- trunk/Source/WebCore/page/mac/EventHandlerMac.mm	2022-02-03 21:30:22 UTC (rev 289073)
+++ trunk/Source/WebCore/page/mac/EventHandlerMac.mm	2022-02-03 21:32:43 UTC (rev 289074)
@@ -815,7 +815,10 @@
         auto* scrollableArea = scrollableAreaForBox(*box);
         if (!scrollableArea)
             continue;
-
+        
+        if (scrollableArea->shouldBlockScrollPropagation(wheelEvent.delta()))
+            return candidate;
+        
         if (wheelEvent.phase() == PlatformWheelEventPhase::MayBegin || wheelEvent.phase() == PlatformWheelEventPhase::Cancelled)
             return candidate;
 

Modified: trunk/Source/WebCore/page/scrolling/ScrollingTreeScrollingNode.cpp (289073 => 289074)


--- trunk/Source/WebCore/page/scrolling/ScrollingTreeScrollingNode.cpp	2022-02-03 21:30:22 UTC (rev 289073)
+++ trunk/Source/WebCore/page/scrolling/ScrollingTreeScrollingNode.cpp	2022-02-03 21:32:43 UTC (rev 289074)
@@ -128,7 +128,7 @@
     // The stateless wheel event doesn't trigger rubber-band.
     // Also rubberband when we should block scroll propagation
     // at this node, which has overscroll behavior that is not none.
-    return (isLatchedNode() || eventTargeting == EventTargeting::NodeOnly || (isRootNode() && !wheelEvent.isNonGestureEvent()) || (shouldBlockScrollPropagation(wheelEvent.delta()) && overscrollBehaviorAllowRubberBand()));
+    return (isLatchedNode() || eventTargeting == EventTargeting::NodeOnly || (isRootNode() && !wheelEvent.isNonGestureEvent()) || (shouldBlockScrollPropagation(wheelEvent.delta()) && overscrollBehaviorAllowsRubberBand()));
 }
 
 bool ScrollingTreeScrollingNode::canHandleWheelEvent(const PlatformWheelEvent& wheelEvent, EventTargeting eventTargeting) const
@@ -404,7 +404,7 @@
 {
     auto filteredDelta = wheelEvent.delta();
 #if PLATFORM(MAC)
-    auto biasedDelta = ScrollingEffectsController::wheelDeltaBiasingTowardsVertical(wheelEvent);
+    auto biasedDelta = ScrollingEffectsController::wheelDeltaBiasingTowardsVertical(wheelEvent.delta());
 #else
     auto biasedDelta = wheelEvent.delta();
 #endif

Modified: trunk/Source/WebCore/page/scrolling/ScrollingTreeScrollingNode.h (289073 => 289074)


--- trunk/Source/WebCore/page/scrolling/ScrollingTreeScrollingNode.h	2022-02-03 21:30:22 UTC (rev 289073)
+++ trunk/Source/WebCore/page/scrolling/ScrollingTreeScrollingNode.h	2022-02-03 21:32:43 UTC (rev 289074)
@@ -165,7 +165,7 @@
     bool verticalOverscrollBehaviorPreventsPropagation() const { return m_scrollableAreaParameters.verticalOverscrollBehavior != OverscrollBehavior::Auto; }
     PlatformWheelEvent eventForPropagation(const PlatformWheelEvent&) const;
     bool shouldBlockScrollPropagation(const FloatSize&) const;
-    bool overscrollBehaviorAllowRubberBand() const { return m_scrollableAreaParameters.horizontalOverscrollBehavior != OverscrollBehavior::None ||  m_scrollableAreaParameters.verticalOverscrollBehavior != OverscrollBehavior::None; }
+    bool overscrollBehaviorAllowsRubberBand() const { return m_scrollableAreaParameters.horizontalOverscrollBehavior != OverscrollBehavior::None ||  m_scrollableAreaParameters.verticalOverscrollBehavior != OverscrollBehavior::None; }
     bool shouldRubberBand(const PlatformWheelEvent&, EventTargeting) const;
     void dumpProperties(WTF::TextStream&, OptionSet<ScrollingStateTreeAsTextBehavior>) const override;
 

Modified: trunk/Source/WebCore/platform/ScrollableArea.cpp (289073 => 289074)


--- trunk/Source/WebCore/platform/ScrollableArea.cpp	2022-02-03 21:30:22 UTC (rev 289073)
+++ trunk/Source/WebCore/platform/ScrollableArea.cpp	2022-02-03 21:32:43 UTC (rev 289074)
@@ -807,4 +807,22 @@
     return ts;
 }
 
+FloatSize ScrollableArea::deltaForPropagation(const FloatSize& biasedDelta) const
+{
+    auto filteredDelta = biasedDelta;
+    if (horizontalOverscrollBehaviorPreventsPropagation())
+        filteredDelta.setWidth(0);
+    if (verticalOverscrollBehaviorPreventsPropagation())
+        filteredDelta.setHeight(0);
+    return filteredDelta;
+}
+
+bool ScrollableArea::shouldBlockScrollPropagation(const FloatSize& biasedDelta) const
+{
+    return ((horizontalOverscrollBehaviorPreventsPropagation() || verticalOverscrollBehaviorPreventsPropagation())
+        && ((horizontalOverscrollBehaviorPreventsPropagation() && verticalOverscrollBehaviorPreventsPropagation())
+        || (horizontalOverscrollBehaviorPreventsPropagation() && !biasedDelta.height()) || (verticalOverscrollBehaviorPreventsPropagation()
+        && !biasedDelta.width())));
+}
+
 } // namespace WebCore

Modified: trunk/Source/WebCore/platform/ScrollableArea.h (289073 => 289074)


--- trunk/Source/WebCore/platform/ScrollableArea.h	2022-02-03 21:30:22 UTC (rev 289073)
+++ trunk/Source/WebCore/platform/ScrollableArea.h	2022-02-03 21:32:43 UTC (rev 289074)
@@ -373,6 +373,12 @@
     }
 
     virtual void didStartScrollAnimation() { }
+    
+    bool horizontalOverscrollBehaviorPreventsPropagation() const { return horizontalOverscrollBehavior() != OverscrollBehavior::Auto; }
+    bool verticalOverscrollBehaviorPreventsPropagation() const { return verticalOverscrollBehavior() != OverscrollBehavior::Auto; }
+    bool overscrollBehaviorAllowsRubberBand() const { return horizontalOverscrollBehavior() != OverscrollBehavior::None || verticalOverscrollBehavior() != OverscrollBehavior::None; }
+    bool shouldBlockScrollPropagation(const FloatSize&) const;
+    FloatSize deltaForPropagation(const FloatSize&) const;
 
 protected:
     WEBCORE_EXPORT ScrollableArea();

Modified: trunk/Source/WebCore/platform/ScrollingEffectsController.h (289073 => 289074)


--- trunk/Source/WebCore/platform/ScrollingEffectsController.h	2022-02-03 21:30:22 UTC (rev 289073)
+++ trunk/Source/WebCore/platform/ScrollingEffectsController.h	2022-02-03 21:32:43 UTC (rev 289074)
@@ -174,7 +174,7 @@
     bool isUserScrollInProgress() const;
 
 #if PLATFORM(MAC)
-    static FloatSize wheelDeltaBiasingTowardsVertical(const PlatformWheelEvent&);
+    static FloatSize wheelDeltaBiasingTowardsVertical(const FloatSize&);
 
     // Returns true if handled.
     bool processWheelEventForScrollSnap(const PlatformWheelEvent&);

Modified: trunk/Source/WebCore/platform/mac/ScrollAnimatorMac.mm (289073 => 289074)


--- trunk/Source/WebCore/platform/mac/ScrollAnimatorMac.mm	2022-02-03 21:30:22 UTC (rev 289073)
+++ trunk/Source/WebCore/platform/mac/ScrollAnimatorMac.mm	2022-02-03 21:32:43 UTC (rev 289074)
@@ -105,6 +105,9 @@
 
 bool ScrollAnimatorMac::allowsVerticalStretching(const PlatformWheelEvent& wheelEvent) const
 {
+    if (m_scrollableArea.verticalOverscrollBehavior() == OverscrollBehavior::None)
+        return false;
+    
     switch (m_scrollableArea.verticalScrollElasticity()) {
     case ScrollElasticity::Automatic: {
         Scrollbar* hScroller = m_scrollableArea.horizontalScrollbar();
@@ -128,6 +131,9 @@
 
 bool ScrollAnimatorMac::allowsHorizontalStretching(const PlatformWheelEvent& wheelEvent) const
 {
+    if (m_scrollableArea.horizontalOverscrollBehavior() == OverscrollBehavior::None)
+        return false;
+    
     switch (m_scrollableArea.horizontalScrollElasticity()) {
     case ScrollElasticity::Automatic: {
         Scrollbar* hScroller = m_scrollableArea.horizontalScrollbar();

Modified: trunk/Source/WebCore/platform/mac/ScrollingEffectsController.mm (289073 => 289074)


--- trunk/Source/WebCore/platform/mac/ScrollingEffectsController.mm	2022-02-03 21:30:22 UTC (rev 289073)
+++ trunk/Source/WebCore/platform/mac/ScrollingEffectsController.mm	2022-02-03 21:32:43 UTC (rev 289074)
@@ -345,9 +345,9 @@
     return canStartAnimation;
 }
 
-FloatSize ScrollingEffectsController::wheelDeltaBiasingTowardsVertical(const PlatformWheelEvent& wheelEvent)
+FloatSize ScrollingEffectsController::wheelDeltaBiasingTowardsVertical(const FloatSize& wheelDelta)
 {
-    return deltaAlignedToDominantAxis(wheelEvent.delta());
+    return deltaAlignedToDominantAxis(wheelDelta);
 }
 
 void ScrollingEffectsController::updateRubberBandAnimatingState()
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to