Title: [258044] trunk
Revision
258044
Author
[email protected]
Date
2020-03-06 17:45:55 -0800 (Fri, 06 Mar 2020)

Log Message

Hit-test CALayers on the scrolling thread for async frame/overflow scrolling
https://bugs.webkit.org/show_bug.cgi?id=208740
<rdar://problem/48028836>

Reviewed by Tim Horton.

Source/WebCore:

Implement hit-testing in the scrolling thread so we can determine which overflow/subframe
to scroll without hitting the main thread.

ScrollingTreeMac overrides scrollingNodeForPoint() and hit-tests through CALayers, starting at the
root content layer. Locking ensures that the CALayer tree doesn't change while we're hit-testing it.
We collect layers for the given point in back-to-front order much like the iOS code _web_findDescendantViewAtPoint
(too different to share though), and consult event regions on PlatformCALayerCocoa's to determine if the
point is inside the part of the layer that should receive events.

To handle the complex stacking/containing block cases, isScrolledBy() consults the scrolling tree.

For testing, fix it so that multiple calls to monitorWheelEvents() in a single test each start
with clean state.

Tests: fast/scrolling/mac/absolute-in-overflow-scroll.html
       fast/scrolling/mac/async-scroll-overflow.html
       fast/scrolling/mac/move-node-in-overflow-scroll.html
       fast/scrolling/mac/overlapped-overflow-scroll.html

* page/scrolling/ScrollingTree.cpp:
(WebCore::ScrollingTree::handleWheelEvent):
(WebCore::ScrollingTree::scrollingNodeForPoint):
* page/scrolling/ScrollingTree.h:
* page/scrolling/mac/ScrollingTreeFrameScrollingNodeMac.h:
* page/scrolling/mac/ScrollingTreeMac.h:
* page/scrolling/mac/ScrollingTreeMac.mm:
(collectDescendantLayersAtPoint):
(scrollingNodeIDForLayer):
(isScrolledBy):
(ScrollingTreeMac::scrollingNodeForPoint):
* testing/js/WebCoreTestSupport.cpp:
(WebCoreTestSupport::monitorWheelEvents): Make sure that each call to eventSender.monitorWheelEvents() clears previous state.

Source/WebKit:

Make sure that each call to eventSender.monitorWheelEvents() clears previous state.

* WebProcess/InjectedBundle/API/c/WKBundlePage.cpp:
(WKBundlePageStartMonitoringScrollOperations):

LayoutTests:

Add some UIHelper functions for mousewheel scrolling, and use them in new tests.
Fix some old malformed expectations.

* fast/scrolling/ios/hit-testing-iframe-001-expected.html: Was malformed.
* fast/scrolling/ios/hit-testing-iframe-002-expected.html:
* fast/scrolling/ios/hit-testing-iframe-003-expected.html:
* fast/scrolling/ios/hit-testing-iframe-004-expected.html:
* fast/scrolling/ios/hit-testing-iframe-005-expected.html:
* fast/scrolling/ios/hit-testing-iframe-006-expected.html:
* fast/scrolling/mac/absolute-in-overflow-scroll-expected.txt: Added.
* fast/scrolling/mac/absolute-in-overflow-scroll.html: Added.
* fast/scrolling/mac/async-scroll-overflow-expected.txt: Added.
* fast/scrolling/mac/async-scroll-overflow.html: Added.
* fast/scrolling/mac/move-node-in-overflow-scroll-expected.txt: Added.
* fast/scrolling/mac/move-node-in-overflow-scroll.html: Added.
* fast/scrolling/mac/overlapped-overflow-scroll-expected.txt: Added.
* fast/scrolling/mac/overlapped-overflow-scroll.html: Added.
* resources/ui-helper.js:
(window.UIHelper.async mouseWheelScrollAt):
(window.UIHelper.async animationFrame):

Modified Paths

Added Paths

Diff

Modified: trunk/LayoutTests/ChangeLog (258043 => 258044)


--- trunk/LayoutTests/ChangeLog	2020-03-07 01:44:56 UTC (rev 258043)
+++ trunk/LayoutTests/ChangeLog	2020-03-07 01:45:55 UTC (rev 258044)
@@ -58,12 +58,41 @@
 
 2020-03-06  Simon Fraser  <[email protected]>
 
+        Hit-test CALayers on the scrolling thread for async frame/overflow scrolling
+        https://bugs.webkit.org/show_bug.cgi?id=208740
+        <rdar://problem/48028836>
+
+        Reviewed by Tim Horton.
+
+        Add some UIHelper functions for mousewheel scrolling, and use them in new tests.
+        Fix some old malformed expectations.
+
+        * fast/scrolling/ios/hit-testing-iframe-001-expected.html: Was malformed.
+        * fast/scrolling/ios/hit-testing-iframe-002-expected.html:
+        * fast/scrolling/ios/hit-testing-iframe-003-expected.html:
+        * fast/scrolling/ios/hit-testing-iframe-004-expected.html:
+        * fast/scrolling/ios/hit-testing-iframe-005-expected.html:
+        * fast/scrolling/ios/hit-testing-iframe-006-expected.html:
+        * fast/scrolling/mac/absolute-in-overflow-scroll-expected.txt: Added.
+        * fast/scrolling/mac/absolute-in-overflow-scroll.html: Added.
+        * fast/scrolling/mac/async-scroll-overflow-expected.txt: Added.
+        * fast/scrolling/mac/async-scroll-overflow.html: Added.
+        * fast/scrolling/mac/move-node-in-overflow-scroll-expected.txt: Added.
+        * fast/scrolling/mac/move-node-in-overflow-scroll.html: Added.
+        * fast/scrolling/mac/overlapped-overflow-scroll-expected.txt: Added.
+        * fast/scrolling/mac/overlapped-overflow-scroll.html: Added.
+        * resources/ui-helper.js:
+        (window.UIHelper.async mouseWheelScrollAt):
+        (window.UIHelper.async animationFrame):
+
+2020-03-06  Simon Fraser  <[email protected]>
+
         Move synchronousScrollingReasons to ScrollingTreeScrollingNode
         https://bugs.webkit.org/show_bug.cgi?id=208721
 
         Reviewed by Antti Koivisto.
 
-		Rebaselined.
+        Rebaselined.
 
         * tiled-drawing/scrolling/scrolling-tree-slow-scrolling-expected.txt:
 

Modified: trunk/LayoutTests/fast/scrolling/ios/hit-testing-iframe-001-expected.html (258043 => 258044)


--- trunk/LayoutTests/fast/scrolling/ios/hit-testing-iframe-001-expected.html	2020-03-07 01:44:56 UTC (rev 258043)
+++ trunk/LayoutTests/fast/scrolling/ios/hit-testing-iframe-001-expected.html	2020-03-07 01:45:55 UTC (rev 258044)
@@ -1,5 +1,5 @@
 <!DOCTYPE html>
-<html
+<html>
   <head>
     <title>Hit testing of iframe</title>
   </head>

Modified: trunk/LayoutTests/fast/scrolling/ios/hit-testing-iframe-002-expected.html (258043 => 258044)


--- trunk/LayoutTests/fast/scrolling/ios/hit-testing-iframe-002-expected.html	2020-03-07 01:44:56 UTC (rev 258043)
+++ trunk/LayoutTests/fast/scrolling/ios/hit-testing-iframe-002-expected.html	2020-03-07 01:45:55 UTC (rev 258044)
@@ -1,5 +1,5 @@
 <!DOCTYPE html>
-<html
+<html>
   <head>
     <title>Hit testing of iframe</title>
   </head>

Modified: trunk/LayoutTests/fast/scrolling/ios/hit-testing-iframe-003-expected.html (258043 => 258044)


--- trunk/LayoutTests/fast/scrolling/ios/hit-testing-iframe-003-expected.html	2020-03-07 01:44:56 UTC (rev 258043)
+++ trunk/LayoutTests/fast/scrolling/ios/hit-testing-iframe-003-expected.html	2020-03-07 01:45:55 UTC (rev 258044)
@@ -1,5 +1,5 @@
 <!DOCTYPE html>
-<html
+<html>
   <head>
     <title>Hit testing of iframe</title>
   </head>

Modified: trunk/LayoutTests/fast/scrolling/ios/hit-testing-iframe-004-expected.html (258043 => 258044)


--- trunk/LayoutTests/fast/scrolling/ios/hit-testing-iframe-004-expected.html	2020-03-07 01:44:56 UTC (rev 258043)
+++ trunk/LayoutTests/fast/scrolling/ios/hit-testing-iframe-004-expected.html	2020-03-07 01:45:55 UTC (rev 258044)
@@ -1,5 +1,5 @@
 <!DOCTYPE html>
-<html
+<html>
   <head>
     <title>Hit testing of iframe</title>
   </head>

Modified: trunk/LayoutTests/fast/scrolling/ios/hit-testing-iframe-005-expected.html (258043 => 258044)


--- trunk/LayoutTests/fast/scrolling/ios/hit-testing-iframe-005-expected.html	2020-03-07 01:44:56 UTC (rev 258043)
+++ trunk/LayoutTests/fast/scrolling/ios/hit-testing-iframe-005-expected.html	2020-03-07 01:45:55 UTC (rev 258044)
@@ -1,5 +1,5 @@
 <!DOCTYPE html>
-<html
+<html>
   <head>
     <title>Hit testing of iframe</title>
   </head>

Modified: trunk/LayoutTests/fast/scrolling/ios/hit-testing-iframe-006-expected.html (258043 => 258044)


--- trunk/LayoutTests/fast/scrolling/ios/hit-testing-iframe-006-expected.html	2020-03-07 01:44:56 UTC (rev 258043)
+++ trunk/LayoutTests/fast/scrolling/ios/hit-testing-iframe-006-expected.html	2020-03-07 01:45:55 UTC (rev 258044)
@@ -1,5 +1,5 @@
 <!DOCTYPE html>
-<html
+<html>
   <head>
     <title>Hit testing of iframe</title>
   </head>

Added: trunk/LayoutTests/fast/scrolling/mac/absolute-in-overflow-scroll-expected.txt (0 => 258044)


--- trunk/LayoutTests/fast/scrolling/mac/absolute-in-overflow-scroll-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/fast/scrolling/mac/absolute-in-overflow-scroll-expected.txt	2020-03-07 01:45:55 UTC (rev 258044)
@@ -0,0 +1,12 @@
+
+Test scroll over content
+PASS overflowScrollEventCount > 0 is true
+PASS windowScrollEventCount == 0 is true
+
+Test scroll over absolute element
+PASS overflowScrollEventCount == 0 is true
+PASS windowScrollEventCount > 0 is true
+PASS successfullyParsed is true
+
+TEST COMPLETE
+

Added: trunk/LayoutTests/fast/scrolling/mac/absolute-in-overflow-scroll.html (0 => 258044)


--- trunk/LayoutTests/fast/scrolling/mac/absolute-in-overflow-scroll.html	                        (rev 0)
+++ trunk/LayoutTests/fast/scrolling/mac/absolute-in-overflow-scroll.html	2020-03-07 01:45:55 UTC (rev 258044)
@@ -0,0 +1,113 @@
+<!DOCTYPE html> <!-- webkit-test-runner [ internal:AsyncOverflowScrollingEnabled=true ] -->
+<html>
+<head>
+    <style>
+        body {
+            height: 1000px;
+        }
+        .container {
+            position: absolute; /* Containing block for the inner absolute */
+            top: 10px;
+            left: 10px;
+        }
+        .scroller {
+            height: 300px;
+            width: 300px;
+            border: 20px solid gray;
+            padding: 5px;
+            overflow: scroll;
+            opacity: 0.8; /* Force stackingc context */
+        }
+        .content {
+            width: 200%;
+            height: 300%;
+        }
+        
+        .absolute {
+            position: absolute;
+            top: 100px;
+            left: 50px;
+            height: 200px;
+            width: 200px;
+            background-color: gray;
+        }
+    </style>
+    <script src=""
+    <script src=""
+    <script>
+        var jsTestIsAsync = true;
+
+        var scroller;
+        var overflowScrollEventCount = 0;
+        var windowScrollEventCount = 0;
+
+        async function resetScrollPositions()
+        {
+            window.scrollTop = 0;
+            scroller.scrollTop = 0;
+            
+            // Wait for scroll events to fire.
+            await UIHelper.animationFrame();
+
+            overflowScrollEventCount = 0;
+            windowScrollEventCount = 0;
+        }
+        
+        async function testScrollOverContent()
+        {
+            debug('');
+            debug('Test scroll over content');
+            await resetScrollPositions();
+            await UIHelper.mouseWheelScrollAt(50, 50);
+
+            shouldBe('overflowScrollEventCount > 0', 'true');
+            shouldBe('windowScrollEventCount == 0', 'true');
+        }
+
+        async function testScrollOverAbsolute()
+        {
+            debug('');
+            debug('Test scroll over absolute element');
+            await resetScrollPositions();
+            await UIHelper.mouseWheelScrollAt(100, 150);
+            
+            shouldBe('overflowScrollEventCount == 0', 'true');
+            shouldBe('windowScrollEventCount > 0', 'true');
+        }
+
+        async function scrollTest()
+        {
+            await testScrollOverContent();
+            await testScrollOverAbsolute();
+
+            finishJSTest();
+        }
+
+        window.addEventListener('load', () => {
+            scroller = document.querySelector('.scroller');
+            scroller.addEventListener('scroll', () => {
+                ++overflowScrollEventCount;
+            }, false);
+
+            window.addEventListener('scroll', () => {
+                ++windowScrollEventCount;
+            }, false);
+
+            setTimeout(scrollTest, 0);
+        }, false);
+
+        var successfullyParsed = true;
+    </script>
+</head>
+<body>
+    <div class="container">
+        <div class="scroller">
+            <div class="content">
+                <div class="absolute">
+                </div>
+            </div>
+        </div>
+    </div>
+    <script src=""
+</body>
+</html>

Added: trunk/LayoutTests/fast/scrolling/mac/async-scroll-overflow-expected.txt (0 => 258044)


--- trunk/LayoutTests/fast/scrolling/mac/async-scroll-overflow-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/fast/scrolling/mac/async-scroll-overflow-expected.txt	2020-03-07 01:45:55 UTC (rev 258044)
@@ -0,0 +1,12 @@
+
+Test scroll over content
+PASS overflowScrollEventCount > 0 is true
+PASS windowScrollEventCount == 0 is true
+
+Test scroll over border
+PASS overflowScrollEventCount == 0 is true
+PASS windowScrollEventCount > 0 is true
+PASS successfullyParsed is true
+
+TEST COMPLETE
+

Added: trunk/LayoutTests/fast/scrolling/mac/async-scroll-overflow.html (0 => 258044)


--- trunk/LayoutTests/fast/scrolling/mac/async-scroll-overflow.html	                        (rev 0)
+++ trunk/LayoutTests/fast/scrolling/mac/async-scroll-overflow.html	2020-03-07 01:45:55 UTC (rev 258044)
@@ -0,0 +1,98 @@
+<!DOCTYPE html> <!-- webkit-test-runner [ internal:AsyncOverflowScrollingEnabled=true ] -->
+<html>
+<head>
+    <style>
+        body {
+            height: 1000px;
+        }
+        .scroller {
+            position: absolute;
+            top: 10px;
+            left: 10px;
+            height: 300px;
+            width: 300px;
+            border: 20px solid gray;
+            padding: 5px;
+            overflow: scroll;
+        }
+        .content {
+            width: 200%;
+            height: 300%;
+        }
+        
+    </style>
+    <script src=""
+    <script src=""
+    <script>
+        var jsTestIsAsync = true;
+
+        var scroller;
+        var overflowScrollEventCount = 0;
+        var windowScrollEventCount = 0;
+
+        async function resetScrollPositions()
+        {
+            window.scrollTop = 0;
+            scroller.scrollTop = 0;
+            
+            // Wait for scroll events to fire.
+            await UIHelper.animationFrame();
+
+            overflowScrollEventCount = 0;
+            windowScrollEventCount = 0;
+        }
+        
+        async function testScrollOverContent()
+        {
+            debug('');
+            debug('Test scroll over content');
+            await resetScrollPositions();
+            await UIHelper.mouseWheelScrollAt(100, 100);
+
+            shouldBe('overflowScrollEventCount > 0', 'true');
+            shouldBe('windowScrollEventCount == 0', 'true');
+        }
+
+        async function testScrollOverBorder()
+        {
+            debug('');
+            debug('Test scroll over border');
+            await resetScrollPositions();
+            await UIHelper.mouseWheelScrollAt(15, 15);
+            
+            shouldBe('overflowScrollEventCount == 0', 'true');
+            shouldBe('windowScrollEventCount > 0', 'true');
+        }
+
+        async function scrollTest()
+        {
+            await testScrollOverContent();
+            await testScrollOverBorder();
+
+            finishJSTest();
+        }
+
+        window.addEventListener('load', () => {
+            scroller = document.querySelector('.scroller');
+            scroller.addEventListener('scroll', () => {
+                ++overflowScrollEventCount;
+            }, false);
+
+            window.addEventListener('scroll', () => {
+                ++windowScrollEventCount;
+            }, false);
+
+            setTimeout(scrollTest, 0);
+        }, false);
+
+        var successfullyParsed = true;
+    </script>
+</head>
+<body>
+    <div class="scroller">
+        <div class="content"></div>
+    </div>
+    <div class="overlapper"></div>
+    <script src=""
+</body>
+</html>

Added: trunk/LayoutTests/fast/scrolling/mac/move-node-in-overflow-scroll-expected.txt (0 => 258044)


--- trunk/LayoutTests/fast/scrolling/mac/move-node-in-overflow-scroll-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/fast/scrolling/mac/move-node-in-overflow-scroll-expected.txt	2020-03-07 01:45:55 UTC (rev 258044)
@@ -0,0 +1,12 @@
+
+Test scroll over content
+PASS overflowScrollEventCount > 0 is true
+PASS windowScrollEventCount == 0 is true
+
+Test scroll over transformed element
+PASS overflowScrollEventCount > 0 is true
+PASS windowScrollEventCount == 0 is true
+PASS successfullyParsed is true
+
+TEST COMPLETE
+

Added: trunk/LayoutTests/fast/scrolling/mac/move-node-in-overflow-scroll.html (0 => 258044)


--- trunk/LayoutTests/fast/scrolling/mac/move-node-in-overflow-scroll.html	                        (rev 0)
+++ trunk/LayoutTests/fast/scrolling/mac/move-node-in-overflow-scroll.html	2020-03-07 01:45:55 UTC (rev 258044)
@@ -0,0 +1,108 @@
+<!DOCTYPE html> <!-- webkit-test-runner [ internal:AsyncOverflowScrollingEnabled=true ] -->
+<html>
+<head>
+    <style>
+        body {
+            height: 1000px;
+        }
+        /* non-stacking context scroller */
+        .scroller {
+            position: absolute;
+            top: 10px;
+            left: 10px;
+            height: 300px;
+            width: 300px;
+            border: 20px solid gray;
+            padding: 5px;
+            overflow: scroll;
+        }
+        .content {
+            width: 200%;
+            height: 300%;
+        }
+        
+        /* Stacking context is the root */
+        .transformed {
+            margin: 50px;
+            height: 200px;
+            width: 200px;
+            background-color: gray;
+            transform: translateZ(1px);
+        }
+    </style>
+    <script src=""
+    <script src=""
+    <script>
+        var jsTestIsAsync = true;
+
+        var scroller;
+        var overflowScrollEventCount = 0;
+        var windowScrollEventCount = 0;
+
+        async function resetScrollPositions()
+        {
+            window.scrollTop = 0;
+            scroller.scrollTop = 0;
+            
+            // Wait for scroll events to fire.
+            await UIHelper.animationFrame();
+
+            overflowScrollEventCount = 0;
+            windowScrollEventCount = 0;
+        }
+        
+        async function testScrollOverContent()
+        {
+            debug('');
+            debug('Test scroll over content');
+            await resetScrollPositions();
+            await UIHelper.mouseWheelScrollAt(50, 50);
+
+            shouldBe('overflowScrollEventCount > 0', 'true');
+            shouldBe('windowScrollEventCount == 0', 'true');
+        }
+
+        async function testScrollOverTransformed()
+        {
+            debug('');
+            debug('Test scroll over transformed element');
+            await resetScrollPositions();
+            await UIHelper.mouseWheelScrollAt(150, 150);
+            
+            shouldBe('overflowScrollEventCount > 0', 'true');
+            shouldBe('windowScrollEventCount == 0', 'true');
+        }
+
+        async function scrollTest()
+        {
+            await testScrollOverContent();
+            await testScrollOverTransformed();
+
+            finishJSTest();
+        }
+
+        window.addEventListener('load', () => {
+            scroller = document.querySelector('.scroller');
+            scroller.addEventListener('scroll', () => {
+                ++overflowScrollEventCount;
+            }, false);
+
+            window.addEventListener('scroll', () => {
+                ++windowScrollEventCount;
+            }, false);
+
+            setTimeout(scrollTest, 0);
+        }, false);
+
+        var successfullyParsed = true;
+    </script>
+</head>
+<body>
+    <div class="scroller">
+        <div class="content">
+            <div class="transformed"></div>
+        </div>
+    </div>
+    <script src=""
+</body>
+</html>

Added: trunk/LayoutTests/fast/scrolling/mac/overlapped-overflow-scroll-expected.txt (0 => 258044)


--- trunk/LayoutTests/fast/scrolling/mac/overlapped-overflow-scroll-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/fast/scrolling/mac/overlapped-overflow-scroll-expected.txt	2020-03-07 01:45:55 UTC (rev 258044)
@@ -0,0 +1,16 @@
+
+Test scroll over content
+PASS overflowScrollEventCount > 0 is true
+PASS windowScrollEventCount == 0 is true
+
+Test scroll over overlapping element
+PASS overflowScrollEventCount == 0 is true
+PASS windowScrollEventCount > 0 is true
+
+Test scroll over overlapping element outside border
+PASS overflowScrollEventCount > 0 is true
+PASS windowScrollEventCount == 0 is true
+PASS successfullyParsed is true
+
+TEST COMPLETE
+

Added: trunk/LayoutTests/fast/scrolling/mac/overlapped-overflow-scroll.html (0 => 258044)


--- trunk/LayoutTests/fast/scrolling/mac/overlapped-overflow-scroll.html	                        (rev 0)
+++ trunk/LayoutTests/fast/scrolling/mac/overlapped-overflow-scroll.html	2020-03-07 01:45:55 UTC (rev 258044)
@@ -0,0 +1,121 @@
+<!DOCTYPE html> <!-- webkit-test-runner [ internal:AsyncOverflowScrollingEnabled=true ] -->
+<html>
+<head>
+    <style>
+        body {
+            height: 1000px;
+        }
+        .scroller {
+            position: absolute;
+            top: 10px;
+            left: 10px;
+            height: 300px;
+            width: 300px;
+            border: 20px solid gray;
+            padding: 5px;
+            overflow: scroll;
+        }
+        .content {
+            width: 200%;
+            height: 300%;
+        }
+        
+        .overlapper {
+            position: absolute;
+            width: 300px;
+            height: 300px;
+            top: 200px;
+            left: 200px;
+            border-radius: 50%;
+            box-shadow: 0 0 20px black; /* Inflate layer size for testing */
+            background-color: rgba(0, 0, 128, 0.5);
+        }
+        
+    </style>
+    <script src=""
+    <script src=""
+    <script>
+        var jsTestIsAsync = true;
+
+        var scroller;
+        var overflowScrollEventCount = 0;
+        var windowScrollEventCount = 0;
+
+        async function resetScrollPositions()
+        {
+            window.scrollTop = 0;
+            scroller.scrollTop = 0;
+            
+            // Wait for scroll events to fire.
+            await UIHelper.animationFrame();
+
+            overflowScrollEventCount = 0;
+            windowScrollEventCount = 0;
+        }
+        
+        async function testScrollOverContent()
+        {
+            debug('');
+            debug('Test scroll over content');
+            await resetScrollPositions();
+            await UIHelper.mouseWheelScrollAt(50, 50);
+
+            shouldBe('overflowScrollEventCount > 0', 'true');
+            shouldBe('windowScrollEventCount == 0', 'true');
+        }
+
+        async function testScrollOverOverlapper()
+        {
+            debug('');
+            debug('Test scroll over overlapping element');
+            await resetScrollPositions();
+            await UIHelper.mouseWheelScrollAt(300, 300);
+            
+            shouldBe('overflowScrollEventCount == 0', 'true');
+            shouldBe('windowScrollEventCount > 0', 'true');
+        }
+
+        async function testScrollOverOverlapperOutsideRadius()
+        {
+            debug('');
+            debug('Test scroll over overlapping element outside border');
+            await resetScrollPositions();
+            await UIHelper.mouseWheelScrollAt(230, 230);
+            
+            shouldBe('overflowScrollEventCount > 0', 'true');
+            shouldBe('windowScrollEventCount == 0', 'true');
+        }
+
+        async function scrollTest()
+        {
+            await testScrollOverContent();
+            await testScrollOverOverlapper();
+            await testScrollOverOverlapperOutsideRadius();
+
+            finishJSTest();
+        }
+
+        window.addEventListener('load', () => {
+            scroller = document.querySelector('.scroller');
+            scroller.addEventListener('scroll', () => {
+                ++overflowScrollEventCount;
+            }, false);
+
+            window.addEventListener('scroll', () => {
+                ++windowScrollEventCount;
+            }, false);
+
+            setTimeout(scrollTest, 0);
+        }, false);
+
+        var successfullyParsed = true;
+    </script>
+</head>
+<body>
+    <div class="scroller">
+        <div class="content"></div>
+    </div>
+    <div class="overlapper"></div>
+    <script src=""
+</body>
+</html>

Modified: trunk/LayoutTests/resources/ui-helper.js (258043 => 258044)


--- trunk/LayoutTests/resources/ui-helper.js	2020-03-07 01:44:56 UTC (rev 258043)
+++ trunk/LayoutTests/resources/ui-helper.js	2020-03-07 01:45:55 UTC (rev 258044)
@@ -28,7 +28,26 @@
         eventSender.mouseMoveTo(x2, y2);
         eventSender.mouseUp();
     }
+    
+    static async mouseWheelScrollAt(x, y)
+    {
+        eventSender.monitorWheelEvents();
+        eventSender.mouseMoveTo(x, y);
+        eventSender.mouseScrollByWithWheelAndMomentumPhases(0, -1, "began", "none");
+        eventSender.mouseScrollByWithWheelAndMomentumPhases(0, -10, "changed", "none");
+        eventSender.mouseScrollByWithWheelAndMomentumPhases(0, 0, "ended", "none");
+        return new Promise(resolve => {
+            eventSender.callAfterScrollingCompletes(() => {
+                requestAnimationFrame(resolve);
+            });
+        });
+    }
 
+    static async animationFrame()
+    {
+        return new Promise(requestAnimationFrame);
+    }
+
     static sendEventStream(eventStream)
     {
         const eventStreamAsString = JSON.stringify(eventStream);

Modified: trunk/Source/WebCore/ChangeLog (258043 => 258044)


--- trunk/Source/WebCore/ChangeLog	2020-03-07 01:44:56 UTC (rev 258043)
+++ trunk/Source/WebCore/ChangeLog	2020-03-07 01:45:55 UTC (rev 258044)
@@ -1,3 +1,44 @@
+2020-03-06  Simon Fraser  <[email protected]>
+
+        Hit-test CALayers on the scrolling thread for async frame/overflow scrolling
+        https://bugs.webkit.org/show_bug.cgi?id=208740
+        <rdar://problem/48028836>
+
+        Reviewed by Tim Horton.
+
+        Implement hit-testing in the scrolling thread so we can determine which overflow/subframe
+        to scroll without hitting the main thread.
+
+        ScrollingTreeMac overrides scrollingNodeForPoint() and hit-tests through CALayers, starting at the
+        root content layer. Locking ensures that the CALayer tree doesn't change while we're hit-testing it.
+        We collect layers for the given point in back-to-front order much like the iOS code _web_findDescendantViewAtPoint
+        (too different to share though), and consult event regions on PlatformCALayerCocoa's to determine if the
+        point is inside the part of the layer that should receive events.
+
+        To handle the complex stacking/containing block cases, isScrolledBy() consults the scrolling tree.
+
+        For testing, fix it so that multiple calls to monitorWheelEvents() in a single test each start
+        with clean state.
+
+        Tests: fast/scrolling/mac/absolute-in-overflow-scroll.html
+               fast/scrolling/mac/async-scroll-overflow.html
+               fast/scrolling/mac/move-node-in-overflow-scroll.html
+               fast/scrolling/mac/overlapped-overflow-scroll.html
+
+        * page/scrolling/ScrollingTree.cpp:
+        (WebCore::ScrollingTree::handleWheelEvent):
+        (WebCore::ScrollingTree::scrollingNodeForPoint):
+        * page/scrolling/ScrollingTree.h:
+        * page/scrolling/mac/ScrollingTreeFrameScrollingNodeMac.h:
+        * page/scrolling/mac/ScrollingTreeMac.h:
+        * page/scrolling/mac/ScrollingTreeMac.mm:
+        (collectDescendantLayersAtPoint):
+        (scrollingNodeIDForLayer):
+        (isScrolledBy):
+        (ScrollingTreeMac::scrollingNodeForPoint):
+        * testing/js/WebCoreTestSupport.cpp:
+        (WebCoreTestSupport::monitorWheelEvents): Make sure that each call to eventSender.monitorWheelEvents() clears previous state.
+
 2020-03-06  Jer Noble  <[email protected]>
 
         [GPUP] Convert CDMFactory away from platformStrategies() and use WebProcess settings instead

Modified: trunk/Source/WebCore/page/scrolling/ScrollingTree.cpp (258043 => 258044)


--- trunk/Source/WebCore/page/scrolling/ScrollingTree.cpp	2020-03-07 01:44:56 UTC (rev 258043)
+++ trunk/Source/WebCore/page/scrolling/ScrollingTree.cpp	2020-03-07 01:45:55 UTC (rev 258044)
@@ -111,7 +111,7 @@
 
     if (m_rootNode) {
         FloatPoint position = wheelEvent.position();
-        ScrollingTreeNode* node = scrollingNodeForPoint(position);
+        auto node = scrollingNodeForPoint(position);
 
         LOG_WITH_STREAM(Scrolling, stream << "ScrollingTree::handleWheelEvent found node " << (node ? node->scrollingNodeID() : 0) << " for point " << position << "\n");
 
@@ -128,10 +128,10 @@
     return ScrollingEventResult::DidNotHandleEvent;
 }
 
-ScrollingTreeNode* ScrollingTree::scrollingNodeForPoint(FloatPoint)
+RefPtr<ScrollingTreeNode> ScrollingTree::scrollingNodeForPoint(FloatPoint)
 {
     ASSERT(asyncFrameOrOverflowScrollingEnabled());
-    return m_rootNode.get();
+    return m_rootNode;
 }
 
 void ScrollingTree::mainFrameViewportChangedViaDelegatedScrolling(const FloatPoint& scrollPosition, const FloatRect& layoutViewport, double)

Modified: trunk/Source/WebCore/page/scrolling/ScrollingTree.h (258043 => 258044)


--- trunk/Source/WebCore/page/scrolling/ScrollingTree.h	2020-03-07 01:44:56 UTC (rev 258043)
+++ trunk/Source/WebCore/page/scrolling/ScrollingTree.h	2020-03-07 01:45:55 UTC (rev 258044)
@@ -177,7 +177,7 @@
 
     void notifyRelatedNodesRecursive(ScrollingTreeNode&);
 
-    ScrollingTreeNode* scrollingNodeForPoint(FloatPoint);
+    WEBCORE_EXPORT virtual RefPtr<ScrollingTreeNode> scrollingNodeForPoint(FloatPoint);
 
     Lock m_treeMutex; // Protects the scrolling tree.
 

Modified: trunk/Source/WebCore/page/scrolling/mac/ScrollingTreeFrameScrollingNodeMac.h (258043 => 258044)


--- trunk/Source/WebCore/page/scrolling/mac/ScrollingTreeFrameScrollingNodeMac.h	2020-03-07 01:44:56 UTC (rev 258043)
+++ trunk/Source/WebCore/page/scrolling/mac/ScrollingTreeFrameScrollingNodeMac.h	2020-03-07 01:45:55 UTC (rev 258044)
@@ -42,6 +42,8 @@
     static Ref<ScrollingTreeFrameScrollingNode> create(ScrollingTree&, ScrollingNodeType, ScrollingNodeID);
     virtual ~ScrollingTreeFrameScrollingNodeMac();
 
+    RetainPtr<CALayer> rootContentsLayer() const { return m_rootContentsLayer; }
+
 protected:
     ScrollingTreeFrameScrollingNodeMac(ScrollingTree&, ScrollingNodeType, ScrollingNodeID);
 

Modified: trunk/Source/WebCore/page/scrolling/mac/ScrollingTreeMac.h (258043 => 258044)


--- trunk/Source/WebCore/page/scrolling/mac/ScrollingTreeMac.h	2020-03-07 01:44:56 UTC (rev 258043)
+++ trunk/Source/WebCore/page/scrolling/mac/ScrollingTreeMac.h	2020-03-07 01:45:55 UTC (rev 258044)
@@ -40,6 +40,8 @@
 
     Ref<ScrollingTreeNode> createScrollingTreeNode(ScrollingNodeType, ScrollingNodeID) final;
 
+    RefPtr<ScrollingTreeNode> scrollingNodeForPoint(FloatPoint) final;
+
     void lockLayersForHitTesting() final;
     void unlockLayersForHitTesting() final;
 

Modified: trunk/Source/WebCore/page/scrolling/mac/ScrollingTreeMac.mm (258043 => 258044)


--- trunk/Source/WebCore/page/scrolling/mac/ScrollingTreeMac.mm	2020-03-07 01:44:56 UTC (rev 258043)
+++ trunk/Source/WebCore/page/scrolling/mac/ScrollingTreeMac.mm	2020-03-07 01:45:55 UTC (rev 258044)
@@ -26,6 +26,7 @@
 #include "config.h"
 #include "ScrollingTreeMac.h"
 
+#include "PlatformCALayer.h"
 #include "ScrollingTreeFixedNode.h"
 #include "ScrollingTreeFrameHostingNode.h"
 #include "ScrollingTreeFrameScrollingNodeMac.h"
@@ -33,6 +34,7 @@
 #include "ScrollingTreeOverflowScrollingNodeMac.h"
 #include "ScrollingTreePositionedNode.h"
 #include "ScrollingTreeStickyNode.h"
+#include "WebLayer.h"
 
 #if ENABLE(ASYNC_SCROLLING) && ENABLE(SCROLLING_THREAD)
 
@@ -71,6 +73,111 @@
     return ScrollingTreeFixedNode::create(*this, nodeID);
 }
 
+
+static void collectDescendantLayersAtPoint(Vector<CALayer *, 16>& layersAtPoint, CALayer *parent, CGPoint point)
+{
+    if (parent.masksToBounds && ![parent containsPoint:point])
+        return;
+
+    for (CALayer *layer in [parent sublayers]) {
+        CALayer *layerWithResolvedAnimations = layer;
+
+        if ([[layer animationKeys] count])
+            layerWithResolvedAnimations = [layer presentationLayer];
+
+        CGPoint subviewPoint = [layerWithResolvedAnimations convertPoint:point fromLayer:parent];
+
+        auto handlesEvent = [&] {
+            if (CGRectIsEmpty([layerWithResolvedAnimations frame]))
+                return false;
+
+            if (![layerWithResolvedAnimations containsPoint:subviewPoint])
+                return false;
+
+            auto platformCALayer = PlatformCALayer::platformCALayerForLayer((__bridge void*)layer);
+            if (platformCALayer) {
+                // Scrolling changes boundsOrigin on the scroll container layer, but we computed its event region ignoring scroll position, so factor out bounds origin.
+                FloatPoint boundsOrigin = layer.bounds.origin;
+                FloatPoint localPoint = subviewPoint - toFloatSize(boundsOrigin);
+                return platformCALayer->eventRegionContainsPoint(IntPoint(localPoint));
+            }
+            
+            return false;
+        }();
+
+        if (handlesEvent)
+            layersAtPoint.append(layer);
+
+        if ([layer sublayers])
+            collectDescendantLayersAtPoint(layersAtPoint, layer, subviewPoint);
+    };
+}
+
+static ScrollingNodeID scrollingNodeIDForLayer(CALayer *layer)
+{
+    auto platformCALayer = PlatformCALayer::platformCALayerForLayer((__bridge void*)layer);
+    return platformCALayer ? platformCALayer->scrollingNodeID() : 0;
+}
+
+static bool isScrolledBy(const ScrollingTree& tree, ScrollingNodeID scrollingNodeID, CALayer *hitLayer)
+{
+    for (CALayer *layer = hitLayer; layer; layer = [layer superlayer]) {
+        auto nodeID = scrollingNodeIDForLayer(layer);
+        if (nodeID == scrollingNodeID)
+            return true;
+
+        auto* scrollingNode = tree.nodeForID(nodeID);
+        if (is<ScrollingTreeOverflowScrollProxyNode>(scrollingNode)) {
+            ScrollingNodeID actingOverflowScrollingNodeID = downcast<ScrollingTreeOverflowScrollProxyNode>(*scrollingNode).overflowScrollingNodeID();
+            if (actingOverflowScrollingNodeID == scrollingNodeID)
+                return true;
+        }
+
+        if (is<ScrollingTreePositionedNode>(scrollingNode)) {
+            if (downcast<ScrollingTreePositionedNode>(*scrollingNode).relatedOverflowScrollingNodes().contains(scrollingNodeID))
+                return false;
+        }
+    }
+
+    return false;
+}
+
+RefPtr<ScrollingTreeNode> ScrollingTreeMac::scrollingNodeForPoint(FloatPoint point)
+{
+    auto* rootScrollingNode = rootNode();
+    if (!rootScrollingNode)
+        return nullptr;
+
+    LockHolder lockHolder(m_layerHitTestMutex);
+
+    auto rootContentsLayer = static_cast<ScrollingTreeFrameScrollingNodeMac*>(rootScrollingNode)->rootContentsLayer();
+
+    Vector<CALayer *, 16> layersAtPoint;
+    collectDescendantLayersAtPoint(layersAtPoint, rootContentsLayer.get(), point);
+
+    LOG_WITH_STREAM(Scrolling, stream << "ScrollingTreeMac " << this << " scrollingNodeForPoint " << point << " found " << layersAtPoint.size() << " layers");
+#if !LOG_DISABLED
+    for (auto *layer : WTF::makeReversedRange(layersAtPoint))
+        LOG_WITH_STREAM(Scrolling, stream << " layer " << [layer description] << " scrolling node " << scrollingNodeIDForLayer(layer));
+#endif
+
+    for (auto *layer : WTF::makeReversedRange(layersAtPoint)) {
+        auto nodeID = scrollingNodeIDForLayer(layer);
+        
+        auto* scrollingNode = nodeForID(nodeID);
+        if (!is<ScrollingTreeScrollingNode>(scrollingNode))
+            continue;
+        
+        if (isScrolledBy(*this, nodeID, layersAtPoint.last()))
+            return scrollingNode;
+
+        // FIXME: Hit-test scroll indicator layers.
+    }
+
+    LOG_WITH_STREAM(Scrolling, stream << "ScrollingTreeMac " << this << " scrollingNodeForPoint " << point << " found no scrollable layers; using root node");
+    return rootScrollingNode;
+}
+
 void ScrollingTreeMac::lockLayersForHitTesting()
 {
     m_layerHitTestMutex.lock();

Modified: trunk/Source/WebCore/testing/js/WebCoreTestSupport.cpp (258043 => 258044)


--- trunk/Source/WebCore/testing/js/WebCoreTestSupport.cpp	2020-03-07 01:44:56 UTC (rev 258043)
+++ trunk/Source/WebCore/testing/js/WebCoreTestSupport.cpp	2020-03-07 01:45:55 UTC (rev 258044)
@@ -85,7 +85,7 @@
     if (!page)
         return;
 
-    page->ensureWheelEventTestMonitor();
+    page->ensureWheelEventTestMonitor().clearAllTestDeferrals();
 }
 
 void setTestCallbackAndStartNotificationTimer(WebCore::Frame& frame, JSContextRef context, JSObjectRef jsCallbackFunction)

Modified: trunk/Source/WebKit/ChangeLog (258043 => 258044)


--- trunk/Source/WebKit/ChangeLog	2020-03-07 01:44:56 UTC (rev 258043)
+++ trunk/Source/WebKit/ChangeLog	2020-03-07 01:45:55 UTC (rev 258044)
@@ -1,3 +1,16 @@
+2020-03-06  Simon Fraser  <[email protected]>
+
+        Hit-test CALayers on the scrolling thread for async frame/overflow scrolling
+        https://bugs.webkit.org/show_bug.cgi?id=208740
+        <rdar://problem/48028836>
+
+        Reviewed by Tim Horton.
+
+        Make sure that each call to eventSender.monitorWheelEvents() clears previous state.
+
+        * WebProcess/InjectedBundle/API/c/WKBundlePage.cpp:
+        (WKBundlePageStartMonitoringScrollOperations):
+
 2020-03-06  Brady Eidson  <[email protected]>
 
         Some PDFPlugin cleanup in prep for incremental loading.

Modified: trunk/Source/WebKit/WebProcess/InjectedBundle/API/c/WKBundlePage.cpp (258043 => 258044)


--- trunk/Source/WebKit/WebProcess/InjectedBundle/API/c/WKBundlePage.cpp	2020-03-07 01:44:56 UTC (rev 258043)
+++ trunk/Source/WebKit/WebProcess/InjectedBundle/API/c/WKBundlePage.cpp	2020-03-07 01:45:55 UTC (rev 258044)
@@ -701,7 +701,7 @@
     if (!page)
         return;
 
-    page->ensureWheelEventTestMonitor();
+    page->ensureWheelEventTestMonitor().clearAllTestDeferrals();
 }
 
 bool WKBundlePageRegisterScrollOperationCompletionCallback(WKBundlePageRef pageRef, WKBundlePageTestNotificationCallback callback, void* context)
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to