Changeset 248433 in webkit


Ignore:
Timestamp:
Aug 8, 2019 11:54:06 AM (5 years ago)
Author:
Wenson Hsieh
Message:

[iOS 13] Taps that interrupt momentum scrolling are recognized as clicks
https://bugs.webkit.org/show_bug.cgi?id=200516
<rdar://problem/53889373>

Reviewed by Tim Horton.

Source/WebKit:

After <https://trac.webkit.org/r247656>, the -tracksImmediatelyWhileDecelerating property of WKScrollView and
WKChildScrollView is set to NO. This means that if a user interacts with the page while the scroll view is
decelerating (e.g. after momentum scrolling), the pan gesture recognizer will not be immediately recognized.
This gives other gesture recognizers, such as the synthetic click (single tap) gesture a chance to instead
recognize first. In this particular bug, this causes taps on the web view that are intended to only stop
momentum scrolling to instead activate clickable elements beneath the touch, such as links and buttons.

To mitigate this, we add some logic to prevent the click gesture recognizer from firing in the case where the
tap also causes the scroll view to decelerate. This heuristic is similar to the one introduced in r219310, which
has the same purpose of hiding gestures that stop momentum scrolling from the page, and also consults
-[UIScrollView _isInterruptingDeceleration].

Tests: fast/scrolling/ios/click-events-during-momentum-scroll-in-main-frame.html

fast/scrolling/ios/click-events-during-momentum-scroll-in-overflow-after-tap-on-body.html
fast/scrolling/ios/click-events-during-momentum-scroll-in-overflow.html

  • UIProcess/ios/WKContentViewInteraction.mm:

(-[WKContentView gestureRecognizerShouldBegin:]):

Return NO in the case of the single tap gesture if the UIScrollView most recently touched by the single tap
gesture (or one of its enclosing scroll views, up to the main WKScrollView) is being interrupted while
decelerating.

  • UIProcess/ios/WKSyntheticTapGestureRecognizer.h:
  • UIProcess/ios/WKSyntheticTapGestureRecognizer.mm:

(-[WKSyntheticTapGestureRecognizer reset]):
(-[WKSyntheticTapGestureRecognizer touchesBegan:withEvent:]):

Teach WKSyntheticTapGestureRecognizer to keep track of the last WKScrollView that was touched, for later use in
-gestureRecognizerShouldBegin:. To do this, we keep a weak reference to the first UIScrollView we find in the
set of touches.

(-[WKSyntheticTapGestureRecognizer lastTouchedScrollView]):

LayoutTests:

Add new layout tests. See below for details.

  • fast/scrolling/ios/click-events-during-momentum-scroll-in-main-frame-expected.txt: Added.
  • fast/scrolling/ios/click-events-during-momentum-scroll-in-main-frame.html: Added.

Add a test to verify that interrupting scrolling in the main frame using a tap doesn't fire a click event.

  • fast/scrolling/ios/click-events-during-momentum-scroll-in-overflow-after-tap-on-body-expected.txt: Added.
  • fast/scrolling/ios/click-events-during-momentum-scroll-in-overflow-after-tap-on-body.html: Added.

Add a test to verify that after triggering momentum scrolling in a fast subscrollable region, tapping outside of
the scroller will still fire a click event.

  • fast/scrolling/ios/click-events-during-momentum-scroll-in-overflow-expected.txt: Added.
  • fast/scrolling/ios/click-events-during-momentum-scroll-in-overflow.html: Added.

Add a test to verify that interrupting scrolling in a fast subscrollable region using a tap doesn't fire a
click event.

  • resources/ui-helper.js:

(window.UIHelper.dragFromPointToPoint):
(window.UIHelper):

Location:
trunk
Files:
6 added
6 edited

Legend:

Unmodified
Added
Removed
  • trunk/LayoutTests/ChangeLog

    r248424 r248433  
     12019-08-08  Wenson Hsieh  <wenson_hsieh@apple.com>
     2
     3        [iOS 13] Taps that interrupt momentum scrolling are recognized as clicks
     4        https://bugs.webkit.org/show_bug.cgi?id=200516
     5        <rdar://problem/53889373>
     6
     7        Reviewed by Tim Horton.
     8
     9        Add new layout tests. See below for details.
     10
     11        * fast/scrolling/ios/click-events-during-momentum-scroll-in-main-frame-expected.txt: Added.
     12        * fast/scrolling/ios/click-events-during-momentum-scroll-in-main-frame.html: Added.
     13
     14        Add a test to verify that interrupting scrolling in the main frame using a tap doesn't fire a click event.
     15
     16        * fast/scrolling/ios/click-events-during-momentum-scroll-in-overflow-after-tap-on-body-expected.txt: Added.
     17        * fast/scrolling/ios/click-events-during-momentum-scroll-in-overflow-after-tap-on-body.html: Added.
     18
     19        Add a test to verify that after triggering momentum scrolling in a fast subscrollable region, tapping outside of
     20        the scroller will still fire a click event.
     21
     22        * fast/scrolling/ios/click-events-during-momentum-scroll-in-overflow-expected.txt: Added.
     23        * fast/scrolling/ios/click-events-during-momentum-scroll-in-overflow.html: Added.
     24
     25        Add a test to verify that interrupting scrolling in a fast subscrollable region using a tap doesn't fire a
     26        click event.
     27
     28        * resources/ui-helper.js:
     29        (window.UIHelper.dragFromPointToPoint):
     30        (window.UIHelper):
     31
    1322019-08-08  Russell Epstein  <repstein@apple.com>
    233
  • trunk/LayoutTests/resources/ui-helper.js

    r247866 r248433  
    10181018        });
    10191019    }
     1020
     1021    static dragFromPointToPoint(fromX, fromY, toX, toY, duration)
     1022    {
     1023        if (!this.isWebKit2() || !this.isIOSFamily()) {
     1024            eventSender.mouseMoveTo(fromX, fromY);
     1025            eventSender.mouseDown();
     1026            eventSender.leapForward(duration * 1000);
     1027            eventSender.mouseMoveTo(toX, toY);
     1028            eventSender.mouseUp();
     1029            return Promise.resolve();
     1030        }
     1031
     1032        return new Promise(resolve => {
     1033            testRunner.runUIScript(`(() => {
     1034                uiController.dragFromPointToPoint(${fromX}, ${fromY}, ${toX}, ${toY}, ${duration}, () => {
     1035                    uiController.uiScriptComplete("");
     1036                });
     1037            })();`, resolve);
     1038        });
     1039    }
    10201040}
  • trunk/Source/WebKit/ChangeLog

    r248430 r248433  
     12019-08-08  Wenson Hsieh  <wenson_hsieh@apple.com>
     2
     3        [iOS 13] Taps that interrupt momentum scrolling are recognized as clicks
     4        https://bugs.webkit.org/show_bug.cgi?id=200516
     5        <rdar://problem/53889373>
     6
     7        Reviewed by Tim Horton.
     8
     9        After <https://trac.webkit.org/r247656>, the -tracksImmediatelyWhileDecelerating property of WKScrollView and
     10        WKChildScrollView is set to NO. This means that if a user interacts with the page while the scroll view is
     11        decelerating (e.g. after momentum scrolling), the pan gesture recognizer will not be immediately recognized.
     12        This gives other gesture recognizers, such as the synthetic click (single tap) gesture a chance to instead
     13        recognize first. In this particular bug, this causes taps on the web view that are intended to only stop
     14        momentum scrolling to instead activate clickable elements beneath the touch, such as links and buttons.
     15
     16        To mitigate this, we add some logic to prevent the click gesture recognizer from firing in the case where the
     17        tap also causes the scroll view to decelerate. This heuristic is similar to the one introduced in r219310, which
     18        has the same purpose of hiding gestures that stop momentum scrolling from the page, and also consults
     19        -[UIScrollView _isInterruptingDeceleration].
     20
     21        Tests:  fast/scrolling/ios/click-events-during-momentum-scroll-in-main-frame.html
     22                fast/scrolling/ios/click-events-during-momentum-scroll-in-overflow-after-tap-on-body.html
     23                fast/scrolling/ios/click-events-during-momentum-scroll-in-overflow.html
     24
     25        * UIProcess/ios/WKContentViewInteraction.mm:
     26        (-[WKContentView gestureRecognizerShouldBegin:]):
     27
     28        Return NO in the case of the single tap gesture if the UIScrollView most recently touched by the single tap
     29        gesture (or one of its enclosing scroll views, up to the main WKScrollView) is being interrupted while
     30        decelerating.
     31
     32        * UIProcess/ios/WKSyntheticTapGestureRecognizer.h:
     33        * UIProcess/ios/WKSyntheticTapGestureRecognizer.mm:
     34        (-[WKSyntheticTapGestureRecognizer reset]):
     35        (-[WKSyntheticTapGestureRecognizer touchesBegan:withEvent:]):
     36
     37        Teach WKSyntheticTapGestureRecognizer to keep track of the last WKScrollView that was touched, for later use in
     38        -gestureRecognizerShouldBegin:. To do this, we keep a weak reference to the first UIScrollView we find in the
     39        set of touches.
     40
     41        (-[WKSyntheticTapGestureRecognizer lastTouchedScrollView]):
     42
    1432019-08-08  Dean Jackson  <dino@apple.com>
    244
  • trunk/Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm

    r248380 r248433  
    20912091        return _webView._stylusTapGestureShouldCreateEditableImage;
    20922092
     2093    if (gestureRecognizer == _singleTapGestureRecognizer) {
     2094        UIScrollView *mainScroller = _webView.scrollView;
     2095        UIView *view = [_singleTapGestureRecognizer lastTouchedScrollView] ?: mainScroller;
     2096        while (view) {
     2097            if ([view isKindOfClass:UIScrollView.class] && [(UIScrollView *)view _isInterruptingDeceleration])
     2098                return NO;
     2099
     2100            if (mainScroller == view)
     2101                break;
     2102
     2103            view = view.superview;
     2104        }
     2105        return YES;
     2106    }
     2107
    20932108    if (gestureRecognizer == _highlightLongPressGestureRecognizer
    20942109        || gestureRecognizer == _doubleTapGestureRecognizer
  • trunk/Source/WebKit/UIProcess/ios/WKSyntheticTapGestureRecognizer.h

    r245639 r248433  
    4040@property (nonatomic, readonly) NSNumber *lastActiveTouchIdentifier;
    4141#endif
     42@property (nonatomic, readonly, weak) UIScrollView *lastTouchedScrollView;
    4243@end
    4344
  • trunk/Source/WebKit/UIProcess/ios/WKSyntheticTapGestureRecognizer.mm

    r247502 r248433  
    3131#import <UIKit/UIGestureRecognizerSubclass.h>
    3232#import <wtf/RetainPtr.h>
     33#import <wtf/WeakObjCPtr.h>
    3334
    3435@implementation WKSyntheticTapGestureRecognizer {
     
    4041    SEL _resetAction;
    4142    RetainPtr<NSNumber> _lastActiveTouchIdentifier;
     43    WeakObjCPtr<UIScrollView> _lastTouchedScrollView;
    4244}
    4345
     
    7678    _lastActiveTouchIdentifier = nil;
    7779#endif
     80    _lastTouchedScrollView = nil;
     81}
     82
     83- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
     84{
     85    [super touchesBegan:touches withEvent:event];
     86
     87    for (UITouch *touch in touches) {
     88        if ([touch.view isKindOfClass:UIScrollView.class]) {
     89            _lastTouchedScrollView = (UIScrollView *)touch.view;
     90            break;
     91        }
     92    }
    7893}
    7994
     
    97112}
    98113
     114- (UIScrollView *)lastTouchedScrollView
     115{
     116    return _lastTouchedScrollView.get().get();
     117}
     118
    99119- (NSNumber*)lastActiveTouchIdentifier
    100120{
Note: See TracChangeset for help on using the changeset viewer.