Title: [270425] trunk
Revision
270425
Author
[email protected]
Date
2020-12-03 23:10:03 -0800 (Thu, 03 Dec 2020)

Log Message

Only the first wheel event in a gesture should be cancelable
https://bugs.webkit.org/show_bug.cgi?id=218764
<rdar://problem/71248946>

Reviewed by Tim Horton.

Source/WebCore:

Implement the WebKit2 version of r270312, where only the first wheel event in a gesture is
cancelable.

When scrolling over an element with handlers, we do event handling on the main thread,
so we can take the compute value of EventHandler's Optional<WheelScrollGestureState>
from the first event and send it back to the scrolling thread.

However, the scrolling thread needs to block until this first event comes back from
the main thread. To achieve this, EventDispatcher::wheelEvent() now dispaches
main thread scrolls from the scrolling thread (not the dispatcher thread), and
waits on m_waitingForBeganEventCondition with a 50ms timeout for that first event to
come back.

In the normal case, main thread handling dispatches the event back to the scrolling
thread for scrolling via handleWheelEventAfterMainThread(), and then calls
wheelEventWasProcessedByMainThread() to signal the condition. If for some reason
handleWheelEventAfterMainThread() doesn't get called (e.g. nothing was scrollable),
then wheelEventWasProcessedByMainThread() still gets called to signal.

If m_waitingForBeganEventCondition times out, then the scrolling thread falls back
to non-blocking behaviour (as if the first event was not canceled).

Finally, when we know the gesture will become non-blocking, we transition to running
the scroll from the scrolling thread, which requires that we set up latching, hence
the changes in ScrollingTreeLatchingController.

Tested by existing tests in fast/events/wheel.

* page/EventHandler.cpp:
(WebCore::EventHandler::wheelEventWasProcessedByMainThread):
(WebCore::EventHandler::handleWheelEventInScrollableArea):
* page/WheelEventTestMonitor.cpp:
(WebCore::operator<<):
* page/WheelEventTestMonitor.h:
* page/mac/EventHandlerMac.mm:
(WebCore::EventHandler::processWheelEventForScrolling):
* page/scrolling/ScrollingTree.cpp:
(WebCore::ScrollingTree::determineWheelEventProcessing):
(WebCore::ScrollingTree::setGestureState):
(WebCore::ScrollingTree::gestureState):
* page/scrolling/ScrollingTree.h:
(WebCore::ScrollingTree::willSendEventToMainThread):
(WebCore::ScrollingTree::waitForEventToBeProcessedByMainThread):
* page/scrolling/ScrollingTreeLatchingController.cpp:
(WebCore::ScrollingTreeLatchingController::receivedWheelEvent):
(WebCore::ScrollingTreeLatchingController::nodeDidHandleEvent):
* page/scrolling/ScrollingTreeLatchingController.h:
* page/scrolling/ThreadedScrollingTree.cpp:
(WebCore::ThreadedScrollingTree::handleWheelEventAfterMainThread):
(WebCore::ThreadedScrollingTree::wheelEventWasProcessedByMainThread):
(WebCore::ThreadedScrollingTree::willSendEventToMainThread):
(WebCore::ThreadedScrollingTree::waitForEventToBeProcessedByMainThread):
* page/scrolling/ThreadedScrollingTree.h:
* page/scrolling/mac/ScrollingCoordinatorMac.mm:
(WebCore::ScrollingCoordinatorMac::handleWheelEventForScrolling): Need to track deferral
for WheelEventTestMonitor.
(WebCore::ScrollingCoordinatorMac::wheelEventWasProcessedByMainThread): This is now synchronous
to the scrolling thread so no need for the deferrer.
(WebCore::nextDeferIdentifier): Deleted.
* page/scrolling/nicosia/ScrollingCoordinatorNicosia.cpp:
(WebCore::ScrollingCoordinatorNicosia::wheelEventWasProcessedByMainThread):
* platform/cocoa/ScrollController.mm:
(WebCore::ScrollController::handleWheelEvent):

Source/WebKit:

In EventDispatcher::wheelEvent(), all wheel events now bounce through the scrolling
thread, even those destined for main thread scrolling. This allows the scrolling thread
to wait on a condition for the event to come back to the scrolling thread via
handleWheelEventAfterMainThread(), since we have to know whether content called
preventDefault() on the first event before sending subsequent events.

* WebProcess/WebPage/EventDispatcher.cpp:
(WebKit::EventDispatcher::wheelEvent):

LayoutTests:

* fast/scrolling/mac/rubberband-overflow-in-wheel-region-root-jiggle.html: Make more robust.
* platform/mac-wk2/fast/events/wheel/wheel-events-become-non-cancelable-expected.txt: Test now passes in WK2.

Modified Paths

Removed Paths

  • trunk/LayoutTests/platform/mac-wk2/fast/events/wheel/

Diff

Modified: trunk/LayoutTests/ChangeLog (270424 => 270425)


--- trunk/LayoutTests/ChangeLog	2020-12-04 06:45:17 UTC (rev 270424)
+++ trunk/LayoutTests/ChangeLog	2020-12-04 07:10:03 UTC (rev 270425)
@@ -1,3 +1,14 @@
+2020-12-03  Simon Fraser  <[email protected]>
+
+        Only the first wheel event in a gesture should be cancelable
+        https://bugs.webkit.org/show_bug.cgi?id=218764
+        <rdar://problem/71248946>
+
+        Reviewed by Tim Horton.
+
+        * fast/scrolling/mac/rubberband-overflow-in-wheel-region-root-jiggle.html: Make more robust.
+        * platform/mac-wk2/fast/events/wheel/wheel-events-become-non-cancelable-expected.txt: Test now passes in WK2.
+
 2020-12-03  Lauro Moura  <[email protected]>
 
         [GTK] Gardening anchor download failures after r270422

Modified: trunk/LayoutTests/fast/scrolling/latching/scroll-latched-nested-div.html (270424 => 270425)


--- trunk/LayoutTests/fast/scrolling/latching/scroll-latched-nested-div.html	2020-12-04 06:45:17 UTC (rev 270424)
+++ trunk/LayoutTests/fast/scrolling/latching/scroll-latched-nested-div.html	2020-12-04 07:10:03 UTC (rev 270425)
@@ -10,6 +10,7 @@
     background-image: repeating-linear-gradient(silver, white 200px);
 }
 </style>
+<script src=""
 <script src=""
 <script>
     jsTestIsAsync = true;
@@ -43,11 +44,9 @@
             testFailed("div received wheel events during the second gesture.");
         else
             testPassed("div did not receive wheel events during the second gesture.");
-
-        finishJSTest();
     }
 
-    function checkForFirstScroll()
+    async function doSecondScroll()
     {
         // 'parent' should not have scrolled, and the content of 'target' should
         // not have moved. However, 'wrapper' should have moved.
@@ -81,10 +80,11 @@
         eventSender.mouseScrollByWithWheelAndMomentumPhases(0, -1, 'none', 'begin');
         eventSender.mouseScrollByWithWheelAndMomentumPhases(0, -1, 'none', 'continue');
         eventSender.mouseScrollByWithWheelAndMomentumPhases(0, 0, 'none', 'end');
-        eventSender.callAfterScrollingCompletes(checkForSecondScroll);
+
+        await UIHelper.waitForScrollCompletion();
     }
 
-    function scrollTest()
+    async function doFirstScroll()
     {
         pageScrollPositionBefore = document.scrollingElement.scrollTop;
 
@@ -110,9 +110,18 @@
         eventSender.mouseScrollByWithWheelAndMomentumPhases(0, -5, 'none', 'begin');
         eventSender.mouseScrollByWithWheelAndMomentumPhases(0, -10, 'none', 'continue');
         eventSender.mouseScrollByWithWheelAndMomentumPhases(0, 0, 'none', 'end');
-        eventSender.callAfterScrollingCompletes(checkForFirstScroll);
+        
+        await UIHelper.waitForScrollCompletion();
     }
 
+    async function scrollTest()
+    {
+        await doFirstScroll();
+        await doSecondScroll();
+        checkForSecondScroll();
+        finishJSTest();
+    }
+
     function setupTopLevel()
     {
         description("Tests that a scrollable div nested inside another scrollable div properly handles wheel events under sub-pixel conditions.");

Modified: trunk/LayoutTests/fast/scrolling/mac/rubberband-overflow-in-wheel-region-root-jiggle.html (270424 => 270425)


--- trunk/LayoutTests/fast/scrolling/mac/rubberband-overflow-in-wheel-region-root-jiggle.html	2020-12-04 06:45:17 UTC (rev 270424)
+++ trunk/LayoutTests/fast/scrolling/mac/rubberband-overflow-in-wheel-region-root-jiggle.html	2020-12-04 07:10:03 UTC (rev 270425)
@@ -51,10 +51,17 @@
 
             // Scroll down to latch, then up to rubberband.
             eventSender.mouseScrollByWithWheelAndMomentumPhases(0, -1, "began", "none");
-            eventSender.mouseScrollByWithWheelAndMomentumPhases(0, -10, "changed", "none");
-            eventSender.mouseScrollByWithWheelAndMomentumPhases(0, -10, "changed", "none");
-            eventSender.mouseScrollByWithWheelAndMomentumPhases(0, 12, "changed", "none");
-            eventSender.mouseScrollByWithWheelAndMomentumPhases(0, 12, "changed", "none");
+            await UIHelper.renderingUpdate();
+            eventSender.mouseScrollByWithWheelAndMomentumPhases(0, -5, "changed", "none");
+            await UIHelper.renderingUpdate();
+            eventSender.mouseScrollByWithWheelAndMomentumPhases(0, -5, "changed", "none");
+            await UIHelper.renderingUpdate();
+            eventSender.mouseScrollByWithWheelAndMomentumPhases(0, 5, "changed", "none");
+            await UIHelper.renderingUpdate();
+            eventSender.mouseScrollByWithWheelAndMomentumPhases(0, 5, "changed", "none");
+            await UIHelper.renderingUpdate();
+            eventSender.mouseScrollByWithWheelAndMomentumPhases(0, 5, "changed", "none");
+            await UIHelper.renderingUpdate();
             eventSender.mouseScrollByWithWheelAndMomentumPhases(0, 0, "ended", "none");
 
             await UIHelper.renderingUpdate();

Modified: trunk/Source/WebCore/ChangeLog (270424 => 270425)


--- trunk/Source/WebCore/ChangeLog	2020-12-04 06:45:17 UTC (rev 270424)
+++ trunk/Source/WebCore/ChangeLog	2020-12-04 07:10:03 UTC (rev 270425)
@@ -1,3 +1,75 @@
+2020-12-03  Simon Fraser  <[email protected]>
+
+        Only the first wheel event in a gesture should be cancelable
+        https://bugs.webkit.org/show_bug.cgi?id=218764
+        <rdar://problem/71248946>
+
+        Reviewed by Tim Horton.
+
+        Implement the WebKit2 version of r270312, where only the first wheel event in a gesture is
+        cancelable.
+
+        When scrolling over an element with handlers, we do event handling on the main thread,
+        so we can take the compute value of EventHandler's Optional<WheelScrollGestureState>
+        from the first event and send it back to the scrolling thread.
+
+        However, the scrolling thread needs to block until this first event comes back from
+        the main thread. To achieve this, EventDispatcher::wheelEvent() now dispaches
+        main thread scrolls from the scrolling thread (not the dispatcher thread), and
+        waits on m_waitingForBeganEventCondition with a 50ms timeout for that first event to
+        come back.
+
+        In the normal case, main thread handling dispatches the event back to the scrolling
+        thread for scrolling via handleWheelEventAfterMainThread(), and then calls
+        wheelEventWasProcessedByMainThread() to signal the condition. If for some reason
+        handleWheelEventAfterMainThread() doesn't get called (e.g. nothing was scrollable),
+        then wheelEventWasProcessedByMainThread() still gets called to signal.
+
+        If m_waitingForBeganEventCondition times out, then the scrolling thread falls back
+        to non-blocking behaviour (as if the first event was not canceled).
+
+        Finally, when we know the gesture will become non-blocking, we transition to running
+        the scroll from the scrolling thread, which requires that we set up latching, hence
+        the changes in ScrollingTreeLatchingController.
+
+        Tested by existing tests in fast/events/wheel.
+
+        * page/EventHandler.cpp:
+        (WebCore::EventHandler::wheelEventWasProcessedByMainThread):
+        (WebCore::EventHandler::handleWheelEventInScrollableArea):
+        * page/WheelEventTestMonitor.cpp:
+        (WebCore::operator<<):
+        * page/WheelEventTestMonitor.h:
+        * page/mac/EventHandlerMac.mm:
+        (WebCore::EventHandler::processWheelEventForScrolling):
+        * page/scrolling/ScrollingTree.cpp:
+        (WebCore::ScrollingTree::determineWheelEventProcessing):
+        (WebCore::ScrollingTree::setGestureState):
+        (WebCore::ScrollingTree::gestureState):
+        * page/scrolling/ScrollingTree.h:
+        (WebCore::ScrollingTree::willSendEventToMainThread):
+        (WebCore::ScrollingTree::waitForEventToBeProcessedByMainThread):
+        * page/scrolling/ScrollingTreeLatchingController.cpp:
+        (WebCore::ScrollingTreeLatchingController::receivedWheelEvent):
+        (WebCore::ScrollingTreeLatchingController::nodeDidHandleEvent):
+        * page/scrolling/ScrollingTreeLatchingController.h:
+        * page/scrolling/ThreadedScrollingTree.cpp:
+        (WebCore::ThreadedScrollingTree::handleWheelEventAfterMainThread):
+        (WebCore::ThreadedScrollingTree::wheelEventWasProcessedByMainThread):
+        (WebCore::ThreadedScrollingTree::willSendEventToMainThread):
+        (WebCore::ThreadedScrollingTree::waitForEventToBeProcessedByMainThread):
+        * page/scrolling/ThreadedScrollingTree.h:
+        * page/scrolling/mac/ScrollingCoordinatorMac.mm:
+        (WebCore::ScrollingCoordinatorMac::handleWheelEventForScrolling): Need to track deferral
+        for WheelEventTestMonitor.
+        (WebCore::ScrollingCoordinatorMac::wheelEventWasProcessedByMainThread): This is now synchronous
+        to the scrolling thread so no need for the deferrer.
+        (WebCore::nextDeferIdentifier): Deleted.
+        * page/scrolling/nicosia/ScrollingCoordinatorNicosia.cpp:
+        (WebCore::ScrollingCoordinatorNicosia::wheelEventWasProcessedByMainThread):
+        * platform/cocoa/ScrollController.mm:
+        (WebCore::ScrollController::handleWheelEvent):
+
 2020-12-03  Kate Cheney  <[email protected]>
 
         Issue logging in to Microsoft Teams if logged into other Microsoft accounts and navigating directly to teams.microsoft.com

Modified: trunk/Source/WebCore/page/EventHandler.cpp (270424 => 270425)


--- trunk/Source/WebCore/page/EventHandler.cpp	2020-12-04 06:45:17 UTC (rev 270424)
+++ trunk/Source/WebCore/page/EventHandler.cpp	2020-12-04 07:10:03 UTC (rev 270425)
@@ -2722,8 +2722,17 @@
     return didHandleEvent;
 }
 
-void EventHandler::wheelEventWasProcessedByMainThread(const PlatformWheelEvent&, OptionSet<EventHandling>)
+void EventHandler::wheelEventWasProcessedByMainThread(const PlatformWheelEvent& wheelEvent, OptionSet<EventHandling> eventHandling)
 {
+    updateWheelGestureState(wheelEvent, eventHandling);
+
+#if ENABLE(ASYNC_SCROLLING)
+    FrameView* view = m_frame.view();
+    if (auto scrollingCoordinator = m_frame.page()->scrollingCoordinator()) {
+        if (scrollingCoordinator->coordinatesScrollingForFrameView(*view))
+            scrollingCoordinator->wheelEventWasProcessedByMainThread(wheelEvent, m_wheelScrollGestureState);
+    }
+#endif
 }
 
 bool EventHandler::platformCompletePlatformWidgetWheelEvent(const PlatformWheelEvent&, const Widget&, const WeakPtr<ScrollableArea>&)
@@ -2982,7 +2991,7 @@
 bool EventHandler::handleWheelEventInScrollableArea(const PlatformWheelEvent& wheelEvent, ScrollableArea& scrollableArea, OptionSet<EventHandling> eventHandling)
 {
     auto gestureState = updateWheelGestureState(wheelEvent, eventHandling);
-    LOG_WITH_STREAM(Scrolling, stream << "EventHandler::handleWheelEventInScrollableArea() - eventHandling " << eventHandling << " -> gesture state " << gestureState);
+    LOG_WITH_STREAM(Scrolling, stream << "EventHandler::handleWheelEventInScrollableArea() " << scrollableArea << " - eventHandling " << eventHandling << " -> gesture state " << gestureState);
     return scrollableArea.handleWheelEventForScrolling(wheelEvent, gestureState);
 }
 

Modified: trunk/Source/WebCore/page/WheelEventTestMonitor.cpp (270424 => 270425)


--- trunk/Source/WebCore/page/WheelEventTestMonitor.cpp	2020-12-04 06:45:17 UTC (rev 270424)
+++ trunk/Source/WebCore/page/WheelEventTestMonitor.cpp	2020-12-04 07:10:03 UTC (rev 270425)
@@ -172,7 +172,7 @@
     switch (reason) {
     case WheelEventTestMonitor::HandlingWheelEvent: ts << "handling wheel event"; break;
     case WheelEventTestMonitor::HandlingWheelEventOnMainThread: ts << "handling wheel event on main thread"; break;
-    case WheelEventTestMonitor::ReportDOMEventHandling: ts << "report DOM event handling"; break;
+    case WheelEventTestMonitor::PostMainThreadWheelEventHandling: ts << "post-main thread event handling"; break;
     case WheelEventTestMonitor::RubberbandInProgress: ts << "rubberbanding"; break;
     case WheelEventTestMonitor::ScrollSnapInProgress: ts << "scroll-snapping"; break;
     case WheelEventTestMonitor::ScrollingThreadSyncNeeded: ts << "scrolling thread sync needed"; break;

Modified: trunk/Source/WebCore/page/WheelEventTestMonitor.h (270424 => 270425)


--- trunk/Source/WebCore/page/WheelEventTestMonitor.h	2020-12-04 06:45:17 UTC (rev 270424)
+++ trunk/Source/WebCore/page/WheelEventTestMonitor.h	2020-12-04 07:10:03 UTC (rev 270425)
@@ -48,14 +48,14 @@
     WEBCORE_EXPORT void clearAllTestDeferrals();
     
     enum DeferReason {
-        HandlingWheelEvent              = 1 << 0,
-        HandlingWheelEventOnMainThread  = 1 << 1,
-        ReportDOMEventHandling          = 1 << 2,
-        RubberbandInProgress            = 1 << 3,
-        ScrollSnapInProgress            = 1 << 4,
-        ScrollingThreadSyncNeeded       = 1 << 5,
-        ContentScrollInProgress         = 1 << 6,
-        RequestedScrollPosition         = 1 << 7,
+        HandlingWheelEvent                  = 1 << 0,
+        HandlingWheelEventOnMainThread      = 1 << 1,
+        PostMainThreadWheelEventHandling    = 1 << 2,
+        RubberbandInProgress                = 1 << 3,
+        ScrollSnapInProgress                = 1 << 4,
+        ScrollingThreadSyncNeeded           = 1 << 5,
+        ContentScrollInProgress             = 1 << 6,
+        RequestedScrollPosition             = 1 << 7,
     };
     typedef const void* ScrollableAreaIdentifier;
 

Modified: trunk/Source/WebCore/page/mac/EventHandlerMac.mm (270424 => 270425)


--- trunk/Source/WebCore/page/mac/EventHandlerMac.mm	2020-12-04 06:45:17 UTC (rev 270424)
+++ trunk/Source/WebCore/page/mac/EventHandlerMac.mm	2020-12-04 07:10:03 UTC (rev 270425)
@@ -954,7 +954,7 @@
         LOG_WITH_STREAM(ScrollLatching, stream << "  EventHandler::processWheelEventForScrolling returning " << didHandleWheelEvent);
         return didHandleWheelEvent;
     }
-    
+
     bool didHandleEvent = handleWheelEventInScrollableArea(wheelEvent, *view, eventHandling);
     m_isHandlingWheelEvent = false;
     return didHandleEvent;

Modified: trunk/Source/WebCore/page/scrolling/ScrollingTree.cpp (270424 => 270425)


--- trunk/Source/WebCore/page/scrolling/ScrollingTree.cpp	2020-12-04 06:45:17 UTC (rev 270424)
+++ trunk/Source/WebCore/page/scrolling/ScrollingTree.cpp	2020-12-04 07:10:03 UTC (rev 270425)
@@ -73,6 +73,8 @@
         LOG_WITH_STREAM(ScrollLatching, stream << "ScrollingTree::determineWheelEventProcessing " << wheelEvent << " have latched node " << latchedNodeAndSteps->scrollingNodeID << " steps " << latchedNodeAndSteps->processingSteps);
         return latchedNodeAndSteps->processingSteps;
     }
+    if (wheelEvent.isGestureStart() || wheelEvent.isNonGestureEvent())
+        m_treeState.gestureState = WTF::nullopt;
 
     auto processingSteps = [&]() -> OptionSet<WheelEventProcessingSteps> {
         if (!m_rootNode)
@@ -96,8 +98,12 @@
 
 #if ENABLE(WHEEL_EVENT_REGIONS)
         auto eventListenerTypes = eventListenerRegionTypesForPoint(position);
-        if (eventListenerTypes.contains(EventListenerRegionType::NonPassiveWheel))
+        if (eventListenerTypes.contains(EventListenerRegionType::NonPassiveWheel)) {
+            if (m_treeState.gestureState.valueOr(WheelScrollGestureState::Blocking) == WheelScrollGestureState::NonBlocking)
+                return { WheelEventProcessingSteps::ScrollingThread, WheelEventProcessingSteps::MainThreadForNonBlockingDOMEventDispatch };
+
             return { WheelEventProcessingSteps::MainThreadForScrolling, WheelEventProcessingSteps::MainThreadForBlockingDOMEventDispatch };
+        }
 
         if (eventListenerTypes.contains(EventListenerRegionType::Wheel))
             return { WheelEventProcessingSteps::ScrollingThread, WheelEventProcessingSteps::MainThreadForNonBlockingDOMEventDispatch };
@@ -482,6 +488,18 @@
     m_treeState.mainFrameScrollPosition = position;
 }
 
+void ScrollingTree::setGestureState(Optional<WheelScrollGestureState> gestureState)
+{
+    LockHolder lock(m_treeStateMutex);
+    m_treeState.gestureState = gestureState;
+}
+
+Optional<WheelScrollGestureState> ScrollingTree::gestureState()
+{
+    LockHolder lock(m_treeStateMutex);
+    return m_treeState.gestureState;
+}
+
 TrackingType ScrollingTree::eventTrackingTypeForPoint(const AtomString& eventName, IntPoint p)
 {
     LockHolder lock(m_treeStateMutex);

Modified: trunk/Source/WebCore/page/scrolling/ScrollingTree.h (270424 => 270425)


--- trunk/Source/WebCore/page/scrolling/ScrollingTree.h	2020-12-04 06:45:17 UTC (rev 270424)
+++ trunk/Source/WebCore/page/scrolling/ScrollingTree.h	2020-12-04 07:10:03 UTC (rev 270425)
@@ -210,6 +210,9 @@
     virtual void lockLayersForHitTesting() { }
     virtual void unlockLayersForHitTesting() { }
 
+    virtual void willSendEventToMainThread(const PlatformWheelEvent&) { }
+    virtual void waitForEventToBeProcessedByMainThread(const PlatformWheelEvent&) { };
+
     Lock& treeMutex() { return m_treeMutex; }
 
     void windowScreenDidChange(PlatformDisplayID, Optional<unsigned> nominalFramesPerSecond);
@@ -245,6 +248,9 @@
     FloatPoint mainFrameScrollPosition() const;
     void setMainFrameScrollPosition(FloatPoint);
 
+    void setGestureState(Optional<WheelScrollGestureState>);
+    Optional<WheelScrollGestureState> gestureState();
+
     Optional<unsigned> nominalFramesPerSecond();
 
     void applyLayerPositionsInternal();
@@ -285,6 +291,7 @@
         FloatPoint mainFrameScrollPosition;
         PlatformDisplayID displayID { 0 };
         Optional<unsigned> nominalFramesPerSecond;
+        Optional<WheelScrollGestureState> gestureState;
         HashSet<ScrollingNodeID> nodesWithActiveRubberBanding;
         HashSet<ScrollingNodeID> nodesWithActiveScrollSnap;
         HashSet<ScrollingNodeID> nodesWithActiveUserScrolls;

Modified: trunk/Source/WebCore/page/scrolling/ScrollingTreeLatchingController.cpp (270424 => 270425)


--- trunk/Source/WebCore/page/scrolling/ScrollingTreeLatchingController.cpp	2020-12-04 06:45:17 UTC (rev 270424)
+++ trunk/Source/WebCore/page/scrolling/ScrollingTreeLatchingController.cpp	2020-12-04 07:10:03 UTC (rev 270425)
@@ -41,15 +41,18 @@
 
 ScrollingTreeLatchingController::ScrollingTreeLatchingController() = default;
 
-void ScrollingTreeLatchingController::receivedWheelEvent(const PlatformWheelEvent& wheelEvent, OptionSet<WheelEventProcessingSteps>, bool allowLatching)
+void ScrollingTreeLatchingController::receivedWheelEvent(const PlatformWheelEvent& wheelEvent, OptionSet<WheelEventProcessingSteps> processingSteps, bool allowLatching)
 {
     if (!allowLatching)
         return;
 
     LockHolder locker(m_latchedNodeMutex);
-    if (wheelEvent.isGestureStart() && m_latchedNodeAndSteps && !latchedNodeIsRelevant()) {
-        LOG_WITH_STREAM(ScrollLatching, stream << "ScrollingTreeLatchingController " << this << " receivedWheelEvent - " << (MonotonicTime::now() - m_lastLatchedNodeInterationTime).milliseconds() << "ms since last event, clearing latched node");
-        m_latchedNodeAndSteps.reset();
+    if (wheelEvent.isGestureStart() && !latchedNodeIsRelevant()) {
+        if (m_latchedNodeAndSteps) {
+            LOG_WITH_STREAM(ScrollLatching, stream << "ScrollingTreeLatchingController " << this << " receivedWheelEvent - " << (MonotonicTime::now() - m_lastLatchedNodeInterationTime).milliseconds() << "ms since last event, clearing latched node");
+            m_latchedNodeAndSteps.reset();
+        }
+        m_processingStepsForCurrentGesture = processingSteps;
     }
 }
 
@@ -96,9 +99,27 @@
         return;
     }
 
-    if (wheelEvent.delta().isZero() || !wheelEvent.isGestureStart())
+    auto shouldLatch = [&]() {
+        if (wheelEvent.delta().isZero())
+            return false;
+
+        if (wheelEvent.isGestureStart())
+            return true;
+
+        if (!wheelEvent.isGestureContinuation())
+            return false;
+
+        if (m_processingStepsForCurrentGesture.valueOr(OptionSet<WheelEventProcessingSteps> { }).contains(WheelEventProcessingSteps::MainThreadForScrolling) && processingSteps.contains(WheelEventProcessingSteps::ScrollingThread))
+            return true;
+
+        return false;
+    };
+    
+    if (!shouldLatch())
         return;
 
+    m_processingStepsForCurrentGesture = processingSteps;
+
     LOG_WITH_STREAM(ScrollLatching, stream << "ScrollingTreeLatchingController " << this << " nodeDidHandleEvent: latching to " << scrollingNodeID);
     m_latchedNodeAndSteps = ScrollingNodeAndProcessingSteps { scrollingNodeID, processingSteps };
     m_lastLatchedNodeInterationTime = MonotonicTime::now();

Modified: trunk/Source/WebCore/page/scrolling/ScrollingTreeLatchingController.h (270424 => 270425)


--- trunk/Source/WebCore/page/scrolling/ScrollingTreeLatchingController.h	2020-12-04 06:45:17 UTC (rev 270424)
+++ trunk/Source/WebCore/page/scrolling/ScrollingTreeLatchingController.h	2020-12-04 07:10:03 UTC (rev 270425)
@@ -64,6 +64,7 @@
 
     mutable Lock m_latchedNodeMutex;
     Optional<ScrollingNodeAndProcessingSteps> m_latchedNodeAndSteps;
+    Optional<OptionSet<WheelEventProcessingSteps>> m_processingStepsForCurrentGesture;
     MonotonicTime m_lastLatchedNodeInterationTime;
 };
 

Modified: trunk/Source/WebCore/page/scrolling/ThreadedScrollingTree.cpp (270424 => 270425)


--- trunk/Source/WebCore/page/scrolling/ThreadedScrollingTree.cpp	2020-12-04 06:45:17 UTC (rev 270424)
+++ trunk/Source/WebCore/page/scrolling/ThreadedScrollingTree.cpp	2020-12-04 07:10:03 UTC (rev 270425)
@@ -64,23 +64,76 @@
     return ScrollingTree::handleWheelEvent(wheelEvent, processingSteps);
 }
 
-bool ThreadedScrollingTree::handleWheelEventAfterMainThread(const PlatformWheelEvent& wheelEvent, ScrollingNodeID targetNodeID, Optional<WheelScrollGestureState>)
+bool ThreadedScrollingTree::handleWheelEventAfterMainThread(const PlatformWheelEvent& wheelEvent, ScrollingNodeID targetNodeID, Optional<WheelScrollGestureState> gestureState)
 {
-    LOG_WITH_STREAM(Scrolling, stream << "ThreadedScrollingTree::handleWheelEventAfterMainThread " << wheelEvent << " node " << targetNodeID);
+    ASSERT(ScrollingThread::isCurrentThread());
 
+    LOG_WITH_STREAM(Scrolling, stream << "ThreadedScrollingTree::handleWheelEventAfterMainThread " << wheelEvent << " node " << targetNodeID << " gestureState " << gestureState);
+
     LockHolder locker(m_treeMutex);
 
-    SetForScope<bool> disallowLatchingScope(m_allowLatching, false);
+    bool allowLatching = false;
+    OptionSet<WheelEventProcessingSteps> processingSteps;
+    if (gestureState.valueOr(WheelScrollGestureState::Blocking) == WheelScrollGestureState::NonBlocking) {
+        allowLatching = true;
+        processingSteps = { WheelEventProcessingSteps::ScrollingThread, WheelEventProcessingSteps::MainThreadForNonBlockingDOMEventDispatch };
+    }
+
+    SetForScope<bool> disallowLatchingScope(m_allowLatching, allowLatching);
     RefPtr<ScrollingTreeNode> targetNode = nodeForID(targetNodeID);
-    auto result = handleWheelEventWithNode(wheelEvent, { }, targetNode.get(), EventTargeting::NodeOnly);
+    auto result = handleWheelEventWithNode(wheelEvent, processingSteps, targetNode.get(), EventTargeting::NodeOnly);
     return result.wasHandled;
 }
 
-void ThreadedScrollingTree::wheelEventWasProcessedByMainThread(const PlatformWheelEvent&, Optional<WheelScrollGestureState>)
+void ThreadedScrollingTree::wheelEventWasProcessedByMainThread(const PlatformWheelEvent& wheelEvent, Optional<WheelScrollGestureState> gestureState)
 {
-    // FIXME: Set state based on EventHandling flags.
+    LOG_WITH_STREAM(Scrolling, stream << "ThreadedScrollingTree::wheelEventWasProcessedByMainThread - gestureState " << gestureState);
+
+    ASSERT(isMainThread());
+    
+    LockHolder locker(m_treeMutex);
+    if (m_receivedBeganEventFromMainThread || !wheelEvent.isGestureStart())
+        return;
+
+    setGestureState(gestureState);
+
+    m_receivedBeganEventFromMainThread = true;
+    m_waitingForBeganEventCondition.notifyOne();
 }
 
+void ThreadedScrollingTree::willSendEventToMainThread(const PlatformWheelEvent&)
+{
+    ASSERT(ScrollingThread::isCurrentThread());
+
+    LockHolder locker(m_treeMutex);
+    m_receivedBeganEventFromMainThread = false;
+}
+
+void ThreadedScrollingTree::waitForEventToBeProcessedByMainThread(const PlatformWheelEvent& wheelEvent)
+{
+    ASSERT(ScrollingThread::isCurrentThread());
+
+    if (!wheelEvent.isGestureStart())
+        return;
+
+    LockHolder locker(m_treeMutex);
+
+    static constexpr auto maxAllowableMainThreadDelay = 50_ms;
+    auto startTime = MonotonicTime::now();
+    auto timeoutTime = startTime + maxAllowableMainThreadDelay;
+
+    bool receivedEvent = m_waitingForBeganEventCondition.waitUntil(m_treeMutex, timeoutTime, [&] {
+        return m_receivedBeganEventFromMainThread;
+    });
+
+    if (!receivedEvent) {
+        // Timed out, go asynchronous.
+        setGestureState(WheelScrollGestureState::NonBlocking);
+    }
+
+    LOG_WITH_STREAM(Scrolling, stream << "ThreadedScrollingTree::waitForBeganEventFromMainThread done - timed out " << !receivedEvent << " gesture state is " << gestureState());
+}
+
 void ThreadedScrollingTree::invalidate()
 {
     // Invalidate is dispatched by the ScrollingCoordinator class on the ScrollingThread

Modified: trunk/Source/WebCore/page/scrolling/ThreadedScrollingTree.h (270424 => 270425)


--- trunk/Source/WebCore/page/scrolling/ThreadedScrollingTree.h	2020-12-04 06:45:17 UTC (rev 270424)
+++ trunk/Source/WebCore/page/scrolling/ThreadedScrollingTree.h	2020-12-04 07:10:03 UTC (rev 270425)
@@ -50,6 +50,9 @@
     bool handleWheelEventAfterMainThread(const PlatformWheelEvent&, ScrollingNodeID, Optional<WheelScrollGestureState>);
     void wheelEventWasProcessedByMainThread(const PlatformWheelEvent&, Optional<WheelScrollGestureState>);
 
+    WEBCORE_EXPORT void willSendEventToMainThread(const PlatformWheelEvent&) final;
+    WEBCORE_EXPORT void waitForEventToBeProcessedByMainThread(const PlatformWheelEvent&) final;
+
     void invalidate() override;
 
     WEBCORE_EXPORT void displayDidRefresh(PlatformDisplayID);
@@ -85,7 +88,7 @@
 
     void displayDidRefreshOnScrollingThread();
     void waitForRenderingUpdateCompletionOrTimeout();
-    
+
     bool canUpdateLayersOnScrollingThread() const;
 
     void scheduleDelayedRenderingUpdateDetectionTimer(Seconds);
@@ -103,6 +106,9 @@
     SynchronizationState m_state { SynchronizationState::Idle };
     Condition m_stateCondition;
 
+    bool m_receivedBeganEventFromMainThread { false };
+    Condition m_waitingForBeganEventCondition;
+
     // Dynamically allocated because it has to use the ScrollingThread's runloop.
     std::unique_ptr<RunLoop::Timer<ThreadedScrollingTree>> m_delayedRenderingUpdateDetectionTimer;
 

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


--- trunk/Source/WebCore/page/scrolling/mac/ScrollingCoordinatorMac.mm	2020-12-04 06:45:17 UTC (rev 270424)
+++ trunk/Source/WebCore/page/scrolling/mac/ScrollingCoordinatorMac.mm	2020-12-04 07:10:03 UTC (rev 270425)
@@ -81,29 +81,23 @@
     if (scrollingTree()->willWheelEventStartSwipeGesture(wheelEvent))
         return false;
 
-    LOG_WITH_STREAM(Scrolling, stream << "ScrollingCoordinatorMac::handleWheelEventForScrolling - sending event to scrolling thread, node " << targetNodeID << " gestureState " << gestureState);
-    
+    LOG_WITH_STREAM(Scrolling, stream << "ScrollingCoordinatorMac::handleWheelEventForScrolling " << wheelEvent << " - sending event to scrolling thread, node " << targetNodeID << " gestureState " << gestureState);
+
+    auto deferrer = WheelEventTestMonitorCompletionDeferrer { m_page->wheelEventTestMonitor().get(), reinterpret_cast<WheelEventTestMonitor::ScrollableAreaIdentifier>(targetNodeID), WheelEventTestMonitor::PostMainThreadWheelEventHandling };
+
     RefPtr<ThreadedScrollingTree> threadedScrollingTree = downcast<ThreadedScrollingTree>(scrollingTree());
-    ScrollingThread::dispatch([threadedScrollingTree, wheelEvent, targetNodeID, gestureState] {
+    ScrollingThread::dispatch([threadedScrollingTree, wheelEvent, targetNodeID, gestureState, deferrer = WTFMove(deferrer)] {
         threadedScrollingTree->handleWheelEventAfterMainThread(wheelEvent, targetNodeID, gestureState);
     });
     return true;
 }
 
-static uint64_t nextDeferIdentifier()
-{
-    static uint64_t deferIdentifier;
-    return ++deferIdentifier;
-}
-
 void ScrollingCoordinatorMac::wheelEventWasProcessedByMainThread(const PlatformWheelEvent& wheelEvent, Optional<WheelScrollGestureState> gestureState)
 {
-    auto deferrer = WheelEventTestMonitorCompletionDeferrer { m_page->wheelEventTestMonitor().get(), reinterpret_cast<WheelEventTestMonitor::ScrollableAreaIdentifier>(nextDeferIdentifier()), WheelEventTestMonitor::ReportDOMEventHandling };
+    LOG_WITH_STREAM(Scrolling, stream << "ScrollingCoordinatorMac::wheelEventWasProcessedByMainThread " << gestureState);
 
     RefPtr<ThreadedScrollingTree> threadedScrollingTree = downcast<ThreadedScrollingTree>(scrollingTree());
-    ScrollingThread::dispatch([threadedScrollingTree, wheelEvent, gestureState, deferrer = WTFMove(deferrer)] {
-        threadedScrollingTree->wheelEventWasProcessedByMainThread(wheelEvent, gestureState);
-    });
+    threadedScrollingTree->wheelEventWasProcessedByMainThread(wheelEvent, gestureState);
 }
 
 void ScrollingCoordinatorMac::scheduleTreeStateCommit()

Modified: trunk/Source/WebCore/page/scrolling/nicosia/ScrollingCoordinatorNicosia.cpp (270424 => 270425)


--- trunk/Source/WebCore/page/scrolling/nicosia/ScrollingCoordinatorNicosia.cpp	2020-12-04 06:45:17 UTC (rev 270424)
+++ trunk/Source/WebCore/page/scrolling/nicosia/ScrollingCoordinatorNicosia.cpp	2020-12-04 07:10:03 UTC (rev 270425)
@@ -83,9 +83,8 @@
 
 void ScrollingCoordinatorNicosia::wheelEventWasProcessedByMainThread(const PlatformWheelEvent& wheelEvent, Optional<WheelScrollGestureState> gestureState)
 {
-    ScrollingThread::dispatch([threadedScrollingTree = makeRef(downcast<ThreadedScrollingTree>(*scrollingTree())), wheelEvent, gestureState] {
-        threadedScrollingTree->wheelEventWasProcessedByMainThread(wheelEvent, gestureState);
-    });
+    RefPtr<ThreadedScrollingTree> threadedScrollingTree = downcast<ThreadedScrollingTree>(scrollingTree());
+    threadedScrollingTree->wheelEventWasProcessedByMainThread(wheelEvent, gestureState);
 }
 
 void ScrollingCoordinatorNicosia::scheduleTreeStateCommit()

Modified: trunk/Source/WebCore/platform/PlatformWheelEvent.h (270424 => 270425)


--- trunk/Source/WebCore/platform/PlatformWheelEvent.h	2020-12-04 06:45:17 UTC (rev 270424)
+++ trunk/Source/WebCore/platform/PlatformWheelEvent.h	2020-12-04 07:10:03 UTC (rev 270425)
@@ -208,7 +208,8 @@
         || m_phase == PlatformWheelEventPhase::Changed
         || m_momentumPhase == PlatformWheelEventPhase::Began
         || m_momentumPhase == PlatformWheelEventPhase::Changed
-        || (m_phase == PlatformWheelEventPhase::Ended && m_momentumPhase == PlatformWheelEventPhase::None);
+        || (m_phase == PlatformWheelEventPhase::Ended && m_momentumPhase == PlatformWheelEventPhase::None)
+        || (m_phase == PlatformWheelEventPhase::None && m_momentumPhase == PlatformWheelEventPhase::Ended);
 }
 
 inline bool PlatformWheelEvent::isGestureContinuation() const

Modified: trunk/Source/WebKit/ChangeLog (270424 => 270425)


--- trunk/Source/WebKit/ChangeLog	2020-12-04 06:45:17 UTC (rev 270424)
+++ trunk/Source/WebKit/ChangeLog	2020-12-04 07:10:03 UTC (rev 270425)
@@ -1,3 +1,20 @@
+2020-12-03  Simon Fraser  <[email protected]>
+
+        Only the first wheel event in a gesture should be cancelable
+        https://bugs.webkit.org/show_bug.cgi?id=218764
+        <rdar://problem/71248946>
+
+        Reviewed by Tim Horton.
+
+        In EventDispatcher::wheelEvent(), all wheel events now bounce through the scrolling
+        thread, even those destined for main thread scrolling. This allows the scrolling thread
+        to wait on a condition for the event to come back to the scrolling thread via
+        handleWheelEventAfterMainThread(), since we have to know whether content called
+        preventDefault() on the first event before sending subsequent events.
+
+        * WebProcess/WebPage/EventDispatcher.cpp:
+        (WebKit::EventDispatcher::wheelEvent):
+
 2020-12-03  Alex Christensen  <[email protected]>
 
         Introduce new download API

Modified: trunk/Source/WebKit/WebProcess/WebPage/EventDispatcher.cpp (270424 => 270425)


--- trunk/Source/WebKit/WebProcess/WebPage/EventDispatcher.cpp	2020-12-04 06:45:17 UTC (rev 270424)
+++ trunk/Source/WebKit/WebProcess/WebPage/EventDispatcher.cpp	2020-12-04 07:10:03 UTC (rev 270425)
@@ -136,12 +136,17 @@
             scrollingTree->setMainFrameCanRubberBand({ canRubberBandAtTop, canRubberBandAtRight, canRubberBandAtBottom, canRubberBandAtLeft });
 
         auto processingSteps = scrollingTree->determineWheelEventProcessing(platformWheelEvent);
-        if (!processingSteps.contains(WheelEventProcessingSteps::ScrollingThread))
-            return processingSteps;
 
         scrollingTree->willProcessWheelEvent();
 
         ScrollingThread::dispatch([scrollingTree, wheelEvent, platformWheelEvent, processingSteps, pageID, protectedThis = makeRef(*this)] {
+            if (processingSteps.contains(WheelEventProcessingSteps::MainThreadForScrolling)) {
+                scrollingTree->willSendEventToMainThread(platformWheelEvent);
+                protectedThis->dispatchWheelEventViaMainThread(pageID, wheelEvent, processingSteps);
+                scrollingTree->waitForEventToBeProcessedByMainThread(platformWheelEvent);
+                return;
+            }
+        
             auto result = scrollingTree->handleWheelEvent(platformWheelEvent, processingSteps);
 
             if (result.needsMainThreadProcessing()) {
@@ -157,18 +162,14 @@
 
         return processingSteps;
     }();
-
-    if (processingSteps.contains(WheelEventProcessingSteps::ScrollingThread))
-        return;
-
 #else
     UNUSED_PARAM(canRubberBandAtLeft);
     UNUSED_PARAM(canRubberBandAtRight);
     UNUSED_PARAM(canRubberBandAtTop);
     UNUSED_PARAM(canRubberBandAtBottom);
-#endif
 
     dispatchWheelEventViaMainThread(pageID, wheelEvent, processingSteps);
+#endif
 }
 
 #if ENABLE(MAC_GESTURE_EVENTS)
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to