Changeset 270425 in webkit


Ignore:
Timestamp:
Dec 3, 2020 11:10:03 PM (3 years ago)
Author:
Simon Fraser
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.
Location:
trunk
Files:
1 deleted
19 edited

Legend:

Unmodified
Added
Removed
  • trunk/LayoutTests/ChangeLog

    r270424 r270425  
     12020-12-03  Simon Fraser  <simon.fraser@apple.com>
     2
     3        Only the first wheel event in a gesture should be cancelable
     4        https://bugs.webkit.org/show_bug.cgi?id=218764
     5        <rdar://problem/71248946>
     6
     7        Reviewed by Tim Horton.
     8
     9        * fast/scrolling/mac/rubberband-overflow-in-wheel-region-root-jiggle.html: Make more robust.
     10        * platform/mac-wk2/fast/events/wheel/wheel-events-become-non-cancelable-expected.txt: Test now passes in WK2.
     11
    1122020-12-03  Lauro Moura  <lmoura@igalia.com>
    213
  • trunk/LayoutTests/fast/scrolling/latching/scroll-latched-nested-div.html

    r259121 r270425  
    1111}
    1212</style>
     13<script src="../../../resources/ui-helper.js"></script>
    1314<script src="../../../resources/js-test-pre.js"></script>
    1415<script>
     
    4445        else
    4546            testPassed("div did not receive wheel events during the second gesture.");
    46 
    47         finishJSTest();
    4847    }
    4948
    50     function checkForFirstScroll()
     49    async function doSecondScroll()
    5150    {
    5251        // 'parent' should not have scrolled, and the content of 'target' should
     
    8281        eventSender.mouseScrollByWithWheelAndMomentumPhases(0, -1, 'none', 'continue');
    8382        eventSender.mouseScrollByWithWheelAndMomentumPhases(0, 0, 'none', 'end');
    84         eventSender.callAfterScrollingCompletes(checkForSecondScroll);
     83
     84        await UIHelper.waitForScrollCompletion();
    8585    }
    8686
    87     function scrollTest()
     87    async function doFirstScroll()
    8888    {
    8989        pageScrollPositionBefore = document.scrollingElement.scrollTop;
     
    111111        eventSender.mouseScrollByWithWheelAndMomentumPhases(0, -10, 'none', 'continue');
    112112        eventSender.mouseScrollByWithWheelAndMomentumPhases(0, 0, 'none', 'end');
    113         eventSender.callAfterScrollingCompletes(checkForFirstScroll);
     113       
     114        await UIHelper.waitForScrollCompletion();
     115    }
     116
     117    async function scrollTest()
     118    {
     119        await doFirstScroll();
     120        await doSecondScroll();
     121        checkForSecondScroll();
     122        finishJSTest();
    114123    }
    115124
  • trunk/LayoutTests/fast/scrolling/mac/rubberband-overflow-in-wheel-region-root-jiggle.html

    r269360 r270425  
    5252            // Scroll down to latch, then up to rubberband.
    5353            eventSender.mouseScrollByWithWheelAndMomentumPhases(0, -1, "began", "none");
    54             eventSender.mouseScrollByWithWheelAndMomentumPhases(0, -10, "changed", "none");
    55             eventSender.mouseScrollByWithWheelAndMomentumPhases(0, -10, "changed", "none");
    56             eventSender.mouseScrollByWithWheelAndMomentumPhases(0, 12, "changed", "none");
    57             eventSender.mouseScrollByWithWheelAndMomentumPhases(0, 12, "changed", "none");
     54            await UIHelper.renderingUpdate();
     55            eventSender.mouseScrollByWithWheelAndMomentumPhases(0, -5, "changed", "none");
     56            await UIHelper.renderingUpdate();
     57            eventSender.mouseScrollByWithWheelAndMomentumPhases(0, -5, "changed", "none");
     58            await UIHelper.renderingUpdate();
     59            eventSender.mouseScrollByWithWheelAndMomentumPhases(0, 5, "changed", "none");
     60            await UIHelper.renderingUpdate();
     61            eventSender.mouseScrollByWithWheelAndMomentumPhases(0, 5, "changed", "none");
     62            await UIHelper.renderingUpdate();
     63            eventSender.mouseScrollByWithWheelAndMomentumPhases(0, 5, "changed", "none");
     64            await UIHelper.renderingUpdate();
    5865            eventSender.mouseScrollByWithWheelAndMomentumPhases(0, 0, "ended", "none");
    5966
  • trunk/Source/WebCore/ChangeLog

    r270421 r270425  
     12020-12-03  Simon Fraser  <simon.fraser@apple.com>
     2
     3        Only the first wheel event in a gesture should be cancelable
     4        https://bugs.webkit.org/show_bug.cgi?id=218764
     5        <rdar://problem/71248946>
     6
     7        Reviewed by Tim Horton.
     8
     9        Implement the WebKit2 version of r270312, where only the first wheel event in a gesture is
     10        cancelable.
     11
     12        When scrolling over an element with handlers, we do event handling on the main thread,
     13        so we can take the compute value of EventHandler's Optional<WheelScrollGestureState>
     14        from the first event and send it back to the scrolling thread.
     15
     16        However, the scrolling thread needs to block until this first event comes back from
     17        the main thread. To achieve this, EventDispatcher::wheelEvent() now dispaches
     18        main thread scrolls from the scrolling thread (not the dispatcher thread), and
     19        waits on m_waitingForBeganEventCondition with a 50ms timeout for that first event to
     20        come back.
     21
     22        In the normal case, main thread handling dispatches the event back to the scrolling
     23        thread for scrolling via handleWheelEventAfterMainThread(), and then calls
     24        wheelEventWasProcessedByMainThread() to signal the condition. If for some reason
     25        handleWheelEventAfterMainThread() doesn't get called (e.g. nothing was scrollable),
     26        then wheelEventWasProcessedByMainThread() still gets called to signal.
     27
     28        If m_waitingForBeganEventCondition times out, then the scrolling thread falls back
     29        to non-blocking behaviour (as if the first event was not canceled).
     30
     31        Finally, when we know the gesture will become non-blocking, we transition to running
     32        the scroll from the scrolling thread, which requires that we set up latching, hence
     33        the changes in ScrollingTreeLatchingController.
     34
     35        Tested by existing tests in fast/events/wheel.
     36
     37        * page/EventHandler.cpp:
     38        (WebCore::EventHandler::wheelEventWasProcessedByMainThread):
     39        (WebCore::EventHandler::handleWheelEventInScrollableArea):
     40        * page/WheelEventTestMonitor.cpp:
     41        (WebCore::operator<<):
     42        * page/WheelEventTestMonitor.h:
     43        * page/mac/EventHandlerMac.mm:
     44        (WebCore::EventHandler::processWheelEventForScrolling):
     45        * page/scrolling/ScrollingTree.cpp:
     46        (WebCore::ScrollingTree::determineWheelEventProcessing):
     47        (WebCore::ScrollingTree::setGestureState):
     48        (WebCore::ScrollingTree::gestureState):
     49        * page/scrolling/ScrollingTree.h:
     50        (WebCore::ScrollingTree::willSendEventToMainThread):
     51        (WebCore::ScrollingTree::waitForEventToBeProcessedByMainThread):
     52        * page/scrolling/ScrollingTreeLatchingController.cpp:
     53        (WebCore::ScrollingTreeLatchingController::receivedWheelEvent):
     54        (WebCore::ScrollingTreeLatchingController::nodeDidHandleEvent):
     55        * page/scrolling/ScrollingTreeLatchingController.h:
     56        * page/scrolling/ThreadedScrollingTree.cpp:
     57        (WebCore::ThreadedScrollingTree::handleWheelEventAfterMainThread):
     58        (WebCore::ThreadedScrollingTree::wheelEventWasProcessedByMainThread):
     59        (WebCore::ThreadedScrollingTree::willSendEventToMainThread):
     60        (WebCore::ThreadedScrollingTree::waitForEventToBeProcessedByMainThread):
     61        * page/scrolling/ThreadedScrollingTree.h:
     62        * page/scrolling/mac/ScrollingCoordinatorMac.mm:
     63        (WebCore::ScrollingCoordinatorMac::handleWheelEventForScrolling): Need to track deferral
     64        for WheelEventTestMonitor.
     65        (WebCore::ScrollingCoordinatorMac::wheelEventWasProcessedByMainThread): This is now synchronous
     66        to the scrolling thread so no need for the deferrer.
     67        (WebCore::nextDeferIdentifier): Deleted.
     68        * page/scrolling/nicosia/ScrollingCoordinatorNicosia.cpp:
     69        (WebCore::ScrollingCoordinatorNicosia::wheelEventWasProcessedByMainThread):
     70        * platform/cocoa/ScrollController.mm:
     71        (WebCore::ScrollController::handleWheelEvent):
     72
    1732020-12-03  Kate Cheney  <katherine_cheney@apple.com>
    274
  • trunk/Source/WebCore/page/EventHandler.cpp

    r270389 r270425  
    27232723}
    27242724
    2725 void EventHandler::wheelEventWasProcessedByMainThread(const PlatformWheelEvent&, OptionSet<EventHandling>)
    2726 {
     2725void EventHandler::wheelEventWasProcessedByMainThread(const PlatformWheelEvent& wheelEvent, OptionSet<EventHandling> eventHandling)
     2726{
     2727    updateWheelGestureState(wheelEvent, eventHandling);
     2728
     2729#if ENABLE(ASYNC_SCROLLING)
     2730    FrameView* view = m_frame.view();
     2731    if (auto scrollingCoordinator = m_frame.page()->scrollingCoordinator()) {
     2732        if (scrollingCoordinator->coordinatesScrollingForFrameView(*view))
     2733            scrollingCoordinator->wheelEventWasProcessedByMainThread(wheelEvent, m_wheelScrollGestureState);
     2734    }
     2735#endif
    27272736}
    27282737
     
    29832992{
    29842993    auto gestureState = updateWheelGestureState(wheelEvent, eventHandling);
    2985     LOG_WITH_STREAM(Scrolling, stream << "EventHandler::handleWheelEventInScrollableArea() - eventHandling " << eventHandling << " -> gesture state " << gestureState);
     2994    LOG_WITH_STREAM(Scrolling, stream << "EventHandler::handleWheelEventInScrollableArea() " << scrollableArea << " - eventHandling " << eventHandling << " -> gesture state " << gestureState);
    29862995    return scrollableArea.handleWheelEventForScrolling(wheelEvent, gestureState);
    29872996}
  • trunk/Source/WebCore/page/WheelEventTestMonitor.cpp

    r270338 r270425  
    173173    case WheelEventTestMonitor::HandlingWheelEvent: ts << "handling wheel event"; break;
    174174    case WheelEventTestMonitor::HandlingWheelEventOnMainThread: ts << "handling wheel event on main thread"; break;
    175     case WheelEventTestMonitor::ReportDOMEventHandling: ts << "report DOM event handling"; break;
     175    case WheelEventTestMonitor::PostMainThreadWheelEventHandling: ts << "post-main thread event handling"; break;
    176176    case WheelEventTestMonitor::RubberbandInProgress: ts << "rubberbanding"; break;
    177177    case WheelEventTestMonitor::ScrollSnapInProgress: ts << "scroll-snapping"; break;
  • trunk/Source/WebCore/page/WheelEventTestMonitor.h

    r270338 r270425  
    4949   
    5050    enum DeferReason {
    51         HandlingWheelEvent              = 1 << 0,
    52         HandlingWheelEventOnMainThread  = 1 << 1,
    53         ReportDOMEventHandling          = 1 << 2,
    54         RubberbandInProgress            = 1 << 3,
    55         ScrollSnapInProgress            = 1 << 4,
    56         ScrollingThreadSyncNeeded       = 1 << 5,
    57         ContentScrollInProgress         = 1 << 6,
    58         RequestedScrollPosition         = 1 << 7,
     51        HandlingWheelEvent                  = 1 << 0,
     52        HandlingWheelEventOnMainThread      = 1 << 1,
     53        PostMainThreadWheelEventHandling    = 1 << 2,
     54        RubberbandInProgress                = 1 << 3,
     55        ScrollSnapInProgress                = 1 << 4,
     56        ScrollingThreadSyncNeeded           = 1 << 5,
     57        ContentScrollInProgress             = 1 << 6,
     58        RequestedScrollPosition             = 1 << 7,
    5959    };
    6060    typedef const void* ScrollableAreaIdentifier;
  • trunk/Source/WebCore/page/mac/EventHandlerMac.mm

    r270389 r270425  
    955955        return didHandleWheelEvent;
    956956    }
    957    
     957
    958958    bool didHandleEvent = handleWheelEventInScrollableArea(wheelEvent, *view, eventHandling);
    959959    m_isHandlingWheelEvent = false;
  • trunk/Source/WebCore/page/scrolling/ScrollingTree.cpp

    r270156 r270425  
    7474        return latchedNodeAndSteps->processingSteps;
    7575    }
     76    if (wheelEvent.isGestureStart() || wheelEvent.isNonGestureEvent())
     77        m_treeState.gestureState = WTF::nullopt;
    7678
    7779    auto processingSteps = [&]() -> OptionSet<WheelEventProcessingSteps> {
     
    9799#if ENABLE(WHEEL_EVENT_REGIONS)
    98100        auto eventListenerTypes = eventListenerRegionTypesForPoint(position);
    99         if (eventListenerTypes.contains(EventListenerRegionType::NonPassiveWheel))
     101        if (eventListenerTypes.contains(EventListenerRegionType::NonPassiveWheel)) {
     102            if (m_treeState.gestureState.valueOr(WheelScrollGestureState::Blocking) == WheelScrollGestureState::NonBlocking)
     103                return { WheelEventProcessingSteps::ScrollingThread, WheelEventProcessingSteps::MainThreadForNonBlockingDOMEventDispatch };
     104
    100105            return { WheelEventProcessingSteps::MainThreadForScrolling, WheelEventProcessingSteps::MainThreadForBlockingDOMEventDispatch };
     106        }
    101107
    102108        if (eventListenerTypes.contains(EventListenerRegionType::Wheel))
     
    483489}
    484490
     491void ScrollingTree::setGestureState(Optional<WheelScrollGestureState> gestureState)
     492{
     493    LockHolder lock(m_treeStateMutex);
     494    m_treeState.gestureState = gestureState;
     495}
     496
     497Optional<WheelScrollGestureState> ScrollingTree::gestureState()
     498{
     499    LockHolder lock(m_treeStateMutex);
     500    return m_treeState.gestureState;
     501}
     502
    485503TrackingType ScrollingTree::eventTrackingTypeForPoint(const AtomString& eventName, IntPoint p)
    486504{
  • trunk/Source/WebCore/page/scrolling/ScrollingTree.h

    r270338 r270425  
    211211    virtual void unlockLayersForHitTesting() { }
    212212
     213    virtual void willSendEventToMainThread(const PlatformWheelEvent&) { }
     214    virtual void waitForEventToBeProcessedByMainThread(const PlatformWheelEvent&) { };
     215
    213216    Lock& treeMutex() { return m_treeMutex; }
    214217
     
    246249    void setMainFrameScrollPosition(FloatPoint);
    247250
     251    void setGestureState(Optional<WheelScrollGestureState>);
     252    Optional<WheelScrollGestureState> gestureState();
     253
    248254    Optional<unsigned> nominalFramesPerSecond();
    249255
     
    286292        PlatformDisplayID displayID { 0 };
    287293        Optional<unsigned> nominalFramesPerSecond;
     294        Optional<WheelScrollGestureState> gestureState;
    288295        HashSet<ScrollingNodeID> nodesWithActiveRubberBanding;
    289296        HashSet<ScrollingNodeID> nodesWithActiveScrollSnap;
  • trunk/Source/WebCore/page/scrolling/ScrollingTreeLatchingController.cpp

    r269973 r270425  
    4242ScrollingTreeLatchingController::ScrollingTreeLatchingController() = default;
    4343
    44 void ScrollingTreeLatchingController::receivedWheelEvent(const PlatformWheelEvent& wheelEvent, OptionSet<WheelEventProcessingSteps>, bool allowLatching)
     44void ScrollingTreeLatchingController::receivedWheelEvent(const PlatformWheelEvent& wheelEvent, OptionSet<WheelEventProcessingSteps> processingSteps, bool allowLatching)
    4545{
    4646    if (!allowLatching)
     
    4848
    4949    LockHolder locker(m_latchedNodeMutex);
    50     if (wheelEvent.isGestureStart() && m_latchedNodeAndSteps && !latchedNodeIsRelevant()) {
    51         LOG_WITH_STREAM(ScrollLatching, stream << "ScrollingTreeLatchingController " << this << " receivedWheelEvent - " << (MonotonicTime::now() - m_lastLatchedNodeInterationTime).milliseconds() << "ms since last event, clearing latched node");
    52         m_latchedNodeAndSteps.reset();
     50    if (wheelEvent.isGestureStart() && !latchedNodeIsRelevant()) {
     51        if (m_latchedNodeAndSteps) {
     52            LOG_WITH_STREAM(ScrollLatching, stream << "ScrollingTreeLatchingController " << this << " receivedWheelEvent - " << (MonotonicTime::now() - m_lastLatchedNodeInterationTime).milliseconds() << "ms since last event, clearing latched node");
     53            m_latchedNodeAndSteps.reset();
     54        }
     55        m_processingStepsForCurrentGesture = processingSteps;
    5356    }
    5457}
     
    97100    }
    98101
    99     if (wheelEvent.delta().isZero() || !wheelEvent.isGestureStart())
     102    auto shouldLatch = [&]() {
     103        if (wheelEvent.delta().isZero())
     104            return false;
     105
     106        if (wheelEvent.isGestureStart())
     107            return true;
     108
     109        if (!wheelEvent.isGestureContinuation())
     110            return false;
     111
     112        if (m_processingStepsForCurrentGesture.valueOr(OptionSet<WheelEventProcessingSteps> { }).contains(WheelEventProcessingSteps::MainThreadForScrolling) && processingSteps.contains(WheelEventProcessingSteps::ScrollingThread))
     113            return true;
     114
     115        return false;
     116    };
     117   
     118    if (!shouldLatch())
    100119        return;
     120
     121    m_processingStepsForCurrentGesture = processingSteps;
    101122
    102123    LOG_WITH_STREAM(ScrollLatching, stream << "ScrollingTreeLatchingController " << this << " nodeDidHandleEvent: latching to " << scrollingNodeID);
  • trunk/Source/WebCore/page/scrolling/ScrollingTreeLatchingController.h

    r269973 r270425  
    6565    mutable Lock m_latchedNodeMutex;
    6666    Optional<ScrollingNodeAndProcessingSteps> m_latchedNodeAndSteps;
     67    Optional<OptionSet<WheelEventProcessingSteps>> m_processingStepsForCurrentGesture;
    6768    MonotonicTime m_lastLatchedNodeInterationTime;
    6869};
  • trunk/Source/WebCore/page/scrolling/ThreadedScrollingTree.cpp

    r270389 r270425  
    6565}
    6666
    67 bool ThreadedScrollingTree::handleWheelEventAfterMainThread(const PlatformWheelEvent& wheelEvent, ScrollingNodeID targetNodeID, Optional<WheelScrollGestureState>)
    68 {
    69     LOG_WITH_STREAM(Scrolling, stream << "ThreadedScrollingTree::handleWheelEventAfterMainThread " << wheelEvent << " node " << targetNodeID);
     67bool ThreadedScrollingTree::handleWheelEventAfterMainThread(const PlatformWheelEvent& wheelEvent, ScrollingNodeID targetNodeID, Optional<WheelScrollGestureState> gestureState)
     68{
     69    ASSERT(ScrollingThread::isCurrentThread());
     70
     71    LOG_WITH_STREAM(Scrolling, stream << "ThreadedScrollingTree::handleWheelEventAfterMainThread " << wheelEvent << " node " << targetNodeID << " gestureState " << gestureState);
    7072
    7173    LockHolder locker(m_treeMutex);
    7274
    73     SetForScope<bool> disallowLatchingScope(m_allowLatching, false);
     75    bool allowLatching = false;
     76    OptionSet<WheelEventProcessingSteps> processingSteps;
     77    if (gestureState.valueOr(WheelScrollGestureState::Blocking) == WheelScrollGestureState::NonBlocking) {
     78        allowLatching = true;
     79        processingSteps = { WheelEventProcessingSteps::ScrollingThread, WheelEventProcessingSteps::MainThreadForNonBlockingDOMEventDispatch };
     80    }
     81
     82    SetForScope<bool> disallowLatchingScope(m_allowLatching, allowLatching);
    7483    RefPtr<ScrollingTreeNode> targetNode = nodeForID(targetNodeID);
    75     auto result = handleWheelEventWithNode(wheelEvent, { }, targetNode.get(), EventTargeting::NodeOnly);
     84    auto result = handleWheelEventWithNode(wheelEvent, processingSteps, targetNode.get(), EventTargeting::NodeOnly);
    7685    return result.wasHandled;
    7786}
    7887
    79 void ThreadedScrollingTree::wheelEventWasProcessedByMainThread(const PlatformWheelEvent&, Optional<WheelScrollGestureState>)
    80 {
    81     // FIXME: Set state based on EventHandling flags.
     88void ThreadedScrollingTree::wheelEventWasProcessedByMainThread(const PlatformWheelEvent& wheelEvent, Optional<WheelScrollGestureState> gestureState)
     89{
     90    LOG_WITH_STREAM(Scrolling, stream << "ThreadedScrollingTree::wheelEventWasProcessedByMainThread - gestureState " << gestureState);
     91
     92    ASSERT(isMainThread());
     93   
     94    LockHolder locker(m_treeMutex);
     95    if (m_receivedBeganEventFromMainThread || !wheelEvent.isGestureStart())
     96        return;
     97
     98    setGestureState(gestureState);
     99
     100    m_receivedBeganEventFromMainThread = true;
     101    m_waitingForBeganEventCondition.notifyOne();
     102}
     103
     104void ThreadedScrollingTree::willSendEventToMainThread(const PlatformWheelEvent&)
     105{
     106    ASSERT(ScrollingThread::isCurrentThread());
     107
     108    LockHolder locker(m_treeMutex);
     109    m_receivedBeganEventFromMainThread = false;
     110}
     111
     112void ThreadedScrollingTree::waitForEventToBeProcessedByMainThread(const PlatformWheelEvent& wheelEvent)
     113{
     114    ASSERT(ScrollingThread::isCurrentThread());
     115
     116    if (!wheelEvent.isGestureStart())
     117        return;
     118
     119    LockHolder locker(m_treeMutex);
     120
     121    static constexpr auto maxAllowableMainThreadDelay = 50_ms;
     122    auto startTime = MonotonicTime::now();
     123    auto timeoutTime = startTime + maxAllowableMainThreadDelay;
     124
     125    bool receivedEvent = m_waitingForBeganEventCondition.waitUntil(m_treeMutex, timeoutTime, [&] {
     126        return m_receivedBeganEventFromMainThread;
     127    });
     128
     129    if (!receivedEvent) {
     130        // Timed out, go asynchronous.
     131        setGestureState(WheelScrollGestureState::NonBlocking);
     132    }
     133
     134    LOG_WITH_STREAM(Scrolling, stream << "ThreadedScrollingTree::waitForBeganEventFromMainThread done - timed out " << !receivedEvent << " gesture state is " << gestureState());
    82135}
    83136
  • trunk/Source/WebCore/page/scrolling/ThreadedScrollingTree.h

    r270389 r270425  
    5151    void wheelEventWasProcessedByMainThread(const PlatformWheelEvent&, Optional<WheelScrollGestureState>);
    5252
     53    WEBCORE_EXPORT void willSendEventToMainThread(const PlatformWheelEvent&) final;
     54    WEBCORE_EXPORT void waitForEventToBeProcessedByMainThread(const PlatformWheelEvent&) final;
     55
    5356    void invalidate() override;
    5457
     
    8689    void displayDidRefreshOnScrollingThread();
    8790    void waitForRenderingUpdateCompletionOrTimeout();
    88    
     91
    8992    bool canUpdateLayersOnScrollingThread() const;
    9093
     
    104107    Condition m_stateCondition;
    105108
     109    bool m_receivedBeganEventFromMainThread { false };
     110    Condition m_waitingForBeganEventCondition;
     111
    106112    // Dynamically allocated because it has to use the ScrollingThread's runloop.
    107113    std::unique_ptr<RunLoop::Timer<ThreadedScrollingTree>> m_delayedRenderingUpdateDetectionTimer;
  • trunk/Source/WebCore/page/scrolling/mac/ScrollingCoordinatorMac.mm

    r270389 r270425  
    8282        return false;
    8383
    84     LOG_WITH_STREAM(Scrolling, stream << "ScrollingCoordinatorMac::handleWheelEventForScrolling - sending event to scrolling thread, node " << targetNodeID << " gestureState " << gestureState);
    85    
     84    LOG_WITH_STREAM(Scrolling, stream << "ScrollingCoordinatorMac::handleWheelEventForScrolling " << wheelEvent << " - sending event to scrolling thread, node " << targetNodeID << " gestureState " << gestureState);
     85
     86    auto deferrer = WheelEventTestMonitorCompletionDeferrer { m_page->wheelEventTestMonitor().get(), reinterpret_cast<WheelEventTestMonitor::ScrollableAreaIdentifier>(targetNodeID), WheelEventTestMonitor::PostMainThreadWheelEventHandling };
     87
    8688    RefPtr<ThreadedScrollingTree> threadedScrollingTree = downcast<ThreadedScrollingTree>(scrollingTree());
    87     ScrollingThread::dispatch([threadedScrollingTree, wheelEvent, targetNodeID, gestureState] {
     89    ScrollingThread::dispatch([threadedScrollingTree, wheelEvent, targetNodeID, gestureState, deferrer = WTFMove(deferrer)] {
    8890        threadedScrollingTree->handleWheelEventAfterMainThread(wheelEvent, targetNodeID, gestureState);
    8991    });
     
    9193}
    9294
    93 static uint64_t nextDeferIdentifier()
    94 {
    95     static uint64_t deferIdentifier;
    96     return ++deferIdentifier;
    97 }
    98 
    9995void ScrollingCoordinatorMac::wheelEventWasProcessedByMainThread(const PlatformWheelEvent& wheelEvent, Optional<WheelScrollGestureState> gestureState)
    10096{
    101     auto deferrer = WheelEventTestMonitorCompletionDeferrer { m_page->wheelEventTestMonitor().get(), reinterpret_cast<WheelEventTestMonitor::ScrollableAreaIdentifier>(nextDeferIdentifier()), WheelEventTestMonitor::ReportDOMEventHandling };
     97    LOG_WITH_STREAM(Scrolling, stream << "ScrollingCoordinatorMac::wheelEventWasProcessedByMainThread " << gestureState);
    10298
    10399    RefPtr<ThreadedScrollingTree> threadedScrollingTree = downcast<ThreadedScrollingTree>(scrollingTree());
    104     ScrollingThread::dispatch([threadedScrollingTree, wheelEvent, gestureState, deferrer = WTFMove(deferrer)] {
    105         threadedScrollingTree->wheelEventWasProcessedByMainThread(wheelEvent, gestureState);
    106     });
     100    threadedScrollingTree->wheelEventWasProcessedByMainThread(wheelEvent, gestureState);
    107101}
    108102
  • trunk/Source/WebCore/page/scrolling/nicosia/ScrollingCoordinatorNicosia.cpp

    r270389 r270425  
    8484void ScrollingCoordinatorNicosia::wheelEventWasProcessedByMainThread(const PlatformWheelEvent& wheelEvent, Optional<WheelScrollGestureState> gestureState)
    8585{
    86     ScrollingThread::dispatch([threadedScrollingTree = makeRef(downcast<ThreadedScrollingTree>(*scrollingTree())), wheelEvent, gestureState] {
    87         threadedScrollingTree->wheelEventWasProcessedByMainThread(wheelEvent, gestureState);
    88     });
     86    RefPtr<ThreadedScrollingTree> threadedScrollingTree = downcast<ThreadedScrollingTree>(scrollingTree());
     87    threadedScrollingTree->wheelEventWasProcessedByMainThread(wheelEvent, gestureState);
    8988}
    9089
  • trunk/Source/WebCore/platform/PlatformWheelEvent.h

    r270389 r270425  
    209209        || m_momentumPhase == PlatformWheelEventPhase::Began
    210210        || m_momentumPhase == PlatformWheelEventPhase::Changed
    211         || (m_phase == PlatformWheelEventPhase::Ended && m_momentumPhase == PlatformWheelEventPhase::None);
     211        || (m_phase == PlatformWheelEventPhase::Ended && m_momentumPhase == PlatformWheelEventPhase::None)
     212        || (m_phase == PlatformWheelEventPhase::None && m_momentumPhase == PlatformWheelEventPhase::Ended);
    212213}
    213214
  • trunk/Source/WebKit/ChangeLog

    r270422 r270425  
     12020-12-03  Simon Fraser  <simon.fraser@apple.com>
     2
     3        Only the first wheel event in a gesture should be cancelable
     4        https://bugs.webkit.org/show_bug.cgi?id=218764
     5        <rdar://problem/71248946>
     6
     7        Reviewed by Tim Horton.
     8
     9        In EventDispatcher::wheelEvent(), all wheel events now bounce through the scrolling
     10        thread, even those destined for main thread scrolling. This allows the scrolling thread
     11        to wait on a condition for the event to come back to the scrolling thread via
     12        handleWheelEventAfterMainThread(), since we have to know whether content called
     13        preventDefault() on the first event before sending subsequent events.
     14
     15        * WebProcess/WebPage/EventDispatcher.cpp:
     16        (WebKit::EventDispatcher::wheelEvent):
     17
    1182020-12-03  Alex Christensen  <achristensen@webkit.org>
    219
  • trunk/Source/WebKit/WebProcess/WebPage/EventDispatcher.cpp

    r270292 r270425  
    137137
    138138        auto processingSteps = scrollingTree->determineWheelEventProcessing(platformWheelEvent);
    139         if (!processingSteps.contains(WheelEventProcessingSteps::ScrollingThread))
    140             return processingSteps;
    141139
    142140        scrollingTree->willProcessWheelEvent();
    143141
    144142        ScrollingThread::dispatch([scrollingTree, wheelEvent, platformWheelEvent, processingSteps, pageID, protectedThis = makeRef(*this)] {
     143            if (processingSteps.contains(WheelEventProcessingSteps::MainThreadForScrolling)) {
     144                scrollingTree->willSendEventToMainThread(platformWheelEvent);
     145                protectedThis->dispatchWheelEventViaMainThread(pageID, wheelEvent, processingSteps);
     146                scrollingTree->waitForEventToBeProcessedByMainThread(platformWheelEvent);
     147                return;
     148            }
     149       
    145150            auto result = scrollingTree->handleWheelEvent(platformWheelEvent, processingSteps);
    146151
     
    158163        return processingSteps;
    159164    }();
    160 
    161     if (processingSteps.contains(WheelEventProcessingSteps::ScrollingThread))
    162         return;
    163 
    164165#else
    165166    UNUSED_PARAM(canRubberBandAtLeft);
     
    167168    UNUSED_PARAM(canRubberBandAtTop);
    168169    UNUSED_PARAM(canRubberBandAtBottom);
    169 #endif
    170170
    171171    dispatchWheelEventViaMainThread(pageID, wheelEvent, processingSteps);
     172#endif
    172173}
    173174
Note: See TracChangeset for help on using the changeset viewer.