Changeset 266333 in webkit


Ignore:
Timestamp:
Aug 30, 2020 10:25:15 AM (4 years ago)
Author:
Simon Fraser
Message:

Rewrite main thread scroll latching logic
https://bugs.webkit.org/show_bug.cgi?id=215979

Reviewed by Tim Horton.
Source/WebCore:

The existing main thread wheel event handling and latching logic had a number of issues,
some of which were indicated via the FIXME comments added in r266016:

  • It tracked scrollable containers as ContainerNodes rather than ScrollableAreas
  • It fetched and used latched state from a different frame, causing an EventHandler to end up scrolling some unrelated frame.
  • Overflow scrolling ignored latched state.
  • The latching stack's purpose was unclear.

This patch fixes those issues. The design is as follow:

  • Latching logic is moved into ScrollLatchingController, which is owned by Page.
  • ScrollLatchingController owns a stack of FrameState.
  • When receiving a wheel event, determineWheelEventTarget() identifies the target ScrollableArea, if any, for that frame only.
  • As hit-testing descends into subframes, state is pushed onto ScrollLatchingController's state stack. Frames with potential scrollers have a non-null ScrollableArea in their state.
  • The latched ScrollableArea is the top non-null ScrollableArea in this stack.
  • EventHandler consults ScrollLatchingController for select, overflow and frame scrolling.

This change fixes an issue where scrolling over an overflow:scroll in the non-fast scrollable
region would rubber-band the overflow, rather than the main page (tested by the adjusted
fast/scrolling/mac/rubberband-overflow-in-wheel-region.html).

Tests: fast/scrolling/latching/latched-scroll-remove-iframe.html

  • Sources.txt:
  • WebCore.xcodeproj/project.pbxproj:
  • dom/Element.cpp:

(WebCore::Element::removedFromAncestor):

  • page/EventHandler.cpp:

(WebCore::EventHandler::EventHandler):
(WebCore::EventHandler::determineWheelEventTarget):
(WebCore::EventHandler::processWheelEventForScrolling):
(WebCore::EventHandler::platformCompletePlatformWidgetWheelEvent):
(WebCore::EventHandler::processWheelEventForScrollSnap):
(WebCore::EventHandler::completeWidgetWheelEvent):
(WebCore::EventHandler::handleWheelEvent):
(WebCore::EventHandler::clearLatchedState):
(WebCore::EventHandler::defaultWheelEventHandler):
(WebCore::EventHandler::clearLatchedStateTimerFired): Deleted.
(WebCore::EventHandler::clearOrScheduleClearingLatchedStateIfNeeded): Deleted.

  • page/EventHandler.h:
  • page/Page.cpp:

(WebCore::Page::startMonitoringWheelEvents):
(WebCore::Page::scrollLatchingController):
(WebCore::Page::scrollLatchingControllerIfExists):
(WebCore::Page::latchingState): Deleted.
(WebCore::Page::pushNewLatchingState): Deleted.
(WebCore::Page::resetLatchingState): Deleted.
(WebCore::Page::popLatchingState): Deleted.
(WebCore::Page::removeLatchingStateForTarget): Deleted.

  • page/Page.h:

(WebCore::Page::latchingStateStack const): Deleted.

  • page/mac/EventHandlerMac.mm:

(WebCore::EventHandler::determineWheelEventTarget):
(WebCore::EventHandler::processWheelEventForScrolling):
(WebCore::EventHandler::platformCompletePlatformWidgetWheelEvent):
(WebCore::EventHandler::processWheelEventForScrollSnap):
(WebCore::deltaIsPredominantlyVertical): Deleted.
(WebCore::scrolledToEdgeInDominantDirection): Deleted.
(WebCore::latchingIsLockedToPlatformFrame): Deleted.
(WebCore::latchingIsLockedToAncestorOfThisFrame): Deleted.
(WebCore::latchedToFrameOrBody): Deleted.
(WebCore::EventHandler::clearOrScheduleClearingLatchedStateIfNeeded): Deleted.
(WebCore::frameViewForLatchingState): Deleted.

  • page/scrolling/ScrollLatchingController.cpp: Added.

(WebCore::ScrollLatchingController::ScrollLatchingController):
(WebCore::ScrollLatchingController::clear):
(WebCore::ScrollLatchingController::clearOrScheduleClearIfNeeded):
(WebCore::ScrollLatchingController::clearTimerFired):
(WebCore::ScrollLatchingController::receivedWheelEvent):
(WebCore::ScrollLatchingController::latchingAllowsScrollingInFrame const):
(WebCore::ScrollLatchingController::updateLatchingStateForFrame):
(WebCore::ScrollLatchingController::getLatchingStateForFrame const):
(WebCore::ScrollLatchingController::removeLatchingStateForTarget):
(WebCore::ScrollLatchingController::removeLatchingStateForFrame):
(WebCore::deltaIsPredominantlyVertical):
(WebCore::ScrollLatchingController::shouldLatchToScrollableArea const):
(WebCore::ScrollLatchingController::hasStateForFrame const):
(WebCore::ScrollLatchingController::stateForFrame):
(WebCore::ScrollLatchingController::stateForFrame const):
(WebCore::ScrollLatchingController::dump const):
(WebCore::operator<<):

  • page/scrolling/ScrollLatchingController.h: Added.

(WebCore::ScrollLatchingController::cumulativeEventDelta const):

  • page/scrolling/ScrollLatchingState.cpp: Removed.
  • page/scrolling/ScrollLatchingState.h: Removed.
  • page/scrolling/ScrollingTreeLatchingController.cpp:

(WebCore::ScrollingTreeLatchingController::receivedWheelEvent):
(WebCore::ScrollingTreeLatchingController::nodeDidHandleEvent):

  • page/scrolling/ThreadedScrollingTree.cpp:

(WebCore::ThreadedScrollingTree::handleWheelEventAfterMainThread):

  • page/scrolling/mac/ScrollingTreeScrollingNodeDelegateMac.mm:

(WebCore::ScrollingTreeScrollingNodeDelegateMac::allowsHorizontalStretching const):
(WebCore::ScrollingTreeScrollingNodeDelegateMac::allowsVerticalStretching const):
(WebCore::newGestureIsStarting): Deleted.

  • platform/PlatformWheelEvent.h:

(WebCore::PlatformWheelEvent::isGestureStart const):
(WebCore::PlatformWheelEvent::isGestureContinuation const):
(WebCore::PlatformWheelEvent::shouldResetLatching const):
(WebCore::PlatformWheelEvent::isNonGestureEvent const):
(WebCore::PlatformWheelEvent::shouldConsiderLatching const): Deleted. Renamed to isGestureStart(). This class
should not prescribe latching behaviors.

LayoutTests:

Add a test for iframe unparenting in the middle of a latched scroll.

  • fast/scrolling/latching/latched-scroll-remove-iframe.html: Added.
  • fast/scrolling/latching/scroll-nested-iframe.html: 1000ms -> 0ms
  • fast/scrolling/mac/rubberband-overflow-in-wheel-region.html: Test needs to latch the overflow by scrolling down then up.
Location:
trunk
Files:
3 added
2 deleted
16 edited

Legend:

Unmodified
Added
Removed
  • trunk/LayoutTests/ChangeLog

    r266332 r266333  
     12020-08-29  Simon Fraser  <simon.fraser@apple.com>
     2
     3        Rewrite main thread scroll latching logic
     4        https://bugs.webkit.org/show_bug.cgi?id=215979
     5
     6        Reviewed by Tim Horton.
     7
     8        Add a test for iframe unparenting in the middle of a latched scroll.
     9
     10        * fast/scrolling/latching/latched-scroll-remove-iframe.html: Added.
     11        * fast/scrolling/latching/scroll-nested-iframe.html: 1000ms -> 0ms
     12        * fast/scrolling/mac/rubberband-overflow-in-wheel-region.html: Test needs to latch the overflow by scrolling down then up.
     13
    1142020-08-30  Youenn Fablet  <youenn@apple.com>
    215
  • trunk/LayoutTests/fast/scrolling/latching/scroll-nested-iframe.html

    r259121 r266333  
    6363            description("Tests that iframe does scroll when inner iframe is NOT scrollable.");
    6464            if (window.eventSender) {
    65                 setTimeout(scrollTest, 1000);
     65                setTimeout(scrollTest, 0);
    6666                return;
    6767            }
  • trunk/LayoutTests/fast/scrolling/mac/rubberband-overflow-in-wheel-region.html

    r265820 r266333  
    4747        {
    4848            eventSender.mouseMoveTo(100, 100);
    49             eventSender.mouseScrollByWithWheelAndMomentumPhases(0, 1, "began", "none");
    50             eventSender.mouseScrollByWithWheelAndMomentumPhases(0, 20, "changed", "none");
     49            // Scroll down to latch, then up to rubberband.
     50            eventSender.mouseScrollByWithWheelAndMomentumPhases(0, -1, "began", "none");
     51            eventSender.mouseScrollByWithWheelAndMomentumPhases(0, -10, "changed", "none");
     52            eventSender.mouseScrollByWithWheelAndMomentumPhases(0, -10, "changed", "none");
     53            eventSender.mouseScrollByWithWheelAndMomentumPhases(0, 12, "changed", "none");
     54            eventSender.mouseScrollByWithWheelAndMomentumPhases(0, 12, "changed", "none");
    5155            eventSender.mouseScrollByWithWheelAndMomentumPhases(0, 0, "ended", "none");
    5256            await UIHelper.renderingUpdate();
  • trunk/Source/WebCore/ChangeLog

    r266332 r266333  
     12020-08-29  Simon Fraser  <simon.fraser@apple.com>
     2
     3        Rewrite main thread scroll latching logic
     4        https://bugs.webkit.org/show_bug.cgi?id=215979
     5
     6        Reviewed by Tim Horton.
     7       
     8        The existing main thread wheel event handling and latching logic had a number of issues,
     9        some of which were indicated via the FIXME comments added in r266016:
     10        - It tracked scrollable containers as ContainerNodes rather than ScrollableAreas
     11        - It fetched and used latched state from a different frame, causing an EventHandler
     12          to end up scrolling some unrelated frame.
     13        - Overflow scrolling ignored latched state.
     14        - The latching stack's purpose was unclear.
     15       
     16        This patch fixes those issues. The design is as follow:
     17        - Latching logic is moved into ScrollLatchingController, which is owned by Page.
     18        - ScrollLatchingController owns a stack of FrameState.
     19        - When receiving a wheel event, determineWheelEventTarget() identifies the target
     20          ScrollableArea, if any, for that frame only.
     21        - As hit-testing descends into subframes, state is pushed onto ScrollLatchingController's
     22          state stack. Frames with potential scrollers have a non-null ScrollableArea in their state.
     23        - The latched ScrollableArea is the top non-null ScrollableArea in this stack.
     24        - EventHandler consults ScrollLatchingController for select, overflow and frame scrolling.
     25       
     26        This change fixes an issue where scrolling over an overflow:scroll in the non-fast scrollable
     27        region would rubber-band the overflow, rather than the main page (tested by the adjusted
     28        fast/scrolling/mac/rubberband-overflow-in-wheel-region.html).
     29
     30        Tests: fast/scrolling/latching/latched-scroll-remove-iframe.html
     31
     32        * Sources.txt:
     33        * WebCore.xcodeproj/project.pbxproj:
     34        * dom/Element.cpp:
     35        (WebCore::Element::removedFromAncestor):
     36        * page/EventHandler.cpp:
     37        (WebCore::EventHandler::EventHandler):
     38        (WebCore::EventHandler::determineWheelEventTarget):
     39        (WebCore::EventHandler::processWheelEventForScrolling):
     40        (WebCore::EventHandler::platformCompletePlatformWidgetWheelEvent):
     41        (WebCore::EventHandler::processWheelEventForScrollSnap):
     42        (WebCore::EventHandler::completeWidgetWheelEvent):
     43        (WebCore::EventHandler::handleWheelEvent):
     44        (WebCore::EventHandler::clearLatchedState):
     45        (WebCore::EventHandler::defaultWheelEventHandler):
     46        (WebCore::EventHandler::clearLatchedStateTimerFired): Deleted.
     47        (WebCore::EventHandler::clearOrScheduleClearingLatchedStateIfNeeded): Deleted.
     48        * page/EventHandler.h:
     49        * page/Page.cpp:
     50        (WebCore::Page::startMonitoringWheelEvents):
     51        (WebCore::Page::scrollLatchingController):
     52        (WebCore::Page::scrollLatchingControllerIfExists):
     53        (WebCore::Page::latchingState): Deleted.
     54        (WebCore::Page::pushNewLatchingState): Deleted.
     55        (WebCore::Page::resetLatchingState): Deleted.
     56        (WebCore::Page::popLatchingState): Deleted.
     57        (WebCore::Page::removeLatchingStateForTarget): Deleted.
     58        * page/Page.h:
     59        (WebCore::Page::latchingStateStack const): Deleted.
     60        * page/mac/EventHandlerMac.mm:
     61        (WebCore::EventHandler::determineWheelEventTarget):
     62        (WebCore::EventHandler::processWheelEventForScrolling):
     63        (WebCore::EventHandler::platformCompletePlatformWidgetWheelEvent):
     64        (WebCore::EventHandler::processWheelEventForScrollSnap):
     65        (WebCore::deltaIsPredominantlyVertical): Deleted.
     66        (WebCore::scrolledToEdgeInDominantDirection): Deleted.
     67        (WebCore::latchingIsLockedToPlatformFrame): Deleted.
     68        (WebCore::latchingIsLockedToAncestorOfThisFrame): Deleted.
     69        (WebCore::latchedToFrameOrBody): Deleted.
     70        (WebCore::EventHandler::clearOrScheduleClearingLatchedStateIfNeeded): Deleted.
     71        (WebCore::frameViewForLatchingState): Deleted.
     72        * page/scrolling/ScrollLatchingController.cpp: Added.
     73        (WebCore::ScrollLatchingController::ScrollLatchingController):
     74        (WebCore::ScrollLatchingController::clear):
     75        (WebCore::ScrollLatchingController::clearOrScheduleClearIfNeeded):
     76        (WebCore::ScrollLatchingController::clearTimerFired):
     77        (WebCore::ScrollLatchingController::receivedWheelEvent):
     78        (WebCore::ScrollLatchingController::latchingAllowsScrollingInFrame const):
     79        (WebCore::ScrollLatchingController::updateLatchingStateForFrame):
     80        (WebCore::ScrollLatchingController::getLatchingStateForFrame const):
     81        (WebCore::ScrollLatchingController::removeLatchingStateForTarget):
     82        (WebCore::ScrollLatchingController::removeLatchingStateForFrame):
     83        (WebCore::deltaIsPredominantlyVertical):
     84        (WebCore::ScrollLatchingController::shouldLatchToScrollableArea const):
     85        (WebCore::ScrollLatchingController::hasStateForFrame const):
     86        (WebCore::ScrollLatchingController::stateForFrame):
     87        (WebCore::ScrollLatchingController::stateForFrame const):
     88        (WebCore::ScrollLatchingController::dump const):
     89        (WebCore::operator<<):
     90        * page/scrolling/ScrollLatchingController.h: Added.
     91        (WebCore::ScrollLatchingController::cumulativeEventDelta const):
     92        * page/scrolling/ScrollLatchingState.cpp: Removed.
     93        * page/scrolling/ScrollLatchingState.h: Removed.
     94        * page/scrolling/ScrollingTreeLatchingController.cpp:
     95        (WebCore::ScrollingTreeLatchingController::receivedWheelEvent):
     96        (WebCore::ScrollingTreeLatchingController::nodeDidHandleEvent):
     97        * page/scrolling/ThreadedScrollingTree.cpp:
     98        (WebCore::ThreadedScrollingTree::handleWheelEventAfterMainThread):
     99        * page/scrolling/mac/ScrollingTreeScrollingNodeDelegateMac.mm:
     100        (WebCore::ScrollingTreeScrollingNodeDelegateMac::allowsHorizontalStretching const):
     101        (WebCore::ScrollingTreeScrollingNodeDelegateMac::allowsVerticalStretching const):
     102        (WebCore::newGestureIsStarting): Deleted.
     103        * platform/PlatformWheelEvent.h:
     104        (WebCore::PlatformWheelEvent::isGestureStart const):
     105        (WebCore::PlatformWheelEvent::isGestureContinuation const):
     106        (WebCore::PlatformWheelEvent::shouldResetLatching const):
     107        (WebCore::PlatformWheelEvent::isNonGestureEvent const):
     108        (WebCore::PlatformWheelEvent::shouldConsiderLatching const): Deleted. Renamed to isGestureStart(). This class
     109        should not prescribe latching behaviors.
     110
    11112020-08-30  Youenn Fablet  <youenn@apple.com>
    2112
  • trunk/Source/WebCore/Sources.txt

    r266332 r266333  
    16851685page/scrolling/AsyncScrollingCoordinator.cpp
    16861686page/scrolling/AxisScrollSnapOffsets.cpp
    1687 page/scrolling/ScrollLatchingState.cpp
     1687page/scrolling/ScrollLatchingController.cpp
    16881688page/scrolling/ScrollingConstraints.cpp
    16891689page/scrolling/ScrollingCoordinator.cpp
  • trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj

    r266332 r266333  
    22162216                7AA3A6A4194B5C22001CBD24 /* TileCoverageMap.h in Headers */ = {isa = PBXBuildFile; fileRef = 7AA3A6A2194B5C22001CBD24 /* TileCoverageMap.h */; };
    22172217                7AABA25A14BC613300AA9A11 /* DOMEditor.h in Headers */ = {isa = PBXBuildFile; fileRef = 7AABA25814BC613300AA9A11 /* DOMEditor.h */; };
    2218                 7AAFE8D019CB8672000F56D8 /* ScrollLatchingState.h in Headers */ = {isa = PBXBuildFile; fileRef = 7AAFE8CE19CB8672000F56D8 /* ScrollLatchingState.h */; };
    22192218                7ADE722610CBBB9B006B3B3A /* ContextMenuProvider.h in Headers */ = {isa = PBXBuildFile; fileRef = 7ADE722510CBBB9B006B3B3A /* ContextMenuProvider.h */; settings = {ATTRIBUTES = (Private, ); }; };
    22202219                7AE335F21ACB09E200E401EF /* WheelEventTestMonitor.h in Headers */ = {isa = PBXBuildFile; fileRef = 7AE335F01ACB09E200E401EF /* WheelEventTestMonitor.h */; settings = {ATTRIBUTES = (Private, ); }; };
     
    59775976                0F94B6542209156C00157014 /* ScrollingTreePositionedNode.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = ScrollingTreePositionedNode.mm; sourceTree = "<group>"; };
    59785977                0F94F37C23661131003AA5C7 /* StyleRuleType.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = StyleRuleType.h; sourceTree = "<group>"; };
     5978                0F9510F024F4769C001F52DC /* ScrollLatchingController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ScrollLatchingController.h; sourceTree = "<group>"; };
     5979                0F9510F224F4769C001F52DC /* ScrollLatchingController.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = ScrollLatchingController.cpp; sourceTree = "<group>"; };
    59795980                0F97A657155DA81E00FADD4C /* DisplayRefreshMonitorIOS.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = DisplayRefreshMonitorIOS.mm; sourceTree = "<group>"; };
    59805981                0F9B547522B4A772007B5E8A /* ScrollingStateOverflowScrollProxyNode.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ScrollingStateOverflowScrollProxyNode.h; sourceTree = "<group>"; };
     
    1000810009                7AABA25714BC613300AA9A11 /* DOMEditor.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DOMEditor.cpp; sourceTree = "<group>"; };
    1000910010                7AABA25814BC613300AA9A11 /* DOMEditor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DOMEditor.h; sourceTree = "<group>"; };
    10010                 7AAFE8CD19CB8672000F56D8 /* ScrollLatchingState.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ScrollLatchingState.cpp; sourceTree = "<group>"; };
    10011                 7AAFE8CE19CB8672000F56D8 /* ScrollLatchingState.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ScrollLatchingState.h; sourceTree = "<group>"; };
    1001210011                7ADE722510CBBB9B006B3B3A /* ContextMenuProvider.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ContextMenuProvider.h; sourceTree = "<group>"; };
    1001310012                7AE335EF1ACB09E200E401EF /* WheelEventTestMonitor.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = WheelEventTestMonitor.cpp; sourceTree = "<group>"; };
     
    1801218011                                A6D5A99A1629D6FF00297330 /* ScrollingTreeScrollingNodeDelegate.cpp */,
    1801318012                                A6D5A99B1629D70000297330 /* ScrollingTreeScrollingNodeDelegate.h */,
    18014                                 7AAFE8CD19CB8672000F56D8 /* ScrollLatchingState.cpp */,
    18015                                 7AAFE8CE19CB8672000F56D8 /* ScrollLatchingState.h */,
     18013                                0F9510F224F4769C001F52DC /* ScrollLatchingController.cpp */,
     18014                                0F9510F024F4769C001F52DC /* ScrollLatchingController.h */,
    1801618015                                F46729251E0DE5AB00ACC3D8 /* ScrollSnapOffsetsInfo.h */,
    1801718016                                0F6383DB18615B29003E5DB5 /* ThreadedScrollingTree.cpp */,
     
    3351433513                                0F94B6492208FE3B00157014 /* ScrollingTreeStickyNode.h in Headers */,
    3351533514                                83C5795D1DA5C301006F9C97 /* ScrollIntoViewOptions.h in Headers */,
    33516                                 7AAFE8D019CB8672000F56D8 /* ScrollLatchingState.h in Headers */,
    3351733515                                83C5795D1DA5C301006F9C86 /* ScrollLogicalPosition.h in Headers */,
    3351833516                                83C5795D1DA5C301006FAC97 /* ScrollOptions.h in Headers */,
  • trunk/Source/WebCore/dom/Element.cpp

    r266269 r266333  
    106106#include "ScriptDisallowedScope.h"
    107107#include "ScrollIntoViewOptions.h"
    108 #include "ScrollLatchingState.h"
     108#include "ScrollLatchingController.h"
    109109#include "SelectorQuery.h"
    110110#include "Settings.h"
     
    22602260        frame->legacyAnimation().cancelAnimations(*this);
    22612261
    2262 #if PLATFORM(MAC)
    2263     if (frame && frame->page())
    2264         frame->page()->removeLatchingStateForTarget(*this);
     2262#if ENABLE(WHEEL_EVENT_LATCHING)
     2263    if (frame && frame->page()) {
     2264        if (auto* scrollLatchingController = frame->page()->scrollLatchingControllerIfExists())
     2265            scrollLatchingController->removeLatchingStateForTarget(*this);
     2266    }
    22652267#endif
    22662268
  • trunk/Source/WebCore/page/EventHandler.cpp

    r266295 r266333  
    9090#include "SVGNames.h"
    9191#include "ScrollAnimator.h"
    92 #include "ScrollLatchingState.h"
     92#include "ScrollLatchingController.h"
    9393#include "Scrollbar.h"
    9494#include "Settings.h"
     
    391391    : m_frame(frame)
    392392    , m_hoverTimer(*this, &EventHandler::hoverTimerFired)
    393 #if PLATFORM(MAC)
    394     , m_clearLatchingStateTimer(*this, &EventHandler::clearLatchedStateTimerFired)
    395 #endif
    396393    , m_autoscrollController(makeUnique<AutoscrollController>())
    397394#if !ENABLE(IOS_TOUCH_EVENTS)
     
    27732770}
    27742771
    2775 void EventHandler::clearLatchedStateTimerFired()
    2776 {
    2777     LOG(ScrollLatching, "EventHandler %p clearLatchedStateTimerFired()", this);
    2778     clearLatchedState();
    2779 }
    2780 
    27812772#if !PLATFORM(MAC)
    27822773
    2783 void EventHandler::determineWheelEventTarget(const PlatformWheelEvent&, const HitTestResult&, RefPtr<Element>&, RefPtr<ContainerNode>&, WeakPtr<ScrollableArea>&, bool&)
     2774void EventHandler::determineWheelEventTarget(const PlatformWheelEvent&, RefPtr<Element>&, WeakPtr<ScrollableArea>&, bool&)
    27842775{
    27852776}
     
    27912782}
    27922783
    2793 bool EventHandler::processWheelEventForScrolling(const PlatformWheelEvent& event, ContainerNode*, const WeakPtr<ScrollableArea>&)
     2784bool EventHandler::processWheelEventForScrolling(const PlatformWheelEvent& event, const WeakPtr<ScrollableArea>&)
    27942785{
    27952786    Ref<Frame> protectedFrame(m_frame);
     
    28032794}
    28042795
    2805 bool EventHandler::platformCompletePlatformWidgetWheelEvent(const PlatformWheelEvent&, const Widget&, ContainerNode*)
     2796bool EventHandler::platformCompletePlatformWidgetWheelEvent(const PlatformWheelEvent&, const Widget&, const WeakPtr<ScrollableArea>&)
    28062797{
    28072798    return true;
     
    28102801void EventHandler::processWheelEventForScrollSnap(const PlatformWheelEvent&, const WeakPtr<ScrollableArea>&)
    28112802{
    2812 }
    2813 
    2814 void EventHandler::clearOrScheduleClearingLatchedStateIfNeeded(const PlatformWheelEvent&)
    2815 {
    2816     clearLatchedState();
    28172803}
    28182804   
     
    28582844}
    28592845
    2860 bool EventHandler::completeWidgetWheelEvent(const PlatformWheelEvent& event, const WeakPtr<Widget>& widget, const WeakPtr<ScrollableArea>& scrollableArea, ContainerNode* scrollableContainer)
     2846bool EventHandler::completeWidgetWheelEvent(const PlatformWheelEvent& event, const WeakPtr<Widget>& widget, const WeakPtr<ScrollableArea>& scrollableArea)
    28612847{
    28622848    m_isHandlingWheelEvent = false;
     
    28742860        return true;
    28752861
    2876     return platformCompletePlatformWidgetWheelEvent(event, *widget.get(), scrollableContainer);
     2862    return platformCompletePlatformWidgetWheelEvent(event, *widget.get(), scrollableArea);
    28772863}
    28782864
     
    28882874    FrameView* view = m_frame.view();
    28892875    if (!view)
     2876        return false;
     2877
     2878    if (!m_frame.page())
    28902879        return false;
    28912880
     
    29082897    setFrameWasScrolledByUser();
    29092898
     2899    if (m_frame.isMainFrame()) {
     2900#if ENABLE(WHEEL_EVENT_LATCHING)
     2901        m_frame.page()->scrollLatchingController().receivedWheelEvent(event);
     2902#endif
     2903        recordWheelEventForDeltaFilter(event);
     2904    }
     2905
    29102906    HitTestRequest request;
    29112907    HitTestResult result(view->windowToContents(event.position()));
    29122908    document->hitTest(request, result);
    29132909
    2914     // FIXME: Why do we have track all three of targetElement, scrollableContainer and ScrollableArea?
    29152910    RefPtr<Element> element = result.targetElement();
    2916     RefPtr<ContainerNode> scrollableContainer;
    29172911    WeakPtr<ScrollableArea> scrollableArea;
    29182912    bool isOverWidget = result.isOverWidget();
    2919    
    2920     // FIXME: Using the value of isOverWidget from the latching state triggers double-recursion into subframes.
     2913
    29212914    // FIXME: Despite doing this up-front search for the correct scrollable area, we dispatch events via elements which
    29222915    // itself finds and tries to scroll overflow scrollers.
    2923     determineWheelEventTarget(event, result, element, scrollableContainer, scrollableArea, isOverWidget);
    2924 
    2925 #if ENABLE(WHEEL_EVENT_LATCHING)
    2926     if (event.phase() == PlatformWheelEventPhaseNone && event.momentumPhase() == PlatformWheelEventPhaseNone && m_frame.page())
    2927         m_frame.page()->resetLatchingState();
    2928 #endif
    2929 
    2930     recordWheelEventForDeltaFilter(event);
     2916    determineWheelEventTarget(event, element, scrollableArea, isOverWidget);
    29312917
    29322918    if (element) {
     
    29342920            if (WeakPtr<Widget> widget = widgetForElement(*element)) {
    29352921                if (passWheelEventToWidget(event, *widget.get()))
    2936                     return completeWidgetWheelEvent(event, widget, scrollableArea, scrollableContainer.get());
     2922                    return completeWidgetWheelEvent(event, widget, scrollableArea);
    29372923            }
    29382924        }
     
    29542940        scrollableArea->setScrollShouldClearLatchedState(false);
    29552941
    2956     // FIXME: processWheelEventForScrolling() is only called for FrameView scrolling, not overflow scrolling, which is confusing.
    2957     bool handledEvent = processWheelEventForScrolling(event, scrollableContainer.get(), scrollableArea);
    2958     processWheelEventForScrollSnap(event, scrollableArea);
     2942    // Event handling may have disconnected m_frame.
     2943    if (!m_frame.page())
     2944        return false;
     2945
     2946    bool handledEvent = false;
     2947#if ENABLE(WHEEL_EVENT_LATCHING)
     2948    WeakPtr<ScrollableArea> latchedScrollableArea;
     2949    if (m_frame.page()->scrollLatchingController().latchingAllowsScrollingInFrame(m_frame, latchedScrollableArea)) {
     2950        // FIXME: processWheelEventForScrolling() is only called for FrameView scrolling, not overflow scrolling, which is confusing.
     2951        handledEvent = processWheelEventForScrolling(event, latchedScrollableArea);
     2952        processWheelEventForScrollSnap(event, latchedScrollableArea);
     2953    }
     2954#endif
    29592955    return handledEvent;
    29602956}
     
    29682964#if ENABLE(WHEEL_EVENT_LATCHING)
    29692965    LOG_WITH_STREAM(ScrollLatching, stream << "EventHandler::clearLatchedState()");
    2970     page->resetLatchingState();
    2971 #endif
    2972     if (auto filter = page->wheelEventDeltaFilter())
     2966    if (auto* scrollLatchingController = page->scrollLatchingControllerIfExists())
     2967        scrollLatchingController->removeLatchingStateForFrame(m_frame);
     2968#endif
     2969    if (auto* filter = page->wheelEventDeltaFilter())
    29732970        filter->endFilteringDeltas();
    29742971}
     
    29792976        return;
    29802977   
     2978    if (!m_frame.page())
     2979        return;
     2980
    29812981    auto protectedFrame = makeRef(m_frame);
    29822982
     
    29892989
    29902990#if ENABLE(WHEEL_EVENT_LATCHING)
    2991     if (m_frame.page() && m_frame.page()->wheelEventDeltaFilter()->isFilteringDeltas()) {
     2991    if (m_frame.page()->wheelEventDeltaFilter()->isFilteringDeltas()) {
    29922992        filteredPlatformDelta = m_frame.page()->wheelEventDeltaFilter()->filteredDelta();
    29932993        filteredVelocity = m_frame.page()->wheelEventDeltaFilter()->filteredVelocity();
     2994    }
     2995
     2996    WeakPtr<ScrollableArea> latchedScroller;
     2997    if (!m_frame.page()->scrollLatchingController().latchingAllowsScrollingInFrame(m_frame, latchedScroller))
     2998        return;
     2999
     3000    if (latchedScroller) {
     3001        if (latchedScroller == m_frame.view()) {
     3002            // FrameView scrolling is handled via processWheelEventForScrolling().
     3003            return;
     3004        }
     3005
     3006        auto platformEvent = wheelEvent.underlyingPlatformEvent();
     3007        if (platformEvent) {
     3008            auto copiedEvent = platformEvent->copyWithDeltasAndVelocity(filteredPlatformDelta.width(), filteredPlatformDelta.height(), filteredVelocity);
     3009            if (latchedScroller->handleWheelEvent(copiedEvent))
     3010                wheelEvent.setDefaultHandled();
     3011            return;
     3012        }
    29943013    }
    29953014#endif
  • trunk/Source/WebCore/page/EventHandler.h

    r265092 r266333  
    452452
    453453    bool passWheelEventToWidget(const PlatformWheelEvent&, Widget&);
    454     void determineWheelEventTarget(const PlatformWheelEvent&, const HitTestResult&, RefPtr<Element>& eventTarget, RefPtr<ContainerNode>& scrollableContainer, WeakPtr<ScrollableArea>&, bool& isOverWidget);
     454    void determineWheelEventTarget(const PlatformWheelEvent&, RefPtr<Element>& eventTarget, WeakPtr<ScrollableArea>&, bool& isOverWidget);
    455455    void recordWheelEventForDeltaFilter(const PlatformWheelEvent&);
    456     bool processWheelEventForScrolling(const PlatformWheelEvent&, ContainerNode* scrollableContainer, const WeakPtr<ScrollableArea>&);
     456    bool processWheelEventForScrolling(const PlatformWheelEvent&, const WeakPtr<ScrollableArea>&);
    457457    void processWheelEventForScrollSnap(const PlatformWheelEvent&, const WeakPtr<ScrollableArea>&);
    458     bool completeWidgetWheelEvent(const PlatformWheelEvent&, const WeakPtr<Widget>&, const WeakPtr<ScrollableArea>&, ContainerNode*);
    459 
    460     bool platformCompletePlatformWidgetWheelEvent(const PlatformWheelEvent&, const Widget&, ContainerNode* scrollableContainer);
     458    bool completeWidgetWheelEvent(const PlatformWheelEvent&, const WeakPtr<Widget>&, const WeakPtr<ScrollableArea>&);
     459
     460    bool platformCompletePlatformWidgetWheelEvent(const PlatformWheelEvent&, const Widget&, const WeakPtr<ScrollableArea>&);
    461461
    462462    void defaultSpaceEventHandler(KeyboardEvent&);
     
    504504#endif
    505505
    506     void clearOrScheduleClearingLatchedStateIfNeeded(const PlatformWheelEvent&);
    507     void clearLatchedStateTimerFired();
    508506    void clearLatchedState();
    509507
     
    536534    Timer m_hoverTimer;
    537535    bool m_hasScheduledCursorUpdate { false };
    538 
    539 #if PLATFORM(MAC)
    540     Timer m_clearLatchingStateTimer;
    541 #endif
    542536
    543537    std::unique_ptr<AutoscrollController> m_autoscrollController;
  • trunk/Source/WebCore/page/Page.cpp

    r266295 r266333  
    113113#include "ScriptDisallowedScope.h"
    114114#include "ScriptedAnimationController.h"
    115 #include "ScrollLatchingState.h"
     115#include "ScrollLatchingController.h"
    116116#include "ScrollingCoordinator.h"
    117117#include "Settings.h"
     
    28392839#if ENABLE(WHEEL_EVENT_LATCHING)
    28402840    if (clearLatchingState)
    2841         resetLatchingState();
     2841        scrollLatchingController().clear();
    28422842#endif
    28432843
     
    31423142
    31433143#if ENABLE(WHEEL_EVENT_LATCHING)
    3144 ScrollLatchingState* Page::latchingState()
    3145 {
    3146     if (m_latchingState.isEmpty())
    3147         return nullptr;
    3148 
    3149     return &m_latchingState.last();
    3150 }
    3151 
    3152 void Page::pushNewLatchingState(ScrollLatchingState&& state)
    3153 {
    3154     m_latchingState.append(WTFMove(state));
    3155 }
    3156 
    3157 void Page::resetLatchingState()
    3158 {
    3159     m_latchingState.clear();
    3160 }
    3161 
    3162 void Page::popLatchingState()
    3163 {
    3164     m_latchingState.removeLast();
    3165     LOG_WITH_STREAM(ScrollLatching, stream << "Page::popLatchingState() - new state " << m_latchingState);
    3166 }
    3167 
    3168 void Page::removeLatchingStateForTarget(Element& targetNode)
    3169 {
    3170     if (m_latchingState.isEmpty())
    3171         return;
    3172 
    3173     m_latchingState.removeAllMatching([&targetNode] (ScrollLatchingState& state) {
    3174         auto* wheelElement = state.wheelEventElement();
    3175         if (!wheelElement)
    3176             return false;
    3177 
    3178         return targetNode.isEqualNode(wheelElement);
    3179     });
    3180     LOG_WITH_STREAM(ScrollLatching, stream << "Page::removeLatchingStateForTarget() - new state " << m_latchingState);
     3144ScrollLatchingController& Page::scrollLatchingController()
     3145{
     3146    if (!m_scrollLatchingController)
     3147        m_scrollLatchingController = makeUnique<ScrollLatchingController>();
     3148       
     3149    return *m_scrollLatchingController;
     3150}
     3151
     3152ScrollLatchingController* Page::scrollLatchingControllerIfExists()
     3153{
     3154    return m_scrollLatchingController.get();
    31813155}
    31823156#endif // ENABLE(WHEEL_EVENT_LATCHING)
  • trunk/Source/WebCore/page/Page.h

    r265623 r266333  
    132132class RenderObject;
    133133class ResourceUsageOverlay;
    134 class ScrollLatchingState;
     134class ScrollLatchingController;
    135135class ScrollingCoordinator;
    136136class ServicesOverlayController;
     
    449449
    450450#if ENABLE(WHEEL_EVENT_LATCHING)
    451     ScrollLatchingState* latchingState();
    452     const Vector<ScrollLatchingState>& latchingStateStack() const { return m_latchingState; }
    453     void pushNewLatchingState(ScrollLatchingState&&);
    454     void popLatchingState();
    455     void resetLatchingState();
    456     void removeLatchingStateForTarget(Element&);
     451    ScrollLatchingController& scrollLatchingController();
     452    ScrollLatchingController* scrollLatchingControllerIfExists();
    457453#endif // ENABLE(WHEEL_EVENT_LATCHING)
    458454
     
    10391035    std::unique_ptr<PerformanceLogging> m_performanceLogging;
    10401036#if ENABLE(WHEEL_EVENT_LATCHING)
    1041     Vector<ScrollLatchingState> m_latchingState;
     1037    std::unique_ptr<ScrollLatchingController> m_scrollLatchingController;
    10421038#endif
    10431039#if PLATFORM(MAC) && (ENABLE(SERVICE_CONTROLS) || ENABLE(TELEPHONE_NUMBER_DETECTION))
  • trunk/Source/WebCore/page/mac/EventHandlerMac.mm

    r266292 r266333  
    6060#import "ScreenProperties.h"
    6161#import "ScrollAnimator.h"
    62 #import "ScrollLatchingState.h"
     62#import "ScrollLatchingController.h"
    6363#import "ScrollableArea.h"
    6464#import "Scrollbar.h"
     
    785785    return box.layer();
    786786}
    787    
     787
     788// FIXME: This could be written in terms of ScrollableArea::enclosingScrollableArea().
    788789static ContainerNode* findEnclosingScrollableContainer(ContainerNode* node, const PlatformWheelEvent& wheelEvent)
    789790{
     
    818819}
    819820
    820 static bool deltaIsPredominantlyVertical(float deltaX, float deltaY)
    821 {
    822     return std::abs(deltaY) > std::abs(deltaX);
    823 }
    824    
    825 static bool scrolledToEdgeInDominantDirection(const ContainerNode& container, const ScrollableArea& area, float deltaX, float deltaY)
    826 {
    827     if (!container.renderer())
    828         return true;
    829 
    830     if (!area.canHaveScrollbars())
    831         return true;
    832 
    833     const RenderStyle& style = container.renderer()->style();
    834 
    835     if (!deltaIsPredominantlyVertical(deltaX, deltaY) && deltaX) {
    836         if (style.overflowX() == Overflow::Hidden)
    837             return true;
    838 
    839         if (deltaX < 0)
    840             return area.scrolledToRight();
    841        
    842         return area.scrolledToLeft();
    843     }
    844 
    845     if (style.overflowY() == Overflow::Hidden)
    846         return true;
    847 
    848     if (deltaY < 0)
    849         return area.scrolledToBottom();
    850    
    851     return area.scrolledToTop();
    852 }
    853 
    854821static WeakPtr<ScrollableArea> scrollableAreaForEventTarget(Element* eventTarget)
    855822{
     
    870837}
    871838
    872 static bool latchingIsLockedToPlatformFrame(const Frame& frame)
    873 {
    874     auto* page = frame.page();
    875     if (!page)
    876         return false;
    877 
    878     ScrollLatchingState* latchedState = page->latchingState();
    879     if (!latchedState)
    880         return false;
    881 
    882     if (frameHasPlatformWidget(frame) && &frame != latchedState->frame())
    883         return true;
    884 
    885     return false;
    886 }
    887 
    888 static bool latchingIsLockedToAncestorOfThisFrame(const Frame& frame)
    889 {
    890     auto* page = frame.page();
    891     if (!page)
    892         return false;
    893 
    894     ScrollLatchingState* latchedState = page->latchingState();
    895     if (!latchedState || !latchedState->frame())
    896         return false;
    897 
    898     if (&frame == latchedState->frame())
    899         return false;
    900 
    901     for (Frame* ancestor = frame.tree().parent(); ancestor; ancestor = ancestor->tree().parent()) {
    902         if (ancestor == latchedState->frame())
    903             return true;
    904     }
    905    
    906     return false;
    907 }
    908 
    909839static WeakPtr<ScrollableArea> scrollableAreaForContainerNode(ContainerNode& container)
    910840{
     
    920850}
    921851
    922 static bool latchedToFrameOrBody(ContainerNode& container)
    923 {
    924     // FIXME(106133): We might need to add or switch to is<HTMLDocumentElement> when this bug is fixed.
    925     return is<HTMLFrameSetElement>(container) || is<HTMLBodyElement>(container);
    926 }
    927 
    928 void EventHandler::clearOrScheduleClearingLatchedStateIfNeeded(const PlatformWheelEvent& event)
    929 {
    930     if (!m_frame.isMainFrame())
    931         return;
    932 
    933     // Platform does not provide an indication that it will switch from non-momentum to momentum scrolling
    934     // when handling wheel events.
    935     // Logic below installs a timer when non-momentum scrolling ends. If momentum scroll does not start within that interval,
    936     // reset the latched state. If it does, stop the timer, leaving the latched state untouched.
    937     if (!m_clearLatchingStateTimer.isActive()) {
    938         if (event.isEndOfNonMomentumScroll()) {
    939             LOG_WITH_STREAM(ScrollLatching, stream << "EventHandler::clearOrScheduleClearingLatchedStateIfNeeded() - event" << event << ", scheduling clear timer");
    940             m_clearLatchingStateTimer.startOneShot(resetLatchedStateTimeout);
    941         }
    942     } else {
    943         // If another wheel event scrolling starts, stop the timer manually, and reset the latched state immediately.
    944         if (event.shouldConsiderLatching()) {
    945             LOG_WITH_STREAM(ScrollLatching, stream << "EventHandler::clearOrScheduleClearingLatchedStateIfNeeded() - event" << event << ", timer pending, another scroll starting");
    946             if (auto* page = m_frame.page())
    947                 page->resetLatchingState();
    948             m_clearLatchingStateTimer.stop();
    949         } else if (event.isTransitioningToMomentumScroll()) {
    950             // Wheel events machinary is transitioning to momentum scrolling, so no need to reset latched state. Stop the timer.
    951             m_clearLatchingStateTimer.stop();
    952         }
    953     }
    954 }
    955 
    956 void EventHandler::determineWheelEventTarget(const PlatformWheelEvent& wheelEvent, const HitTestResult& result, RefPtr<Element>& wheelEventTarget, RefPtr<ContainerNode>& scrollableContainer, WeakPtr<ScrollableArea>& scrollableArea, bool& isOverWidget)
    957 {
    958     clearOrScheduleClearingLatchedStateIfNeeded(wheelEvent);
    959 
     852void EventHandler::determineWheelEventTarget(const PlatformWheelEvent& wheelEvent, RefPtr<Element>& wheelEventTarget, WeakPtr<ScrollableArea>& scrollableArea, bool& isOverWidget)
     853{
    960854    auto* page = m_frame.page();
    961855    if (!page)
     
    963857
    964858    auto* view = m_frame.view();
    965 
    966859    if (!view)
    967         scrollableContainer = wheelEventTarget;
     860        return;
     861
     862    if (eventTargetIsPlatformWidget(wheelEventTarget.get()))
     863        scrollableArea = scrollableAreaForEventTarget(wheelEventTarget.get());
    968864    else {
    969         if (eventTargetIsPlatformWidget(wheelEventTarget.get())) {
    970             scrollableContainer = wheelEventTarget;
    971             scrollableArea = scrollableAreaForEventTarget(wheelEventTarget.get());
    972         } else {
    973             scrollableContainer = findEnclosingScrollableContainer(wheelEventTarget.get(), wheelEvent);
    974             if (scrollableContainer && !is<HTMLIFrameElement>(wheelEventTarget))
    975                 scrollableArea = scrollableAreaForContainerNode(*scrollableContainer);
    976             else {
    977                 // FIXME: Why does this assume the body? What if we hit an iframe inside an overflow:scroll?
    978                 scrollableContainer = view->frame().document()->bodyOrFrameset();
    979                 scrollableArea = makeWeakPtr(static_cast<ScrollableArea&>(*view));
    980             }
    981 
    982             LOG_WITH_STREAM(ScrollLatching, stream << "EventHandler::determineWheelEventTarget() - event" << wheelEvent << " found scrollableContainer" << ValueOrNull(scrollableContainer.get()) << " scrollableArea " << (scrollableArea ? scrollableArea.get() : nullptr));
    983         }
    984     }
     865        auto* scrollableContainer = findEnclosingScrollableContainer(wheelEventTarget.get(), wheelEvent);
     866        if (scrollableContainer)
     867            scrollableArea = scrollableAreaForContainerNode(*scrollableContainer);
     868        else
     869            scrollableArea = makeWeakPtr(static_cast<ScrollableArea&>(*view));
     870    }
     871
     872    LOG_WITH_STREAM(ScrollLatching, stream << "EventHandler::determineWheelEventTarget() - event" << wheelEvent << " found scrollableArea " << ValueOrNull(scrollableArea.get()) << ", latching state is " << page->scrollLatchingController());
    985873
    986874    if (scrollableArea && page->isMonitoringWheelEvents())
    987875        scrollableArea->scrollAnimator().setWheelEventTestMonitor(page->wheelEventTestMonitor());
    988876
    989     auto* latchingState = page->latchingState();
    990     if (wheelEvent.shouldConsiderLatching()) {
    991         if (scrollableContainer && scrollableArea) {
    992             bool startingAtScrollLimit = scrolledToEdgeInDominantDirection(*scrollableContainer, *scrollableArea.get(), wheelEvent.deltaX(), wheelEvent.deltaY());
    993             if (!startingAtScrollLimit) {
    994                 ScrollLatchingState latchingState;
    995                 latchingState.setWheelEventElement(wheelEventTarget.get());
    996                 latchingState.setFrame(&m_frame);
    997                 latchingState.setScrollableContainer(scrollableContainer.get());
    998                 latchingState.setWidgetIsLatched(result.isOverWidget());
    999                 page->pushNewLatchingState(WTFMove(latchingState));
    1000 
    1001                 page->wheelEventDeltaFilter()->beginFilteringDeltas();
    1002                 isOverWidget = result.isOverWidget();
    1003             }
    1004 
    1005             LOG_WITH_STREAM(ScrollLatching, stream << "EventHandler::determineWheelEventTarget() - considering latching for " << wheelEvent << ", at scroll limit " << startingAtScrollLimit << ", latching state " << page->latchingStateStack());
    1006         }
    1007     } else if (wheelEvent.shouldResetLatching()) {
    1008         clearLatchedState();
    1009         LOG_WITH_STREAM(ScrollLatching, stream << "EventHandler::determineWheelEventTarget() - reset latching for event " << wheelEvent << " latching state " << page->latchingStateStack());
    1010     }
    1011 
    1012     // FIXME: This can use a stale laching state, before we just pushed or cleared.
    1013     if (!wheelEvent.shouldResetLatching() && latchingState && latchingState->wheelEventElement()) {
    1014         if (latchingIsLockedToPlatformFrame(m_frame))
    1015             return;
    1016 
    1017         if (latchingIsLockedToAncestorOfThisFrame(m_frame))
    1018             return;
    1019 
    1020         wheelEventTarget = latchingState->wheelEventElement();
    1021         isOverWidget = latchingState->widgetIsLatched();
    1022         scrollableContainer = latchingState->scrollableContainer();
    1023 
    1024         if (scrollableContainer) {
    1025             if (!latchedToFrameOrBody(*scrollableContainer) && !latchingState->widgetIsLatched())
    1026                 scrollableArea = scrollableAreaForContainerNode(*scrollableContainer);
    1027         }
    1028     }
     877    if (wheelEvent.shouldResetLatching() || wheelEvent.isNonGestureEvent())
     878        return;
     879
     880    if (m_frame.isMainFrame() && wheelEvent.isGestureStart())
     881        page->wheelEventDeltaFilter()->beginFilteringDeltas();
     882
     883    page->scrollLatchingController().updateAndFetchLatchingStateForFrame(m_frame, wheelEvent, wheelEventTarget, scrollableArea, isOverWidget);
    1029884}
    1030885
     
    1048903}
    1049904
    1050 static FrameView* frameViewForLatchingState(Frame& frame, const ScrollLatchingState& latchingState)
    1051 {
    1052     if (latchingIsLockedToPlatformFrame(frame))
    1053         return frame.view();
    1054 
    1055     return latchingState.frame() ? latchingState.frame()->view() : frame.view();
    1056 }
    1057 
    1058 bool EventHandler::processWheelEventForScrolling(const PlatformWheelEvent& wheelEvent, ContainerNode* scrollableContainer, const WeakPtr<ScrollableArea>& scrollableArea)
    1059 {
    1060     LOG_WITH_STREAM(ScrollLatching, stream << "EventHandler::processWheelEventForScrolling " << wheelEvent << " - scrollableContainer " << scrollableContainer << " scrollableArea " << scrollableArea.get() << " use latched element " << wheelEvent.useLatchedEventElement());
     905bool EventHandler::processWheelEventForScrolling(const PlatformWheelEvent& wheelEvent, const WeakPtr<ScrollableArea>& scrollableArea)
     906{
     907    LOG_WITH_STREAM(ScrollLatching, stream << "EventHandler::processWheelEventForScrolling " << wheelEvent << " - scrollableArea " << ValueOrNull(scrollableArea.get()) << " use latched element " << wheelEvent.useLatchedEventElement());
     908
     909#if ASSERT_ENABLED
     910    {
     911        // FIXME: Clean up processWheelEventForScrollSnap() and then turn this into an early return.
     912        WeakPtr<ScrollableArea> latchedScrollableArea;
     913        ASSERT(m_frame.page()->scrollLatchingController().latchingAllowsScrollingInFrame(m_frame, latchedScrollableArea));
     914    }
     915#endif
    1061916
    1062917    Ref<Frame> protectedFrame(m_frame);
     918
     919    if (!m_frame.page())
     920        return false;
    1063921
    1064922    FrameView* view = m_frame.view();
     
    1067925        return false;
    1068926
    1069     const auto* latchingState = m_frame.page() ? m_frame.page()->latchingState() : nullptr;
    1070 
    1071     if (wheelEvent.useLatchedEventElement() && !latchingIsLockedToAncestorOfThisFrame(m_frame) && latchingState && latchingState->scrollableContainer()) {
     927    // We handle non-view scrollableAreas elsewhere.
     928    if (wheelEvent.useLatchedEventElement() && scrollableArea) {
    1072929        m_isHandlingWheelEvent = false;
    1073930
    1074         LOG_WITH_STREAM(ScrollLatching, stream << "  latching state " << *latchingState);
    1075 
    1076         // WebKit2 code path
    1077         if (!frameHasPlatformWidget(m_frame) && scrollableContainer == latchingState->scrollableContainer() && scrollableArea && view != scrollableArea) {
    1078             // If we did not start at the scroll limit, do not pass the event on to be handled by enclosing scrollable regions.
    1079             LOG_WITH_STREAM(Scrolling, stream << "EventHandler " << this << " processWheelEventForScrolling - latched to " << scrollableArea.get() << " and not propagating");
     931        LOG_WITH_STREAM(ScrollLatching, stream << "  latching state " << m_frame.page()->scrollLatchingController());
     932
     933        if (!frameHasPlatformWidget(m_frame) && scrollableArea != view) {
     934            LOG_WITH_STREAM(Scrolling, stream << "  latched to non-view scroller " << scrollableArea << " and not propagating");
    1080935            return true;
    1081936        }
    1082937
    1083         // FIXME: This set 'view' to a FrameView that is not this EventHandler's FrameView, which then gets scrolled from here, which is wrong.
    1084         view = frameViewForLatchingState(m_frame, *latchingState);
    1085         ASSERT(view);
     938        LOG_WITH_STREAM(ScrollLatching, stream << " sending to view " << *view);
    1086939
    1087940        bool didHandleWheelEvent = view->wheelEvent(wheelEvent);
    1088         if (scrollableContainer == latchingState->scrollableContainer()) {
    1089             // If we are just starting a scroll event, and have nowhere left to scroll, allow
    1090             // the enclosing frame to handle the scroll.
    1091             didHandleWheelEvent = true;
    1092         }
    1093 
    1094941        // If the platform widget is handling the event, we always want to return false.
    1095         if (scrollableArea == view && view->platformWidget())
     942        if (view->platformWidget())
    1096943            didHandleWheelEvent = false;
    1097        
     944
     945        LOG_WITH_STREAM(ScrollLatching, stream << "  EventHandler::processWheelEventForScrolling returning " << didHandleWheelEvent);
    1098946        return didHandleWheelEvent;
    1099947    }
     
    1104952}
    1105953
    1106 bool EventHandler::platformCompletePlatformWidgetWheelEvent(const PlatformWheelEvent& wheelEvent, const Widget& widget, ContainerNode* scrollableContainer)
     954bool EventHandler::platformCompletePlatformWidgetWheelEvent(const PlatformWheelEvent& wheelEvent, const Widget& widget, const WeakPtr<ScrollableArea>& scrollableArea)
    1107955{
    1108956    // WebKit1: Prevent multiple copies of the scrollWheel event from being sent to the NSScrollView widget.
     
    1110958        return true;
    1111959
    1112     const auto* latchingState = m_frame.page() ? m_frame.page()->latchingState() : nullptr;
    1113     if (!latchingState)
    1114         return false;
    1115 
    1116     if (wheelEvent.useLatchedEventElement() && latchingState->scrollableContainer() && scrollableContainer == latchingState->scrollableContainer())
    1117         return true;
    1118 
    1119     return false;
     960    if (!m_frame.page())
     961        return false;
     962
     963    WeakPtr<ScrollableArea> latchedScrollableArea;
     964    if (!m_frame.page()->scrollLatchingController().latchingAllowsScrollingInFrame(m_frame, latchedScrollableArea))
     965        return false;
     966
     967    return wheelEvent.useLatchedEventElement() && latchedScrollableArea && scrollableArea == latchedScrollableArea;
    1120968}
    1121969
     
    1130978
    1131979#if ENABLE(CSS_SCROLL_SNAP)
    1132     if (ScrollAnimator* scrollAnimator = scrollableArea->existingScrollAnimator())
     980    if (auto* scrollAnimator = scrollableArea->existingScrollAnimator())
    1133981        scrollAnimator->processWheelEventForScrollSnap(wheelEvent);
    1134982#endif
  • trunk/Source/WebCore/page/scrolling/ScrollingTreeLatchingController.cpp

    r266262 r266333  
    3737namespace WebCore {
    3838
    39 // See also EventHandlerMac.cpp
     39// See also ScrollLatchingController.cpp
    4040static const Seconds resetLatchedStateTimeout { 100_ms };
    4141
     
    4848
    4949    LockHolder locker(m_latchedNodeMutex);
    50     if (wheelEvent.shouldConsiderLatching() && m_latchedNodeID && !latchedNodeIsRelevant()) {
     50    if (wheelEvent.isGestureStart() && m_latchedNodeID && !latchedNodeIsRelevant()) {
    5151        LOG_WITH_STREAM(ScrollLatching, stream << "ScrollingTreeLatchingController " << this << " receivedWheelEvent - " << (MonotonicTime::now() - m_lastLatchedNodeInterationTime).milliseconds() << "ms since last event, clearing latched node");
    5252        m_latchedNodeID.reset();
     
    8888    }
    8989
    90     if (wheelEvent.delta().isZero() || !wheelEvent.shouldConsiderLatching())
     90    if (wheelEvent.delta().isZero() || !wheelEvent.isGestureStart())
    9191        return;
    9292
  • trunk/Source/WebCore/page/scrolling/ThreadedScrollingTree.cpp

    r266292 r266333  
    6464bool ThreadedScrollingTree::handleWheelEventAfterMainThread(const PlatformWheelEvent& wheelEvent, ScrollingNodeID targetNodeID)
    6565{
     66    LOG_WITH_STREAM(Scrolling, stream << "ThreadedScrollingTree::handleWheelEventAfterMainThread " << wheelEvent);
    6667    SetForScope<bool> disallowLatchingScope(m_allowLatching, false);
    6768
  • trunk/Source/WebCore/page/scrolling/mac/ScrollingTreeScrollingNodeDelegateMac.mm

    r264908 r266333  
    168168// FIXME: We should find a way to share some of the code from newGestureIsStarting(), isAlreadyPinnedInDirectionOfGesture(),
    169169// allowsVerticalStretching(), and allowsHorizontalStretching() with the implementation in ScrollAnimatorMac.
    170 // This is also the same as PlatformWheelEvent::shouldConsiderLatching().
    171 static bool newGestureIsStarting(const PlatformWheelEvent& wheelEvent)
    172 {
    173     return wheelEvent.phase() == PlatformWheelEventPhaseMayBegin || wheelEvent.phase() == PlatformWheelEventPhaseBegan;
    174 }
    175 
    176170bool ScrollingTreeScrollingNodeDelegateMac::isAlreadyPinnedInDirectionOfGesture(const PlatformWheelEvent& wheelEvent, ScrollEventAxis axis) const
    177171{
     
    201195    case ScrollElasticityAutomatic: {
    202196        bool scrollbarsAllowStretching = hasEnabledHorizontalScrollbar() || !hasEnabledVerticalScrollbar();
    203         bool eventPreventsStretching = newGestureIsStarting(wheelEvent) && isAlreadyPinnedInDirectionOfGesture(wheelEvent, ScrollEventAxis::Horizontal);
     197        bool eventPreventsStretching = wheelEvent.isGestureStart() && isAlreadyPinnedInDirectionOfGesture(wheelEvent, ScrollEventAxis::Horizontal);
    204198        return scrollbarsAllowStretching && !eventPreventsStretching;
    205199    }
     
    223217    case ScrollElasticityAutomatic: {
    224218        bool scrollbarsAllowStretching = hasEnabledVerticalScrollbar() || !hasEnabledHorizontalScrollbar();
    225         bool eventPreventsStretching = newGestureIsStarting(wheelEvent) && isAlreadyPinnedInDirectionOfGesture(wheelEvent, ScrollEventAxis::Vertical);
     219        bool eventPreventsStretching = wheelEvent.isGestureStart() && isAlreadyPinnedInDirectionOfGesture(wheelEvent, ScrollEventAxis::Vertical);
    226220        return scrollbarsAllowStretching && !eventPreventsStretching;
    227221    }
  • trunk/Source/WebCore/platform/PlatformWheelEvent.h

    r262294 r266333  
    152152#if ENABLE(ASYNC_SCROLLING)
    153153    bool useLatchedEventElement() const;
    154     bool shouldConsiderLatching() const;
     154    bool isGestureStart() const;
     155    bool isGestureContinuation() const; // The fingers-down part of the gesture excluding momentum.
    155156    bool shouldResetLatching() const;
     157    bool isNonGestureEvent() const;
    156158    bool isEndOfMomentumScroll() const;
    157159#else
     
    211213}
    212214
    213 inline bool PlatformWheelEvent::shouldConsiderLatching() const
    214 {
    215     // FIXME: This should disallow latching if the delta is zero.
     215inline bool PlatformWheelEvent::isGestureStart() const
     216{
    216217    return m_phase == PlatformWheelEventPhaseBegan || m_phase == PlatformWheelEventPhaseMayBegin;
    217218}
    218219
     220inline bool PlatformWheelEvent::isGestureContinuation() const
     221{
     222    return m_phase == PlatformWheelEventPhaseChanged;
     223}
     224
    219225inline bool PlatformWheelEvent::shouldResetLatching() const
    220226{
    221     return m_phase == PlatformWheelEventPhaseCancelled || m_phase == PlatformWheelEventPhaseMayBegin || isEndOfMomentumScroll();
     227    return m_phase == PlatformWheelEventPhaseCancelled || m_phase == PlatformWheelEventPhaseMayBegin || (m_phase == PlatformWheelEventPhaseNone && m_momentumPhase == PlatformWheelEventPhaseNone) || isEndOfMomentumScroll();
     228}
     229
     230inline bool PlatformWheelEvent::isNonGestureEvent() const
     231{
     232    return m_phase == PlatformWheelEventPhaseNone && m_momentumPhase == PlatformWheelEventPhaseNone;
    222233}
    223234
Note: See TracChangeset for help on using the changeset viewer.