Title: [261427] trunk
Revision
261427
Author
[email protected]
Date
2020-05-08 17:02:45 -0700 (Fri, 08 May 2020)

Log Message

Async overflow scroll: sometimes a <select> becomes non-scrollable
https://bugs.webkit.org/show_bug.cgi?id=211433
<rdar://problem/62338474>

Reviewed by Dean Jackson.

Source/WebCore:

Scrollable <select>s (RenderListBox) contribute to the non-fast scrollable region, so
wheel events inside them are propagated to the main thread. If the select is scrolled
to the end, the event will propagate to the enclosing scroller, then then FrameView::wheelEvent()
may send it back to the scrolling thread. If the scrolling thread is processing such an event
after the main thread, it should not perform any latching on the main thread.

Test: scrollingcoordinator/mac/latching/scrolling-select-should-not-latch-mainframe.html

* page/scrolling/ScrollingTree.cpp:
(WebCore::ScrollingTree::shouldHandleWheelEventSynchronously):
(WebCore::ScrollingTree::handleWheelEvent):
* page/scrolling/ScrollingTree.h:
* page/scrolling/ScrollingTreeLatchingController.cpp:
(WebCore::ScrollingTreeLatchingController::receivedWheelEvent):
(WebCore::ScrollingTreeLatchingController::latchedNodeForEvent const):
(WebCore::ScrollingTreeLatchingController::nodeDidHandleEvent):
(WebCore::ScrollingTreeLatchingController::clearLatchedNode):
* page/scrolling/ScrollingTreeLatchingController.h:
* page/scrolling/ThreadedScrollingTree.cpp:
(WebCore::ThreadedScrollingTree::handleWheelEventAfterMainThread):
* page/scrolling/ThreadedScrollingTree.h:
* page/scrolling/mac/ScrollingCoordinatorMac.mm:
(WebCore::ScrollingCoordinatorMac::handleWheelEvent):
* platform/PlatformWheelEvent.cpp:
(WebCore::operator<<):

LayoutTests:

* scrollingcoordinator/mac/latching/scrolling-select-should-not-latch-mainframe-expected.txt: Added.
* scrollingcoordinator/mac/latching/scrolling-select-should-not-latch-mainframe.html: Added.

Modified Paths

Added Paths

Diff

Modified: trunk/LayoutTests/ChangeLog (261426 => 261427)


--- trunk/LayoutTests/ChangeLog	2020-05-09 00:02:41 UTC (rev 261426)
+++ trunk/LayoutTests/ChangeLog	2020-05-09 00:02:45 UTC (rev 261427)
@@ -1,5 +1,16 @@
 2020-05-08  Simon Fraser  <[email protected]>
 
+        Async overflow scroll: sometimes a <select> becomes non-scrollable
+        https://bugs.webkit.org/show_bug.cgi?id=211433
+        <rdar://problem/62338474>
+
+        Reviewed by Dean Jackson.
+
+        * scrollingcoordinator/mac/latching/scrolling-select-should-not-latch-mainframe-expected.txt: Added.
+        * scrollingcoordinator/mac/latching/scrolling-select-should-not-latch-mainframe.html: Added.
+
+2020-05-08  Simon Fraser  <[email protected]>
+
         Overflow scrollers in iframes don't get mouseMovedInContentArea()
         https://bugs.webkit.org/show_bug.cgi?id=211347
         <rdar://problem/62784560>

Added: trunk/LayoutTests/scrollingcoordinator/mac/latching/scrolling-select-should-not-latch-mainframe-expected.txt (0 => 261427)


--- trunk/LayoutTests/scrollingcoordinator/mac/latching/scrolling-select-should-not-latch-mainframe-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/scrollingcoordinator/mac/latching/scrolling-select-should-not-latch-mainframe-expected.txt	2020-05-09 00:02:45 UTC (rev 261427)
@@ -0,0 +1,15 @@
+
+Tests that scrolling over a <select> doesn't latch on the document
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS select.scrollTop is 90
+PASS window.pageYOffset is 0
+Scrolling up then down in the select
+PASS select.scrollTop > 0 is true
+PASS window.pageYOffset is 0
+PASS successfullyParsed is true
+
+TEST COMPLETE
+

Added: trunk/LayoutTests/scrollingcoordinator/mac/latching/scrolling-select-should-not-latch-mainframe.html (0 => 261427)


--- trunk/LayoutTests/scrollingcoordinator/mac/latching/scrolling-select-should-not-latch-mainframe.html	                        (rev 0)
+++ trunk/LayoutTests/scrollingcoordinator/mac/latching/scrolling-select-should-not-latch-mainframe.html	2020-05-09 00:02:45 UTC (rev 261427)
@@ -0,0 +1,95 @@
+<!DOCTYPE html> <!-- webkit-test-runner [ internal:AsyncOverflowScrollingEnabled=true ] -->
+<html>
+<head>
+    <style>
+        body {
+            height: 2000px;
+        }
+        select {
+            display: block;
+            font-size: 11pt;
+            margin: 10px;
+            border: 1px solid black;
+        }
+    </style>
+    <script src=""
+    <script src=""
+    <script>
+        jsTestIsAsync = true;
+
+        let select;
+        async function scrollTest()
+        {
+            select = document.getElementsByTagName('select')[0];
+
+            select.scrollTop = 90;
+            shouldBe('select.scrollTop', '90');
+            shouldBe('window.pageYOffset', '0');
+
+            if (!window.eventSender) {
+                finishJSTest();
+                return;
+            }
+
+            const selectBounds = select.getBoundingClientRect();
+            const x = selectBounds.left + 10;
+            const y = selectBounds.top + 10;
+
+            // We have to send all these as one sequence, because monitorWheelEvents() clears the latching state.
+            eventSender.monitorWheelEvents();
+            eventSender.mouseMoveTo(x, y);
+
+            debug('Scrolling up then down in the select');
+            // Scroll to the top of the select.
+            eventSender.mouseScrollByWithWheelAndMomentumPhases(0, 1, "began", "none");
+            eventSender.mouseScrollByWithWheelAndMomentumPhases(0, 20, "changed", "none");
+            eventSender.mouseScrollByWithWheelAndMomentumPhases(0, 0, "ended", "none");
+
+            await UIHelper.animationFrame();
+
+            // Scroll up again to trigger a document bounce.
+            eventSender.mouseScrollByWithWheelAndMomentumPhases(0, 1, "began", "none");
+            eventSender.mouseScrollByWithWheelAndMomentumPhases(0, 20, "changed", "none");
+            eventSender.mouseScrollByWithWheelAndMomentumPhases(0, 0, "ended", "none");
+
+            await UIHelper.animationFrame();
+
+            // Scroll down. This should scroll the select, not the document.
+            eventSender.mouseScrollByWithWheelAndMomentumPhases(0, -2, "began", "none");
+            eventSender.mouseScrollByWithWheelAndMomentumPhases(0, -20, "changed", "none");
+            eventSender.mouseScrollByWithWheelAndMomentumPhases(0, -20, "changed", "none");
+            eventSender.mouseScrollByWithWheelAndMomentumPhases(0, 0, "ended", "none");
+
+            await UIHelper.waitForScrollCompletion();
+
+            shouldBeTrue('select.scrollTop > 0');
+            shouldBe('window.pageYOffset', '0');
+
+            finishJSTest();
+        }
+
+        window.addEventListener('load', () => {
+            description("Tests that scrolling over a &lt;select&gt; doesn't latch on the document");
+            setTimeout(scrollTest, 0);
+        }, false);
+    </script>
+</head>
+<body>
+    <select size="5">
+        <option>January</option>
+        <option>February</option>
+        <option>March</option>
+        <option>April</option>
+        <option>May</option>
+        <option>June</option>
+        <option>July</option>
+        <option>August</option>
+        <option>September</option>
+        <option>October</option>
+        <option>November</option>
+        <option>December</option>
+    </select>
+<div id="console"></div>
+<script src=""
+</body>
+</html>

Modified: trunk/Source/WebCore/ChangeLog (261426 => 261427)


--- trunk/Source/WebCore/ChangeLog	2020-05-09 00:02:41 UTC (rev 261426)
+++ trunk/Source/WebCore/ChangeLog	2020-05-09 00:02:45 UTC (rev 261427)
@@ -1,3 +1,37 @@
+2020-05-08  Simon Fraser  <[email protected]>
+
+        Async overflow scroll: sometimes a <select> becomes non-scrollable
+        https://bugs.webkit.org/show_bug.cgi?id=211433
+        <rdar://problem/62338474>
+
+        Reviewed by Dean Jackson.
+
+        Scrollable <select>s (RenderListBox) contribute to the non-fast scrollable region, so
+        wheel events inside them are propagated to the main thread. If the select is scrolled
+        to the end, the event will propagate to the enclosing scroller, then then FrameView::wheelEvent()
+        may send it back to the scrolling thread. If the scrolling thread is processing such an event
+        after the main thread, it should not perform any latching on the main thread.
+
+        Test: scrollingcoordinator/mac/latching/scrolling-select-should-not-latch-mainframe.html
+
+        * page/scrolling/ScrollingTree.cpp:
+        (WebCore::ScrollingTree::shouldHandleWheelEventSynchronously):
+        (WebCore::ScrollingTree::handleWheelEvent):
+        * page/scrolling/ScrollingTree.h:
+        * page/scrolling/ScrollingTreeLatchingController.cpp:
+        (WebCore::ScrollingTreeLatchingController::receivedWheelEvent):
+        (WebCore::ScrollingTreeLatchingController::latchedNodeForEvent const):
+        (WebCore::ScrollingTreeLatchingController::nodeDidHandleEvent):
+        (WebCore::ScrollingTreeLatchingController::clearLatchedNode):
+        * page/scrolling/ScrollingTreeLatchingController.h:
+        * page/scrolling/ThreadedScrollingTree.cpp:
+        (WebCore::ThreadedScrollingTree::handleWheelEventAfterMainThread):
+        * page/scrolling/ThreadedScrollingTree.h:
+        * page/scrolling/mac/ScrollingCoordinatorMac.mm:
+        (WebCore::ScrollingCoordinatorMac::handleWheelEvent):
+        * platform/PlatformWheelEvent.cpp:
+        (WebCore::operator<<):
+
 2020-05-08  Darin Adler  <[email protected]>
 
         Remove now-unneeded HAVE(AVFOUNDATION_LOADER_DELEGATE)

Modified: trunk/Source/WebCore/page/scrolling/ScrollingTree.cpp (261426 => 261427)


--- trunk/Source/WebCore/page/scrolling/ScrollingTree.cpp	2020-05-09 00:02:41 UTC (rev 261426)
+++ trunk/Source/WebCore/page/scrolling/ScrollingTree.cpp	2020-05-09 00:02:45 UTC (rev 261427)
@@ -68,10 +68,11 @@
     // This method is invoked by the event handling thread
     LockHolder lock(m_treeStateMutex);
 
-    if (m_latchingController.latchedNodeForEvent(wheelEvent))
+    LOG_WITH_STREAM(ScrollLatching, stream << "ScrollingTree::shouldHandleWheelEventSynchronously " << wheelEvent << " have latched node " << m_latchingController.latchedNodeForEvent(wheelEvent, m_allowLatching));
+    if (m_latchingController.latchedNodeForEvent(wheelEvent, m_allowLatching))
         return false;
 
-    m_latchingController.receivedWheelEvent(wheelEvent);
+    m_latchingController.receivedWheelEvent(wheelEvent, m_allowLatching);
     
     if (!m_treeState.eventTrackingRegions.isEmpty() && m_rootNode) {
         FloatPoint position = wheelEvent.position();
@@ -100,7 +101,7 @@
     if (isMonitoringWheelEvents())
         receivedWheelEvent(wheelEvent);
 
-    m_latchingController.receivedWheelEvent(wheelEvent);
+    m_latchingController.receivedWheelEvent(wheelEvent, m_allowLatching);
 
     auto result = [&] {
         if (!asyncFrameOrOverflowScrollingEnabled()) {
@@ -115,7 +116,7 @@
 
         m_gestureState.receivedWheelEvent(wheelEvent);
 
-        if (auto latchedNodeID = m_latchingController.latchedNodeForEvent(wheelEvent)) {
+        if (auto latchedNodeID = m_latchingController.latchedNodeForEvent(wheelEvent, m_allowLatching)) {
             LOG_WITH_STREAM(ScrollLatching, stream << "ScrollingTree::handleWheelEvent: has latched node " << latchedNodeID);
             auto* node = nodeForID(*latchedNodeID);
             if (is<ScrollingTreeScrollingNode>(node)) {
@@ -138,7 +139,7 @@
                 auto result = scrollingNode.handleWheelEvent(wheelEvent);
 
                 if (result == ScrollingEventResult::DidHandleEvent) {
-                    m_latchingController.nodeDidHandleEvent(scrollingNode.scrollingNodeID(), wheelEvent);
+                    m_latchingController.nodeDidHandleEvent(scrollingNode.scrollingNodeID(), wheelEvent, m_allowLatching);
                     m_gestureState.nodeDidHandleEvent(scrollingNode.scrollingNodeID(), wheelEvent);
                 }
 

Modified: trunk/Source/WebCore/page/scrolling/ScrollingTree.h (261426 => 261427)


--- trunk/Source/WebCore/page/scrolling/ScrollingTree.h	2020-05-09 00:02:41 UTC (rev 261426)
+++ trunk/Source/WebCore/page/scrolling/ScrollingTree.h	2020-05-09 00:02:45 UTC (rev 261427)
@@ -235,6 +235,9 @@
     Lock m_swipeStateMutex;
     SwipeState m_swipeState;
 
+protected:
+    bool m_allowLatching { true };
+
 private:
     unsigned m_fixedOrStickyNodeCount { 0 };
     bool m_isHandlingProgrammaticScroll { false };

Modified: trunk/Source/WebCore/page/scrolling/ScrollingTreeLatchingController.cpp (261426 => 261427)


--- trunk/Source/WebCore/page/scrolling/ScrollingTreeLatchingController.cpp	2020-05-09 00:02:41 UTC (rev 261426)
+++ trunk/Source/WebCore/page/scrolling/ScrollingTreeLatchingController.cpp	2020-05-09 00:02:45 UTC (rev 261427)
@@ -41,8 +41,11 @@
 
 ScrollingTreeLatchingController::ScrollingTreeLatchingController() = default;
 
-void ScrollingTreeLatchingController::receivedWheelEvent(const PlatformWheelEvent& wheelEvent)
+void ScrollingTreeLatchingController::receivedWheelEvent(const PlatformWheelEvent& wheelEvent, bool allowLatching)
 {
+    if (!allowLatching)
+        return;
+
     LockHolder locker(m_latchedNodeMutex);
     if (wheelEvent.shouldConsiderLatching()) {
         if (m_latchedNodeID) {
@@ -55,8 +58,11 @@
     }
 }
 
-Optional<ScrollingNodeID> ScrollingTreeLatchingController::latchedNodeForEvent(const PlatformWheelEvent& wheelEvent) const
+Optional<ScrollingNodeID> ScrollingTreeLatchingController::latchedNodeForEvent(const PlatformWheelEvent& wheelEvent, bool allowLatching) const
 {
+    if (!allowLatching)
+        return WTF::nullopt;
+
     LockHolder locker(m_latchedNodeMutex);
 
     // If we have a latched node, use it.
@@ -74,8 +80,11 @@
     return m_latchedNodeID;
 }
 
-void ScrollingTreeLatchingController::nodeDidHandleEvent(ScrollingNodeID scrollingNodeID, const PlatformWheelEvent& wheelEvent)
+void ScrollingTreeLatchingController::nodeDidHandleEvent(ScrollingNodeID scrollingNodeID, const PlatformWheelEvent& wheelEvent, bool allowLatching)
 {
+    if (!allowLatching)
+        return;
+
     LockHolder locker(m_latchedNodeMutex);
 
     if (wheelEvent.useLatchedEventElement() && m_latchedNodeID == scrollingNodeID) {
@@ -101,6 +110,7 @@
 void ScrollingTreeLatchingController::clearLatchedNode()
 {
     LockHolder locker(m_latchedNodeMutex);
+    LOG_WITH_STREAM(ScrollLatching, stream << "ScrollingTreeLatchingController " << this << " clearLatchedNode (was " << m_latchedNodeID << ")");
     m_latchedNodeID.reset();
 }
 

Modified: trunk/Source/WebCore/page/scrolling/ScrollingTreeLatchingController.h (261426 => 261427)


--- trunk/Source/WebCore/page/scrolling/ScrollingTreeLatchingController.h	2020-05-09 00:02:41 UTC (rev 261426)
+++ trunk/Source/WebCore/page/scrolling/ScrollingTreeLatchingController.h	2020-05-09 00:02:45 UTC (rev 261427)
@@ -41,9 +41,9 @@
 public:
     ScrollingTreeLatchingController();
 
-    void receivedWheelEvent(const PlatformWheelEvent&);
-    Optional<ScrollingNodeID> latchedNodeForEvent(const PlatformWheelEvent&) const;
-    void nodeDidHandleEvent(ScrollingNodeID, const PlatformWheelEvent&);
+    void receivedWheelEvent(const PlatformWheelEvent&, bool allowLatching);
+    Optional<ScrollingNodeID> latchedNodeForEvent(const PlatformWheelEvent&, bool allowLatching) const;
+    void nodeDidHandleEvent(ScrollingNodeID, const PlatformWheelEvent&, bool allowLatching);
 
     Optional<ScrollingNodeID> latchedNodeID() const;
 

Modified: trunk/Source/WebCore/page/scrolling/ThreadedScrollingTree.cpp (261426 => 261427)


--- trunk/Source/WebCore/page/scrolling/ThreadedScrollingTree.cpp	2020-05-09 00:02:41 UTC (rev 261426)
+++ trunk/Source/WebCore/page/scrolling/ThreadedScrollingTree.cpp	2020-05-09 00:02:45 UTC (rev 261427)
@@ -36,6 +36,7 @@
 #include "ScrollingTreeOverflowScrollProxyNode.h"
 #include "ScrollingTreeScrollingNode.h"
 #include <wtf/RunLoop.h>
+#include <wtf/SetForScope.h>
 
 namespace WebCore {
 
@@ -71,6 +72,12 @@
     return ScrollingTree::handleWheelEvent(wheelEvent);
 }
 
+ScrollingEventResult ThreadedScrollingTree::handleWheelEventAfterMainThread(const PlatformWheelEvent& wheelEvent)
+{
+    SetForScope<bool> disallowLatchingScope(m_allowLatching, false);
+    return handleWheelEvent(wheelEvent);
+}
+
 void ThreadedScrollingTree::invalidate()
 {
     // Invalidate is dispatched by the ScrollingCoordinator class on the ScrollingThread

Modified: trunk/Source/WebCore/page/scrolling/ThreadedScrollingTree.h (261426 => 261427)


--- trunk/Source/WebCore/page/scrolling/ThreadedScrollingTree.h	2020-05-09 00:02:41 UTC (rev 261426)
+++ trunk/Source/WebCore/page/scrolling/ThreadedScrollingTree.h	2020-05-09 00:02:45 UTC (rev 261427)
@@ -46,6 +46,8 @@
 
     ScrollingEventResult handleWheelEvent(const PlatformWheelEvent&) override;
 
+    ScrollingEventResult handleWheelEventAfterMainThread(const PlatformWheelEvent&);
+
     // Can be called from any thread. Will try to handle the wheel event on the scrolling thread.
     // Returns true if the wheel event can be handled on the scrolling thread and false if the
     // event must be sent again to the WebCore event handler.

Modified: trunk/Source/WebCore/page/scrolling/mac/ScrollingCoordinatorMac.mm (261426 => 261427)


--- trunk/Source/WebCore/page/scrolling/mac/ScrollingCoordinatorMac.mm	2020-05-09 00:02:41 UTC (rev 261426)
+++ trunk/Source/WebCore/page/scrolling/mac/ScrollingCoordinatorMac.mm	2020-05-09 00:02:45 UTC (rev 261427)
@@ -83,7 +83,7 @@
 
     RefPtr<ThreadedScrollingTree> threadedScrollingTree = downcast<ThreadedScrollingTree>(scrollingTree());
     ScrollingThread::dispatch([threadedScrollingTree, wheelEvent] {
-        threadedScrollingTree->handleWheelEvent(wheelEvent);
+        threadedScrollingTree->handleWheelEventAfterMainThread(wheelEvent);
     });
     return ScrollingEventResult::DidHandleEvent;
 }

Modified: trunk/Source/WebCore/platform/PlatformWheelEvent.cpp (261426 => 261427)


--- trunk/Source/WebCore/platform/PlatformWheelEvent.cpp	2020-05-09 00:02:41 UTC (rev 261426)
+++ trunk/Source/WebCore/platform/PlatformWheelEvent.cpp	2020-05-09 00:02:45 UTC (rev 261427)
@@ -53,7 +53,7 @@
     ts << "PlatformWheelEvent " << &event << " at " << event.position() << " deltaX " << event.deltaX() << " deltaY " << event.deltaY();
 
 #if ENABLE(KINETIC_SCROLLING)
-    ts << " phase \"" << event.phase() << "\" momentumum phase \"" << event.momentumPhase() << "\"";
+    ts << " phase \"" << event.phase() << "\" momentum phase \"" << event.momentumPhase() << "\"";
 #endif
 
     return ts;
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to