WebKit Bugzilla
New
Browse
Search+
Log In
×
Sign in with GitHub
or
Remember my login
Create Account
·
Forgot Password
Forgotten password account recovery
[patch]
Patch
bug-218764-20201113193708.patch (text/plain), 56.46 KB, created by
Simon Fraser (smfr)
on 2020-11-13 19:37:09 PST
(
hide
)
Description:
Patch
Filename:
MIME Type:
Creator:
Simon Fraser (smfr)
Created:
2020-11-13 19:37:09 PST
Size:
56.46 KB
patch
obsolete
>Subversion Revision: 269780 >diff --git a/Source/WebCore/ChangeLog b/Source/WebCore/ChangeLog >index 95209cc866506fc01ee40dbbbd23fcbc2b49bd6a..a4bc768a57f83e1b62b7a07192a0cefaa7561bba 100644 >--- a/Source/WebCore/ChangeLog >+++ b/Source/WebCore/ChangeLog >@@ -1,3 +1,85 @@ >+2020-11-13 Simon Fraser <simon.fraser@apple.com> >+ >+ 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 NOBODY (OOPS!). >+ >+ When handling a sequence of wheel events in a gesture in a region with non-passive wheel >+ event handlers, only allow the first event in the sequence to call preventDefault(); >+ subsequent events are marked as non-cancelable, so preventDefault() will have no effect. >+ >+ This allows us to do async scrolling even while in regions with active wheel event handlers, >+ if those handlers don't call preventDefault() on the first event. This matches Blink >+ behavior. >+ >+ To achieve this, track some bits in EventHandling to know whether the event was dispatched >+ to script, and whether it was canceled. Send this data back to the scrolling thread via >+ wheelEventWasProcessedByMainThread(), where is sets an Optional<bool> on ScrollingTree. This >+ flag is consulted by ScrollingTree::determineWheelEventProcessing(), and if we have received >+ it, and the event was not canceled, we use MainThreadForNonBlockingDOMEventDispatch when >+ sending events to the main thread. This is used to mark the event as non-cancelable in >+ EventHandler::handleWheelEvent(). >+ >+ Add another ReportDOMEventHandling bit to WheelEventTestMonitor to track the propagation >+ of EventHandling state back to the scrolling thread. >+ >+ Tests: fast/events/wheel/first-wheel-event-cancelable.html >+ fast/events/wheel/wheel-event-in-passive-region-non-cancelable.html >+ fast/events/wheel/wheel-events-become-non-cancelable.html >+ >+ * dom/Element.cpp: >+ (WebCore::Element::dispatchWheelEvent): >+ * dom/Element.h: >+ * dom/WheelEvent.cpp: >+ (WebCore::WheelEvent::WheelEvent): >+ (WebCore::WheelEvent::create): >+ * dom/WheelEvent.h: >+ * page/EventHandler.cpp: >+ (WebCore::EventHandler::wheelEventWasProcessedByMainThread): >+ (WebCore::EventHandler::handleWheelEvent): >+ (WebCore::EventHandler::handleWheelEventInternal): >+ * page/EventHandler.h: >+ * page/FrameView.cpp: >+ (WebCore::FrameView::wheelEvent): >+ * page/PointerLockController.cpp: >+ (WebCore::PointerLockController::dispatchLockedWheelEvent): >+ * page/WheelEventTestMonitor.cpp: >+ (WebCore::operator<<): >+ * page/WheelEventTestMonitor.h: >+ * page/ios/EventHandlerIOS.mm: >+ (WebCore::EventHandler::wheelEvent): >+ * page/mac/EventHandlerMac.mm: >+ (WebCore::EventHandler::wheelEvent): >+ (WebCore::EventHandler::wheelEventWasProcessedByMainThread): >+ * page/scrolling/ScrollingCoordinator.h: >+ (WebCore::ScrollingCoordinator::performDefaultWheelEventHandling): >+ (WebCore::ScrollingCoordinator::wheelEventWasProcessedByMainThread): >+ (WebCore::ScrollingCoordinator::handleWheelEvent): Deleted. >+ * page/scrolling/ScrollingTree.cpp: >+ (WebCore::ScrollingTree::determineWheelEventProcessing): >+ (WebCore::ScrollingTree::handleWheelEvent): >+ (WebCore::ScrollingTree::setGestureRequiresSynchronousEventDispatch): >+ * page/scrolling/ScrollingTree.h: >+ (WebCore::WheelEventHandlingResult::needsMainThreadProcessing const): >+ * page/scrolling/ThreadedScrollingTree.cpp: >+ (WebCore::ThreadedScrollingTree::handleWheelEventAfterMainThread): >+ (WebCore::ThreadedScrollingTree::wheelEventWasProcessedByMainThread): >+ * page/scrolling/ThreadedScrollingTree.h: >+ * page/scrolling/mac/ScrollingCoordinatorMac.h: >+ * page/scrolling/mac/ScrollingCoordinatorMac.mm: >+ (WebCore::ScrollingCoordinatorMac::performDefaultWheelEventHandling): >+ (WebCore::nextDeferIdentifier): >+ (WebCore::ScrollingCoordinatorMac::wheelEventWasProcessedByMainThread): >+ (WebCore::ScrollingCoordinatorMac::handleWheelEvent): Deleted. >+ * page/scrolling/mac/ScrollingTreeOverflowScrollingNodeMac.mm: >+ (WebCore::ScrollingTreeOverflowScrollingNodeMac::handleWheelEvent): >+ * platform/PlatformEvent.h: >+ * platform/PlatformWheelEvent.cpp: >+ (WebCore::operator<<): >+ * platform/PlatformWheelEvent.h: >+ > 2020-11-12 Simon Fraser <simon.fraser@apple.com> > > Force wheel event listeners on the root to be passive >diff --git a/Source/WebKit/ChangeLog b/Source/WebKit/ChangeLog >index 27ba66c872510e8a2f7b1660bcda9d563f64c0ac..9bbdee7f020b18d775ab69a95f7fcf207f9bc8a9 100644 >--- a/Source/WebKit/ChangeLog >+++ b/Source/WebKit/ChangeLog >@@ -1,3 +1,35 @@ >+2020-11-13 Simon Fraser <simon.fraser@apple.com> >+ >+ 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 NOBODY (OOPS!). >+ >+ When handling a sequence of wheel events in a gesture in a region with non-passive wheel >+ event handlers, only allow the first event in the sequence to call preventDefault(); >+ subsequent events are marked as non-cancelable, so preventDefault() will have no effect. >+ >+ This allows us to do async scrolling even while in regions with active wheel event handlers, >+ if those handlers don't call preventDefault() on the first event. This matches Blink >+ behavior. >+ >+ To achieve this, track some bits in EventHandling to know whether the event was dispatched >+ to script, and whether it was canceled. Send this data back to the scrolling thread via >+ wheelEventWasProcessedByMainThread(), where is sets an Optional<bool> on ScrollingTree. This >+ flag is consulted by ScrollingTree::determineWheelEventProcessing(), and if we have received >+ it, and the event was not canceled, we use MainThreadForNonBlockingDOMEventDispatch when >+ sending events to the main thread. This is used to mark the event as non-cancelable in >+ EventHandler::handleWheelEvent(). >+ >+ Add another ReportDOMEventHandling bit to WheelEventTestMonitor to track the propagation >+ of EventHandling state back to the scrolling thread. >+ >+ * UIProcess/RemoteLayerTree/RemoteScrollingCoordinatorProxy.cpp: >+ (WebKit::RemoteScrollingCoordinatorProxy::handleWheelEvent): >+ * WebProcess/WebPage/EventDispatcher.cpp: >+ (WebKit::EventDispatcher::wheelEvent): >+ > 2020-11-12 Simon Fraser <simon.fraser@apple.com> > > Force wheel event listeners on the root to be passive >diff --git a/Source/WebCore/dom/Element.cpp b/Source/WebCore/dom/Element.cpp >index f77e5660c989a651c7a9138488a71e22ba971342..99ec5cc31621b0a5e0060fa56dda04caaa9c96bd 100644 >--- a/Source/WebCore/dom/Element.cpp >+++ b/Source/WebCore/dom/Element.cpp >@@ -420,9 +420,9 @@ bool Element::dispatchMouseEvent(const PlatformMouseEvent& platformEvent, const > return didNotSwallowEvent; > } > >-bool Element::dispatchWheelEvent(const PlatformWheelEvent& platformEvent) >+bool Element::dispatchWheelEvent(const PlatformWheelEvent& platformEvent, OptionSet<EventHandling>& processing, Event::IsCancelable isCancelable) > { >- auto event = WheelEvent::create(platformEvent, document().windowProxy()); >+ auto event = WheelEvent::create(platformEvent, document().windowProxy(), isCancelable); > > // Events with no deltas are important because they convey platform information about scroll gestures > // and momentum beginning or ending. However, those events should not be sent to the DOM since some >@@ -430,12 +430,21 @@ bool Element::dispatchWheelEvent(const PlatformWheelEvent& platformEvent) > // event handler, and our platform code will correctly handle the phase changes. Calling stopPropagation() > // will prevent the event from being sent to the DOM, but will still call the default event handler. > // FIXME: Move this logic into WheelEvent::create. >- if (!platformEvent.deltaX() && !platformEvent.deltaY()) >+ if (platformEvent.delta().isZero()) > event->stopPropagation(); >+ else >+ processing.add(EventHandling::DispatchedToDOM); > > dispatchEvent(event); > > LOG_WITH_STREAM(Scrolling, stream << "Element " << *this << " dispatchWheelEvent: defaultPrevented " << event->defaultPrevented() << " defaultHandled " << event->defaultHandled()); >+ >+ if (event->defaultPrevented()) >+ processing.add(EventHandling::DefaultPrevented); >+ >+ if (event->defaultHandled()) >+ processing.add(EventHandling::DefaultHandled); >+ > return !event->defaultPrevented() && !event->defaultHandled(); > } > >diff --git a/Source/WebCore/dom/Element.h b/Source/WebCore/dom/Element.h >index 9bc7d20c9dccd71dbf9433da1547420e6a71b333..fcb7ceb625b9143978cfa77fd3fe8a78cf41d9d3 100644 >--- a/Source/WebCore/dom/Element.h >+++ b/Source/WebCore/dom/Element.h >@@ -66,6 +66,8 @@ struct KeyframeAnimationOptions; > struct ScrollIntoViewOptions; > struct ScrollToOptions; > >+enum class EventProcessing : uint8_t; >+ > #if ENABLE(INTERSECTION_OBSERVER) > struct IntersectionObserverData; > #endif >@@ -530,7 +532,7 @@ public: > void setSavedLayerScrollPosition(const IntPoint&); > > bool dispatchMouseEvent(const PlatformMouseEvent&, const AtomString& eventType, int clickCount = 0, Element* relatedTarget = nullptr); >- bool dispatchWheelEvent(const PlatformWheelEvent&); >+ bool dispatchWheelEvent(const PlatformWheelEvent&, OptionSet<EventHandling>&, Event::IsCancelable = Event::IsCancelable::Yes); > bool dispatchKeyEvent(const PlatformKeyboardEvent&); > bool dispatchSimulatedClick(Event* underlyingEvent, SimulatedClickMouseEventOptions = SendNoEvents, SimulatedClickVisualOptions = ShowPressedLook); > void dispatchFocusInEvent(const AtomString& eventType, RefPtr<Element>&& oldFocusedElement); >diff --git a/Source/WebCore/dom/WheelEvent.cpp b/Source/WebCore/dom/WheelEvent.cpp >index 6f5d15fb2d3a490107a7eb8f771b95dc43422fcd..48ea5bd314a5230365b317b588108ad677432765 100644 >--- a/Source/WebCore/dom/WheelEvent.cpp >+++ b/Source/WebCore/dom/WheelEvent.cpp >@@ -50,8 +50,8 @@ inline WheelEvent::WheelEvent(const AtomString& type, const Init& initializer) > { > } > >-inline WheelEvent::WheelEvent(const PlatformWheelEvent& event, RefPtr<WindowProxy>&& view) >- : MouseEvent(eventNames().wheelEvent, CanBubble::Yes, IsCancelable::Yes, IsComposed::Yes, event.timestamp().approximateMonotonicTime(), WTFMove(view), 0, >+inline WheelEvent::WheelEvent(const PlatformWheelEvent& event, RefPtr<WindowProxy>&& view, IsCancelable isCancelable) >+ : MouseEvent(eventNames().wheelEvent, CanBubble::Yes, isCancelable, IsComposed::Yes, event.timestamp().approximateMonotonicTime(), WTFMove(view), 0, > event.globalPosition(), event.position() , { }, event.modifiers(), 0, 0, nullptr, 0, 0, IsSimulated::No, IsTrusted::Yes) > , m_wheelDelta(event.wheelTicksX() * TickMultiplier, event.wheelTicksY() * TickMultiplier) > , m_deltaX(-event.deltaX()) >@@ -61,9 +61,9 @@ inline WheelEvent::WheelEvent(const PlatformWheelEvent& event, RefPtr<WindowProx > { > } > >-Ref<WheelEvent> WheelEvent::create(const PlatformWheelEvent& event, RefPtr<WindowProxy>&& view) >+Ref<WheelEvent> WheelEvent::create(const PlatformWheelEvent& event, RefPtr<WindowProxy>&& view, IsCancelable isCancelable) > { >- return adoptRef(*new WheelEvent(event, WTFMove(view))); >+ return adoptRef(*new WheelEvent(event, WTFMove(view), isCancelable)); > } > > Ref<WheelEvent> WheelEvent::createForBindings() >diff --git a/Source/WebCore/dom/WheelEvent.h b/Source/WebCore/dom/WheelEvent.h >index cfd597754f5ca00e870df2f2744817586a8beac6..6244a3221b6a30574e4f54050f9fd5188cb0723f 100644 >--- a/Source/WebCore/dom/WheelEvent.h >+++ b/Source/WebCore/dom/WheelEvent.h >@@ -40,7 +40,7 @@ public: > DOM_DELTA_PAGE > }; > >- static Ref<WheelEvent> create(const PlatformWheelEvent&, RefPtr<WindowProxy>&&); >+ static Ref<WheelEvent> create(const PlatformWheelEvent&, RefPtr<WindowProxy>&&, IsCancelable = IsCancelable::Yes); > static Ref<WheelEvent> createForBindings(); > > struct Init : MouseEventInit { >@@ -76,7 +76,7 @@ public: > private: > WheelEvent(); > WheelEvent(const AtomString&, const Init&); >- WheelEvent(const PlatformWheelEvent&, RefPtr<WindowProxy>&&); >+ WheelEvent(const PlatformWheelEvent&, RefPtr<WindowProxy>&&, IsCancelable); > > EventInterface eventInterface() const final; > >diff --git a/Source/WebCore/page/EventHandler.cpp b/Source/WebCore/page/EventHandler.cpp >index 5be980462613e21d3b12e6e0e533d6104899d2f1..8a14941959bb3bc7808d1d4d4da5a64bffbbdb16 100644 >--- a/Source/WebCore/page/EventHandler.cpp >+++ b/Source/WebCore/page/EventHandler.cpp >@@ -2793,6 +2793,10 @@ bool EventHandler::processWheelEventForScrolling(const PlatformWheelEvent& event > return didHandleEvent; > } > >+void EventHandler::wheelEventWasProcessedByMainThread(const PlatformWheelEvent&, OptionSet<EventHandling>) >+{ >+} >+ > bool EventHandler::platformCompletePlatformWidgetWheelEvent(const PlatformWheelEvent&, const Widget&, const WeakPtr<ScrollableArea>&) > { > return true; >@@ -2863,6 +2867,14 @@ bool EventHandler::completeWidgetWheelEvent(const PlatformWheelEvent& event, con > } > > bool EventHandler::handleWheelEvent(const PlatformWheelEvent& event, OptionSet<WheelEventProcessingSteps> processingSteps) >+{ >+ OptionSet<EventHandling> handling; >+ bool handled = handleWheelEventInternal(event, processingSteps, handling); >+ wheelEventWasProcessedByMainThread(event, handling); >+ return handled; >+} >+ >+bool EventHandler::handleWheelEventInternal(const PlatformWheelEvent& event, OptionSet<WheelEventProcessingSteps> processingSteps, OptionSet<EventHandling>& handling) > { > auto* document = m_frame.document(); > if (!document) >@@ -2925,7 +2937,8 @@ bool EventHandler::handleWheelEvent(const PlatformWheelEvent& event, OptionSet<W > } > } > >- if (!element->dispatchWheelEvent(event)) { >+ auto isCancelable = processingSteps.contains(WheelEventProcessingSteps::MainThreadForBlockingDOMEventDispatch) ? Event::IsCancelable::Yes : Event::IsCancelable::No; >+ if (!element->dispatchWheelEvent(event, handling, isCancelable)) { > m_isHandlingWheelEvent = false; > if (scrollableArea && scrollableArea->scrollShouldClearLatchedState()) { > // Web developer is controlling scrolling, so don't attempt to latch. >@@ -2955,6 +2968,7 @@ bool EventHandler::handleWheelEvent(const PlatformWheelEvent& event, OptionSet<W > handledEvent = processWheelEventForScrolling(event, scrollableArea); > processWheelEventForScrollSnap(event, scrollableArea); > } >+ > return handledEvent; > } > >diff --git a/Source/WebCore/page/EventHandler.h b/Source/WebCore/page/EventHandler.h >index e8558865654f934385d280eb39ffa18334a8cd8a..90c787b67e2f3dc29d4588eebf587fa9351de9d9 100644 >--- a/Source/WebCore/page/EventHandler.h >+++ b/Source/WebCore/page/EventHandler.h >@@ -208,8 +208,11 @@ public: > bool handleMouseMoveEvent(const PlatformMouseEvent&, HitTestResult* = nullptr, bool onlyUpdateScrollbars = false); > WEBCORE_EXPORT bool handleMouseReleaseEvent(const PlatformMouseEvent&); > bool handleMouseForceEvent(const PlatformMouseEvent&); >+ > WEBCORE_EXPORT bool handleWheelEvent(const PlatformWheelEvent&, OptionSet<WheelEventProcessingSteps>); > void defaultWheelEventHandler(Node*, WheelEvent&); >+ void wheelEventWasProcessedByMainThread(const PlatformWheelEvent&, OptionSet<EventHandling>); >+ > bool handlePasteGlobalSelection(const PlatformMouseEvent&); > > #if ENABLE(IOS_TOUCH_EVENTS) || ENABLE(IOS_GESTURE_EVENTS) >@@ -452,6 +455,7 @@ private: > > bool passMouseDownEventToWidget(Widget*); > >+ bool handleWheelEventInternal(const PlatformWheelEvent&, OptionSet<WheelEventProcessingSteps>, OptionSet<EventHandling>&); > bool passWheelEventToWidget(const PlatformWheelEvent&, Widget&, OptionSet<WheelEventProcessingSteps>); > void determineWheelEventTarget(const PlatformWheelEvent&, RefPtr<Element>& eventTarget, WeakPtr<ScrollableArea>&, bool& isOverWidget); > void recordWheelEventForDeltaFilter(const PlatformWheelEvent&); >diff --git a/Source/WebCore/page/FrameView.cpp b/Source/WebCore/page/FrameView.cpp >index 47d03107f04a2948dfe9782b330059a88d1c532a..08b3c10fcfe162762ae422016b521dab51e864d6 100644 >--- a/Source/WebCore/page/FrameView.cpp >+++ b/Source/WebCore/page/FrameView.cpp >@@ -5145,7 +5145,7 @@ bool FrameView::wheelEvent(const PlatformWheelEvent& wheelEvent) > #if ENABLE(ASYNC_SCROLLING) > if (auto scrollingCoordinator = this->scrollingCoordinator()) { > if (scrollingCoordinator->coordinatesScrollingForFrameView(*this)) >- return scrollingCoordinator->handleWheelEvent(*this, wheelEvent, scrollingNodeID()); >+ return scrollingCoordinator->performDefaultWheelEventHandling(wheelEvent, scrollingNodeID()); > } > #endif > >diff --git a/Source/WebCore/page/PointerLockController.cpp b/Source/WebCore/page/PointerLockController.cpp >index eadcee36e973a1dd3be7687e89d07b43949d23dd..cf1ac5d0a238b453815ecdda8212727a12d8ea70 100644 >--- a/Source/WebCore/page/PointerLockController.cpp >+++ b/Source/WebCore/page/PointerLockController.cpp >@@ -193,7 +193,8 @@ void PointerLockController::dispatchLockedWheelEvent(const PlatformWheelEvent& e > if (!m_element || !m_element->document().frame()) > return; > >- m_element->dispatchWheelEvent(event); >+ OptionSet<EventHandling> defaultHandling; >+ m_element->dispatchWheelEvent(event, defaultHandling); > } > > void PointerLockController::clearElement() >diff --git a/Source/WebCore/page/WheelEventTestMonitor.cpp b/Source/WebCore/page/WheelEventTestMonitor.cpp >index 0e1ab4dc99d9faf978f121cf6f15f3e06c7dd838..6377202866181f0c6ddbde2a73ec783f8487c70b 100644 >--- a/Source/WebCore/page/WheelEventTestMonitor.cpp >+++ b/Source/WebCore/page/WheelEventTestMonitor.cpp >@@ -170,6 +170,7 @@ TextStream& operator<<(TextStream& ts, WheelEventTestMonitor::DeferReason reason > switch (reason) { > case WheelEventTestMonitor::HandlingWheelEvent: ts << "handling wheel event"; break; > case WheelEventTestMonitor::HandlingWheelEventOnMainThread: ts << "handling wheel event on main thread"; break; >+ case WheelEventTestMonitor::ReportDOMEventHandling: ts << "report DOM event handling"; break; > case WheelEventTestMonitor::RubberbandInProgress: ts << "rubberbanding"; break; > case WheelEventTestMonitor::ScrollSnapInProgress: ts << "scroll-snapping"; break; > case WheelEventTestMonitor::ScrollingThreadSyncNeeded: ts << "scrolling thread sync needed"; break; >diff --git a/Source/WebCore/page/WheelEventTestMonitor.h b/Source/WebCore/page/WheelEventTestMonitor.h >index 026103f4ec83eb2689fa2b3ec1225f050ae638ed..92077e9d5fab0ef9fe26f0552573728c1c7675cd 100644 >--- a/Source/WebCore/page/WheelEventTestMonitor.h >+++ b/Source/WebCore/page/WheelEventTestMonitor.h >@@ -50,11 +50,12 @@ public: > enum DeferReason { > HandlingWheelEvent = 1 << 0, > HandlingWheelEventOnMainThread = 1 << 1, >- RubberbandInProgress = 1 << 2, >- ScrollSnapInProgress = 1 << 3, >- ScrollingThreadSyncNeeded = 1 << 4, >- ContentScrollInProgress = 1 << 5, >- RequestedScrollPosition = 1 << 6, >+ ReportDOMEventHandling = 1 << 2, >+ RubberbandInProgress = 1 << 3, >+ ScrollSnapInProgress = 1 << 4, >+ ScrollingThreadSyncNeeded = 1 << 5, >+ ContentScrollInProgress = 1 << 6, >+ RequestedScrollPosition = 1 << 7, > }; > typedef const void* ScrollableAreaIdentifier; > >diff --git a/Source/WebCore/page/ios/EventHandlerIOS.mm b/Source/WebCore/page/ios/EventHandlerIOS.mm >index c555245721e9ff230ed5bdc9dd22e5f140b87cf2..7d1d5a4c7cea5a67b8f31218806690e6deb2ea6e 100644 >--- a/Source/WebCore/page/ios/EventHandlerIOS.mm >+++ b/Source/WebCore/page/ios/EventHandlerIOS.mm >@@ -107,7 +107,7 @@ bool EventHandler::wheelEvent(WebEvent *event) > > CurrentEventScope scope(event); > >- bool eventWasHandled = handleWheelEvent(PlatformEventFactory::createPlatformWheelEvent(event), { WheelEventProcessingSteps::MainThreadForScrolling, WheelEventProcessingSteps::MainThreadForDOMEventDispatch }); >+ bool eventWasHandled = handleWheelEvent(PlatformEventFactory::createPlatformWheelEvent(event), { WheelEventProcessingSteps::MainThreadForScrolling, WheelEventProcessingSteps::MainThreadForBlockingDOMEventDispatch }); > event.wasHandled = eventWasHandled; > return eventWasHandled; > } >diff --git a/Source/WebCore/page/mac/EventHandlerMac.mm b/Source/WebCore/page/mac/EventHandlerMac.mm >index af735ee7aea678305425c3138d8500c6d8ef7370..ea3577ea418f4e2948e18ca91917ce602c6e6537 100644 >--- a/Source/WebCore/page/mac/EventHandlerMac.mm >+++ b/Source/WebCore/page/mac/EventHandlerMac.mm >@@ -63,6 +63,7 @@ > #import "ScrollLatchingController.h" > #import "ScrollableArea.h" > #import "Scrollbar.h" >+#import "ScrollingCoordinator.h" > #import "Settings.h" > #import "ShadowRoot.h" > #import "SimpleRange.h" >@@ -146,7 +147,7 @@ bool EventHandler::wheelEvent(NSEvent *event) > return false; > > CurrentEventScope scope(event, nil); >- return handleWheelEvent(PlatformEventFactory::createPlatformWheelEvent(event, page->chrome().platformPageClient()), { WheelEventProcessingSteps::MainThreadForScrolling, WheelEventProcessingSteps::MainThreadForDOMEventDispatch }); >+ return handleWheelEvent(PlatformEventFactory::createPlatformWheelEvent(event, page->chrome().platformPageClient()), { WheelEventProcessingSteps::MainThreadForScrolling, WheelEventProcessingSteps::MainThreadForBlockingDOMEventDispatch }); > } > > bool EventHandler::keyEvent(NSEvent *event) >@@ -952,6 +953,23 @@ bool EventHandler::processWheelEventForScrolling(const PlatformWheelEvent& wheel > return didHandleEvent; > } > >+void EventHandler::wheelEventWasProcessedByMainThread(const PlatformWheelEvent& wheelEvent, OptionSet<EventHandling> defaultHandling) >+{ >+#if ENABLE(ASYNC_SCROLLING) >+ if (!m_frame.page()) >+ return; >+ >+ FrameView* view = m_frame.view(); >+ if (!view) >+ return; >+ >+ if (auto scrollingCoordinator = m_frame.page()->scrollingCoordinator()) { >+ if (scrollingCoordinator->coordinatesScrollingForFrameView(*view)) >+ scrollingCoordinator->wheelEventWasProcessedByMainThread(wheelEvent, defaultHandling); >+ } >+#endif >+} >+ > bool EventHandler::platformCompletePlatformWidgetWheelEvent(const PlatformWheelEvent& wheelEvent, const Widget& widget, const WeakPtr<ScrollableArea>& scrollableArea) > { > // WebKit1: Prevent multiple copies of the scrollWheel event from being sent to the NSScrollView widget. >diff --git a/Source/WebCore/page/scrolling/ScrollingCoordinator.h b/Source/WebCore/page/scrolling/ScrollingCoordinator.h >index 2c53ec05123c235a00a6fd7d32004ae763d338eb..7b74f8a9051e3599c4dbf42bafc089e1be48f4d1 100644 >--- a/Source/WebCore/page/scrolling/ScrollingCoordinator.h >+++ b/Source/WebCore/page/scrolling/ScrollingCoordinator.h >@@ -127,7 +127,8 @@ public: > // These virtual functions are currently unique to the threaded scrolling architecture. > virtual void commitTreeStateIfNeeded() { } > virtual bool requestScrollPositionUpdate(ScrollableArea&, const IntPoint&, ScrollType = ScrollType::Programmatic, ScrollClamping = ScrollClamping::Clamped) { return false; } >- virtual bool handleWheelEvent(FrameView&, const PlatformWheelEvent&, ScrollingNodeID) { return false; } >+ virtual bool performDefaultWheelEventHandling(const PlatformWheelEvent&, ScrollingNodeID) { return false; } >+ virtual void wheelEventWasProcessedByMainThread(const PlatformWheelEvent&, OptionSet<EventHandling>) { } > > // Create an unparented node. > virtual ScrollingNodeID createNode(ScrollingNodeType, ScrollingNodeID newNodeID) { return newNodeID; } >diff --git a/Source/WebCore/page/scrolling/ScrollingTree.cpp b/Source/WebCore/page/scrolling/ScrollingTree.cpp >index 0da73ecd395f6117b4088d90d51b1040317001e0..699df3ce0d9501674129018f434a167247c149ce 100644 >--- a/Source/WebCore/page/scrolling/ScrollingTree.cpp >+++ b/Source/WebCore/page/scrolling/ScrollingTree.cpp >@@ -73,6 +73,9 @@ OptionSet<WheelEventProcessingSteps> ScrollingTree::determineWheelEventProcessin > LOG_WITH_STREAM(ScrollLatching, stream << "ScrollingTree::determineWheelEventProcessing " << wheelEvent << " have latched node " << latchedNodeAndSteps->scrollingNodeID << " steps " << latchedNodeAndSteps->processingSteps); > return latchedNodeAndSteps->processingSteps; > } >+ >+ if (wheelEvent.isGestureBegin() || wheelEvent.isNonGestureEvent()) >+ m_treeState.requiresSynchronousEventDispatch = WTF::nullopt; > > m_latchingController.receivedWheelEvent(wheelEvent, m_allowLatching); > >@@ -93,16 +96,20 @@ OptionSet<WheelEventProcessingSteps> ScrollingTree::determineWheelEventProcessin > LOG_WITH_STREAM(Scrolling, stream << "\nScrollingTree::determineWheelEventProcessing: wheelEvent " << wheelEvent << " mapped to content point " << position << ", in non-fast region " << isSynchronousDispatchRegion); > > if (isSynchronousDispatchRegion) >- return { WheelEventProcessingSteps::MainThreadForScrolling, WheelEventProcessingSteps::MainThreadForDOMEventDispatch }; >+ return { WheelEventProcessingSteps::MainThreadForScrolling, WheelEventProcessingSteps::MainThreadForBlockingDOMEventDispatch }; > } > > #if ENABLE(WHEEL_EVENT_REGIONS) > auto eventListenerTypes = eventListenerRegionTypesForPoint(position); >- if (eventListenerTypes.contains(EventListenerRegionType::NonPassiveWheel)) >- return { WheelEventProcessingSteps::MainThreadForScrolling, WheelEventProcessingSteps::MainThreadForDOMEventDispatch }; >+ if (eventListenerTypes.contains(EventListenerRegionType::NonPassiveWheel)) { >+ if (m_treeState.requiresSynchronousEventDispatch && !*m_treeState.requiresSynchronousEventDispatch) >+ return { WheelEventProcessingSteps::ScrollingThread, WheelEventProcessingSteps::MainThreadForNonBlockingDOMEventDispatch }; >+ >+ return { WheelEventProcessingSteps::MainThreadForScrolling, WheelEventProcessingSteps::MainThreadForBlockingDOMEventDispatch }; >+ } > > if (eventListenerTypes.contains(EventListenerRegionType::Wheel)) >- return { WheelEventProcessingSteps::ScrollingThread, WheelEventProcessingSteps::MainThreadForDOMEventDispatch }; >+ return { WheelEventProcessingSteps::ScrollingThread, WheelEventProcessingSteps::MainThreadForNonBlockingDOMEventDispatch }; > #endif > return { WheelEventProcessingSteps::ScrollingThread }; > }(); >@@ -163,7 +170,8 @@ WheelEventHandlingResult ScrollingTree::handleWheelEvent(const PlatformWheelEven > return handleWheelEventWithNode(wheelEvent, processingSteps, node.get()); > }(); > >- result.steps.add(processingSteps & WheelEventProcessingSteps::MainThreadForDOMEventDispatch); >+ static constexpr OptionSet<WheelEventProcessingSteps> mainThreadSteps = { WheelEventProcessingSteps::MainThreadForNonBlockingDOMEventDispatch, WheelEventProcessingSteps::MainThreadForBlockingDOMEventDispatch }; >+ result.steps.add(processingSteps & mainThreadSteps); > return result; > } > >@@ -482,6 +490,12 @@ void ScrollingTree::setMainFrameScrollPosition(FloatPoint position) > m_treeState.mainFrameScrollPosition = position; > } > >+void ScrollingTree::setGestureRequiresSynchronousEventDispatch(bool requiresSynchronousDispatch) >+{ >+ LockHolder lock(m_treeStateMutex); >+ m_treeState.requiresSynchronousEventDispatch = requiresSynchronousDispatch; >+} >+ > TrackingType ScrollingTree::eventTrackingTypeForPoint(const AtomString& eventName, IntPoint p) > { > LockHolder lock(m_treeStateMutex); >diff --git a/Source/WebCore/page/scrolling/ScrollingTree.h b/Source/WebCore/page/scrolling/ScrollingTree.h >index 3e79feb49b1259196ec1ca664097855f9364af82..fd45154990f356451e3571de2aac6d5c2913e470 100644 >--- a/Source/WebCore/page/scrolling/ScrollingTree.h >+++ b/Source/WebCore/page/scrolling/ScrollingTree.h >@@ -59,7 +59,7 @@ using PlatformDisplayID = uint32_t; > struct WheelEventHandlingResult { > OptionSet<WheelEventProcessingSteps> steps; > bool wasHandled { false }; >- bool needsMainThreadProcessing() const { return steps.containsAny({ WheelEventProcessingSteps::MainThreadForScrolling, WheelEventProcessingSteps::MainThreadForDOMEventDispatch }); } >+ bool needsMainThreadProcessing() const { return steps.containsAny({ WheelEventProcessingSteps::MainThreadForScrolling, WheelEventProcessingSteps::MainThreadForNonBlockingDOMEventDispatch, WheelEventProcessingSteps::MainThreadForBlockingDOMEventDispatch }); } > > static WheelEventHandlingResult handled(OptionSet<WheelEventProcessingSteps> steps = { }) > { >@@ -238,6 +238,8 @@ protected: > FloatPoint mainFrameScrollPosition() const; > void setMainFrameScrollPosition(FloatPoint); > >+ void setGestureRequiresSynchronousEventDispatch(bool); >+ > Optional<unsigned> nominalFramesPerSecond(); > > void applyLayerPositionsInternal(); >@@ -278,6 +280,7 @@ private: > FloatPoint mainFrameScrollPosition; > PlatformDisplayID displayID { 0 }; > Optional<unsigned> nominalFramesPerSecond; >+ Optional<bool> requiresSynchronousEventDispatch; > HashSet<ScrollingNodeID> nodesWithActiveRubberBanding; > HashSet<ScrollingNodeID> nodesWithActiveScrollSnap; > HashSet<ScrollingNodeID> nodesWithActiveUserScrolls; >diff --git a/Source/WebCore/page/scrolling/ThreadedScrollingTree.cpp b/Source/WebCore/page/scrolling/ThreadedScrollingTree.cpp >index 9379456eae17b0982cb5d52d995fcaded54b7d4d..f7ed1eefd280116329bad3faa9b367c9c2e0dc44 100644 >--- a/Source/WebCore/page/scrolling/ThreadedScrollingTree.cpp >+++ b/Source/WebCore/page/scrolling/ThreadedScrollingTree.cpp >@@ -67,13 +67,23 @@ WheelEventHandlingResult ThreadedScrollingTree::handleWheelEvent(const PlatformW > bool ThreadedScrollingTree::handleWheelEventAfterMainThread(const PlatformWheelEvent& wheelEvent, ScrollingNodeID targetNodeID) > { > LOG_WITH_STREAM(Scrolling, stream << "ThreadedScrollingTree::handleWheelEventAfterMainThread " << wheelEvent); >- SetForScope<bool> disallowLatchingScope(m_allowLatching, false); > >+ LockHolder locker(m_treeMutex); >+ >+ SetForScope<bool> disallowLatchingScope(m_allowLatching, false); > RefPtr<ScrollingTreeNode> targetNode = nodeForID(targetNodeID); > auto result = handleWheelEventWithNode(wheelEvent, { }, targetNode.get()); > return result.wasHandled; > } > >+void ThreadedScrollingTree::wheelEventWasProcessedByMainThread(const PlatformWheelEvent&, OptionSet<EventHandling> eventHandling) >+{ >+ LOG_WITH_STREAM(Scrolling, stream << "ThreadedScrollingTree::wheelEventWasProcessedByMainThread - handling " << eventHandling); >+ >+ if (eventHandling.contains(EventHandling::DispatchedToDOM)) >+ setGestureRequiresSynchronousEventDispatch(eventHandling.contains(EventHandling::DefaultPrevented)); >+} >+ > void ThreadedScrollingTree::invalidate() > { > // Invalidate is dispatched by the ScrollingCoordinator class on the ScrollingThread >diff --git a/Source/WebCore/page/scrolling/ThreadedScrollingTree.h b/Source/WebCore/page/scrolling/ThreadedScrollingTree.h >index 4f68ebc543ce3485c93e0f6e065f18a222f89077..5f0a755442fb4a1b0296b4d5b5c041104c3f4eaf 100644 >--- a/Source/WebCore/page/scrolling/ThreadedScrollingTree.h >+++ b/Source/WebCore/page/scrolling/ThreadedScrollingTree.h >@@ -48,6 +48,7 @@ public: > WheelEventHandlingResult handleWheelEvent(const PlatformWheelEvent&, OptionSet<WheelEventProcessingSteps>) override; > > bool handleWheelEventAfterMainThread(const PlatformWheelEvent&, ScrollingNodeID); >+ void wheelEventWasProcessedByMainThread(const PlatformWheelEvent&, OptionSet<EventHandling>); > > void invalidate() override; > >diff --git a/Source/WebCore/page/scrolling/mac/ScrollingCoordinatorMac.h b/Source/WebCore/page/scrolling/mac/ScrollingCoordinatorMac.h >index b22a76b0914bad66914fdfdd2ce6d597e52f6157..ee575677d1b11dc2c8fe404a3d244881918a9f03 100644 >--- a/Source/WebCore/page/scrolling/mac/ScrollingCoordinatorMac.h >+++ b/Source/WebCore/page/scrolling/mac/ScrollingCoordinatorMac.h >@@ -41,7 +41,8 @@ public: > void commitTreeStateIfNeeded() final; > > // Handle the wheel event on the scrolling thread. Returns whether the event was handled or not. >- bool handleWheelEvent(FrameView&, const PlatformWheelEvent&, ScrollingNodeID) override; >+ bool performDefaultWheelEventHandling(const PlatformWheelEvent&, ScrollingNodeID) final; >+ void wheelEventWasProcessedByMainThread(const PlatformWheelEvent&, OptionSet<EventHandling>) final; > > private: > void scheduleTreeStateCommit() final; >diff --git a/Source/WebCore/page/scrolling/mac/ScrollingCoordinatorMac.mm b/Source/WebCore/page/scrolling/mac/ScrollingCoordinatorMac.mm >index b49f10c9fcc0fafbd958d72e0a0d68dc5279bf3d..75506f21367ad46adec91e1c5c8482178f352b45 100644 >--- a/Source/WebCore/page/scrolling/mac/ScrollingCoordinatorMac.mm >+++ b/Source/WebCore/page/scrolling/mac/ScrollingCoordinatorMac.mm >@@ -73,7 +73,7 @@ void ScrollingCoordinatorMac::pageDestroyed() > }); > } > >-bool ScrollingCoordinatorMac::handleWheelEvent(FrameView&, const PlatformWheelEvent& wheelEvent, ScrollingNodeID targetNode) >+bool ScrollingCoordinatorMac::performDefaultWheelEventHandling(const PlatformWheelEvent& wheelEvent, ScrollingNodeID targetNode) > { > ASSERT(isMainThread()); > ASSERT(m_page); >@@ -93,6 +93,29 @@ bool ScrollingCoordinatorMac::handleWheelEvent(FrameView&, const PlatformWheelEv > return true; > } > >+static uint64_t nextDeferIdentifier() >+{ >+ static uint64_t deferIdentifier; >+ return ++deferIdentifier; >+} >+ >+void ScrollingCoordinatorMac::wheelEventWasProcessedByMainThread(const PlatformWheelEvent& wheelEvent, OptionSet<EventHandling> defaultHandling) >+{ >+ uint64_t deferIdentifier = 0; >+ if (m_page && m_page->isMonitoringWheelEvents()) { >+ deferIdentifier = nextDeferIdentifier(); >+ m_page->wheelEventTestMonitor()->deferForReason(reinterpret_cast<WheelEventTestMonitor::ScrollableAreaIdentifier>(deferIdentifier), WheelEventTestMonitor::ReportDOMEventHandling); >+ } >+ >+ RefPtr<ThreadedScrollingTree> threadedScrollingTree = downcast<ThreadedScrollingTree>(scrollingTree()); >+ ScrollingThread::dispatch([threadedScrollingTree, wheelEvent, defaultHandling, deferIdentifier] { >+ threadedScrollingTree->wheelEventWasProcessedByMainThread(wheelEvent, defaultHandling); >+ >+ if (threadedScrollingTree->isMonitoringWheelEvents()) >+ threadedScrollingTree->removeWheelEventTestCompletionDeferralForReason(reinterpret_cast<WheelEventTestMonitor::ScrollableAreaIdentifier>(deferIdentifier), WheelEventTestMonitor::ReportDOMEventHandling); >+ }); >+} >+ > void ScrollingCoordinatorMac::scheduleTreeStateCommit() > { > // FIXME: This one needs work. >diff --git a/Source/WebCore/page/scrolling/mac/ScrollingTreeOverflowScrollingNodeMac.mm b/Source/WebCore/page/scrolling/mac/ScrollingTreeOverflowScrollingNodeMac.mm >index 18a5256c83a7f38e3b7d0860cfc796f9acbd94f3..647c729fc2ed9221814b774e142a024916bcd5ac 100644 >--- a/Source/WebCore/page/scrolling/mac/ScrollingTreeOverflowScrollingNodeMac.mm >+++ b/Source/WebCore/page/scrolling/mac/ScrollingTreeOverflowScrollingNodeMac.mm >@@ -77,7 +77,7 @@ WheelEventHandlingResult ScrollingTreeOverflowScrollingNodeMac::handleWheelEvent > { > #if ENABLE(SCROLLING_THREAD) > if (hasSynchronousScrollingReasons()) >- return { { WheelEventProcessingSteps::MainThreadForScrolling, WheelEventProcessingSteps::MainThreadForDOMEventDispatch }, false }; >+ return { { WheelEventProcessingSteps::MainThreadForScrolling, WheelEventProcessingSteps::MainThreadForNonBlockingDOMEventDispatch }, false }; > #endif > > if (!canHandleWheelEvent(wheelEvent)) >diff --git a/Source/WebCore/page/scrolling/nicosia/ScrollingCoordinatorNicosia.cpp b/Source/WebCore/page/scrolling/nicosia/ScrollingCoordinatorNicosia.cpp >index d9e1e6b2813cc456de8c3c01815b21c607d84187..9dac12d2584601e71e5dec0f65bf63615a37ab79 100644 >--- a/Source/WebCore/page/scrolling/nicosia/ScrollingCoordinatorNicosia.cpp >+++ b/Source/WebCore/page/scrolling/nicosia/ScrollingCoordinatorNicosia.cpp >@@ -69,7 +69,7 @@ void ScrollingCoordinatorNicosia::commitTreeStateIfNeeded() > m_scrollingStateTreeCommitterTimer.stop(); > } > >-bool ScrollingCoordinatorNicosia::handleWheelEvent(FrameView&, const PlatformWheelEvent& wheelEvent, ScrollingNodeID targetNode) >+bool ScrollingCoordinatorNicosia::performDefaultWheelEventHandling(FrameView&, const PlatformWheelEvent& wheelEvent, ScrollingNodeID targetNode) > { > ASSERT(isMainThread()); > ASSERT(m_page); >@@ -81,6 +81,13 @@ bool ScrollingCoordinatorNicosia::handleWheelEvent(FrameView&, const PlatformWhe > return true; > } > >+void ScrollingCoordinatorMac::wheelEventWasProcessedByMainThread(const PlatformWheelEvent& wheelEvent, OptionSet<EventHandling> defaultHandling) >+{ >+ ScrollingThread::dispatch([threadedScrollingTree = makeRef(downcast<ThreadedScrollingTree>(*scrollingTree())), wheelEvent, defaultHandling] { >+ threadedScrollingTree->wheelEventWasProcessedByMainThread(wheelEvent, defaultHandling); >+ }); >+} >+ > void ScrollingCoordinatorNicosia::scheduleTreeStateCommit() > { > if (!m_scrollingStateTreeCommitterTimer.isActive()) >diff --git a/Source/WebCore/page/scrolling/nicosia/ScrollingCoordinatorNicosia.h b/Source/WebCore/page/scrolling/nicosia/ScrollingCoordinatorNicosia.h >index 845665ce7718c60dd4db12806cdf3c9154f8e4f2..1061257b8c947201590a9596bf1ab8490c059d24 100644 >--- a/Source/WebCore/page/scrolling/nicosia/ScrollingCoordinatorNicosia.h >+++ b/Source/WebCore/page/scrolling/nicosia/ScrollingCoordinatorNicosia.h >@@ -44,7 +44,8 @@ public: > > void commitTreeStateIfNeeded() override; > >- bool handleWheelEvent(FrameView&, const PlatformWheelEvent&, ScrollingNodeID) override; >+ bool performDefaultWheelEventHandling(FrameView&, const PlatformWheelEvent&, ScrollingNodeID) override; >+ void wheelEventWasProcessedByMainThread(const PlatformWheelEvent&, OptionSet<EventHandling>) override; > > private: > void scheduleTreeStateCommit() override; >diff --git a/Source/WebCore/platform/PlatformEvent.h b/Source/WebCore/platform/PlatformEvent.h >index 7b9b73c9d932dca71b6c8591e21f6beba78eb881..e6ca92e4f765c8291e9781b0df2872f2d950f7c3 100644 >--- a/Source/WebCore/platform/PlatformEvent.h >+++ b/Source/WebCore/platform/PlatformEvent.h >@@ -30,6 +30,12 @@ > > namespace WebCore { > >+enum class EventHandling : uint8_t { >+ DispatchedToDOM = 1 << 0, >+ DefaultPrevented = 1 << 1, >+ DefaultHandled = 1 << 2, >+}; >+ > class PlatformEvent { > public: > enum Type : uint8_t { >diff --git a/Source/WebCore/platform/PlatformWheelEvent.cpp b/Source/WebCore/platform/PlatformWheelEvent.cpp >index 42663b78a45edd899f505001d615ac96efc70716..ec152a3e4884394fda6541eb4059c20b40f1837a 100644 >--- a/Source/WebCore/platform/PlatformWheelEvent.cpp >+++ b/Source/WebCore/platform/PlatformWheelEvent.cpp >@@ -64,7 +64,18 @@ TextStream& operator<<(TextStream& ts, WheelEventProcessingSteps steps) > switch (steps) { > case WheelEventProcessingSteps::ScrollingThread: ts << "scrolling thread"; break; > case WheelEventProcessingSteps::MainThreadForScrolling: ts << "main thread scrolling"; break; >- case WheelEventProcessingSteps::MainThreadForDOMEventDispatch: ts << "main thread DOM event dispatch"; break; >+ case WheelEventProcessingSteps::MainThreadForNonBlockingDOMEventDispatch: ts << "main thread non-blocking DOM event dispatch"; break; >+ case WheelEventProcessingSteps::MainThreadForBlockingDOMEventDispatch: ts << "main thread blocking DOM event dispatch"; break; >+ } >+ return ts; >+} >+ >+TextStream& operator<<(TextStream& ts, EventHandling steps) >+{ >+ switch (steps) { >+ case EventHandling::DispatchedToDOM: ts << "dispatched to DOM"; break; >+ case EventHandling::DefaultPrevented: ts << "default prevented"; break; >+ case EventHandling::DefaultHandled: ts << "default handled"; break; > } > return ts; > } >diff --git a/Source/WebCore/platform/PlatformWheelEvent.h b/Source/WebCore/platform/PlatformWheelEvent.h >index 3d5693d82e09f330cafb89f8b5ee28760b932fba..5d4864d455a03416187ca16a625726abbc4f37f7 100644 >--- a/Source/WebCore/platform/PlatformWheelEvent.h >+++ b/Source/WebCore/platform/PlatformWheelEvent.h >@@ -37,9 +37,10 @@ class TextStream; > namespace WebCore { > > enum class WheelEventProcessingSteps : uint8_t { >- ScrollingThread = 1 << 0, >- MainThreadForScrolling = 1 << 1, >- MainThreadForDOMEventDispatch = 1 << 2, >+ ScrollingThread = 1 << 0, >+ MainThreadForScrolling = 1 << 1, >+ MainThreadForNonBlockingDOMEventDispatch = 1 << 2, >+ MainThreadForBlockingDOMEventDispatch = 1 << 3, > }; > > // The ScrollByPixelWheelEvent is a fine-grained event that specifies the precise number of pixels to scroll. >@@ -265,5 +266,6 @@ inline FloatPoint PlatformWheelEvent::swipeVelocity() const > > WEBCORE_EXPORT WTF::TextStream& operator<<(WTF::TextStream&, const PlatformWheelEvent&); > WEBCORE_EXPORT WTF::TextStream& operator<<(WTF::TextStream&, WheelEventProcessingSteps); >+WEBCORE_EXPORT WTF::TextStream& operator<<(WTF::TextStream&, EventHandling); > > } // namespace WebCore >diff --git a/Source/WebKit/UIProcess/RemoteLayerTree/RemoteScrollingCoordinatorProxy.cpp b/Source/WebKit/UIProcess/RemoteLayerTree/RemoteScrollingCoordinatorProxy.cpp >index da51955bb038f8bf7728dec5616be60efbbeeb23..9df914d5dac5954bf3c2bc5e0becfa6195333188 100644 >--- a/Source/WebKit/UIProcess/RemoteLayerTree/RemoteScrollingCoordinatorProxy.cpp >+++ b/Source/WebKit/UIProcess/RemoteLayerTree/RemoteScrollingCoordinatorProxy.cpp >@@ -180,7 +180,7 @@ bool RemoteScrollingCoordinatorProxy::handleWheelEvent(const PlatformWheelEvent& > return false; > > auto processingSteps = m_scrollingTree->determineWheelEventProcessing(wheelEvent); >- if (processingSteps.containsAny({ WheelEventProcessingSteps::MainThreadForScrolling, WheelEventProcessingSteps::MainThreadForDOMEventDispatch })) >+ if (processingSteps.containsAny({ WheelEventProcessingSteps::MainThreadForScrolling, WheelEventProcessingSteps::MainThreadForNonBlockingDOMEventDispatch, WheelEventProcessingSteps::MainThreadForBlockingDOMEventDispatch })) > return false; > > if (m_scrollingTree->willWheelEventStartSwipeGesture(wheelEvent)) >diff --git a/Source/WebKit/WebProcess/WebPage/EventDispatcher.cpp b/Source/WebKit/WebProcess/WebPage/EventDispatcher.cpp >index 549c03adc5219778894d213366d7fceccb5eed51..7598ecfb4b7248e3d68f92e349ca3026ee9014f4 100644 >--- a/Source/WebKit/WebProcess/WebPage/EventDispatcher.cpp >+++ b/Source/WebKit/WebProcess/WebPage/EventDispatcher.cpp >@@ -121,14 +121,14 @@ void EventDispatcher::wheelEvent(PageIdentifier pageID, const WebWheelEvent& whe > } > #endif > >- auto processingSteps = OptionSet<WebCore::WheelEventProcessingSteps> { WheelEventProcessingSteps::MainThreadForScrolling, WheelEventProcessingSteps::MainThreadForDOMEventDispatch }; >+ auto processingSteps = OptionSet<WebCore::WheelEventProcessingSteps> { WheelEventProcessingSteps::MainThreadForScrolling, WheelEventProcessingSteps::MainThreadForBlockingDOMEventDispatch }; > #if ENABLE(SCROLLING_THREAD) > processingSteps = [&]() -> OptionSet<WheelEventProcessingSteps> { > LockHolder locker(m_scrollingTreesMutex); > > auto scrollingTree = m_scrollingTrees.get(pageID); > if (!scrollingTree) >- return { WheelEventProcessingSteps::MainThreadForScrolling, WheelEventProcessingSteps::MainThreadForDOMEventDispatch }; >+ return { WheelEventProcessingSteps::MainThreadForScrolling, WheelEventProcessingSteps::MainThreadForBlockingDOMEventDispatch }; > > // FIXME: It's pretty horrible that we're updating the back/forward state here. > // WebCore should always know the current state and know when it changes so the >diff --git a/Source/WebKitLegacy/win/WebView.cpp b/Source/WebKitLegacy/win/WebView.cpp >index fba19d875190a3e8f644869d42c0ce235adee9b0..f77c262e094f1b22b27ed1d24a2c64d0e2bc372a 100644 >--- a/Source/WebKitLegacy/win/WebView.cpp >+++ b/Source/WebKitLegacy/win/WebView.cpp >@@ -2137,7 +2137,7 @@ bool WebView::mouseWheel(WPARAM wParam, LPARAM lParam, bool isMouseHWheel) > if (!coreFrame) > return false; > >- return coreFrame->eventHandler().handleWheelEvent(wheelEvent, { WheelEventProcessingSteps::MainThreadForScrolling, WheelEventProcessingSteps::MainThreadForDOMEventDispatch }); >+ return coreFrame->eventHandler().handleWheelEvent(wheelEvent, { WheelEventProcessingSteps::MainThreadForScrolling, WheelEventProcessingSteps::MainThreadForBlockingDOMEventDispatch }); > } > > bool WebView::verticalScroll(WPARAM wParam, LPARAM /*lParam*/) >diff --git a/LayoutTests/ChangeLog b/LayoutTests/ChangeLog >index 6363da06bf93180f208ab1527e599c97f3203d09..ab68dbec2f52d96d61b67d16d0314d9cfdc25bdb 100644 >--- a/LayoutTests/ChangeLog >+++ b/LayoutTests/ChangeLog >@@ -1,3 +1,37 @@ >+2020-11-13 Simon Fraser <simon.fraser@apple.com> >+ >+ 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 NOBODY (OOPS!). >+ >+ When handling a sequence of wheel events in a gesture in a region with non-passive wheel >+ event handlers, only allow the first event in the sequence to call preventDefault(); >+ subsequent events are marked as non-cancelable, so preventDefault() will have no effect. >+ >+ This allows us to do async scrolling even while in regions with active wheel event handlers, >+ if those handlers don't call preventDefault() on the first event. This matches Blink >+ behavior. >+ >+ To achieve this, track some bits in EventHandling to know whether the event was dispatched >+ to script, and whether it was canceled. Send this data back to the scrolling thread via >+ wheelEventWasProcessedByMainThread(), where is sets an Optional<bool> on ScrollingTree. This >+ flag is consulted by ScrollingTree::determineWheelEventProcessing(), and if we have received >+ it, and the event was not canceled, we use MainThreadForNonBlockingDOMEventDispatch when >+ sending events to the main thread. This is used to mark the event as non-cancelable in >+ EventHandler::handleWheelEvent(). >+ >+ Add another ReportDOMEventHandling bit to WheelEventTestMonitor to track the propagation >+ of EventHandling state back to the scrolling thread. >+ >+ * fast/events/wheel/first-wheel-event-cancelable-expected.txt: Added. >+ * fast/events/wheel/first-wheel-event-cancelable.html: Added. >+ * fast/events/wheel/wheel-event-in-passive-region-non-cancelable-expected.txt: Added. >+ * fast/events/wheel/wheel-event-in-passive-region-non-cancelable.html: Added. >+ * fast/events/wheel/wheel-events-become-non-cancelable-expected.txt: Added. >+ * fast/events/wheel/wheel-events-become-non-cancelable.html: Added. >+ > 2020-11-12 Simon Fraser <simon.fraser@apple.com> > > Force wheel event listeners on the root to be passive >diff --git a/LayoutTests/fast/events/wheel/first-wheel-event-cancelable-expected.txt b/LayoutTests/fast/events/wheel/first-wheel-event-cancelable-expected.txt >new file mode 100644 >index 0000000000000000000000000000000000000000..2fa59dcc303426aaef5fdc3c74a7097df453faa1 >--- /dev/null >+++ b/LayoutTests/fast/events/wheel/first-wheel-event-cancelable-expected.txt >@@ -0,0 +1,10 @@ >+Tests preventDefault on the first event results in the rest of the events being cancelable >+PASS windowScrollEventCount is 0 >+PASS firstWasCancelable is true >+PASS firstWasCanceled is true >+PASS subsequentWereCancelable is true >+PASS subsequentWereCanceled is true >+PASS successfullyParsed is true >+ >+TEST COMPLETE >+ >diff --git a/LayoutTests/fast/events/wheel/first-wheel-event-cancelable.html b/LayoutTests/fast/events/wheel/first-wheel-event-cancelable.html >new file mode 100644 >index 0000000000000000000000000000000000000000..3e01d8746d7a5b991ff34473e98e04f6964b12a6 >--- /dev/null >+++ b/LayoutTests/fast/events/wheel/first-wheel-event-cancelable.html >@@ -0,0 +1,73 @@ >+<!DOCTYPE html> >+<html> >+<head> >+ <style> >+ body { >+ height: 5000px; >+ } >+ >+ #target { >+ width: 200px; >+ height: 200px; >+ background-color: silver; >+ margin: 20px; >+ } >+ </style> >+ <script src="../../../resources/js-test-pre.js"></script> >+ <script src="../../../resources/ui-helper.js"></script> >+ <script> >+ var jsTestIsAsync = true; >+ >+ let wheelEventCount = 0; >+ let windowScrollEventCount = 0; >+ >+ let firstWasCancelable; >+ let firstWasCanceled; >+ let secondWasCancelable; >+ let secondWasCanceled; >+ >+ async function testScroll() >+ { >+ await UIHelper.mouseWheelScrollAt(100, 100); >+ shouldBe('windowScrollEventCount', '0'); >+ >+ shouldBeTrue('firstWasCancelable'); >+ shouldBeTrue('firstWasCanceled'); >+ >+ shouldBeTrue('secondWasCancelable'); >+ shouldBeTrue('secondWasCanceled'); >+ >+ finishJSTest(); >+ } >+ >+ window.addEventListener('load', () => { >+ debug('Tests preventDefault on the first event results in the rest of the events being cancelable') >+ let target = document.getElementById('target'); >+ >+ target.addEventListener('wheel', (event) => { >+ if (!wheelEventCount) { >+ firstWasCancelable = event.cancelable; >+ event.preventDefault(); >+ firstWasCanceled = event.defaultPrevented; >+ } else if (wheelEventCount == 1) { >+ secondWasCancelable = event.cancelable; >+ event.preventDefault(); >+ secondWasCanceled = event.defaultPrevented; >+ } >+ ++wheelEventCount; >+ }); >+ >+ window.addEventListener('scroll', () => { >+ ++windowScrollEventCount; >+ }, false); >+ >+ setTimeout(testScroll, 0); >+ }, false); >+ </script> >+</head> >+<body> >+ <div id="target"></div> >+ <div id="console"></div> >+ <script src="../../../resources/js-test-post.js"></script> >+</body> >+</html> >diff --git a/LayoutTests/fast/events/wheel/wheel-event-in-passive-region-non-cancelable-expected.txt b/LayoutTests/fast/events/wheel/wheel-event-in-passive-region-non-cancelable-expected.txt >new file mode 100644 >index 0000000000000000000000000000000000000000..dc712971f3a9db3bfb9c78d7fa705e126461b5b1 >--- /dev/null >+++ b/LayoutTests/fast/events/wheel/wheel-event-in-passive-region-non-cancelable-expected.txt >@@ -0,0 +1,8 @@ >+Tests that wheel events in the passive region are non-cancelable >+PASS windowScrollEventCount > 0 is true >+PASS wasCancelable is false >+PASS wasCanceled is false >+PASS successfullyParsed is true >+ >+TEST COMPLETE >+ >diff --git a/LayoutTests/fast/events/wheel/wheel-event-in-passive-region-non-cancelable.html b/LayoutTests/fast/events/wheel/wheel-event-in-passive-region-non-cancelable.html >new file mode 100644 >index 0000000000000000000000000000000000000000..f9190e2b084d86a13deeee2a5052e400e4863f53 >--- /dev/null >+++ b/LayoutTests/fast/events/wheel/wheel-event-in-passive-region-non-cancelable.html >@@ -0,0 +1,64 @@ >+<!DOCTYPE html> >+<html> >+<head> >+ <style> >+ body { >+ height: 5000px; >+ } >+ >+ #target { >+ width: 200px; >+ height: 200px; >+ background-color: silver; >+ margin: 20px; >+ } >+ </style> >+ <script src="../../../resources/js-test-pre.js"></script> >+ <script src="../../../resources/ui-helper.js"></script> >+ <script> >+ var jsTestIsAsync = true; >+ >+ let isFirst = true; >+ let windowScrollEventCount = 0; >+ let wasCancelable; >+ let wasCanceled; >+ >+ async function testScroll() >+ { >+ await UIHelper.mouseWheelScrollAt(100, 100); >+ shouldBeTrue('windowScrollEventCount > 0'); >+ >+ shouldBeFalse('wasCancelable'); >+ shouldBeFalse('wasCanceled'); >+ >+ finishJSTest(); >+ } >+ >+ window.addEventListener('load', () => { >+ debug('Tests that wheel events in the passive region are non-cancelable') >+ let target = document.getElementById('target'); >+ >+ target.addEventListener('wheel', (event) => { >+ if (isFirst) { >+ wasCancelable = event.cancelable; >+ event.preventDefault(); >+ wasCanceled = event.defaultPrevented; >+ isFirst = false; >+ } >+ >+ }, { passive: true }); >+ >+ window.addEventListener('scroll', () => { >+ ++windowScrollEventCount; >+ }, false); >+ >+ setTimeout(testScroll, 0); >+ }, false); >+ </script> >+</head> >+<body> >+ <div id="target"></div> >+ <div id="console"></div> >+ <script src="../../../resources/js-test-post.js"></script> >+</body> >+</html> >diff --git a/LayoutTests/fast/events/wheel/wheel-events-become-non-cancelable-expected.txt b/LayoutTests/fast/events/wheel/wheel-events-become-non-cancelable-expected.txt >new file mode 100644 >index 0000000000000000000000000000000000000000..954a118ded4a5d6d7af60607c707454f54ecb904 >--- /dev/null >+++ b/LayoutTests/fast/events/wheel/wheel-events-become-non-cancelable-expected.txt >@@ -0,0 +1,7 @@ >+Tests that events in the gesture are non-cancelable if preventDefault was not called on the first event >+PASS firstWasCancelable is true >+PASS becameNonCancelable is true >+PASS successfullyParsed is true >+ >+TEST COMPLETE >+ >diff --git a/LayoutTests/fast/events/wheel/wheel-events-become-non-cancelable.html b/LayoutTests/fast/events/wheel/wheel-events-become-non-cancelable.html >new file mode 100644 >index 0000000000000000000000000000000000000000..c94cdaae178a38e493cb670be58c5c29015f4ede >--- /dev/null >+++ b/LayoutTests/fast/events/wheel/wheel-events-become-non-cancelable.html >@@ -0,0 +1,81 @@ >+<!DOCTYPE html> >+<html> >+<head> >+ <style> >+ body { >+ height: 5000px; >+ } >+ >+ #target { >+ width: 200px; >+ height: 1000px; >+ background-color: silver; >+ margin: 20px; >+ } >+ </style> >+ <script src="../../../resources/js-test-pre.js"></script> >+ <script src="../../../resources/ui-helper.js"></script> >+ <script> >+ var jsTestIsAsync = true; >+ >+ let wheelEventCount = 0; >+ >+ let firstWasCancelable; >+ let becameNonCancelable; >+ >+ async function testScroll() >+ { >+ if (!eventSender) { >+ finishJSTest(); >+ return; >+ } >+ eventSender.monitorWheelEvents(); >+ eventSender.mouseMoveTo(100, 100); >+ eventSender.mouseScrollByWithWheelAndMomentumPhases(0, 0, "began", "none"); >+ await UIHelper.renderingUpdate(); >+ eventSender.mouseScrollByWithWheelAndMomentumPhases(0, -5, "changed", "none"); >+ await UIHelper.renderingUpdate(); >+ eventSender.mouseScrollByWithWheelAndMomentumPhases(0, -5, "changed", "none"); >+ await UIHelper.renderingUpdate(); >+ eventSender.mouseScrollByWithWheelAndMomentumPhases(0, -5, "changed", "none"); >+ await UIHelper.renderingUpdate(); >+ eventSender.mouseScrollByWithWheelAndMomentumPhases(0, -5, "changed", "none"); >+ await UIHelper.renderingUpdate(); >+ eventSender.mouseScrollByWithWheelAndMomentumPhases(0, 0, "ended", "none"); >+ >+ await UIHelper.waitForScrollCompletion(); >+ >+ shouldBeTrue('firstWasCancelable'); >+ shouldBeTrue('becameNonCancelable'); >+ >+ finishJSTest(); >+ } >+ >+ window.addEventListener('load', () => { >+ debug('Tests that events in the gesture are non-cancelable if preventDefault was not called on the first event'); >+ let target = document.getElementById('target'); >+ >+ target.addEventListener('wheel', (event) => { >+ if (wheelEventCount == 0) { >+ firstWasCancelable = event.cancelable; >+ } else { >+ // Wait for the first non-cancelable event and check that all subsequent events are non-cancelable. >+ if (becameNonCancelable && event.cancelable) >+ testFailed('Should not see cancelable event after non-cancelable event') >+ >+ if (!event.cancelable) >+ becameNonCancelable = true; >+ } >+ ++wheelEventCount; >+ }); >+ >+ setTimeout(testScroll, 0); >+ }, false); >+ </script> >+</head> >+<body> >+ <div id="target"></div> >+ <div id="console"></div> >+ <script src="../../../resources/js-test-post.js"></script> >+</body> >+</html>
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Formatted Diff
|
Diff
Attachments on
bug 218764
:
414114
|
414116
|
414117
|
414119
|
414745
|
414748
|
414749
|
415347
|
415351
|
415387