Changeset 270712 in webkit


Ignore:
Timestamp:
Dec 11, 2020 2:59:22 PM (3 years ago)
Author:
timothy_horton@apple.com
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).

Location:
trunk
Files:
28 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/WTF/ChangeLog

    r270710 r270712  
     12020-12-11  Tim Horton  <timothy_horton@apple.com>
     2
     3        Trackpad and Mouse scroll events on iPad only fire "pointermove" -- not "wheel"
     4        https://bugs.webkit.org/show_bug.cgi?id=210071
     5        <rdar://problem/54616853>
     6
     7        Reviewed by Simon Fraser.
     8
     9        * wtf/PlatformEnableCocoa.h:
     10        Enable wheel event regions on iOS + macCatalyst.
     11
    1122020-12-11  Brent Fulgham  <bfulgham@apple.com>
    213
  • trunk/Source/WTF/wtf/PlatformEnableCocoa.h

    r270565 r270712  
    523523#endif
    524524
    525 #if !defined(ENABLE_WHEEL_EVENT_REGIONS) && PLATFORM(MAC)
     525#if !defined(ENABLE_WHEEL_EVENT_REGIONS) && (PLATFORM(MAC) || PLATFORM(IOS) || PLATFORM(MACCATALYST))
    526526#define ENABLE_WHEEL_EVENT_REGIONS 1
    527527#endif
  • trunk/Source/WebCore/ChangeLog

    r270708 r270712  
     12020-12-11  Tim Horton  <timothy_horton@apple.com>
     2
     3        Trackpad and Mouse scroll events on iPad only fire "pointermove" -- not "wheel"
     4        https://bugs.webkit.org/show_bug.cgi?id=210071
     5        <rdar://problem/54616853>
     6
     7        Reviewed by Simon Fraser.
     8
     9        * page/EventHandler.cpp:
     10        (WebCore::EventHandler::handleWheelEventInternal):
     11        Fix a minor logic error when WHEEL_EVENT_LATCHING is off; allowScrolling
     12        would always be true, even if the set of processing steps does not include any scrolling steps.
     13
     14        * rendering/EventRegion.h:
     15        (WebCore::EventRegion::encode const):
     16        (WebCore::EventRegion::decode):
     17        Encode/decode the wheel and passive wheel event regions.
     18
    1192020-12-11  Peng Liu  <peng.liu6@apple.com>
    220
  • trunk/Source/WebCore/page/EventHandler.cpp

    r270446 r270712  
    29152915
    29162916    bool handledEvent = false;
    2917     bool allowScrolling = true;
     2917    bool allowScrolling = m_currentWheelEventAllowsScrolling;
     2918
    29182919#if ENABLE(WHEEL_EVENT_LATCHING)
    2919     allowScrolling = m_currentWheelEventAllowsScrolling && m_frame.page()->scrollLatchingController().latchingAllowsScrollingInFrame(m_frame, scrollableArea);
    2920 #endif
     2920    if (allowScrolling)
     2921        allowScrolling = m_frame.page()->scrollLatchingController().latchingAllowsScrollingInFrame(m_frame, scrollableArea);
     2922#endif
     2923
    29212924    if (allowScrolling) {
    29222925        // FIXME: processWheelEventForScrolling() is only called for FrameView scrolling, not overflow scrolling, which is confusing.
  • trunk/Source/WebCore/page/EventHandler.h

    r270446 r270712  
    347347    WEBCORE_EXPORT Optional<Cursor> selectCursor(const HitTestResult&, bool shiftKey);
    348348
     349#if ENABLE(KINETIC_SCROLLING)
     350    Optional<WheelScrollGestureState> wheelScrollGestureState() const { return m_wheelScrollGestureState; }
     351#endif
     352
    349353#if ENABLE(DRAG_SUPPORT)
    350354    Element* draggingElement() const;
  • trunk/Source/WebCore/rendering/EventRegion.h

    r263762 r270712  
    8383
    8484#if ENABLE(WHEEL_EVENT_REGIONS)
    85     OptionSet<EventListenerRegionType> eventListenerRegionTypesForPoint(const IntPoint&) const;
     85    WEBCORE_EXPORT OptionSet<EventListenerRegionType> eventListenerRegionTypesForPoint(const IntPoint&) const;
    8686    const Region& eventListenerRegionForType(EventListenerRegionType) const;
    8787#endif
     
    126126{
    127127    encoder << m_region;
     128#if ENABLE(WHEEL_EVENT_REGIONS)
     129    encoder << m_wheelEventListenerRegion;
     130    encoder << m_nonPassiveWheelEventListenerRegion;
     131#endif
    128132#if ENABLE(TOUCH_ACTION_REGIONS)
    129133    encoder << m_touchActionRegions;
     
    144148    EventRegion eventRegion;
    145149    eventRegion.m_region = WTFMove(*region);
     150
     151#if ENABLE(WHEEL_EVENT_REGIONS)
     152    Optional<Region> wheelEventListenerRegion;
     153    decoder >> wheelEventListenerRegion;
     154    if (!wheelEventListenerRegion)
     155        return WTF::nullopt;
     156
     157    eventRegion.m_wheelEventListenerRegion = WTFMove(*wheelEventListenerRegion);
     158
     159    Optional<Region> nonPassiveWheelEventListenerRegion;
     160    decoder >> nonPassiveWheelEventListenerRegion;
     161    if (!nonPassiveWheelEventListenerRegion)
     162        return WTF::nullopt;
     163
     164    eventRegion.m_nonPassiveWheelEventListenerRegion = WTFMove(*nonPassiveWheelEventListenerRegion);
     165#endif
    146166
    147167#if ENABLE(TOUCH_ACTION_REGIONS)
  • trunk/Source/WebKit/ChangeLog

    r270710 r270712  
     12020-12-11  Tim Horton  <timothy_horton@apple.com>
     2
     3        Trackpad and Mouse scroll events on iPad only fire "pointermove" -- not "wheel"
     4        https://bugs.webkit.org/show_bug.cgi?id=210071
     5        <rdar://problem/54616853>
     6
     7        Reviewed by Simon Fraser.
     8
     9        * Platform/spi/ios/UIKitSPI.h:
     10        Add some SPI.
     11
     12        * Shared/ios/WebIOSEventFactory.h:
     13        * Shared/ios/WebIOSEventFactory.mm:
     14        (toWebPhase):
     15        (WebIOSEventFactory::createWebWheelEvent):
     16        Add a UIScrollEvent->WebWheelEvent conversion helper.
     17
     18        * UIProcess/API/Cocoa/WKWebViewInternal.h:
     19        * UIProcess/API/ios/WKWebViewIOS.h:
     20        * UIProcess/API/ios/WKWebViewIOS.mm:
     21        (-[WKWebView _setupScrollAndContentViews]):
     22        Enable async UIScrollEvent handling for WKScrollView.
     23
     24        (-[WKWebView _scrollView:asynchronouslyHandleScrollEvent:completion:]):
     25        Adopt new UIKit SPI to asynchronously defer UIScrollEvents.
     26        We pass them to the Web Content process, where they are processed
     27        *only* for event handling, not for scrolling.
     28
     29        If the event is not cancelable, we will synchronously reply that it was
     30        not handled; if it is cancelable, or we don't yet know if it will be,
     31        we'll wait to hear back from the Web Content process before replying.
     32
     33        UIKit will wait until our reply to apply the UIScrollEvent to the UIScrollView.
     34
     35        * UIProcess/PageClient.h:
     36        * UIProcess/RemoteLayerTree/ios/RemoteLayerTreeViews.h:
     37        * UIProcess/RemoteLayerTree/ios/RemoteLayerTreeViews.mm:
     38        (WebKit::eventListenerTypesAtPoint):
     39        Expose a mechanism for retrieving the event listener types at a given point,
     40        similar to the existing mechanism for touch event listeners.
     41
     42        (-[WKChildScrollView initWithFrame:]):
     43        Enable async UIScrollEvent handling for WKChildScrollView.
     44
     45        * UIProcess/RemoteLayerTree/ios/ScrollingTreeScrollingNodeDelegateIOS.h:
     46        * UIProcess/RemoteLayerTree/ios/ScrollingTreeScrollingNodeDelegateIOS.mm:
     47        (-[WKScrollingNodeScrollViewDelegate _scrollView:asynchronouslyHandleScrollEvent:completion:]):
     48        (WebKit::ScrollingTreeScrollingNodeDelegateIOS::handleAsynchronousCancelableScrollEvent):
     49        Plumb async scroll events for sub-scrollable regions through PageClient
     50        to WKWebView; we don't actually care which UIScrollView they're handed to,
     51        since we re-hit-test ourselves.
     52
     53        * UIProcess/WebPageProxy.cpp:
     54        (WebKit::WebPageProxy::dispatchWheelEventWithoutScrolling):
     55        * UIProcess/WebPageProxy.h:
     56        * UIProcess/ios/PageClientImplIOS.h:
     57        * UIProcess/ios/PageClientImplIOS.mm:
     58        (WebKit::PageClientImpl::handleAsynchronousCancelableScrollEvent):
     59        * WebProcess/WebPage/WebPage.cpp:
     60        (WebKit::WebPage::wheelEvent):
     61        (WebKit::WebPage::dispatchWheelEventWithoutScrolling):
     62        * WebProcess/WebPage/WebPage.h:
     63        * WebProcess/WebPage/WebPage.messages.in:
     64
    1652020-12-11  Brent Fulgham  <bfulgham@apple.com>
    266
  • trunk/Source/WebKit/Platform/spi/ios/UIKitSPI.h

    r270669 r270712  
    6363#import <UIKit/UIResponder_Private.h>
    6464#import <UIKit/UIScene_Private.h>
     65#import <UIKit/UIScrollEvent_Private.h>
     66#import <UIKit/UIScrollView_ForWebKitOnly.h>
    6567#import <UIKit/UIScrollView_Private.h>
    6668#import <UIKit/UIStringDrawing_Private.h>
     
    417419@property (nonatomic, readonly) UIEdgeInsets _systemContentInset;
    418420@property (nonatomic, readonly) UIEdgeInsets _effectiveContentInset;
    419 @end
     421@property (nonatomic, getter=_allowsAsyncScrollEvent, setter=_setAllowsAsyncScrollEvent:) BOOL _allowsAsyncScrollEvent;
     422@end
     423
     424typedef NS_ENUM(NSUInteger, UIScrollPhase) {
     425    UIScrollPhaseNone,
     426    UIScrollPhaseMayBegin,
     427    UIScrollPhaseBegan,
     428    UIScrollPhaseChanged,
     429    UIScrollPhaseEnded,
     430    UIScrollPhaseCancelled
     431};
     432
     433@interface UIScrollEvent : UIEvent
     434
     435@property (assign, readonly) UIScrollPhase phase;
     436- (CGPoint)locationInView:(UIView *)view;
     437- (CGVector)_adjustedAcceleratedDeltaInView:(UIView *)view;
     438
     439@end
     440
    420441
    421442@interface NSString (UIKitDetails)
  • trunk/Source/WebKit/Shared/ios/WebIOSEventFactory.h

    r267916 r270712  
    3030#import "WebKeyboardEvent.h"
    3131#import "WebMouseEvent.h"
     32#import "WebWheelEvent.h"
    3233#import <UIKit/UIKit.h>
    3334#import <WebCore/WebEvent.h>
     35
     36OBJC_CLASS UIScrollEvent;
    3437
    3538class WebIOSEventFactory {
     
    3740    static WebKit::WebKeyboardEvent createWebKeyboardEvent(::WebEvent *, bool handledByInputMethod);
    3841    static WebKit::WebMouseEvent createWebMouseEvent(::WebEvent *);
     42    static WebKit::WebWheelEvent createWebWheelEvent(UIScrollEvent *, UIView *contentView);
    3943
    4044    static UIKeyModifierFlags toUIKeyModifierFlags(OptionSet<WebKit::WebEvent::Modifier>);
  • trunk/Source/WebKit/Shared/ios/WebIOSEventFactory.mm

    r244975 r270712  
    2929#if PLATFORM(IOS_FAMILY)
    3030
     31#import "UIKitSPI.h"
    3132#import <WebCore/KeyEventCodesIOS.h>
    3233#import <WebCore/PlatformEventFactoryIOS.h>
     
    131132}
    132133
     134static WebKit::WebWheelEvent::Phase toWebPhase(UIScrollPhase phase)
     135{
     136    switch (phase) {
     137    case UIScrollPhaseNone:
     138        return WebKit::WebWheelEvent::PhaseNone;
     139    case UIScrollPhaseMayBegin:
     140        return WebKit::WebWheelEvent::PhaseMayBegin;
     141    case UIScrollPhaseBegan:
     142        return WebKit::WebWheelEvent::PhaseBegan;
     143    case UIScrollPhaseChanged:
     144        return WebKit::WebWheelEvent::PhaseChanged;
     145    case UIScrollPhaseEnded:
     146        return WebKit::WebWheelEvent::PhaseEnded;
     147    case UIScrollPhaseCancelled:
     148        return WebKit::WebWheelEvent::PhaseCancelled;
     149    default:
     150        ASSERT_NOT_REACHED();
     151        return WebKit::WebWheelEvent::PhaseNone;
     152    }
     153}
     154
     155WebKit::WebWheelEvent WebIOSEventFactory::createWebWheelEvent(UIScrollEvent *event, UIView *contentView)
     156{
     157    WebCore::IntPoint scrollLocation = WebCore::roundedIntPoint([event locationInView:contentView]);
     158    CGVector deltaVector = [event _adjustedAcceleratedDeltaInView:contentView];
     159    WebCore::FloatSize delta(deltaVector.dx, deltaVector.dy);
     160
     161    return {
     162        WebKit::WebEvent::Wheel,
     163        scrollLocation,
     164        scrollLocation,
     165        delta,
     166        { } /* wheelTicks */,
     167        WebKit::WebWheelEvent::Granularity::ScrollByPixelWheelEvent,
     168        false,
     169        toWebPhase(event.phase),
     170        WebKit::WebWheelEvent::PhaseNone,
     171        true,
     172        1,
     173        delta,
     174        { },
     175        MonotonicTime::fromRawSeconds(event.timestamp).approximateWallTime()
     176    };
     177}
     178
    133179#endif // PLATFORM(IOS_FAMILY)
  • trunk/Source/WebKit/UIProcess/API/Cocoa/WKWebViewInternal.h

    r266890 r270712  
    6666}
    6767
     68namespace WebCore {
     69enum class WheelScrollGestureState : uint8_t;
     70}
     71
    6872namespace WebKit {
    6973enum class ContinueUnsafeLoad : bool;
     
    230234    BOOL _visibleContentRectUpdateScheduledFromScrollViewInStableState;
    231235
     236    Optional<WebCore::WheelScrollGestureState> _currentScrollGestureState;
     237
    232238    _WKDragInteractionPolicy _dragInteractionPolicy;
    233239
  • trunk/Source/WebKit/UIProcess/API/ios/WKWebViewIOS.h

    r266342 r270712  
    2525
    2626#import "WKWebViewInternal.h"
     27
     28@class UIScrollEvent;
    2729
    2830#if PLATFORM(IOS_FAMILY)
     
    126128- (void)_updateScrollViewInsetAdjustmentBehavior;
    127129
     130- (BOOL)_effectiveAppearanceIsDark;
     131- (BOOL)_effectiveUserInterfaceLevelIsElevated;
     132
     133#if HAVE(UISCROLLVIEW_ASYNCHRONOUS_SCROLL_EVENT_HANDLING)
     134- (void)_scrollView:(UIScrollView *)scrollView asynchronouslyHandleScrollEvent:(UIScrollEvent *)scrollEvent completion:(void (^)(BOOL handled))completion;
     135#endif
     136
    128137@property (nonatomic, readonly) WKPasswordView *_passwordView;
    129138@property (nonatomic, readonly) WKWebViewContentProviderRegistry *_contentProviderRegistry;
     
    138147@property (nonatomic, readonly) int32_t _deviceOrientation;
    139148
    140 - (BOOL)_effectiveAppearanceIsDark;
    141 - (BOOL)_effectiveUserInterfaceLevelIsElevated;
    142 
    143149@end
    144150
  • trunk/Source/WebKit/UIProcess/API/ios/WKWebViewIOS.mm

    r270669 r270712  
    3030
    3131#import "FrontBoardServicesSPI.h"
     32#import "NativeWebWheelEvent.h"
    3233#import "NavigationState.h"
    3334#import "RemoteLayerTreeDrawingAreaProxy.h"
    3435#import "RemoteLayerTreeScrollingPerformanceData.h"
     36#import "RemoteLayerTreeViews.h"
    3537#import "RemoteScrollingCoordinatorProxy.h"
    3638#import "VideoFullscreenManagerProxy.h"
     
    4850#import "WKWebViewPrivateForTestingIOS.h"
    4951#import "WebBackForwardList.h"
     52#import "WebIOSEventFactory.h"
    5053#import "WebPageProxy.h"
    5154#import "_WKActivatedElementInfoInternal.h"
     
    141144    [_scrollView setInternalDelegate:self];
    142145    [_scrollView setBouncesZoom:YES];
     146
     147#if HAVE(UISCROLLVIEW_ASYNCHRONOUS_SCROLL_EVENT_HANDLING)
     148    [_scrollView _setAllowsAsyncScrollEvent:YES];
     149#endif
    143150
    144151    if ([_scrollView respondsToSelector:@selector(_setAvoidsJumpOnInterruptedBounce:)]) {
     
    15931600}
    15941601
     1602#if HAVE(UISCROLLVIEW_ASYNCHRONOUS_SCROLL_EVENT_HANDLING)
     1603- (void)_scrollView:(UIScrollView *)scrollView asynchronouslyHandleScrollEvent:(UIScrollEvent *)scrollEvent completion:(void (^)(BOOL handled))completion
     1604{
     1605    if (scrollEvent.phase == UIScrollPhaseMayBegin) {
     1606        completion(NO);
     1607        return;
     1608    }
     1609
     1610    WebCore::IntPoint scrollLocation = WebCore::roundedIntPoint([scrollEvent locationInView:_contentView.get()]);
     1611    auto eventListeners = WebKit::eventListenerTypesAtPoint(_contentView.get(), scrollLocation);
     1612    bool hasWheelHandlers = eventListeners.contains(WebCore::EventListenerRegionType::Wheel);
     1613    if (!hasWheelHandlers) {
     1614        completion(NO);
     1615        return;
     1616    }
     1617
     1618    bool isFirstEventInGesture = scrollEvent.phase == UIScrollPhaseBegan;
     1619    if (isFirstEventInGesture)
     1620        _currentScrollGestureState = WTF::nullopt;
     1621
     1622    bool hasActiveWheelHandlers = eventListeners.contains(WebCore::EventListenerRegionType::NonPassiveWheel);
     1623    bool isCancelable = hasActiveWheelHandlers && (!_currentScrollGestureState || _currentScrollGestureState == WebCore::WheelScrollGestureState::Blocking);
     1624    auto event = WebIOSEventFactory::createWebWheelEvent(scrollEvent, _contentView.get());
     1625
     1626    _page->dispatchWheelEventWithoutScrolling(event, [weakSelf = WeakObjCPtr<WKWebView>(self), strongCompletion = makeBlockPtr(completion), isCancelable, isFirstEventInGesture](bool handled) {
     1627        auto strongSelf = weakSelf.get();
     1628        if (!strongSelf) {
     1629            strongCompletion(NO);
     1630            return;
     1631        }
     1632
     1633        if (isCancelable) {
     1634            if (isFirstEventInGesture)
     1635                strongSelf->_currentScrollGestureState = handled ? WebCore::WheelScrollGestureState::Blocking : WebCore::WheelScrollGestureState::NonBlocking;
     1636            strongCompletion(handled);
     1637        }
     1638    });
     1639
     1640    if (!isCancelable)
     1641        completion(NO);
     1642}
     1643#endif // HAVE(UISCROLLVIEW_ASYNCHRONOUS_SCROLL_EVENT_HANDLING)
     1644
    15951645- (void)scrollViewDidScroll:(UIScrollView *)scrollView
    15961646{
  • trunk/Source/WebKit/UIProcess/PageClient.h

    r270573 r270712  
    6666OBJC_CLASS NSTextAlternatives;
    6767OBJC_CLASS UIGestureRecognizer;
     68OBJC_CLASS UIScrollEvent;
     69OBJC_CLASS UIScrollView;
    6870OBJC_CLASS _WKRemoteObjectRegistry;
    6971
     
    464466
    465467    virtual void handleAutocorrectionContext(const WebAutocorrectionContext&) = 0;
     468
     469#if HAVE(UISCROLLVIEW_ASYNCHRONOUS_SCROLL_EVENT_HANDLING)
     470    virtual void handleAsynchronousCancelableScrollEvent(UIScrollView *, UIScrollEvent *, void (^completion)(BOOL handled)) = 0;
     471#endif
    466472#endif
    467473
  • trunk/Source/WebKit/UIProcess/RemoteLayerTree/ios/RemoteLayerTreeViews.h

    r266342 r270712  
    7272UIScrollView *findActingScrollParent(UIScrollView *, const RemoteLayerTreeHost&);
    7373
     74OptionSet<WebCore::EventListenerRegionType> eventListenerTypesAtPoint(UIView *rootView, const WebCore::IntPoint&);
     75
    7476#if ENABLE(EDITABLE_REGION)
    7577bool mayContainEditableElementsInRect(UIView *rootView, const WebCore::FloatRect&);
  • trunk/Source/WebKit/UIProcess/RemoteLayerTree/ios/RemoteLayerTreeViews.mm

    r266342 r270712  
    206206}
    207207
     208#if ENABLE(WHEEL_EVENT_REGIONS)
     209OptionSet<WebCore::EventListenerRegionType> eventListenerTypesAtPoint(UIView *rootView, const WebCore::IntPoint& point)
     210{
     211    Vector<UIView *, 16> viewsAtPoint;
     212    collectDescendantViewsAtPoint(viewsAtPoint, rootView, point, nil);
     213
     214    if (viewsAtPoint.isEmpty())
     215        return { };
     216
     217    UIView *hitView = nil;
     218    for (auto *view : WTF::makeReversedRange(viewsAtPoint)) {
     219        if ([view isKindOfClass:[WKCompositingView class]]) {
     220            hitView = view;
     221            break;
     222        }
     223    }
     224
     225    if (!hitView)
     226        return { };
     227
     228    CGPoint hitViewPoint = [hitView convertPoint:point fromView:rootView];
     229
     230    auto* node = RemoteLayerTreeNode::forCALayer(hitView.layer);
     231    if (!node)
     232        return { };
     233
     234    return node->eventRegion().eventListenerRegionTypesForPoint(WebCore::IntPoint(hitViewPoint));
     235}
     236#endif
     237
    208238UIScrollView *findActingScrollParent(UIScrollView *scrollView, const RemoteLayerTreeHost& host)
    209239{
     
    405435#endif
    406436
     437#if HAVE(UISCROLLVIEW_ASYNCHRONOUS_SCROLL_EVENT_HANDLING)
     438    [self _setAllowsAsyncScrollEvent:YES];
     439#endif
     440
    407441    return self;
    408442}
  • trunk/Source/WebKit/UIProcess/RemoteLayerTree/ios/ScrollingTreeScrollingNodeDelegateIOS.h

    r260366 r270712  
    6666    void repositionScrollingLayers();
    6767
     68#if HAVE(UISCROLLVIEW_ASYNCHRONOUS_SCROLL_EVENT_HANDLING)
     69    void handleAsynchronousCancelableScrollEvent(UIScrollView *, UIScrollEvent *, void (^completion)(BOOL handled));
     70#endif
     71
    6872    OptionSet<WebCore::TouchAction> activeTouchActions() const { return m_activeTouchActions; }
    6973    void computeActiveTouchActionsForGestureRecognizer(UIGestureRecognizer*);
  • trunk/Source/WebKit/UIProcess/RemoteLayerTree/ios/ScrollingTreeScrollingNodeDelegateIOS.mm

    r270390 r270712  
    177177}
    178178
     179#if HAVE(UISCROLLVIEW_ASYNCHRONOUS_SCROLL_EVENT_HANDLING)
     180- (void)_scrollView:(UIScrollView *)scrollView asynchronouslyHandleScrollEvent:(UIScrollEvent *)scrollEvent completion:(void (^)(BOOL handled))completion
     181{
     182    _scrollingTreeNodeDelegate->handleAsynchronousCancelableScrollEvent(scrollView, scrollEvent, completion);
     183}
     184#endif
     185
    179186- (void)scrollViewWillBeginZooming:(UIScrollView *)scrollView withView:(UIView *)view
    180187{
     
    299306}
    300307
     308#if HAVE(UISCROLLVIEW_ASYNCHRONOUS_SCROLL_EVENT_HANDLING)
     309void ScrollingTreeScrollingNodeDelegateIOS::handleAsynchronousCancelableScrollEvent(UIScrollView *scrollView, UIScrollEvent *scrollEvent, void (^completion)(BOOL handled))
     310{
     311    auto& scrollingCoordinatorProxy = downcast<WebKit::RemoteScrollingTree>(scrollingTree()).scrollingCoordinatorProxy();
     312    scrollingCoordinatorProxy.webPageProxy().pageClient().handleAsynchronousCancelableScrollEvent(scrollView, scrollEvent, completion);
     313}
     314#endif
     315
    301316void ScrollingTreeScrollingNodeDelegateIOS::repositionScrollingLayers()
    302317{
  • trunk/Source/WebKit/UIProcess/WebPageProxy.cpp

    r270682 r270712  
    26392639}
    26402640
     2641void WebPageProxy::dispatchWheelEventWithoutScrolling(const WebWheelEvent& event, CompletionHandler<void(bool)>&& completionHandler)
     2642{
     2643    sendWithAsyncReply(Messages::WebPage::DispatchWheelEventWithoutScrolling(event), WTFMove(completionHandler));
     2644}
     2645
    26412646void WebPageProxy::handleWheelEvent(const NativeWebWheelEvent& event)
    26422647{
  • trunk/Source/WebKit/UIProcess/WebPageProxy.h

    r270669 r270712  
    18471847#endif
    18481848
     1849    void dispatchWheelEventWithoutScrolling(const WebWheelEvent&, CompletionHandler<void(bool)>&&);
     1850
    18491851private:
    18501852    WebPageProxy(PageClient&, WebProcessProxy&, Ref<API::PageConfiguration>&&);
  • trunk/Source/WebKit/UIProcess/ios/PageClientImplIOS.h

    r269394 r270712  
    272272    void showDictationAlternativeUI(const WebCore::FloatRect&, WebCore::DictationContext) final;
    273273
     274#if HAVE(UISCROLLVIEW_ASYNCHRONOUS_SCROLL_EVENT_HANDLING)
     275    void handleAsynchronousCancelableScrollEvent(UIScrollView *, UIScrollEvent *, void (^completion)(BOOL handled)) final;
     276#endif
     277
    274278    WeakObjCPtr<WKContentView> m_contentView;
    275279    RetainPtr<WKEditorUndoTarget> m_undoTarget;
  • trunk/Source/WebKit/UIProcess/ios/PageClientImplIOS.mm

    r269394 r270712  
    962962}
    963963
     964#if HAVE(UISCROLLVIEW_ASYNCHRONOUS_SCROLL_EVENT_HANDLING)
     965void PageClientImpl::handleAsynchronousCancelableScrollEvent(UIScrollView *scrollView, UIScrollEvent *scrollEvent, void (^completion)(BOOL handled))
     966{
     967    [m_webView _scrollView:scrollView asynchronouslyHandleScrollEvent:scrollEvent completion:completion];
     968}
     969#endif
     970
    964971} // namespace WebKit
    965972
  • trunk/Source/WebKit/WebProcess/WebPage/WebPage.cpp

    r270710 r270712  
    28712871}
    28722872
    2873 void WebPage::wheelEvent(const WebWheelEvent& wheelEvent, OptionSet<WheelEventProcessingSteps> processingSteps)
     2873bool WebPage::wheelEvent(const WebWheelEvent& wheelEvent, OptionSet<WheelEventProcessingSteps> processingSteps)
    28742874{
    28752875    m_userActivity.impulse();
     
    28812881    if (processingSteps.contains(WheelEventProcessingSteps::MainThreadForScrolling))
    28822882        send(Messages::WebPageProxy::DidReceiveEvent(static_cast<uint32_t>(wheelEvent.type()), handled));
     2883
     2884    return handled;
     2885}
     2886
     2887void WebPage::dispatchWheelEventWithoutScrolling(const WebWheelEvent& wheelEvent, CompletionHandler<void(bool)>&& completionHandler)
     2888{
     2889#if ENABLE(KINETIC_SCROLLING)
     2890    auto gestureState = m_page->mainFrame().eventHandler().wheelScrollGestureState();
     2891    bool isCancelable = !gestureState || gestureState == WheelScrollGestureState::Blocking;
     2892#else
     2893    bool isCancelable = true;
     2894#endif
     2895    bool handled = this->wheelEvent(wheelEvent, { isCancelable ? WheelEventProcessingSteps::MainThreadForBlockingDOMEventDispatch : WheelEventProcessingSteps::MainThreadForNonBlockingDOMEventDispatch });
     2896    completionHandler(handled);
    28832897}
    28842898
  • trunk/Source/WebKit/WebProcess/WebPage/WebPage.h

    r270587 r270712  
    10141014#endif
    10151015
    1016     void wheelEvent(const WebWheelEvent&, OptionSet<WebCore::WheelEventProcessingSteps>);
     1016    bool wheelEvent(const WebWheelEvent&, OptionSet<WebCore::WheelEventProcessingSteps>);
    10171017
    10181018    void wheelEventHandlersChanged(bool);
     
    13711371    bool createAppHighlightInSelectedRange(CreateNewGroupForHighlight);
    13721372#endif
     1373
     1374    void dispatchWheelEventWithoutScrolling(const WebWheelEvent&, CompletionHandler<void(bool)>&&);
    13731375
    13741376private:
  • trunk/Source/WebKit/WebProcess/WebPage/WebPage.messages.in

    r270512 r270712  
    622622#endif
    623623
     624    DispatchWheelEventWithoutScrolling(WebKit::WebWheelEvent event) -> (bool handled) Async
    624625}
  • trunk/Tools/ChangeLog

    r270697 r270712  
     12020-12-11  Tim Horton  <timothy_horton@apple.com>
     2
     3        Trackpad and Mouse scroll events on iPad only fire "pointermove" -- not "wheel"
     4        https://bugs.webkit.org/show_bug.cgi?id=210071
     5        <rdar://problem/54616853>
     6
     7        Reviewed by Simon Fraser.
     8
     9        * TestWebKitAPI/Tests/ios/WKScrollViewTests.mm:
     10        (-[WKUIScrollEvent initWithPhase:location:delta:]):
     11        (-[WKUIScrollEvent phase]):
     12        (-[WKUIScrollEvent locationInView:]):
     13        (-[WKUIScrollEvent _adjustedAcceleratedDeltaInView:]):
     14        (TEST):
     15        * TestWebKitAPI/ios/UIKitSPI.h:
     16        Add a very simple test that directly calls the new UIScrollViewDelegate SPI
     17        and verifies that only the first event is cancelable (unless the first event
     18        is canceled, in which case all subsequent events are cancelable).
     19
    1202020-12-11  Jonathan Bedard  <jbedard@apple.com>
    221
  • trunk/Tools/TestWebKitAPI/Tests/ios/WKScrollViewTests.mm

    r260366 r270712  
    3030#import "PlatformUtilities.h"
    3131#import "TestWKWebView.h"
     32#import "UIKitSPI.h"
    3233#import <WebKit/WKWebViewPrivate.h>
     34
     35#if HAVE(UISCROLLVIEW_ASYNCHRONOUS_SCROLL_EVENT_HANDLING)
     36@interface WKUIScrollEvent : UIScrollEvent
     37
     38- (instancetype)initWithPhase:(UIScrollPhase)phase location:(CGPoint)location delta:(CGVector)delta;
     39
     40@end
     41
     42@implementation WKUIScrollEvent {
     43    UIScrollPhase _phase;
     44    CGPoint _location;
     45    CGVector _delta;
     46}
     47
     48- (instancetype)initWithPhase:(UIScrollPhase)phase location:(CGPoint)location delta:(CGVector)delta
     49{
     50    self = [super init];
     51    if (!self)
     52        return nil;
     53
     54    _phase = phase;
     55    _location = location;
     56    _delta = delta;
     57
     58    return self;
     59}
     60
     61- (UIScrollPhase)phase
     62{
     63    return _phase;
     64}
     65
     66- (CGPoint)locationInView:(UIView *)view
     67{
     68    return _location;
     69}
     70
     71- (CGVector)_adjustedAcceleratedDeltaInView:(UIView *)view
     72{
     73    return _delta;
     74}
     75
     76@end
     77#endif // HAVE(UISCROLLVIEW_ASYNCHRONOUS_SCROLL_EVENT_HANDLING)
    3378
    3479static void traverseLayerTree(CALayer *layer, void(^block)(CALayer *))
     
    72117}
    73118
     119#if HAVE(UISCROLLVIEW_ASYNCHRONOUS_SCROLL_EVENT_HANDLING)
     120TEST(WKScrollViewTests, AsynchronousWheelEventHandling)
     121{
     122    auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]);
     123    [webView synchronouslyLoadHTMLString:@""
     124        "<style>#handler { width: 200px; height: 200px; }</style>"
     125        "<div id='handler'></div>"
     126        "<script>window.preventDefaultOnScrollEvents = false;"
     127        "document.getElementById('handler').addEventListener('wheel', "
     128        "function (e) {"
     129        "   window.lastWheelEvent = e;"
     130        "   if (window.preventDefaultOnScrollEvents)"
     131        "       e.preventDefault();"
     132        "})</script>"];
     133    [webView waitForNextPresentationUpdate];
     134
     135    __block bool done;
     136    __block bool wasHandled;
     137
     138    auto synchronouslyHandleScrollEvent = ^(UIScrollPhase phase, CGPoint location, CGVector delta) {
     139        done = false;
     140        auto event = adoptNS([[WKUIScrollEvent alloc] initWithPhase:phase location:location delta:delta]);
     141        [webView _scrollView:[webView scrollView] asynchronouslyHandleScrollEvent:event.get() completion:^(BOOL handled) {
     142            wasHandled = handled;
     143            done = true;
     144        }];
     145        TestWebKitAPI::Util::run(&done);
     146    };
     147
     148    // Don't preventDefault() at all.
     149    synchronouslyHandleScrollEvent(UIScrollPhaseMayBegin, CGPointMake(100, 100), CGVectorMake(0, 0));
     150    EXPECT_FALSE(wasHandled);
     151    synchronouslyHandleScrollEvent(UIScrollPhaseBegan, CGPointMake(100, 100), CGVectorMake(0, 10));
     152    EXPECT_FALSE(wasHandled);
     153    EXPECT_TRUE([[webView objectByEvaluatingJavaScript:@"window.lastWheelEvent.cancelable"] intValue]);
     154    EXPECT_EQ(-10, [[webView objectByEvaluatingJavaScript:@"window.lastWheelEvent.deltaY"] intValue]);
     155    synchronouslyHandleScrollEvent(UIScrollPhaseChanged, CGPointMake(100, 100), CGVectorMake(0, 10));
     156    EXPECT_FALSE(wasHandled);
     157    synchronouslyHandleScrollEvent(UIScrollPhaseEnded, CGPointMake(100, 100), CGVectorMake(0, 0));
     158    EXPECT_FALSE(wasHandled);
     159
     160    // preventDefault() on all events.
     161    [webView stringByEvaluatingJavaScript:@"window.preventDefaultOnScrollEvents = true;"];
     162    synchronouslyHandleScrollEvent(UIScrollPhaseMayBegin, CGPointMake(100, 100), CGVectorMake(0, 0));
     163    EXPECT_FALSE(wasHandled);
     164    synchronouslyHandleScrollEvent(UIScrollPhaseBegan, CGPointMake(100, 100), CGVectorMake(0, 10));
     165    EXPECT_TRUE(wasHandled);
     166    synchronouslyHandleScrollEvent(UIScrollPhaseChanged, CGPointMake(100, 100), CGVectorMake(0, 10));
     167    EXPECT_TRUE(wasHandled);
     168    synchronouslyHandleScrollEvent(UIScrollPhaseEnded, CGPointMake(100, 100), CGVectorMake(0, 0));
     169    EXPECT_FALSE(wasHandled);
     170
     171    // preventDefault() on all but the begin event; it will be ignored.
     172    [webView stringByEvaluatingJavaScript:@"window.preventDefaultOnScrollEvents = false;"];
     173    synchronouslyHandleScrollEvent(UIScrollPhaseMayBegin, CGPointMake(100, 100), CGVectorMake(0, 0));
     174    EXPECT_FALSE(wasHandled);
     175    synchronouslyHandleScrollEvent(UIScrollPhaseBegan, CGPointMake(100, 100), CGVectorMake(0, 10));
     176    EXPECT_TRUE([[webView objectByEvaluatingJavaScript:@"window.lastWheelEvent.cancelable"] intValue]);
     177    EXPECT_FALSE(wasHandled);
     178    [webView stringByEvaluatingJavaScript:@"window.preventDefaultOnScrollEvents = true;"];
     179    synchronouslyHandleScrollEvent(UIScrollPhaseChanged, CGPointMake(100, 100), CGVectorMake(0, 10));
     180    EXPECT_FALSE(wasHandled);
     181    EXPECT_FALSE([[webView objectByEvaluatingJavaScript:@"window.lastWheelEvent.cancelable"] intValue]);
     182    synchronouslyHandleScrollEvent(UIScrollPhaseEnded, CGPointMake(100, 100), CGVectorMake(0, 0));
     183    EXPECT_FALSE(wasHandled);
     184
     185    // preventDefault() on the begin event, and some subsequent events.
     186    [webView stringByEvaluatingJavaScript:@"window.preventDefaultOnScrollEvents = true;"];
     187    synchronouslyHandleScrollEvent(UIScrollPhaseMayBegin, CGPointMake(100, 100), CGVectorMake(0, 0));
     188    EXPECT_FALSE(wasHandled);
     189    synchronouslyHandleScrollEvent(UIScrollPhaseBegan, CGPointMake(100, 100), CGVectorMake(0, 10));
     190    EXPECT_TRUE(wasHandled);
     191    synchronouslyHandleScrollEvent(UIScrollPhaseChanged, CGPointMake(100, 100), CGVectorMake(0, 10));
     192    EXPECT_TRUE(wasHandled);
     193    [webView stringByEvaluatingJavaScript:@"window.preventDefaultOnScrollEvents = false;"];
     194    synchronouslyHandleScrollEvent(UIScrollPhaseChanged, CGPointMake(100, 100), CGVectorMake(0, 10));
     195    EXPECT_FALSE(wasHandled);
     196    synchronouslyHandleScrollEvent(UIScrollPhaseEnded, CGPointMake(100, 100), CGVectorMake(0, 0));
     197    EXPECT_FALSE(wasHandled);
     198}
     199#endif // HAVE(UISCROLLVIEW_ASYNCHRONOUS_SCROLL_EVENT_HANDLING)
     200
    74201#endif // PLATFORM(IOS_FAMILY)
  • trunk/Tools/TestWebKitAPI/ios/UIKitSPI.h

    r270565 r270712  
    3838#import <UIKit/UIResponder_Private.h>
    3939#import <UIKit/UIScreen_Private.h>
     40#import <UIKit/UIScrollEvent_Private.h>
     41#import <UIKit/UIScrollView_ForWebKitOnly.h>
    4042#import <UIKit/UIScrollView_Private.h>
    4143#import <UIKit/UITextAutofillSuggestion.h>
     
    241243@end
    242244
     245@interface UIScrollEvent : UIEvent
     246@end
     247
     248@interface NSObject (UIScrollViewDelegate_ForWebKitOnly)
     249- (void)_scrollView:(UIScrollView *)scrollView asynchronouslyHandleScrollEvent:(UIScrollEvent *)scrollEvent completion:(void (^)(BOOL handled))completion;
     250@end
     251
    243252#endif // USE(APPLE_INTERNAL_SDK)
    244253
Note: See TracChangeset for help on using the changeset viewer.