Diff
Modified: trunk/LayoutTests/ChangeLog (270311 => 270312)
--- trunk/LayoutTests/ChangeLog 2020-12-01 18:00:27 UTC (rev 270311)
+++ trunk/LayoutTests/ChangeLog 2020-12-01 18:01:31 UTC (rev 270312)
@@ -1,3 +1,19 @@
+2020-11-30 Simon Fraser <[email protected]>
+
+ [WK1] Only the first wheel event in a gesture should be cancelable
+ https://bugs.webkit.org/show_bug.cgi?id=219384
+
+ Reviewed by Chris Dumez.
+
+ Add a temporary failing result for WK2 until the async scrolling implementation lands.
+
+ * fast/events/wheel/first-wheel-event-cancelable-expected.txt: Added.
+ * fast/events/wheel/first-wheel-event-cancelable.html: Added.
+ * fast/events/wheel/wheel-events-become-non-cancelable-expected.txt: Added.
+ * fast/events/wheel/wheel-events-become-non-cancelable.html: Added.
+ * platform/mac-wk2/fast/events/wheel/wheel-events-become-non-cancelable-expected.txt: Added.
+ * platform/win/TestExpectations:
+
2020-12-01 Youenn Fablet <[email protected]>
REGRESSION(STP116): wpt.live/webrtc/RTCRtpTransceiver.https.html crashes on load
Added: trunk/LayoutTests/fast/events/wheel/first-wheel-event-cancelable-expected.txt (0 => 270312)
--- trunk/LayoutTests/fast/events/wheel/first-wheel-event-cancelable-expected.txt (rev 0)
+++ trunk/LayoutTests/fast/events/wheel/first-wheel-event-cancelable-expected.txt 2020-12-01 18:01:31 UTC (rev 270312)
@@ -0,0 +1,14 @@
+Tests preventDefault on the first event results in the rest of the events being cancelable
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS windowScrollEventCount is 0
+PASS firstWasCancelable is true
+PASS firstWasCanceled is true
+PASS secondWasCancelable is true
+PASS secondWasCanceled is true
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
Added: trunk/LayoutTests/fast/events/wheel/first-wheel-event-cancelable.html (0 => 270312)
--- trunk/LayoutTests/fast/events/wheel/first-wheel-event-cancelable.html (rev 0)
+++ trunk/LayoutTests/fast/events/wheel/first-wheel-event-cancelable.html 2020-12-01 18:01:31 UTC (rev 270312)
@@ -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=""
+ <script src=""
+ <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', () => {
+ description('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=""
+</body>
+</html>
Added: trunk/LayoutTests/fast/events/wheel/wheel-events-become-non-cancelable-expected.txt (0 => 270312)
--- trunk/LayoutTests/fast/events/wheel/wheel-events-become-non-cancelable-expected.txt (rev 0)
+++ trunk/LayoutTests/fast/events/wheel/wheel-events-become-non-cancelable-expected.txt 2020-12-01 18:01:31 UTC (rev 270312)
@@ -0,0 +1,11 @@
+Tests that events in the gesture are non-cancelable if preventDefault was not called on the first event
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS firstWasCancelable is true
+PASS becameNonCancelable is true
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
Added: trunk/LayoutTests/fast/events/wheel/wheel-events-become-non-cancelable.html (0 => 270312)
--- trunk/LayoutTests/fast/events/wheel/wheel-events-become-non-cancelable.html (rev 0)
+++ trunk/LayoutTests/fast/events/wheel/wheel-events-become-non-cancelable.html 2020-12-01 18:01:31 UTC (rev 270312)
@@ -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=""
+ <script src=""
+ <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, -1, "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', () => {
+ description('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=""
+</body>
+</html>
Added: trunk/LayoutTests/platform/mac-wk2/fast/events/wheel/wheel-events-become-non-cancelable-expected.txt (0 => 270312)
--- trunk/LayoutTests/platform/mac-wk2/fast/events/wheel/wheel-events-become-non-cancelable-expected.txt (rev 0)
+++ trunk/LayoutTests/platform/mac-wk2/fast/events/wheel/wheel-events-become-non-cancelable-expected.txt 2020-12-01 18:01:31 UTC (rev 270312)
@@ -0,0 +1,11 @@
+Tests that events in the gesture are non-cancelable if preventDefault was not called on the first event
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS firstWasCancelable is true
+FAIL becameNonCancelable should be true (of type boolean). Was undefined (of type undefined).
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
Modified: trunk/LayoutTests/platform/win/TestExpectations (270311 => 270312)
--- trunk/LayoutTests/platform/win/TestExpectations 2020-12-01 18:00:27 UTC (rev 270311)
+++ trunk/LayoutTests/platform/win/TestExpectations 2020-12-01 18:01:31 UTC (rev 270312)
@@ -282,6 +282,8 @@
fast/events/wheel/wheel-event-listeners-on-window-left-active.html [ Skip ]
fast/events/wheel/wheel-event-listeners-on-window-made-passive.html [ Skip ]
fast/events/wheel/wheel-event-in-passive-region-non-cancelable.html [ Skip ]
+fast/events/wheel/wheel-events-become-non-cancelable.html [ Skip ]
+fast/events/wheel/first-wheel-event-cancelable.html [ Skip ]
scrollbars/scroll-rtl-or-bt-layer.html [ Timeout ]
webkit.org/b/208559 fast/scrolling/arrow-key-scroll-in-rtl-document.html [ Skip ]
Modified: trunk/Source/WebCore/ChangeLog (270311 => 270312)
--- trunk/Source/WebCore/ChangeLog 2020-12-01 18:00:27 UTC (rev 270311)
+++ trunk/Source/WebCore/ChangeLog 2020-12-01 18:01:31 UTC (rev 270312)
@@ -1,3 +1,32 @@
+2020-11-30 Simon Fraser <[email protected]>
+
+ [WK1] Only the first wheel event in a gesture should be cancelable
+ https://bugs.webkit.org/show_bug.cgi?id=219384
+
+ Reviewed by Chris Dumez.
+
+ Implement the logic described at <https://w3c.github.io/uievents/#cancelability-of-wheel-events>,
+ where only the first wheel event in a sequence is cancelable, and, if not canceled, then the
+ rest of the events in the sequence become non-cancelable.
+
+ This is done for the non-async scrolling code path (i.e. WebKitLegacy) by storing
+ a Optional<WheelScrollGestureState> on EventHandler, which is cleared when we receive
+ the "begin" event, set when we finish processing that event, then consulted for subsequent
+ move events.
+
+ Tests: fast/events/wheel/first-wheel-event-cancelable.html
+ fast/events/wheel/wheel-events-become-non-cancelable.html
+
+ * page/EventHandler.h:
+ * page/ios/EventHandlerIOS.mm:
+ (WebCore::EventHandler::wheelEvent):
+ * page/mac/EventHandlerMac.mm:
+ (WebCore::EventHandler::wheelEvent):
+ (WebCore::EventHandler::wheelEventWasProcessedByMainThread):
+ * platform/PlatformWheelEvent.cpp:
+ (WebCore::operator<<):
+ * platform/PlatformWheelEvent.h:
+
2020-12-01 Youenn Fablet <[email protected]>
REGRESSION(STP116): wpt.live/webrtc/RTCRtpTransceiver.https.html crashes on load
Modified: trunk/Source/WebCore/page/EventHandler.h (270311 => 270312)
--- trunk/Source/WebCore/page/EventHandler.h 2020-12-01 18:00:27 UTC (rev 270311)
+++ trunk/Source/WebCore/page/EventHandler.h 2020-12-01 18:01:31 UTC (rev 270312)
@@ -97,6 +97,7 @@
struct DragState;
enum class WheelEventProcessingSteps : uint8_t;
+enum class WheelScrollGestureState : uint8_t;
#if ENABLE(DRAG_SUPPORT)
extern const int LinkDragHysteresis;
@@ -606,6 +607,7 @@
#if PLATFORM(COCOA)
NSView *m_mouseDownView { nullptr };
bool m_sendingEventToSubview { false };
+ Optional<WheelScrollGestureState> m_wheelScrollGestureState;
#endif
#if PLATFORM(MAC)
Modified: trunk/Source/WebCore/page/ios/EventHandlerIOS.mm (270311 => 270312)
--- trunk/Source/WebCore/page/ios/EventHandlerIOS.mm 2020-12-01 18:00:27 UTC (rev 270311)
+++ trunk/Source/WebCore/page/ios/EventHandlerIOS.mm 2020-12-01 18:01:31 UTC (rev 270312)
@@ -107,7 +107,17 @@
CurrentEventScope scope(event);
- bool eventWasHandled = handleWheelEvent(PlatformEventFactory::createPlatformWheelEvent(event), { WheelEventProcessingSteps::MainThreadForScrolling, WheelEventProcessingSteps::MainThreadForBlockingDOMEventDispatch });
+ auto wheelEvent = PlatformEventFactory::createPlatformWheelEvent(event);
+ OptionSet<WheelEventProcessingSteps> processingSteps = { WheelEventProcessingSteps::MainThreadForScrolling, WheelEventProcessingSteps::MainThreadForBlockingDOMEventDispatch };
+
+ if (wheelEvent.isGestureStart())
+ m_wheelScrollGestureState = WTF::nullopt;
+ else if (wheelEvent.phase() == PlatformWheelEventPhase::Changed || wheelEvent.momentumPhase() == PlatformWheelEventPhase::Changed) {
+ if (m_wheelScrollGestureState && *m_wheelScrollGestureState == WheelScrollGestureState::NonBlocking)
+ processingSteps = { WheelEventProcessingSteps::MainThreadForScrolling, WheelEventProcessingSteps::MainThreadForNonBlockingDOMEventDispatch };
+ }
+
+ bool eventWasHandled = handleWheelEvent(wheelEvent, processingSteps);
event.wasHandled = eventWasHandled;
return eventWasHandled;
}
Modified: trunk/Source/WebCore/page/mac/EventHandlerMac.mm (270311 => 270312)
--- trunk/Source/WebCore/page/mac/EventHandlerMac.mm 2020-12-01 18:00:27 UTC (rev 270311)
+++ trunk/Source/WebCore/page/mac/EventHandlerMac.mm 2020-12-01 18:01:31 UTC (rev 270312)
@@ -149,6 +149,13 @@
CurrentEventScope scope(event, nil);
auto wheelEvent = PlatformEventFactory::createPlatformWheelEvent(event, page->chrome().platformPageClient());
OptionSet<WheelEventProcessingSteps> processingSteps = { WheelEventProcessingSteps::MainThreadForScrolling, WheelEventProcessingSteps::MainThreadForBlockingDOMEventDispatch };
+
+ if (wheelEvent.isGestureStart())
+ m_wheelScrollGestureState = WTF::nullopt;
+ else if (wheelEvent.phase() == PlatformWheelEventPhase::Changed || wheelEvent.momentumPhase() == PlatformWheelEventPhase::Changed) {
+ if (m_frame.settings().wheelEventGesturesBecomeNonBlocking() && m_wheelScrollGestureState && *m_wheelScrollGestureState == WheelScrollGestureState::NonBlocking)
+ processingSteps = { WheelEventProcessingSteps::MainThreadForScrolling, WheelEventProcessingSteps::MainThreadForNonBlockingDOMEventDispatch };
+ }
return handleWheelEvent(wheelEvent, processingSteps);
}
@@ -969,6 +976,9 @@
if (scrollingCoordinator->coordinatesScrollingForFrameView(*view))
scrollingCoordinator->wheelEventWasProcessedByMainThread(wheelEvent, eventHandling);
}
+
+ if (wheelEvent.isGestureStart() && eventHandling.contains(EventHandling::DispatchedToDOM))
+ m_wheelScrollGestureState = eventHandling.contains(EventHandling::DefaultPrevented) ? WheelScrollGestureState::Blocking : WheelScrollGestureState::NonBlocking;
#else
UNUSED_PARAM(wheelEvent);
UNUSED_PARAM(eventHandling);
Modified: trunk/Source/WebCore/platform/PlatformWheelEvent.cpp (270311 => 270312)
--- trunk/Source/WebCore/platform/PlatformWheelEvent.cpp 2020-12-01 18:00:27 UTC (rev 270311)
+++ trunk/Source/WebCore/platform/PlatformWheelEvent.cpp 2020-12-01 18:01:31 UTC (rev 270312)
@@ -80,4 +80,13 @@
return ts;
}
+TextStream& operator<<(TextStream& ts, WheelScrollGestureState state)
+{
+ switch (state) {
+ case WheelScrollGestureState::Blocking: ts << "blocking"; break;
+ case WheelScrollGestureState::NonBlocking: ts << "non-blocking"; break;
+ }
+ return ts;
+}
+
} // namespace WebCore
Modified: trunk/Source/WebCore/platform/PlatformWheelEvent.h (270311 => 270312)
--- trunk/Source/WebCore/platform/PlatformWheelEvent.h 2020-12-01 18:00:27 UTC (rev 270311)
+++ trunk/Source/WebCore/platform/PlatformWheelEvent.h 2020-12-01 18:01:31 UTC (rev 270312)
@@ -43,6 +43,11 @@
MainThreadForBlockingDOMEventDispatch = 1 << 3,
};
+enum class WheelScrollGestureState : uint8_t {
+ Blocking,
+ NonBlocking
+};
+
// The ScrollByPixelWheelEvent is a fine-grained event that specifies the precise number of pixels to scroll.
// It is sent directly by touch pads on macOS, or synthesized when platforms generate line-by-line scrolling events.
//
@@ -267,5 +272,6 @@
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);
+WEBCORE_EXPORT WTF::TextStream& operator<<(WTF::TextStream&, WheelScrollGestureState);
} // namespace WebCore