Title: [270712] trunk
Revision
270712
Author
timothy_hor...@apple.com
Date
2020-12-11 14:59:22 -0800 (Fri, 11 Dec 2020)

Log Message

Trackpad and Mouse scroll events on iPad only fire "pointermove" -- not "wheel"
https://bugs.webkit.org/show_bug.cgi?id=210071
<rdar://problem/54616853>

Reviewed by Simon Fraser.

Source/WebCore:

* page/EventHandler.cpp:
(WebCore::EventHandler::handleWheelEventInternal):
Fix a minor logic error when WHEEL_EVENT_LATCHING is off; allowScrolling
would always be true, even if the set of processing steps does not include any scrolling steps.

* rendering/EventRegion.h:
(WebCore::EventRegion::encode const):
(WebCore::EventRegion::decode):
Encode/decode the wheel and passive wheel event regions.

Source/WebKit:

* Platform/spi/ios/UIKitSPI.h:
Add some SPI.

* Shared/ios/WebIOSEventFactory.h:
* Shared/ios/WebIOSEventFactory.mm:
(toWebPhase):
(WebIOSEventFactory::createWebWheelEvent):
Add a UIScrollEvent->WebWheelEvent conversion helper.

* UIProcess/API/Cocoa/WKWebViewInternal.h:
* UIProcess/API/ios/WKWebViewIOS.h:
* UIProcess/API/ios/WKWebViewIOS.mm:
(-[WKWebView _setupScrollAndContentViews]):
Enable async UIScrollEvent handling for WKScrollView.

(-[WKWebView _scrollView:asynchronouslyHandleScrollEvent:completion:]):
Adopt new UIKit SPI to asynchronously defer UIScrollEvents.
We pass them to the Web Content process, where they are processed
*only* for event handling, not for scrolling.

If the event is not cancelable, we will synchronously reply that it was
not handled; if it is cancelable, or we don't yet know if it will be,
we'll wait to hear back from the Web Content process before replying.

UIKit will wait until our reply to apply the UIScrollEvent to the UIScrollView.

* UIProcess/PageClient.h:
* UIProcess/RemoteLayerTree/ios/RemoteLayerTreeViews.h:
* UIProcess/RemoteLayerTree/ios/RemoteLayerTreeViews.mm:
(WebKit::eventListenerTypesAtPoint):
Expose a mechanism for retrieving the event listener types at a given point,
similar to the existing mechanism for touch event listeners.

(-[WKChildScrollView initWithFrame:]):
Enable async UIScrollEvent handling for WKChildScrollView.

* UIProcess/RemoteLayerTree/ios/ScrollingTreeScrollingNodeDelegateIOS.h:
* UIProcess/RemoteLayerTree/ios/ScrollingTreeScrollingNodeDelegateIOS.mm:
(-[WKScrollingNodeScrollViewDelegate _scrollView:asynchronouslyHandleScrollEvent:completion:]):
(WebKit::ScrollingTreeScrollingNodeDelegateIOS::handleAsynchronousCancelableScrollEvent):
Plumb async scroll events for sub-scrollable regions through PageClient
to WKWebView; we don't actually care which UIScrollView they're handed to,
since we re-hit-test ourselves.

* UIProcess/WebPageProxy.cpp:
(WebKit::WebPageProxy::dispatchWheelEventWithoutScrolling):
* UIProcess/WebPageProxy.h:
* UIProcess/ios/PageClientImplIOS.h:
* UIProcess/ios/PageClientImplIOS.mm:
(WebKit::PageClientImpl::handleAsynchronousCancelableScrollEvent):
* WebProcess/WebPage/WebPage.cpp:
(WebKit::WebPage::wheelEvent):
(WebKit::WebPage::dispatchWheelEventWithoutScrolling):
* WebProcess/WebPage/WebPage.h:
* WebProcess/WebPage/WebPage.messages.in:

Source/WTF:

* wtf/PlatformEnableCocoa.h:
Enable wheel event regions on iOS + macCatalyst.

Tools:

* TestWebKitAPI/Tests/ios/WKScrollViewTests.mm:
(-[WKUIScrollEvent initWithPhase:location:delta:]):
(-[WKUIScrollEvent phase]):
(-[WKUIScrollEvent locationInView:]):
(-[WKUIScrollEvent _adjustedAcceleratedDeltaInView:]):
(TEST):
* TestWebKitAPI/ios/UIKitSPI.h:
Add a very simple test that directly calls the new UIScrollViewDelegate SPI
and verifies that only the first event is cancelable (unless the first event
is canceled, in which case all subsequent events are cancelable).

Modified Paths

Diff

Modified: trunk/Source/WTF/ChangeLog (270711 => 270712)


--- trunk/Source/WTF/ChangeLog	2020-12-11 22:57:14 UTC (rev 270711)
+++ trunk/Source/WTF/ChangeLog	2020-12-11 22:59:22 UTC (rev 270712)
@@ -1,3 +1,14 @@
+2020-12-11  Tim Horton  <timothy_hor...@apple.com>
+
+        Trackpad and Mouse scroll events on iPad only fire "pointermove" -- not "wheel"
+        https://bugs.webkit.org/show_bug.cgi?id=210071
+        <rdar://problem/54616853>
+
+        Reviewed by Simon Fraser.
+
+        * wtf/PlatformEnableCocoa.h:
+        Enable wheel event regions on iOS + macCatalyst.
+
 2020-12-11  Brent Fulgham  <bfulg...@apple.com>
 
         Expose API for enabling/disabling Private Click Measurement

Modified: trunk/Source/WTF/wtf/PlatformEnableCocoa.h (270711 => 270712)


--- trunk/Source/WTF/wtf/PlatformEnableCocoa.h	2020-12-11 22:57:14 UTC (rev 270711)
+++ trunk/Source/WTF/wtf/PlatformEnableCocoa.h	2020-12-11 22:59:22 UTC (rev 270712)
@@ -522,7 +522,7 @@
 #define ENABLE_TOUCH_ACTION_REGIONS 1
 #endif
 
-#if !defined(ENABLE_WHEEL_EVENT_REGIONS) && PLATFORM(MAC)
+#if !defined(ENABLE_WHEEL_EVENT_REGIONS) && (PLATFORM(MAC) || PLATFORM(IOS) || PLATFORM(MACCATALYST))
 #define ENABLE_WHEEL_EVENT_REGIONS 1
 #endif
 

Modified: trunk/Source/WebCore/ChangeLog (270711 => 270712)


--- trunk/Source/WebCore/ChangeLog	2020-12-11 22:57:14 UTC (rev 270711)
+++ trunk/Source/WebCore/ChangeLog	2020-12-11 22:59:22 UTC (rev 270712)
@@ -1,3 +1,21 @@
+2020-12-11  Tim Horton  <timothy_hor...@apple.com>
+
+        Trackpad and Mouse scroll events on iPad only fire "pointermove" -- not "wheel"
+        https://bugs.webkit.org/show_bug.cgi?id=210071
+        <rdar://problem/54616853>
+
+        Reviewed by Simon Fraser.
+
+        * page/EventHandler.cpp:
+        (WebCore::EventHandler::handleWheelEventInternal):
+        Fix a minor logic error when WHEEL_EVENT_LATCHING is off; allowScrolling
+        would always be true, even if the set of processing steps does not include any scrolling steps.
+
+        * rendering/EventRegion.h:
+        (WebCore::EventRegion::encode const):
+        (WebCore::EventRegion::decode):
+        Encode/decode the wheel and passive wheel event regions.
+
 2020-12-11  Peng Liu  <peng.l...@apple.com>
 
         [Media in GPU Process][MSE] Implement some required functions with new IPC messages and remove some unneeded functions

Modified: trunk/Source/WebCore/page/EventHandler.cpp (270711 => 270712)


--- trunk/Source/WebCore/page/EventHandler.cpp	2020-12-11 22:57:14 UTC (rev 270711)
+++ trunk/Source/WebCore/page/EventHandler.cpp	2020-12-11 22:59:22 UTC (rev 270712)
@@ -2914,10 +2914,13 @@
         return false;
 
     bool handledEvent = false;
-    bool allowScrolling = true;
+    bool allowScrolling = m_currentWheelEventAllowsScrolling;
+
 #if ENABLE(WHEEL_EVENT_LATCHING)
-    allowScrolling = m_currentWheelEventAllowsScrolling && m_frame.page()->scrollLatchingController().latchingAllowsScrollingInFrame(m_frame, scrollableArea);
+    if (allowScrolling)
+        allowScrolling = m_frame.page()->scrollLatchingController().latchingAllowsScrollingInFrame(m_frame, scrollableArea);
 #endif
+
     if (allowScrolling) {
         // FIXME: processWheelEventForScrolling() is only called for FrameView scrolling, not overflow scrolling, which is confusing.
         handledEvent = processWheelEventForScrolling(event, scrollableArea, handling);

Modified: trunk/Source/WebCore/page/EventHandler.h (270711 => 270712)


--- trunk/Source/WebCore/page/EventHandler.h	2020-12-11 22:57:14 UTC (rev 270711)
+++ trunk/Source/WebCore/page/EventHandler.h	2020-12-11 22:59:22 UTC (rev 270712)
@@ -346,6 +346,10 @@
 
     WEBCORE_EXPORT Optional<Cursor> selectCursor(const HitTestResult&, bool shiftKey);
 
+#if ENABLE(KINETIC_SCROLLING)
+    Optional<WheelScrollGestureState> wheelScrollGestureState() const { return m_wheelScrollGestureState; }
+#endif
+
 #if ENABLE(DRAG_SUPPORT)
     Element* draggingElement() const;
 #endif

Modified: trunk/Source/WebCore/rendering/EventRegion.h (270711 => 270712)


--- trunk/Source/WebCore/rendering/EventRegion.h	2020-12-11 22:57:14 UTC (rev 270711)
+++ trunk/Source/WebCore/rendering/EventRegion.h	2020-12-11 22:59:22 UTC (rev 270712)
@@ -82,7 +82,7 @@
 #endif
 
 #if ENABLE(WHEEL_EVENT_REGIONS)
-    OptionSet<EventListenerRegionType> eventListenerRegionTypesForPoint(const IntPoint&) const;
+    WEBCORE_EXPORT OptionSet<EventListenerRegionType> eventListenerRegionTypesForPoint(const IntPoint&) const;
     const Region& eventListenerRegionForType(EventListenerRegionType) const;
 #endif
 
@@ -125,6 +125,10 @@
 void EventRegion::encode(Encoder& encoder) const
 {
     encoder << m_region;
+#if ENABLE(WHEEL_EVENT_REGIONS)
+    encoder << m_wheelEventListenerRegion;
+    encoder << m_nonPassiveWheelEventListenerRegion;
+#endif
 #if ENABLE(TOUCH_ACTION_REGIONS)
     encoder << m_touchActionRegions;
 #endif
@@ -144,6 +148,22 @@
     EventRegion eventRegion;
     eventRegion.m_region = WTFMove(*region);
 
+#if ENABLE(WHEEL_EVENT_REGIONS)
+    Optional<Region> wheelEventListenerRegion;
+    decoder >> wheelEventListenerRegion;
+    if (!wheelEventListenerRegion)
+        return WTF::nullopt;
+
+    eventRegion.m_wheelEventListenerRegion = WTFMove(*wheelEventListenerRegion);
+
+    Optional<Region> nonPassiveWheelEventListenerRegion;
+    decoder >> nonPassiveWheelEventListenerRegion;
+    if (!nonPassiveWheelEventListenerRegion)
+        return WTF::nullopt;
+
+    eventRegion.m_nonPassiveWheelEventListenerRegion = WTFMove(*nonPassiveWheelEventListenerRegion);
+#endif
+
 #if ENABLE(TOUCH_ACTION_REGIONS)
     Optional<Vector<Region>> touchActionRegions;
     decoder >> touchActionRegions;

Modified: trunk/Source/WebKit/ChangeLog (270711 => 270712)


--- trunk/Source/WebKit/ChangeLog	2020-12-11 22:57:14 UTC (rev 270711)
+++ trunk/Source/WebKit/ChangeLog	2020-12-11 22:59:22 UTC (rev 270712)
@@ -1,3 +1,67 @@
+2020-12-11  Tim Horton  <timothy_hor...@apple.com>
+
+        Trackpad and Mouse scroll events on iPad only fire "pointermove" -- not "wheel"
+        https://bugs.webkit.org/show_bug.cgi?id=210071
+        <rdar://problem/54616853>
+
+        Reviewed by Simon Fraser.
+
+        * Platform/spi/ios/UIKitSPI.h:
+        Add some SPI.
+
+        * Shared/ios/WebIOSEventFactory.h:
+        * Shared/ios/WebIOSEventFactory.mm:
+        (toWebPhase):
+        (WebIOSEventFactory::createWebWheelEvent):
+        Add a UIScrollEvent->WebWheelEvent conversion helper.
+
+        * UIProcess/API/Cocoa/WKWebViewInternal.h:
+        * UIProcess/API/ios/WKWebViewIOS.h:
+        * UIProcess/API/ios/WKWebViewIOS.mm:
+        (-[WKWebView _setupScrollAndContentViews]):
+        Enable async UIScrollEvent handling for WKScrollView.
+
+        (-[WKWebView _scrollView:asynchronouslyHandleScrollEvent:completion:]):
+        Adopt new UIKit SPI to asynchronously defer UIScrollEvents.
+        We pass them to the Web Content process, where they are processed 
+        *only* for event handling, not for scrolling.
+
+        If the event is not cancelable, we will synchronously reply that it was
+        not handled; if it is cancelable, or we don't yet know if it will be,
+        we'll wait to hear back from the Web Content process before replying.
+
+        UIKit will wait until our reply to apply the UIScrollEvent to the UIScrollView.
+
+        * UIProcess/PageClient.h:
+        * UIProcess/RemoteLayerTree/ios/RemoteLayerTreeViews.h:
+        * UIProcess/RemoteLayerTree/ios/RemoteLayerTreeViews.mm:
+        (WebKit::eventListenerTypesAtPoint):
+        Expose a mechanism for retrieving the event listener types at a given point,
+        similar to the existing mechanism for touch event listeners.
+
+        (-[WKChildScrollView initWithFrame:]):
+        Enable async UIScrollEvent handling for WKChildScrollView.
+
+        * UIProcess/RemoteLayerTree/ios/ScrollingTreeScrollingNodeDelegateIOS.h:
+        * UIProcess/RemoteLayerTree/ios/ScrollingTreeScrollingNodeDelegateIOS.mm:
+        (-[WKScrollingNodeScrollViewDelegate _scrollView:asynchronouslyHandleScrollEvent:completion:]):
+        (WebKit::ScrollingTreeScrollingNodeDelegateIOS::handleAsynchronousCancelableScrollEvent):
+        Plumb async scroll events for sub-scrollable regions through PageClient
+        to WKWebView; we don't actually care which UIScrollView they're handed to,
+        since we re-hit-test ourselves.
+
+        * UIProcess/WebPageProxy.cpp:
+        (WebKit::WebPageProxy::dispatchWheelEventWithoutScrolling):
+        * UIProcess/WebPageProxy.h:
+        * UIProcess/ios/PageClientImplIOS.h:
+        * UIProcess/ios/PageClientImplIOS.mm:
+        (WebKit::PageClientImpl::handleAsynchronousCancelableScrollEvent):
+        * WebProcess/WebPage/WebPage.cpp:
+        (WebKit::WebPage::wheelEvent):
+        (WebKit::WebPage::dispatchWheelEventWithoutScrolling):
+        * WebProcess/WebPage/WebPage.h:
+        * WebProcess/WebPage/WebPage.messages.in:
+
 2020-12-11  Brent Fulgham  <bfulg...@apple.com>
 
         Expose API for enabling/disabling Private Click Measurement

Modified: trunk/Source/WebKit/Platform/spi/ios/UIKitSPI.h (270711 => 270712)


--- trunk/Source/WebKit/Platform/spi/ios/UIKitSPI.h	2020-12-11 22:57:14 UTC (rev 270711)
+++ trunk/Source/WebKit/Platform/spi/ios/UIKitSPI.h	2020-12-11 22:59:22 UTC (rev 270712)
@@ -62,6 +62,8 @@
 #import <UIKit/UIPresentationController_Private.h>
 #import <UIKit/UIResponder_Private.h>
 #import <UIKit/UIScene_Private.h>
+#import <UIKit/UIScrollEvent_Private.h>
+#import <UIKit/UIScrollView_ForWebKitOnly.h>
 #import <UIKit/UIScrollView_Private.h>
 #import <UIKit/UIStringDrawing_Private.h>
 #import <UIKit/UITableViewCell_Private.h>
@@ -416,8 +418,27 @@
 @property (nonatomic, getter=_indicatorInsetAdjustmentBehavior, setter=_setIndicatorInsetAdjustmentBehavior:) UIScrollViewIndicatorInsetAdjustmentBehavior indicatorInsetAdjustmentBehavior;
 @property (nonatomic, readonly) UIEdgeInsets _systemContentInset;
 @property (nonatomic, readonly) UIEdgeInsets _effectiveContentInset;
+@property (nonatomic, getter=_allowsAsyncScrollEvent, setter=_setAllowsAsyncScrollEvent:) BOOL _allowsAsyncScrollEvent;
 @end
 
+typedef NS_ENUM(NSUInteger, UIScrollPhase) {
+    UIScrollPhaseNone,
+    UIScrollPhaseMayBegin,
+    UIScrollPhaseBegan,
+    UIScrollPhaseChanged,
+    UIScrollPhaseEnded,
+    UIScrollPhaseCancelled
+};
+
+@interface UIScrollEvent : UIEvent
+
+@property (assign, readonly) UIScrollPhase phase;
+- (CGPoint)locationInView:(UIView *)view;
+- (CGVector)_adjustedAcceleratedDeltaInView:(UIView *)view;
+
+@end
+
+
 @interface NSString (UIKitDetails)
 - (CGSize)_legacy_sizeWithFont:(UIFont *)font forWidth:(CGFloat)width lineBreakMode:(NSLineBreakMode)lineBreakMode;
 - (CGSize)_legacy_sizeWithFont:(UIFont *)font minFontSize:(CGFloat)minFontSize actualFontSize:(CGFloat *)actualFontSize forWidth:(CGFloat)width lineBreakMode:(NSLineBreakMode)lineBreakMode;

Modified: trunk/Source/WebKit/Shared/ios/WebIOSEventFactory.h (270711 => 270712)


--- trunk/Source/WebKit/Shared/ios/WebIOSEventFactory.h	2020-12-11 22:57:14 UTC (rev 270711)
+++ trunk/Source/WebKit/Shared/ios/WebIOSEventFactory.h	2020-12-11 22:59:22 UTC (rev 270712)
@@ -29,13 +29,17 @@
 
 #import "WebKeyboardEvent.h"
 #import "WebMouseEvent.h"
+#import "WebWheelEvent.h"
 #import <UIKit/UIKit.h>
 #import <WebCore/WebEvent.h>
 
+OBJC_CLASS UIScrollEvent;
+
 class WebIOSEventFactory {
 public:
     static WebKit::WebKeyboardEvent createWebKeyboardEvent(::WebEvent *, bool handledByInputMethod);
     static WebKit::WebMouseEvent createWebMouseEvent(::WebEvent *);
+    static WebKit::WebWheelEvent createWebWheelEvent(UIScrollEvent *, UIView *contentView);
 
     static UIKeyModifierFlags toUIKeyModifierFlags(OptionSet<WebKit::WebEvent::Modifier>);
 };

Modified: trunk/Source/WebKit/Shared/ios/WebIOSEventFactory.mm (270711 => 270712)


--- trunk/Source/WebKit/Shared/ios/WebIOSEventFactory.mm	2020-12-11 22:57:14 UTC (rev 270711)
+++ trunk/Source/WebKit/Shared/ios/WebIOSEventFactory.mm	2020-12-11 22:59:22 UTC (rev 270712)
@@ -28,6 +28,7 @@
 
 #if PLATFORM(IOS_FAMILY)
 
+#import "UIKitSPI.h"
 #import <WebCore/KeyEventCodesIOS.h>
 #import <WebCore/PlatformEventFactoryIOS.h>
 
@@ -130,4 +131,49 @@
     return WebKit::WebMouseEvent(type, button, buttons, position, position, deltaX, deltaY, deltaZ, clickCount, OptionSet<WebKit::WebEvent::Modifier> { }, WallTime::fromRawSeconds(timestamp));
 }
 
+static WebKit::WebWheelEvent::Phase toWebPhase(UIScrollPhase phase)
+{
+    switch (phase) {
+    case UIScrollPhaseNone:
+        return WebKit::WebWheelEvent::PhaseNone;
+    case UIScrollPhaseMayBegin:
+        return WebKit::WebWheelEvent::PhaseMayBegin;
+    case UIScrollPhaseBegan:
+        return WebKit::WebWheelEvent::PhaseBegan;
+    case UIScrollPhaseChanged:
+        return WebKit::WebWheelEvent::PhaseChanged;
+    case UIScrollPhaseEnded:
+        return WebKit::WebWheelEvent::PhaseEnded;
+    case UIScrollPhaseCancelled:
+        return WebKit::WebWheelEvent::PhaseCancelled;
+    default:
+        ASSERT_NOT_REACHED();
+        return WebKit::WebWheelEvent::PhaseNone;
+    }
+}
+
+WebKit::WebWheelEvent WebIOSEventFactory::createWebWheelEvent(UIScrollEvent *event, UIView *contentView)
+{
+    WebCore::IntPoint scrollLocation = WebCore::roundedIntPoint([event locationInView:contentView]);
+    CGVector deltaVector = [event _adjustedAcceleratedDeltaInView:contentView];
+    WebCore::FloatSize delta(deltaVector.dx, deltaVector.dy);
+
+    return {
+        WebKit::WebEvent::Wheel,
+        scrollLocation,
+        scrollLocation,
+        delta,
+        { } /* wheelTicks */,
+        WebKit::WebWheelEvent::Granularity::ScrollByPixelWheelEvent,
+        false,
+        toWebPhase(event.phase),
+        WebKit::WebWheelEvent::PhaseNone,
+        true,
+        1,
+        delta,
+        { },
+        MonotonicTime::fromRawSeconds(event.timestamp).approximateWallTime()
+    };
+}
+
 #endif // PLATFORM(IOS_FAMILY)

Modified: trunk/Source/WebKit/UIProcess/API/Cocoa/WKWebViewInternal.h (270711 => 270712)


--- trunk/Source/WebKit/UIProcess/API/Cocoa/WKWebViewInternal.h	2020-12-11 22:57:14 UTC (rev 270711)
+++ trunk/Source/WebKit/UIProcess/API/Cocoa/WKWebViewInternal.h	2020-12-11 22:59:22 UTC (rev 270712)
@@ -65,6 +65,10 @@
 class Attachment;
 }
 
+namespace WebCore {
+enum class WheelScrollGestureState : uint8_t;
+}
+
 namespace WebKit {
 enum class ContinueUnsafeLoad : bool;
 class IconLoadingDelegate;
@@ -229,6 +233,8 @@
     BOOL _hasScheduledVisibleRectUpdate;
     BOOL _visibleContentRectUpdateScheduledFromScrollViewInStableState;
 
+    Optional<WebCore::WheelScrollGestureState> _currentScrollGestureState;
+
     _WKDragInteractionPolicy _dragInteractionPolicy;
 
     // For release-logging for <rdar://problem/39281269>.

Modified: trunk/Source/WebKit/UIProcess/API/ios/WKWebViewIOS.h (270711 => 270712)


--- trunk/Source/WebKit/UIProcess/API/ios/WKWebViewIOS.h	2020-12-11 22:57:14 UTC (rev 270711)
+++ trunk/Source/WebKit/UIProcess/API/ios/WKWebViewIOS.h	2020-12-11 22:59:22 UTC (rev 270712)
@@ -25,6 +25,8 @@
 
 #import "WKWebViewInternal.h"
 
+@class UIScrollEvent;
+
 #if PLATFORM(IOS_FAMILY)
 
 @interface WKWebView (WKViewInternalIOS)
@@ -125,6 +127,13 @@
 - (WebCore::FloatSize)activeViewLayoutSize:(const CGRect&)bounds;
 - (void)_updateScrollViewInsetAdjustmentBehavior;
 
+- (BOOL)_effectiveAppearanceIsDark;
+- (BOOL)_effectiveUserInterfaceLevelIsElevated;
+
+#if HAVE(UISCROLLVIEW_ASYNCHRONOUS_SCROLL_EVENT_HANDLING)
+- (void)_scrollView:(UIScrollView *)scrollView asynchronouslyHandleScrollEvent:(UIScrollEvent *)scrollEvent completion:(void (^)(BOOL handled))completion;
+#endif
+
 @property (nonatomic, readonly) WKPasswordView *_passwordView;
 @property (nonatomic, readonly) WKWebViewContentProviderRegistry *_contentProviderRegistry;
 @property (nonatomic, readonly) WKSelectionGranularity _selectionGranularity;
@@ -137,9 +146,6 @@
 @property (nonatomic, readonly, getter=_isRetainingActiveFocusedState) BOOL _retainingActiveFocusedState;
 @property (nonatomic, readonly) int32_t _deviceOrientation;
 
-- (BOOL)_effectiveAppearanceIsDark;
-- (BOOL)_effectiveUserInterfaceLevelIsElevated;
-
 @end
 
 #endif // PLATFORM(IOS_FAMILY)

Modified: trunk/Source/WebKit/UIProcess/API/ios/WKWebViewIOS.mm (270711 => 270712)


--- trunk/Source/WebKit/UIProcess/API/ios/WKWebViewIOS.mm	2020-12-11 22:57:14 UTC (rev 270711)
+++ trunk/Source/WebKit/UIProcess/API/ios/WKWebViewIOS.mm	2020-12-11 22:59:22 UTC (rev 270712)
@@ -29,9 +29,11 @@
 #if PLATFORM(IOS_FAMILY)
 
 #import "FrontBoardServicesSPI.h"
+#import "NativeWebWheelEvent.h"
 #import "NavigationState.h"
 #import "RemoteLayerTreeDrawingAreaProxy.h"
 #import "RemoteLayerTreeScrollingPerformanceData.h"
+#import "RemoteLayerTreeViews.h"
 #import "RemoteScrollingCoordinatorProxy.h"
 #import "VideoFullscreenManagerProxy.h"
 #import "ViewGestureController.h"
@@ -47,6 +49,7 @@
 #import "WKWebViewPrivate.h"
 #import "WKWebViewPrivateForTestingIOS.h"
 #import "WebBackForwardList.h"
+#import "WebIOSEventFactory.h"
 #import "WebPageProxy.h"
 #import "_WKActivatedElementInfoInternal.h"
 #import <WebCore/GraphicsContextCG.h>
@@ -141,6 +144,10 @@
     [_scrollView setInternalDelegate:self];
     [_scrollView setBouncesZoom:YES];
 
+#if HAVE(UISCROLLVIEW_ASYNCHRONOUS_SCROLL_EVENT_HANDLING)
+    [_scrollView _setAllowsAsyncScrollEvent:YES];
+#endif
+
     if ([_scrollView respondsToSelector:@selector(_setAvoidsJumpOnInterruptedBounce:)]) {
         [_scrollView setTracksImmediatelyWhileDecelerating:NO];
         [_scrollView _setAvoidsJumpOnInterruptedBounce:YES];
@@ -1592,6 +1599,49 @@
     return adjustedContentOffset;
 }
 
+#if HAVE(UISCROLLVIEW_ASYNCHRONOUS_SCROLL_EVENT_HANDLING)
+- (void)_scrollView:(UIScrollView *)scrollView asynchronouslyHandleScrollEvent:(UIScrollEvent *)scrollEvent completion:(void (^)(BOOL handled))completion
+{
+    if (scrollEvent.phase == UIScrollPhaseMayBegin) {
+        completion(NO);
+        return;
+    }
+
+    WebCore::IntPoint scrollLocation = WebCore::roundedIntPoint([scrollEvent locationInView:_contentView.get()]);
+    auto eventListeners = WebKit::eventListenerTypesAtPoint(_contentView.get(), scrollLocation);
+    bool hasWheelHandlers = eventListeners.contains(WebCore::EventListenerRegionType::Wheel);
+    if (!hasWheelHandlers) {
+        completion(NO);
+        return;
+    }
+
+    bool isFirstEventInGesture = scrollEvent.phase == UIScrollPhaseBegan;
+    if (isFirstEventInGesture)
+        _currentScrollGestureState = WTF::nullopt;
+
+    bool hasActiveWheelHandlers = eventListeners.contains(WebCore::EventListenerRegionType::NonPassiveWheel);
+    bool isCancelable = hasActiveWheelHandlers && (!_currentScrollGestureState || _currentScrollGestureState == WebCore::WheelScrollGestureState::Blocking);
+    auto event = WebIOSEventFactory::createWebWheelEvent(scrollEvent, _contentView.get());
+
+    _page->dispatchWheelEventWithoutScrolling(event, [weakSelf = WeakObjCPtr<WKWebView>(self), strongCompletion = makeBlockPtr(completion), isCancelable, isFirstEventInGesture](bool handled) {
+        auto strongSelf = weakSelf.get();
+        if (!strongSelf) {
+            strongCompletion(NO);
+            return;
+        }
+
+        if (isCancelable) {
+            if (isFirstEventInGesture)
+                strongSelf->_currentScrollGestureState = handled ? WebCore::WheelScrollGestureState::Blocking : WebCore::WheelScrollGestureState::NonBlocking;
+            strongCompletion(handled);
+        }
+    });
+
+    if (!isCancelable)
+        completion(NO);
+}
+#endif // HAVE(UISCROLLVIEW_ASYNCHRONOUS_SCROLL_EVENT_HANDLING)
+
 - (void)scrollViewDidScroll:(UIScrollView *)scrollView
 {
     if (![self usesStandardContentView] && [_customContentView respondsToSelector:@selector(web_scrollViewDidScroll:)])

Modified: trunk/Source/WebKit/UIProcess/PageClient.h (270711 => 270712)


--- trunk/Source/WebKit/UIProcess/PageClient.h	2020-12-11 22:57:14 UTC (rev 270711)
+++ trunk/Source/WebKit/UIProcess/PageClient.h	2020-12-11 22:59:22 UTC (rev 270712)
@@ -65,6 +65,8 @@
 OBJC_CLASS NSSet;
 OBJC_CLASS NSTextAlternatives;
 OBJC_CLASS UIGestureRecognizer;
+OBJC_CLASS UIScrollEvent;
+OBJC_CLASS UIScrollView;
 OBJC_CLASS _WKRemoteObjectRegistry;
 
 #if USE(APPKIT)
@@ -463,7 +465,11 @@
     virtual void disableInspectorNodeSearch() = 0;
 
     virtual void handleAutocorrectionContext(const WebAutocorrectionContext&) = 0;
+
+#if HAVE(UISCROLLVIEW_ASYNCHRONOUS_SCROLL_EVENT_HANDLING)
+    virtual void handleAsynchronousCancelableScrollEvent(UIScrollView *, UIScrollEvent *, void (^completion)(BOOL handled)) = 0;
 #endif
+#endif
 
     // Auxiliary Client Creation
 #if ENABLE(FULLSCREEN_API)

Modified: trunk/Source/WebKit/UIProcess/RemoteLayerTree/ios/RemoteLayerTreeViews.h (270711 => 270712)


--- trunk/Source/WebKit/UIProcess/RemoteLayerTree/ios/RemoteLayerTreeViews.h	2020-12-11 22:57:14 UTC (rev 270711)
+++ trunk/Source/WebKit/UIProcess/RemoteLayerTree/ios/RemoteLayerTreeViews.h	2020-12-11 22:59:22 UTC (rev 270712)
@@ -71,6 +71,8 @@
 OptionSet<WebCore::TouchAction> touchActionsForPoint(UIView *rootView, const WebCore::IntPoint&);
 UIScrollView *findActingScrollParent(UIScrollView *, const RemoteLayerTreeHost&);
 
+OptionSet<WebCore::EventListenerRegionType> eventListenerTypesAtPoint(UIView *rootView, const WebCore::IntPoint&);
+
 #if ENABLE(EDITABLE_REGION)
 bool mayContainEditableElementsInRect(UIView *rootView, const WebCore::FloatRect&);
 #endif

Modified: trunk/Source/WebKit/UIProcess/RemoteLayerTree/ios/RemoteLayerTreeViews.mm (270711 => 270712)


--- trunk/Source/WebKit/UIProcess/RemoteLayerTree/ios/RemoteLayerTreeViews.mm	2020-12-11 22:57:14 UTC (rev 270711)
+++ trunk/Source/WebKit/UIProcess/RemoteLayerTree/ios/RemoteLayerTreeViews.mm	2020-12-11 22:59:22 UTC (rev 270712)
@@ -205,6 +205,36 @@
     return node->eventRegion().touchActionsForPoint(WebCore::IntPoint(hitViewPoint));
 }
 
+#if ENABLE(WHEEL_EVENT_REGIONS)
+OptionSet<WebCore::EventListenerRegionType> eventListenerTypesAtPoint(UIView *rootView, const WebCore::IntPoint& point)
+{
+    Vector<UIView *, 16> viewsAtPoint;
+    collectDescendantViewsAtPoint(viewsAtPoint, rootView, point, nil);
+
+    if (viewsAtPoint.isEmpty())
+        return { };
+
+    UIView *hitView = nil;
+    for (auto *view : WTF::makeReversedRange(viewsAtPoint)) {
+        if ([view isKindOfClass:[WKCompositingView class]]) {
+            hitView = view;
+            break;
+        }
+    }
+
+    if (!hitView)
+        return { };
+
+    CGPoint hitViewPoint = [hitView convertPoint:point fromView:rootView];
+
+    auto* node = RemoteLayerTreeNode::forCALayer(hitView.layer);
+    if (!node)
+        return { };
+
+    return node->eventRegion().eventListenerRegionTypesForPoint(WebCore::IntPoint(hitViewPoint));
+}
+#endif
+
 UIScrollView *findActingScrollParent(UIScrollView *scrollView, const RemoteLayerTreeHost& host)
 {
     HashSet<WebCore::GraphicsLayer::PlatformLayerID> scrollersToSkip;
@@ -404,6 +434,10 @@
     self.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
 #endif
 
+#if HAVE(UISCROLLVIEW_ASYNCHRONOUS_SCROLL_EVENT_HANDLING)
+    [self _setAllowsAsyncScrollEvent:YES];
+#endif
+
     return self;
 }
 

Modified: trunk/Source/WebKit/UIProcess/RemoteLayerTree/ios/ScrollingTreeScrollingNodeDelegateIOS.h (270711 => 270712)


--- trunk/Source/WebKit/UIProcess/RemoteLayerTree/ios/ScrollingTreeScrollingNodeDelegateIOS.h	2020-12-11 22:57:14 UTC (rev 270711)
+++ trunk/Source/WebKit/UIProcess/RemoteLayerTree/ios/ScrollingTreeScrollingNodeDelegateIOS.h	2020-12-11 22:59:22 UTC (rev 270712)
@@ -65,6 +65,10 @@
 
     void repositionScrollingLayers();
 
+#if HAVE(UISCROLLVIEW_ASYNCHRONOUS_SCROLL_EVENT_HANDLING)
+    void handleAsynchronousCancelableScrollEvent(UIScrollView *, UIScrollEvent *, void (^completion)(BOOL handled));
+#endif
+
     OptionSet<WebCore::TouchAction> activeTouchActions() const { return m_activeTouchActions; }
     void computeActiveTouchActionsForGestureRecognizer(UIGestureRecognizer*);
     void clearActiveTouchActions() { m_activeTouchActions = { }; }

Modified: trunk/Source/WebKit/UIProcess/RemoteLayerTree/ios/ScrollingTreeScrollingNodeDelegateIOS.mm (270711 => 270712)


--- trunk/Source/WebKit/UIProcess/RemoteLayerTree/ios/ScrollingTreeScrollingNodeDelegateIOS.mm	2020-12-11 22:57:14 UTC (rev 270711)
+++ trunk/Source/WebKit/UIProcess/RemoteLayerTree/ios/ScrollingTreeScrollingNodeDelegateIOS.mm	2020-12-11 22:59:22 UTC (rev 270712)
@@ -176,6 +176,13 @@
     return adjustedContentOffset;
 }
 
+#if HAVE(UISCROLLVIEW_ASYNCHRONOUS_SCROLL_EVENT_HANDLING)
+- (void)_scrollView:(UIScrollView *)scrollView asynchronouslyHandleScrollEvent:(UIScrollEvent *)scrollEvent completion:(void (^)(BOOL handled))completion
+{
+    _scrollingTreeNodeDelegate->handleAsynchronousCancelableScrollEvent(scrollView, scrollEvent, completion);
+}
+#endif
+
 - (void)scrollViewWillBeginZooming:(UIScrollView *)scrollView withView:(UIView *)view
 {
     [self cancelPointersForGestureRecognizer:scrollView.pinchGestureRecognizer];
@@ -298,6 +305,14 @@
     }
 }
 
+#if HAVE(UISCROLLVIEW_ASYNCHRONOUS_SCROLL_EVENT_HANDLING)
+void ScrollingTreeScrollingNodeDelegateIOS::handleAsynchronousCancelableScrollEvent(UIScrollView *scrollView, UIScrollEvent *scrollEvent, void (^completion)(BOOL handled))
+{
+    auto& scrollingCoordinatorProxy = downcast<WebKit::RemoteScrollingTree>(scrollingTree()).scrollingCoordinatorProxy();
+    scrollingCoordinatorProxy.webPageProxy().pageClient().handleAsynchronousCancelableScrollEvent(scrollView, scrollEvent, completion);
+}
+#endif
+
 void ScrollingTreeScrollingNodeDelegateIOS::repositionScrollingLayers()
 {
     BEGIN_BLOCK_OBJC_EXCEPTIONS

Modified: trunk/Source/WebKit/UIProcess/WebPageProxy.cpp (270711 => 270712)


--- trunk/Source/WebKit/UIProcess/WebPageProxy.cpp	2020-12-11 22:57:14 UTC (rev 270711)
+++ trunk/Source/WebKit/UIProcess/WebPageProxy.cpp	2020-12-11 22:59:22 UTC (rev 270712)
@@ -2638,6 +2638,11 @@
     m_callbackHandlersAfterProcessingPendingMouseEvents.clear();
 }
 
+void WebPageProxy::dispatchWheelEventWithoutScrolling(const WebWheelEvent& event, CompletionHandler<void(bool)>&& completionHandler)
+{
+    sendWithAsyncReply(Messages::WebPage::DispatchWheelEventWithoutScrolling(event), WTFMove(completionHandler));
+}
+
 void WebPageProxy::handleWheelEvent(const NativeWebWheelEvent& event)
 {
 #if ENABLE(ASYNC_SCROLLING) && PLATFORM(COCOA)

Modified: trunk/Source/WebKit/UIProcess/WebPageProxy.h (270711 => 270712)


--- trunk/Source/WebKit/UIProcess/WebPageProxy.h	2020-12-11 22:57:14 UTC (rev 270711)
+++ trunk/Source/WebKit/UIProcess/WebPageProxy.h	2020-12-11 22:59:22 UTC (rev 270712)
@@ -1846,6 +1846,8 @@
     void changeUniversalAccessZoomFocus(const WebCore::IntRect&, const WebCore::IntRect&);
 #endif
 
+    void dispatchWheelEventWithoutScrolling(const WebWheelEvent&, CompletionHandler<void(bool)>&&);
+
 private:
     WebPageProxy(PageClient&, WebProcessProxy&, Ref<API::PageConfiguration>&&);
     void platformInitialize();

Modified: trunk/Source/WebKit/UIProcess/ios/PageClientImplIOS.h (270711 => 270712)


--- trunk/Source/WebKit/UIProcess/ios/PageClientImplIOS.h	2020-12-11 22:57:14 UTC (rev 270711)
+++ trunk/Source/WebKit/UIProcess/ios/PageClientImplIOS.h	2020-12-11 22:59:22 UTC (rev 270712)
@@ -271,6 +271,10 @@
 
     void showDictationAlternativeUI(const WebCore::FloatRect&, WebCore::DictationContext) final;
 
+#if HAVE(UISCROLLVIEW_ASYNCHRONOUS_SCROLL_EVENT_HANDLING)
+    void handleAsynchronousCancelableScrollEvent(UIScrollView *, UIScrollEvent *, void (^completion)(BOOL handled)) final;
+#endif
+
     WeakObjCPtr<WKContentView> m_contentView;
     RetainPtr<WKEditorUndoTarget> m_undoTarget;
 };

Modified: trunk/Source/WebKit/UIProcess/ios/PageClientImplIOS.mm (270711 => 270712)


--- trunk/Source/WebKit/UIProcess/ios/PageClientImplIOS.mm	2020-12-11 22:57:14 UTC (rev 270711)
+++ trunk/Source/WebKit/UIProcess/ios/PageClientImplIOS.mm	2020-12-11 22:59:22 UTC (rev 270712)
@@ -961,6 +961,13 @@
 #endif
 }
 
+#if HAVE(UISCROLLVIEW_ASYNCHRONOUS_SCROLL_EVENT_HANDLING)
+void PageClientImpl::handleAsynchronousCancelableScrollEvent(UIScrollView *scrollView, UIScrollEvent *scrollEvent, void (^completion)(BOOL handled))
+{
+    [m_webView _scrollView:scrollView asynchronouslyHandleScrollEvent:scrollEvent completion:completion];
+}
+#endif
+
 } // namespace WebKit
 
 #endif // PLATFORM(IOS_FAMILY)

Modified: trunk/Source/WebKit/WebProcess/WebPage/WebPage.cpp (270711 => 270712)


--- trunk/Source/WebKit/WebProcess/WebPage/WebPage.cpp	2020-12-11 22:57:14 UTC (rev 270711)
+++ trunk/Source/WebKit/WebProcess/WebPage/WebPage.cpp	2020-12-11 22:59:22 UTC (rev 270712)
@@ -2870,7 +2870,7 @@
     return page->userInputBridge().handleWheelEvent(platformWheelEvent, processingSteps);
 }
 
-void WebPage::wheelEvent(const WebWheelEvent& wheelEvent, OptionSet<WheelEventProcessingSteps> processingSteps)
+bool WebPage::wheelEvent(const WebWheelEvent& wheelEvent, OptionSet<WheelEventProcessingSteps> processingSteps)
 {
     m_userActivity.impulse();
 
@@ -2880,8 +2880,22 @@
 
     if (processingSteps.contains(WheelEventProcessingSteps::MainThreadForScrolling))
         send(Messages::WebPageProxy::DidReceiveEvent(static_cast<uint32_t>(wheelEvent.type()), handled));
+
+    return handled;
 }
 
+void WebPage::dispatchWheelEventWithoutScrolling(const WebWheelEvent& wheelEvent, CompletionHandler<void(bool)>&& completionHandler)
+{
+#if ENABLE(KINETIC_SCROLLING)
+    auto gestureState = m_page->mainFrame().eventHandler().wheelScrollGestureState();
+    bool isCancelable = !gestureState || gestureState == WheelScrollGestureState::Blocking;
+#else
+    bool isCancelable = true;
+#endif
+    bool handled = this->wheelEvent(wheelEvent, { isCancelable ? WheelEventProcessingSteps::MainThreadForBlockingDOMEventDispatch : WheelEventProcessingSteps::MainThreadForNonBlockingDOMEventDispatch });
+    completionHandler(handled);
+}
+
 static bool handleKeyEvent(const WebKeyboardEvent& keyboardEvent, Page* page)
 {
     if (!page->mainFrame().view())

Modified: trunk/Source/WebKit/WebProcess/WebPage/WebPage.h (270711 => 270712)


--- trunk/Source/WebKit/WebProcess/WebPage/WebPage.h	2020-12-11 22:57:14 UTC (rev 270711)
+++ trunk/Source/WebKit/WebProcess/WebPage/WebPage.h	2020-12-11 22:59:22 UTC (rev 270712)
@@ -1013,7 +1013,7 @@
     void contextMenuShowing() { m_isShowingContextMenu = true; }
 #endif
 
-    void wheelEvent(const WebWheelEvent&, OptionSet<WebCore::WheelEventProcessingSteps>);
+    bool wheelEvent(const WebWheelEvent&, OptionSet<WebCore::WheelEventProcessingSteps>);
 
     void wheelEventHandlersChanged(bool);
     void recomputeShortCircuitHorizontalWheelEventsState();
@@ -1371,6 +1371,8 @@
     bool createAppHighlightInSelectedRange(CreateNewGroupForHighlight);
 #endif
 
+    void dispatchWheelEventWithoutScrolling(const WebWheelEvent&, CompletionHandler<void(bool)>&&);
+
 private:
     WebPage(WebCore::PageIdentifier, WebPageCreationParameters&&);
 

Modified: trunk/Source/WebKit/WebProcess/WebPage/WebPage.messages.in (270711 => 270712)


--- trunk/Source/WebKit/WebProcess/WebPage/WebPage.messages.in	2020-12-11 22:57:14 UTC (rev 270711)
+++ trunk/Source/WebKit/WebProcess/WebPage/WebPage.messages.in	2020-12-11 22:59:22 UTC (rev 270712)
@@ -621,4 +621,5 @@
     CreateAppHighlightInSelectedRange(enum:bool WebKit::CreateNewGroupForHighlight createNewGroup)
 #endif
 
+    DispatchWheelEventWithoutScrolling(WebKit::WebWheelEvent event) -> (bool handled) Async
 }

Modified: trunk/Tools/ChangeLog (270711 => 270712)


--- trunk/Tools/ChangeLog	2020-12-11 22:57:14 UTC (rev 270711)
+++ trunk/Tools/ChangeLog	2020-12-11 22:59:22 UTC (rev 270712)
@@ -1,3 +1,22 @@
+2020-12-11  Tim Horton  <timothy_hor...@apple.com>
+
+        Trackpad and Mouse scroll events on iPad only fire "pointermove" -- not "wheel"
+        https://bugs.webkit.org/show_bug.cgi?id=210071
+        <rdar://problem/54616853>
+
+        Reviewed by Simon Fraser.
+
+        * TestWebKitAPI/Tests/ios/WKScrollViewTests.mm:
+        (-[WKUIScrollEvent initWithPhase:location:delta:]):
+        (-[WKUIScrollEvent phase]):
+        (-[WKUIScrollEvent locationInView:]):
+        (-[WKUIScrollEvent _adjustedAcceleratedDeltaInView:]):
+        (TEST):
+        * TestWebKitAPI/ios/UIKitSPI.h:
+        Add a very simple test that directly calls the new UIScrollViewDelegate SPI
+        and verifies that only the first event is cancelable (unless the first event
+        is canceled, in which case all subsequent events are cancelable).
+
 2020-12-11  Jonathan Bedard  <jbed...@apple.com>
 
         [webkitscmpy] Do not use actual URLs in testing

Modified: trunk/Tools/TestWebKitAPI/Tests/ios/WKScrollViewTests.mm (270711 => 270712)


--- trunk/Tools/TestWebKitAPI/Tests/ios/WKScrollViewTests.mm	2020-12-11 22:57:14 UTC (rev 270711)
+++ trunk/Tools/TestWebKitAPI/Tests/ios/WKScrollViewTests.mm	2020-12-11 22:59:22 UTC (rev 270712)
@@ -29,8 +29,53 @@
 
 #import "PlatformUtilities.h"
 #import "TestWKWebView.h"
+#import "UIKitSPI.h"
 #import <WebKit/WKWebViewPrivate.h>
 
+#if HAVE(UISCROLLVIEW_ASYNCHRONOUS_SCROLL_EVENT_HANDLING)
+@interface WKUIScrollEvent : UIScrollEvent
+
+- (instancetype)initWithPhase:(UIScrollPhase)phase location:(CGPoint)location delta:(CGVector)delta;
+
+@end
+
+@implementation WKUIScrollEvent {
+    UIScrollPhase _phase;
+    CGPoint _location;
+    CGVector _delta;
+}
+
+- (instancetype)initWithPhase:(UIScrollPhase)phase location:(CGPoint)location delta:(CGVector)delta
+{
+    self = [super init];
+    if (!self)
+        return nil;
+
+    _phase = phase;
+    _location = location;
+    _delta = delta;
+
+    return self;
+}
+
+- (UIScrollPhase)phase
+{
+    return _phase;
+}
+
+- (CGPoint)locationInView:(UIView *)view
+{
+    return _location;
+}
+
+- (CGVector)_adjustedAcceleratedDeltaInView:(UIView *)view
+{
+    return _delta;
+}
+
+@end
+#endif // HAVE(UISCROLLVIEW_ASYNCHRONOUS_SCROLL_EVENT_HANDLING)
+
 static void traverseLayerTree(CALayer *layer, void(^block)(CALayer *))
 {
     for (CALayer *child in layer.sublayers)
@@ -71,4 +116,86 @@
     EXPECT_TRUE(foundLayerForFixedNavigationBar);
 }
 
+#if HAVE(UISCROLLVIEW_ASYNCHRONOUS_SCROLL_EVENT_HANDLING)
+TEST(WKScrollViewTests, AsynchronousWheelEventHandling)
+{
+    auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]);
+    [webView synchronouslyLoadHTMLString:@""
+        "<style>#handler { width: 200px; height: 200px; }</style>"
+        "<div id='handler'></div>"
+        "<script>window.preventDefaultOnScrollEvents = false;"
+        "document.getElementById('handler').addEventListener('wheel', "
+        "function (e) {"
+        "   window.lastWheelEvent = e;"
+        "   if (window.preventDefaultOnScrollEvents)"
+        "       e.preventDefault();"
+        "})</script>"];
+    [webView waitForNextPresentationUpdate];
+
+    __block bool done;
+    __block bool wasHandled;
+
+    auto synchronouslyHandleScrollEvent = ^(UIScrollPhase phase, CGPoint location, CGVector delta) {
+        done = false;
+        auto event = adoptNS([[WKUIScrollEvent alloc] initWithPhase:phase location:location delta:delta]);
+        [webView _scrollView:[webView scrollView] asynchronouslyHandleScrollEvent:event.get() completion:^(BOOL handled) {
+            wasHandled = handled;
+            done = true;
+        }];
+        TestWebKitAPI::Util::run(&done);
+    };
+
+    // Don't preventDefault() at all.
+    synchronouslyHandleScrollEvent(UIScrollPhaseMayBegin, CGPointMake(100, 100), CGVectorMake(0, 0));
+    EXPECT_FALSE(wasHandled);
+    synchronouslyHandleScrollEvent(UIScrollPhaseBegan, CGPointMake(100, 100), CGVectorMake(0, 10));
+    EXPECT_FALSE(wasHandled);
+    EXPECT_TRUE([[webView objectByEvaluatingJavaScript:@"window.lastWheelEvent.cancelable"] intValue]);
+    EXPECT_EQ(-10, [[webView objectByEvaluatingJavaScript:@"window.lastWheelEvent.deltaY"] intValue]);
+    synchronouslyHandleScrollEvent(UIScrollPhaseChanged, CGPointMake(100, 100), CGVectorMake(0, 10));
+    EXPECT_FALSE(wasHandled);
+    synchronouslyHandleScrollEvent(UIScrollPhaseEnded, CGPointMake(100, 100), CGVectorMake(0, 0));
+    EXPECT_FALSE(wasHandled);
+
+    // preventDefault() on all events.
+    [webView stringByEvaluatingJavaScript:@"window.preventDefaultOnScrollEvents = true;"];
+    synchronouslyHandleScrollEvent(UIScrollPhaseMayBegin, CGPointMake(100, 100), CGVectorMake(0, 0));
+    EXPECT_FALSE(wasHandled);
+    synchronouslyHandleScrollEvent(UIScrollPhaseBegan, CGPointMake(100, 100), CGVectorMake(0, 10));
+    EXPECT_TRUE(wasHandled);
+    synchronouslyHandleScrollEvent(UIScrollPhaseChanged, CGPointMake(100, 100), CGVectorMake(0, 10));
+    EXPECT_TRUE(wasHandled);
+    synchronouslyHandleScrollEvent(UIScrollPhaseEnded, CGPointMake(100, 100), CGVectorMake(0, 0));
+    EXPECT_FALSE(wasHandled);
+
+    // preventDefault() on all but the begin event; it will be ignored.
+    [webView stringByEvaluatingJavaScript:@"window.preventDefaultOnScrollEvents = false;"];
+    synchronouslyHandleScrollEvent(UIScrollPhaseMayBegin, CGPointMake(100, 100), CGVectorMake(0, 0));
+    EXPECT_FALSE(wasHandled);
+    synchronouslyHandleScrollEvent(UIScrollPhaseBegan, CGPointMake(100, 100), CGVectorMake(0, 10));
+    EXPECT_TRUE([[webView objectByEvaluatingJavaScript:@"window.lastWheelEvent.cancelable"] intValue]);
+    EXPECT_FALSE(wasHandled);
+    [webView stringByEvaluatingJavaScript:@"window.preventDefaultOnScrollEvents = true;"];
+    synchronouslyHandleScrollEvent(UIScrollPhaseChanged, CGPointMake(100, 100), CGVectorMake(0, 10));
+    EXPECT_FALSE(wasHandled);
+    EXPECT_FALSE([[webView objectByEvaluatingJavaScript:@"window.lastWheelEvent.cancelable"] intValue]);
+    synchronouslyHandleScrollEvent(UIScrollPhaseEnded, CGPointMake(100, 100), CGVectorMake(0, 0));
+    EXPECT_FALSE(wasHandled);
+
+    // preventDefault() on the begin event, and some subsequent events.
+    [webView stringByEvaluatingJavaScript:@"window.preventDefaultOnScrollEvents = true;"];
+    synchronouslyHandleScrollEvent(UIScrollPhaseMayBegin, CGPointMake(100, 100), CGVectorMake(0, 0));
+    EXPECT_FALSE(wasHandled);
+    synchronouslyHandleScrollEvent(UIScrollPhaseBegan, CGPointMake(100, 100), CGVectorMake(0, 10));
+    EXPECT_TRUE(wasHandled);
+    synchronouslyHandleScrollEvent(UIScrollPhaseChanged, CGPointMake(100, 100), CGVectorMake(0, 10));
+    EXPECT_TRUE(wasHandled);
+    [webView stringByEvaluatingJavaScript:@"window.preventDefaultOnScrollEvents = false;"];
+    synchronouslyHandleScrollEvent(UIScrollPhaseChanged, CGPointMake(100, 100), CGVectorMake(0, 10));
+    EXPECT_FALSE(wasHandled);
+    synchronouslyHandleScrollEvent(UIScrollPhaseEnded, CGPointMake(100, 100), CGVectorMake(0, 0));
+    EXPECT_FALSE(wasHandled);
+}
+#endif // HAVE(UISCROLLVIEW_ASYNCHRONOUS_SCROLL_EVENT_HANDLING)
+
 #endif // PLATFORM(IOS_FAMILY)

Modified: trunk/Tools/TestWebKitAPI/ios/UIKitSPI.h (270711 => 270712)


--- trunk/Tools/TestWebKitAPI/ios/UIKitSPI.h	2020-12-11 22:57:14 UTC (rev 270711)
+++ trunk/Tools/TestWebKitAPI/ios/UIKitSPI.h	2020-12-11 22:59:22 UTC (rev 270712)
@@ -37,6 +37,8 @@
 #import <UIKit/UIKeyboard_Private.h>
 #import <UIKit/UIResponder_Private.h>
 #import <UIKit/UIScreen_Private.h>
+#import <UIKit/UIScrollEvent_Private.h>
+#import <UIKit/UIScrollView_ForWebKitOnly.h>
 #import <UIKit/UIScrollView_Private.h>
 #import <UIKit/UITextAutofillSuggestion.h>
 #import <UIKit/UITextInputMultiDocument.h>
@@ -240,6 +242,13 @@
 @property (nonatomic, getter=_isAutomaticContentOffsetAdjustmentEnabled, setter=_setAutomaticContentOffsetAdjustmentEnabled:) BOOL isAutomaticContentOffsetAdjustmentEnabled;
 @end
 
+@interface UIScrollEvent : UIEvent
+@end
+
+@interface NSObject (UIScrollViewDelegate_ForWebKitOnly)
+- (void)_scrollView:(UIScrollView *)scrollView asynchronouslyHandleScrollEvent:(UIScrollEvent *)scrollEvent completion:(void (^)(BOOL handled))completion;
+@end
+
 #endif // USE(APPLE_INTERNAL_SDK)
 
 #define UIWKDocumentRequestMarkedTextRects (1 << 5)
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to