Changeset 253267 in webkit


Ignore:
Timestamp:
Dec 7, 2019, 7:57:23 PM (5 years ago)
Author:
Wenson Hsieh
Message:

[iOS] WKWebView touch event gesture recognition should not block the application process main thread when possible
https://bugs.webkit.org/show_bug.cgi?id=204664
<rdar://problem/38670692>

Reviewed by Tim Horton.

Source/WebKit:

Adds a mechanism that allows some sync touch events on iOS to be sent asynchronously. To do this, we use the
deferring gesture mechanism introduced in trac.webkit.org/r253005 to defer all gestures under WKContentView (and
WebKit-owned scroll views) when a touch starts, such that they will not recognize until we know that the page
has either prevented default or not (assuming that the touch was over an active listener). See below for more
details.

Tests: fast/events/touch/ios/prevent-default-on-touch-start-with-slow-event-listener.html

fast/events/touch/ios/scroll-on-touch-start-with-slow-event-listener.html

  • UIProcess/GenericCallback.h:
  • UIProcess/PageClient.h:
  • UIProcess/RemoteLayerTree/ios/RemoteLayerTreeViews.mm:

(-[WKChildScrollView gestureRecognizer:shouldRequireFailureOfGestureRecognizer:]):
(-[WKChildScrollView gestureRecognizer:shouldBeRequiredToFailByGestureRecognizer:]):

Implement gesture recognizer delegate hooks to add dynamic failure requirements between a child scroll view's
gestures and the new deferring gesture recognizers on WKContentView. This allows pan gestures over a scrollable
container to hold off on recognizing while the deferring gesture recognizer has not failed yet.

  • UIProcess/WebPageProxy.cpp:

(WebKit::WebPageProxy::handlePreventableTouchEvent):
(WebKit::WebPageProxy::handleUnpreventableTouchEvent):

Rename handleTouchEventSynchronously and handleTouchEventAsynchronously to handlePreventableTouchEvent and
handleUnpreventableTouchEvent, respectively. Instead of always sending touchstart events that may prevent native
gestures synchronously, we may now go through the same EventDispatcher::TouchEvent codepath used when
dispatching touch events in passive tracking regions. However, in the case of preventable touchstarts, we
additionally store a completion callback that is invoked after the touch event has been handled by the page; we
then either un-defer or prevent native gestures here (depending on whether the page prevented default) by
calling PageClient::doneDeferringNativeGestures.

Non-touchstart events are still dispatched synchronously, to ensure that calling preventDefault() on touchmove
and touchend continue to prevent default gestures from recognizing.

(WebKit::WebPageProxy::boolCallback):
(WebKit::WebPageProxy::handleTouchEventSynchronously): Deleted.
(WebKit::WebPageProxy::handleTouchEventAsynchronously): Deleted.

See above.

  • UIProcess/WebPageProxy.h:

(WebKit::WebPageProxy::isHandlingPreventableTouchStart const):

This is used in WKContentView to determine whether deferring gestures need to remain active after the touch
ends. See below for more detail.

  • UIProcess/WebPageProxy.messages.in:
  • UIProcess/ios/PageClientImplIOS.h:
  • UIProcess/ios/PageClientImplIOS.mm:

(WebKit::PageClientImpl::doneDeferringNativeGestures):

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

(-[UIGestureRecognizer _wk_cancel]):
(-[WKContentView setupInteraction]):
(-[WKContentView cleanupInteraction]):
(-[WKContentView _removeDefaultGestureRecognizers]):
(-[WKContentView _addDefaultGestureRecognizers]):

Add and remove the new deferring gesture recognizers here.

(-[WKContentView _webTouchEventsRecognized:]):
(-[WKContentView _webTouchEvent:preventsNativeGestures:]):
(-[WKContentView _doneDeferringNativeGestures:]):
(-[WKContentView gestureRecognizer:shouldRecognizeSimultaneouslyWithGestureRecognizer:]):
(-[WKContentView ensurePositionInformationIsUpToDate:]):

Drive-by fix: add a missing hasRunningProcess check that causes a flaky assertion under
AuxiliaryProcessProxy::connection() in layout tests.

(-[WKContentView gestureRecognizer:shouldRequireFailureOfGestureRecognizer:]):
(-[WKContentView gestureRecognizer:shouldBeRequiredToFailByGestureRecognizer:]):

Add dynamic failure requirements between WKContentView's gestures (including all text interaction, context menu,
and drag and drop gestures) and the new deferring gesture recognizers.

(-[WKContentView _didStartProvisionalLoadForMainFrame]):

Force the two-finger double tap gesture recognizer to reset when loading a new page. Without this, the layout
test fast/events/ios/click-event-while-editing-node.html will rarely fail when run after a test that dispatches
a two-finger tap, such as fast/events/ios/click-event-two-finger-single-tap-meta-key.html. This is because the
new deferring gestures will temporarily unite multi-finger tap gestures with one-finger double tap gestures in
the same subgraph when performing a tap gesture with more than one finger. This means that there's a 300 ms
delay before a normal single tap can be recognized again, which (without forcing the two-finger double tap to
reset) would cause a subsequent test that loads in under 300 ms and attempts to send a tap to fail.

(-[WKContentView deferringGestureRecognizer:shouldDeferGesturesAfterBeginningTouchesWithEvent:]):

Avoid deferring native gestures if the scroll view is decelerating; this matches behavior of the web touch event
gesture recognizer.

(-[WKContentView deferringGestureRecognizer:shouldDeferGesturesAfterEndingTouchesWithEvent:]):

Normally, after -touchesEnded:withEvent:, we stop deferring native gesture recognizers by failing the deferring
gestures. However, if we're still waiting for a response from the web process, then let
-_doneDeferringNativeGestures: handle this instead.

(-[WKContentView deferringGestureRecognizer:shouldDeferOtherGestureRecognizer:]):
(-[WKContentView deferringGestureRecognizer:shouldDeferGesturesWithEvent:]): Deleted.

Renamed to -shouldDeferGesturesAfterBeginningTouchesWithEvent:.

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

(-[WKDeferringGestureRecognizer touchesBegan:withEvent:]):
(-[WKDeferringGestureRecognizer touchesEnded:withEvent:]):

Override this and add a new delegate hook to determine whether we want the deferring gesture recognizer to
immediately fail when touches end. It's important to override this and transition to failure state in this case,
since not doing so could mean that the deferring gestures stay in Possible state forever; this may lead to the
gesture subgraph containing these deferring gestures being unable to reset, since it's waiting for the deferring
gesture to either fail or end.

  • UIProcess/ios/WKScrollView.mm:

(-[WKScrollView gestureRecognizer:shouldRequireFailureOfGestureRecognizer:]):
(-[WKScrollView gestureRecognizer:shouldBeRequiredToFailByGestureRecognizer:]):

Defer more scroll view gestures.

  • WebProcess/WebPage/EventDispatcher.cpp:

(WebKit::EventDispatcher::touchEvent):

Add an optional CallbackID parameter to this IPC message. If a callback ID is given, then we avoid coalescing
the touch event. To implement this, we additionally refactor the queued touch events map to contain lists of
<WebTouchEvent, Optional<CallbackID>> pairs; if a queued touch event has a corresponding CallbackID, then we
fire the callback corresponding to the ID, indicating whether the touch event was handled by the page.

  • WebProcess/WebPage/EventDispatcher.h:
  • WebProcess/WebPage/EventDispatcher.messages.in:
  • WebProcess/WebPage/WebPage.h:
  • WebProcess/WebPage/ios/WebPageIOS.mm:

(WebKit::WebPage::dispatchAsynchronousTouchEvents):

LayoutTests:

  • fast/events/touch/ios/prevent-default-on-touch-start-with-slow-event-listener-expected.txt: Added.
  • fast/events/touch/ios/prevent-default-on-touch-start-with-slow-event-listener.html: Added.
  • fast/events/touch/ios/scroll-on-touch-start-with-slow-event-listener-expected.txt: Added.
  • fast/events/touch/ios/scroll-on-touch-start-with-slow-event-listener.html: Added.

Add new layout tests to cover behaviors when panning over active touchstart handlers that spin for an extended
length of time (in this case, 400 milliseconds) in overflow scrolling containers. A touchstart handler that
prevents default should still block scrolling, and a touchstart handler that does not should still allow the
user to scroll.

  • fast/events/touch/ios/show-modal-alert-during-touch-start.html:
  • http/tests/adClickAttribution/anchor-tag-attributes-validation-expected.txt:
  • http/tests/security/anchor-download-block-crossorigin-expected.txt:

Rebaseline these tests by changing some line numbers.

  • resources/ui-helper.js:

(window.UIHelper.sendEventStream.return.new.Promise):
(window.UIHelper.sendEventStream):

Add a new UIHelper method to send a JSON object as an event stream.

(UIHelper.EventStreamBuilder.prototype._reset):
(UIHelper.EventStreamBuilder.prototype.begin):
(UIHelper.EventStreamBuilder.prototype.move):
(UIHelper.EventStreamBuilder.prototype.end):
(UIHelper.EventStreamBuilder.prototype.takeResult):

Add a new helper class to make it easier to construct event streams, for the purposes of sending to
UIScriptController::sendEventStream.

Location:
trunk
Files:
4 added
24 edited

Legend:

Unmodified
Added
Removed
  • trunk/LayoutTests/ChangeLog

    r253265 r253267  
     12019-12-07  Wenson Hsieh  <wenson_hsieh@apple.com>
     2
     3        [iOS] WKWebView touch event gesture recognition should not block the application process main thread when possible
     4        https://bugs.webkit.org/show_bug.cgi?id=204664
     5        <rdar://problem/38670692>
     6
     7        Reviewed by Tim Horton.
     8
     9        * fast/events/touch/ios/prevent-default-on-touch-start-with-slow-event-listener-expected.txt: Added.
     10        * fast/events/touch/ios/prevent-default-on-touch-start-with-slow-event-listener.html: Added.
     11        * fast/events/touch/ios/scroll-on-touch-start-with-slow-event-listener-expected.txt: Added.
     12        * fast/events/touch/ios/scroll-on-touch-start-with-slow-event-listener.html: Added.
     13
     14        Add new layout tests to cover behaviors when panning over active touchstart handlers that spin for an extended
     15        length of time (in this case, 400 milliseconds) in overflow scrolling containers. A touchstart handler that
     16        prevents default should still block scrolling, and a touchstart handler that does not should still allow the
     17        user to scroll.
     18
     19        * fast/events/touch/ios/show-modal-alert-during-touch-start.html:
     20        * http/tests/adClickAttribution/anchor-tag-attributes-validation-expected.txt:
     21        * http/tests/security/anchor-download-block-crossorigin-expected.txt:
     22
     23        Rebaseline these tests by changing some line numbers.
     24
     25        * resources/ui-helper.js:
     26        (window.UIHelper.sendEventStream.return.new.Promise):
     27        (window.UIHelper.sendEventStream):
     28
     29        Add a new UIHelper method to send a JSON object as an event stream.
     30
     31        (UIHelper.EventStreamBuilder.prototype._reset):
     32        (UIHelper.EventStreamBuilder.prototype.begin):
     33        (UIHelper.EventStreamBuilder.prototype.move):
     34        (UIHelper.EventStreamBuilder.prototype.end):
     35        (UIHelper.EventStreamBuilder.prototype.takeResult):
     36
     37        Add a new helper class to make it easier to construct event streams, for the purposes of sending to
     38        UIScriptController::sendEventStream.
     39
    1402019-12-07  Ryosuke Niwa  <rniwa@webkit.org>
    241
  • trunk/LayoutTests/fast/events/touch/ios/show-modal-alert-during-touch-start.html

    r241009 r253267  
    3232
    3333target.addEventListener("touchstart", () => alert("This is a modal alert."));
    34 target.addEventListener("touchend", () => testRunner.notifyDone());
     34target.addEventListener("touchend", () => testRunner.notifyDone(), { passive : true });
    3535addEventListener("load", async () => await UIHelper.activateAt(100, 100));
    3636</script>
  • trunk/LayoutTests/http/tests/adClickAttribution/anchor-tag-attributes-validation-expected.txt

    r249125 r253267  
    1 CONSOLE MESSAGE: line 155: adcampaignid must have a non-negative value less than or equal to 63 for Ad Click Attribution.
    2 CONSOLE MESSAGE: line 155: adcampaignid must have a non-negative value less than or equal to 63 for Ad Click Attribution.
    3 CONSOLE MESSAGE: line 155: adcampaignid can not be converted to a non-negative integer which is required for Ad Click Attribution.
    4 CONSOLE MESSAGE: line 155: adcampaignid can not be converted to a non-negative integer which is required for Ad Click Attribution.
    5 CONSOLE MESSAGE: line 155: adcampaignid can not be converted to a non-negative integer which is required for Ad Click Attribution.
    6 CONSOLE MESSAGE: line 155: addestination could not be converted to a valid HTTP-family URL.
    7 CONSOLE MESSAGE: line 155: addestination could not be converted to a valid HTTP-family URL.
    8 CONSOLE MESSAGE: line 155: addestination could not be converted to a valid HTTP-family URL.
    9 CONSOLE MESSAGE: line 155: Both adcampaignid and addestination need to be set for Ad Click Attribution to work.
    10 CONSOLE MESSAGE: line 155: Both adcampaignid and addestination need to be set for Ad Click Attribution to work.
    11 CONSOLE MESSAGE: line 155: addestination can not be the same site as the current website.
     1CONSOLE MESSAGE: line 169: adcampaignid must have a non-negative value less than or equal to 63 for Ad Click Attribution.
     2CONSOLE MESSAGE: line 169: adcampaignid must have a non-negative value less than or equal to 63 for Ad Click Attribution.
     3CONSOLE MESSAGE: line 169: adcampaignid can not be converted to a non-negative integer which is required for Ad Click Attribution.
     4CONSOLE MESSAGE: line 169: adcampaignid can not be converted to a non-negative integer which is required for Ad Click Attribution.
     5CONSOLE MESSAGE: line 169: adcampaignid can not be converted to a non-negative integer which is required for Ad Click Attribution.
     6CONSOLE MESSAGE: line 169: addestination could not be converted to a valid HTTP-family URL.
     7CONSOLE MESSAGE: line 169: addestination could not be converted to a valid HTTP-family URL.
     8CONSOLE MESSAGE: line 169: addestination could not be converted to a valid HTTP-family URL.
     9CONSOLE MESSAGE: line 169: Both adcampaignid and addestination need to be set for Ad Click Attribution to work.
     10CONSOLE MESSAGE: line 169: Both adcampaignid and addestination need to be set for Ad Click Attribution to work.
     11CONSOLE MESSAGE: line 169: addestination can not be the same site as the current website.
    1212Test for validity of ad click attribution attributes on anchor tags.
    1313
  • trunk/LayoutTests/http/tests/security/anchor-download-block-crossorigin-expected.txt

    r249112 r253267  
    1 CONSOLE MESSAGE: line 155: The download attribute on anchor was ignored because its href URL has a different security origin.
     1CONSOLE MESSAGE: line 169: The download attribute on anchor was ignored because its href URL has a different security origin.
    22Tests that the download attribute is ignored if the link is cross origin.
    33
  • trunk/LayoutTests/resources/ui-helper.js

    r252608 r253267  
    2828        eventSender.mouseMoveTo(x2, y2);
    2929        eventSender.mouseUp();
     30    }
     31
     32    static sendEventStream(eventStream)
     33    {
     34        const eventStreamAsString = JSON.stringify(eventStream);
     35        return new Promise(resolve => {
     36            testRunner.runUIScript(`
     37                (function() {
     38                    uiController.sendEventStream(\`${eventStreamAsString}\`, () => {
     39                        uiController.uiScriptComplete();
     40                    });
     41                })();
     42            `, resolve);
     43        });
    3044    }
    3145
     
    11061120    }
    11071121}
     1122
     1123UIHelper.EventStreamBuilder = class {
     1124    constructor()
     1125    {
     1126        // FIXME: This could support additional customization options, such as interpolation, timestep, and different
     1127        // digitizer indices in the future. For now, just make it simpler to string together sequences of pan gestures.
     1128        this._reset();
     1129    }
     1130
     1131    _reset() {
     1132        this.events = [];
     1133        this.currentTimeOffset = 0;
     1134        this.currentX = 0;
     1135        this.currentY = 0;
     1136    }
     1137
     1138    begin(x, y) {
     1139        console.assert(this.currentTimeOffset === 0);
     1140        this.events.push({
     1141            interpolate : "linear",
     1142            timestep : 0.016,
     1143            coordinateSpace : "content",
     1144            startEvent : {
     1145                inputType : "hand",
     1146                timeOffset : this.currentTimeOffset,
     1147                touches : [{ inputType : "finger", phase : "began", id : 1, x : x, y : y, pressure : 0 }]
     1148            },
     1149            endEvent : {
     1150                inputType : "hand",
     1151                timeOffset : this.currentTimeOffset,
     1152                touches : [{ inputType : "finger", phase : "began", id : 1, x : x, y : y, pressure : 0 }]
     1153            }
     1154        });
     1155        this.currentX = x;
     1156        this.currentY = y;
     1157        return this;
     1158    }
     1159
     1160    move(x, y, duration = 0) {
     1161        const previousTimeOffset = this.currentTimeOffset;
     1162        this.currentTimeOffset += duration;
     1163        this.events.push({
     1164            interpolate : "linear",
     1165            timestep : 0.016,
     1166            coordinateSpace : "content",
     1167            startEvent : {
     1168                inputType : "hand",
     1169                timeOffset : previousTimeOffset,
     1170                touches : [{ inputType : "finger", phase : "moved", id : 1, x : this.currentX, y : this.currentY, pressure : 0 }]
     1171            },
     1172            endEvent : {
     1173                inputType : "hand",
     1174                timeOffset : this.currentTimeOffset,
     1175                touches : [{ inputType : "finger", phase : "moved", id : 1, x : x, y : y, pressure : 0 }]
     1176            }
     1177        });
     1178        this.currentX = x;
     1179        this.currentY = y;
     1180        return this;
     1181    }
     1182
     1183    end() {
     1184        this.events.push({
     1185            interpolate : "linear",
     1186            timestep : 0.016,
     1187            coordinateSpace : "content",
     1188            startEvent : {
     1189                inputType : "hand",
     1190                timeOffset : this.currentTimeOffset,
     1191                touches : [{ inputType : "finger", phase : "ended", id : 1, x : this.currentX, y : this.currentY, pressure : 0 }]
     1192            },
     1193            endEvent : {
     1194                inputType : "hand",
     1195                timeOffset : this.currentTimeOffset,
     1196                touches : [{ inputType : "finger", phase : "ended", id : 1, x : this.currentX, y : this.currentY, pressure : 0 }]
     1197            }
     1198        });
     1199        return this;
     1200    }
     1201
     1202    takeResult() {
     1203        const events = this.events;
     1204        this._reset();
     1205        return { "events": events };
     1206    }
     1207}
  • trunk/Source/WebKit/ChangeLog

    r253256 r253267  
     12019-12-07  Wenson Hsieh  <wenson_hsieh@apple.com>
     2
     3        [iOS] WKWebView touch event gesture recognition should not block the application process main thread when possible
     4        https://bugs.webkit.org/show_bug.cgi?id=204664
     5        <rdar://problem/38670692>
     6
     7        Reviewed by Tim Horton.
     8
     9        Adds a mechanism that allows some sync touch events on iOS to be sent asynchronously. To do this, we use the
     10        deferring gesture mechanism introduced in trac.webkit.org/r253005 to defer all gestures under WKContentView (and
     11        WebKit-owned scroll views) when a touch starts, such that they will not recognize until we know that the page
     12        has either prevented default or not (assuming that the touch was over an active listener). See below for more
     13        details.
     14
     15        Tests: fast/events/touch/ios/prevent-default-on-touch-start-with-slow-event-listener.html
     16               fast/events/touch/ios/scroll-on-touch-start-with-slow-event-listener.html
     17
     18        * UIProcess/GenericCallback.h:
     19        * UIProcess/PageClient.h:
     20        * UIProcess/RemoteLayerTree/ios/RemoteLayerTreeViews.mm:
     21        (-[WKChildScrollView gestureRecognizer:shouldRequireFailureOfGestureRecognizer:]):
     22        (-[WKChildScrollView gestureRecognizer:shouldBeRequiredToFailByGestureRecognizer:]):
     23
     24        Implement gesture recognizer delegate hooks to add dynamic failure requirements between a child scroll view's
     25        gestures and the new deferring gesture recognizers on WKContentView. This allows pan gestures over a scrollable
     26        container to hold off on recognizing while the deferring gesture recognizer has not failed yet.
     27
     28        * UIProcess/WebPageProxy.cpp:
     29        (WebKit::WebPageProxy::handlePreventableTouchEvent):
     30        (WebKit::WebPageProxy::handleUnpreventableTouchEvent):
     31
     32        Rename handleTouchEventSynchronously and handleTouchEventAsynchronously to handlePreventableTouchEvent and
     33        handleUnpreventableTouchEvent, respectively. Instead of always sending touchstart events that may prevent native
     34        gestures synchronously, we may now go through the same `EventDispatcher::TouchEvent` codepath used when
     35        dispatching touch events in passive tracking regions. However, in the case of preventable touchstarts, we
     36        additionally store a completion callback that is invoked after the touch event has been handled by the page; we
     37        then either un-defer or prevent native gestures here (depending on whether the page prevented default) by
     38        calling PageClient::doneDeferringNativeGestures.
     39
     40        Non-touchstart events are still dispatched synchronously, to ensure that calling preventDefault() on touchmove
     41        and touchend continue to prevent default gestures from recognizing.
     42
     43        (WebKit::WebPageProxy::boolCallback):
     44        (WebKit::WebPageProxy::handleTouchEventSynchronously): Deleted.
     45        (WebKit::WebPageProxy::handleTouchEventAsynchronously): Deleted.
     46
     47        See above.
     48
     49        * UIProcess/WebPageProxy.h:
     50        (WebKit::WebPageProxy::isHandlingPreventableTouchStart const):
     51
     52        This is used in WKContentView to determine whether deferring gestures need to remain active after the touch
     53        ends. See below for more detail.
     54
     55        * UIProcess/WebPageProxy.messages.in:
     56        * UIProcess/ios/PageClientImplIOS.h:
     57        * UIProcess/ios/PageClientImplIOS.mm:
     58        (WebKit::PageClientImpl::doneDeferringNativeGestures):
     59        * UIProcess/ios/WKContentViewInteraction.h:
     60        * UIProcess/ios/WKContentViewInteraction.mm:
     61        (-[UIGestureRecognizer _wk_cancel]):
     62        (-[WKContentView setupInteraction]):
     63        (-[WKContentView cleanupInteraction]):
     64        (-[WKContentView _removeDefaultGestureRecognizers]):
     65        (-[WKContentView _addDefaultGestureRecognizers]):
     66
     67        Add and remove the new deferring gesture recognizers here.
     68
     69        (-[WKContentView _webTouchEventsRecognized:]):
     70        (-[WKContentView _webTouchEvent:preventsNativeGestures:]):
     71        (-[WKContentView _doneDeferringNativeGestures:]):
     72        (-[WKContentView gestureRecognizer:shouldRecognizeSimultaneouslyWithGestureRecognizer:]):
     73        (-[WKContentView ensurePositionInformationIsUpToDate:]):
     74
     75        Drive-by fix: add a missing hasRunningProcess check that causes a flaky assertion under
     76        `AuxiliaryProcessProxy::connection()` in layout tests.
     77
     78        (-[WKContentView gestureRecognizer:shouldRequireFailureOfGestureRecognizer:]):
     79        (-[WKContentView gestureRecognizer:shouldBeRequiredToFailByGestureRecognizer:]):
     80
     81        Add dynamic failure requirements between WKContentView's gestures (including all text interaction, context menu,
     82        and drag and drop gestures) and the new deferring gesture recognizers.
     83
     84        (-[WKContentView _didStartProvisionalLoadForMainFrame]):
     85
     86        Force the two-finger double tap gesture recognizer to reset when loading a new page. Without this, the layout
     87        test fast/events/ios/click-event-while-editing-node.html will rarely fail when run after a test that dispatches
     88        a two-finger tap, such as fast/events/ios/click-event-two-finger-single-tap-meta-key.html. This is because the
     89        new deferring gestures will temporarily unite multi-finger tap gestures with one-finger double tap gestures in
     90        the same subgraph when performing a tap gesture with more than one finger. This means that there's a 300 ms
     91        delay before a normal single tap can be recognized again, which (without forcing the two-finger double tap to
     92        reset) would cause a subsequent test that loads in under 300 ms and attempts to send a tap to fail.
     93
     94        (-[WKContentView deferringGestureRecognizer:shouldDeferGesturesAfterBeginningTouchesWithEvent:]):
     95
     96        Avoid deferring native gestures if the scroll view is decelerating; this matches behavior of the web touch event
     97        gesture recognizer.
     98
     99        (-[WKContentView deferringGestureRecognizer:shouldDeferGesturesAfterEndingTouchesWithEvent:]):
     100
     101        Normally, after -touchesEnded:withEvent:, we stop deferring native gesture recognizers by failing the deferring
     102        gestures. However, if we're still waiting for a response from the web process, then let
     103        -_doneDeferringNativeGestures: handle this instead.
     104
     105        (-[WKContentView deferringGestureRecognizer:shouldDeferOtherGestureRecognizer:]):
     106        (-[WKContentView deferringGestureRecognizer:shouldDeferGesturesWithEvent:]): Deleted.
     107
     108        Renamed to -shouldDeferGesturesAfterBeginningTouchesWithEvent:.
     109
     110        * UIProcess/ios/WKDeferringGestureRecognizer.h:
     111        * UIProcess/ios/WKDeferringGestureRecognizer.mm:
     112        (-[WKDeferringGestureRecognizer touchesBegan:withEvent:]):
     113        (-[WKDeferringGestureRecognizer touchesEnded:withEvent:]):
     114
     115        Override this and add a new delegate hook to determine whether we want the deferring gesture recognizer to
     116        immediately fail when touches end. It's important to override this and transition to failure state in this case,
     117        since not doing so could mean that the deferring gestures stay in Possible state forever; this may lead to the
     118        gesture subgraph containing these deferring gestures being unable to reset, since it's waiting for the deferring
     119        gesture to either fail or end.
     120
     121        * UIProcess/ios/WKScrollView.mm:
     122        (-[WKScrollView gestureRecognizer:shouldRequireFailureOfGestureRecognizer:]):
     123        (-[WKScrollView gestureRecognizer:shouldBeRequiredToFailByGestureRecognizer:]):
     124
     125        Defer more scroll view gestures.
     126
     127        * WebProcess/WebPage/EventDispatcher.cpp:
     128        (WebKit::EventDispatcher::touchEvent):
     129
     130        Add an optional CallbackID parameter to this IPC message. If a callback ID is given, then we avoid coalescing
     131        the touch event. To implement this, we additionally refactor the queued touch events map to contain lists of
     132        <WebTouchEvent, Optional<CallbackID>> pairs; if a queued touch event has a corresponding CallbackID, then we
     133        fire the callback corresponding to the ID, indicating whether the touch event was handled by the page.
     134
     135        * WebProcess/WebPage/EventDispatcher.h:
     136        * WebProcess/WebPage/EventDispatcher.messages.in:
     137        * WebProcess/WebPage/WebPage.h:
     138        * WebProcess/WebPage/ios/WebPageIOS.mm:
     139        (WebKit::WebPage::dispatchAsynchronousTouchEvents):
     140
    11412019-12-07  David Quesada  <david_quesada@apple.com>
    2142
  • trunk/Source/WebKit/UIProcess/GenericCallback.h

    r252011 r253267  
    158158typedef GenericCallback<const Vector<WebCore::IntRect>&, double, WebCore::FloatBoxExtent> ComputedPagesCallback;
    159159typedef GenericCallback<const ShareableBitmap::Handle&> ImageCallback;
     160typedef GenericCallback<bool> BoolCallback;
    160161
    161162template<typename T>
  • trunk/Source/WebKit/UIProcess/PageClient.h

    r252366 r253267  
    302302    virtual void doneWithTouchEvent(const NativeWebTouchEvent&, bool wasEventHandled) = 0;
    303303#endif
     304#if ENABLE(IOS_TOUCH_EVENTS)
     305    virtual void doneDeferringNativeGestures(bool preventNativeGestures) = 0;
     306#endif
    304307
    305308    virtual RefPtr<WebPopupMenuProxy> createPopupMenuProxy(WebPageProxy&) = 0;
  • trunk/Source/WebKit/UIProcess/RemoteLayerTree/ios/RemoteLayerTreeViews.mm

    r248819 r253267  
    3333#import "RemoteLayerTreeNode.h"
    3434#import "UIKitSPI.h"
     35#import "WKDeferringGestureRecognizer.h"
    3536#import "WKDrawingView.h"
    3637#import <WebCore/Region.h>
     
    317318}
    318319
     320- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRequireFailureOfGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
     321{
     322    if ([otherGestureRecognizer isKindOfClass:WKDeferringGestureRecognizer.class])
     323        return [(WKDeferringGestureRecognizer *)otherGestureRecognizer shouldDeferGestureRecognizer:gestureRecognizer];
     324
     325    return NO;
     326}
     327
     328- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldBeRequiredToFailByGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
     329{
     330    if ([gestureRecognizer isKindOfClass:WKDeferringGestureRecognizer.class])
     331        return [(WKDeferringGestureRecognizer *)gestureRecognizer shouldDeferGestureRecognizer:otherGestureRecognizer];
     332
     333    return NO;
     334}
     335
    319336@end
    320337
  • trunk/Source/WebKit/UIProcess/WebPageProxy.cpp

    r253256 r253267  
    172172#include <stdio.h>
    173173#include <wtf/NeverDestroyed.h>
     174#include <wtf/Scope.h>
    174175#include <wtf/SystemTracing.h>
    175176#include <wtf/URL.h>
     
    27952796
    27962797#if ENABLE(IOS_TOUCH_EVENTS)
    2797 void WebPageProxy::handleTouchEventSynchronously(NativeWebTouchEvent& event)
     2798void WebPageProxy::handlePreventableTouchEvent(NativeWebTouchEvent& event)
    27982799{
    27992800    if (!hasRunningProcess())
     
    28042805    updateTouchEventTracking(event);
    28052806
     2807    auto handleAllTouchPointsReleased = WTF::makeScopeExit([&] {
     2808        if (!event.allTouchPointsAreReleased())
     2809            return;
     2810
     2811        m_touchAndPointerEventTracking.reset();
     2812        didReleaseAllTouchPoints();
     2813    });
     2814
    28062815    TrackingType touchEventsTrackingType = touchEventTrackingType(event);
    2807     if (touchEventsTrackingType == TrackingType::NotTracking)
    2808         return;
     2816    if (touchEventsTrackingType == TrackingType::NotTracking) {
     2817        if (!isHandlingPreventableTouchStart())
     2818            pageClient().doneDeferringNativeGestures(false);
     2819        return;
     2820    }
    28092821
    28102822    if (touchEventsTrackingType == TrackingType::Asynchronous) {
     
    28162828        // We can use asynchronous dispatch and pretend to the client that the page does nothing with the events.
    28172829        event.setCanPreventNativeGestures(false);
    2818         handleTouchEventAsynchronously(event);
     2830        handleUnpreventableTouchEvent(event);
    28192831        didReceiveEvent(event.type(), false);
     2832        if (!isHandlingPreventableTouchStart())
     2833            pageClient().doneDeferringNativeGestures(false);
     2834        return;
     2835    }
     2836
     2837    if (event.type() == WebEvent::TouchStart) {
     2838        ++m_handlingPreventableTouchStartCount;
     2839        Function<void(bool, CallbackBase::Error)> completionHandler = [this, protectedThis = makeRef(*this), event](bool handled, CallbackBase::Error error) {
     2840            ASSERT(m_handlingPreventableTouchStartCount);
     2841            if (m_handlingPreventableTouchStartCount)
     2842                --m_handlingPreventableTouchStartCount;
     2843
     2844            if (error == CallbackBase::Error::ProcessExited)
     2845                return;
     2846
     2847            bool handledOrFailedWithError = handled || error != CallbackBase::Error::None;
     2848            didReceiveEvent(event.type(), handledOrFailedWithError);
     2849            pageClient().doneWithTouchEvent(event, handledOrFailedWithError);
     2850            if (!isHandlingPreventableTouchStart())
     2851                pageClient().doneDeferringNativeGestures(handledOrFailedWithError);
     2852        };
     2853
     2854        auto callbackID = m_callbacks.put(WTFMove(completionHandler), m_process->throttler().backgroundActivity("WebPageProxy::handlePreventableTouchEvent"_s));
     2855        m_process->send(Messages::EventDispatcher::TouchEvent(m_webPageID, event, callbackID), 0);
    28202856        return;
    28212857    }
     
    28292865    didReceiveEvent(event.type(), handled);
    28302866    pageClient().doneWithTouchEvent(event, handled);
     2867    pageClient().doneDeferringNativeGestures(handled);
    28312868    m_process->responsivenessTimer().stop();
    2832 
    2833     if (event.allTouchPointsAreReleased()) {
    2834         m_touchAndPointerEventTracking.reset();
    2835         didReleaseAllTouchPoints();
    2836     }
    28372869}
    28382870
     
    28452877}
    28462878
    2847 void WebPageProxy::handleTouchEventAsynchronously(const NativeWebTouchEvent& event)
     2879void WebPageProxy::handleUnpreventableTouchEvent(const NativeWebTouchEvent& event)
    28482880{
    28492881    if (!hasRunningProcess())
     
    28542886        return;
    28552887
    2856     m_process->send(Messages::EventDispatcher::TouchEvent(m_webPageID, event), 0);
     2888    m_process->send(Messages::EventDispatcher::TouchEvent(m_webPageID, event, WTF::nullopt), 0);
    28572889
    28582890    if (event.allTouchPointsAreReleased()) {
     
    66986730
    66996731    callback->performCallbackWithReturnValue(API::Data::create(dataReference.data(), dataReference.size()).ptr());
     6732}
     6733
     6734void WebPageProxy::boolCallback(bool result, CallbackID callbackID)
     6735{
     6736    auto callback = m_callbacks.take<BoolCallback>(callbackID);
     6737    if (!callback) {
     6738        ASSERT_NOT_REACHED();
     6739        return;
     6740    }
     6741
     6742    callback->performCallbackWithReturnValue(result);
    67006743}
    67016744
  • trunk/Source/WebKit/UIProcess/WebPageProxy.h

    r253256 r253267  
    877877#if ENABLE(IOS_TOUCH_EVENTS)
    878878    void resetPotentialTapSecurityOrigin();
    879     void handleTouchEventSynchronously(NativeWebTouchEvent&);
    880     void handleTouchEventAsynchronously(const NativeWebTouchEvent&);
     879    void handlePreventableTouchEvent(NativeWebTouchEvent&);
     880    void handleUnpreventableTouchEvent(const NativeWebTouchEvent&);
    881881
    882882#elif ENABLE(TOUCH_EVENTS)
     
    16301630
    16311631    void setOrientationForMediaCapture(uint64_t);
     1632
     1633    bool isHandlingPreventableTouchStart() const { return m_handlingPreventableTouchStartCount; }
    16321634
    16331635private:
     
    19351937    void dataCallback(const IPC::DataReference&, CallbackID);
    19361938    void imageCallback(const ShareableBitmap::Handle&, CallbackID);
     1939    void boolCallback(bool result, CallbackID);
    19371940    void stringCallback(const String&, CallbackID);
    19381941    void invalidateStringCallback(CallbackID);
     
    24172420#endif
    24182421
     2422    uint64_t m_handlingPreventableTouchStartCount { 0 };
     2423
    24192424#if ENABLE(INPUT_TYPE_COLOR)
    24202425    RefPtr<WebColorPicker> m_colorPicker;
  • trunk/Source/WebKit/UIProcess/WebPageProxy.messages.in

    r253187 r253267  
    168168    ImageCallback(WebKit::ShareableBitmap::Handle bitmapHandle, WebKit::CallbackID callbackID)
    169169    StringCallback(String resultString, WebKit::CallbackID callbackID)
     170    BoolCallback(bool result, WebKit::CallbackID callbackID)
    170171    InvalidateStringCallback(WebKit::CallbackID callbackID)
    171172    ScriptValueCallback(IPC::DataReference resultData, bool hadException, struct WebCore::ExceptionDetails details, WebKit::CallbackID callbackID)
  • trunk/Source/WebKit/UIProcess/ios/PageClientImplIOS.h

    r250780 r253267  
    105105    void doneWithTouchEvent(const NativeWebTouchEvent&, bool wasEventHandled) override;
    106106#endif
     107#if ENABLE(IOS_TOUCH_EVENTS)
     108    void doneDeferringNativeGestures(bool preventNativeGestures) override;
     109#endif
    107110    RefPtr<WebPopupMenuProxy> createPopupMenuProxy(WebPageProxy&) override;
    108111    Ref<WebCore::ValidationBubble> createValidationBubble(const String& message, const WebCore::ValidationBubble::Settings&) final;
  • trunk/Source/WebKit/UIProcess/ios/PageClientImplIOS.mm

    r251361 r253267  
    419419#endif
    420420
     421#if ENABLE(IOS_TOUCH_EVENTS)
     422
     423void PageClientImpl::doneDeferringNativeGestures(bool preventNativeGestures)
     424{
     425    [m_contentView _doneDeferringNativeGestures:preventNativeGestures];
     426}
     427
     428#endif // ENABLE(IOS_TOUCH_EVENTS)
     429
    421430RefPtr<WebPopupMenuProxy> PageClientImpl::createPopupMenuProxy(WebPageProxy&)
    422431{
  • trunk/Source/WebKit/UIProcess/ios/WKContentViewInteraction.h

    r253005 r253267  
    206206
    207207@interface WKContentView () {
     208#if ENABLE(IOS_TOUCH_EVENTS)
    208209    RetainPtr<WKDeferringGestureRecognizer> _deferringGestureRecognizerForImmediatelyResettableGestures;
    209210    RetainPtr<WKDeferringGestureRecognizer> _deferringGestureRecognizerForDelayedResettableGestures;
     211#endif
    210212    RetainPtr<UIWebTouchEventsGestureRecognizer> _touchEventGestureRecognizer;
    211213
     
    463465#if ENABLE(TOUCH_EVENTS)
    464466- (void)_webTouchEvent:(const WebKit::NativeWebTouchEvent&)touchEvent preventsNativeGestures:(BOOL)preventsDefault;
     467#endif
     468#if ENABLE(IOS_TOUCH_EVENTS)
     469- (void)_doneDeferringNativeGestures:(BOOL)preventNativeGestures;
    465470#endif
    466471- (void)_commitPotentialTapFailed;
  • trunk/Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm

    r253005 r253267  
    644644#endif // ENABLE(DRAG_SUPPORT)
    645645
     646@implementation UIGestureRecognizer (WKContentViewHelpers)
     647
     648- (void)_wk_cancel
     649{
     650    [self setEnabled:NO];
     651    [self setEnabled:YES];
     652}
     653
     654@end
     655
    646656@interface WKContentView (WKInteractionPrivate)
    647657- (void)accessibilitySpeakSelectionSetContent:(NSString *)string;
     
    741751    [_touchActionDownSwipeGestureRecognizer setDelegate:self];
    742752    [self addGestureRecognizer:_touchActionDownSwipeGestureRecognizer.get()];
     753#endif
     754
     755#if ENABLE(IOS_TOUCH_EVENTS)
     756    _deferringGestureRecognizerForImmediatelyResettableGestures = adoptNS([[WKDeferringGestureRecognizer alloc] initWithDeferringGestureDelegate:self]);
     757    [_deferringGestureRecognizerForImmediatelyResettableGestures setName:@"Touch event deferrer (immediate reset)"];
     758    [_deferringGestureRecognizerForImmediatelyResettableGestures setDelegate:self];
     759    [self addGestureRecognizer:_deferringGestureRecognizerForImmediatelyResettableGestures.get()];
     760
     761    _deferringGestureRecognizerForDelayedResettableGestures = adoptNS([[WKDeferringGestureRecognizer alloc] initWithDeferringGestureDelegate:self]);
     762    [_deferringGestureRecognizerForDelayedResettableGestures setName:@"Touch event deferrer (delayed reset)"];
     763    [_deferringGestureRecognizerForDelayedResettableGestures setDelegate:self];
     764    [self addGestureRecognizer:_deferringGestureRecognizerForDelayedResettableGestures.get()];
    743765#endif
    744766
     
    906928    [self removeGestureRecognizer:_touchEventGestureRecognizer.get()];
    907929
     930#if ENABLE(IOS_TOUCH_EVENTS)
     931    [_deferringGestureRecognizerForImmediatelyResettableGestures setDelegate:nil];
     932    [self removeGestureRecognizer:_deferringGestureRecognizerForImmediatelyResettableGestures.get()];
     933
     934    [_deferringGestureRecognizerForDelayedResettableGestures setDelegate:nil];
     935    [self removeGestureRecognizer:_deferringGestureRecognizerForDelayedResettableGestures.get()];
     936#endif
     937
    908938#if HAVE(HOVER_GESTURE_RECOGNIZER)
    909939    [_mouseGestureRecognizer setDelegate:nil];
     
    10181048- (void)_removeDefaultGestureRecognizers
    10191049{
     1050#if ENABLE(IOS_TOUCH_EVENTS)
     1051    [self removeGestureRecognizer:_deferringGestureRecognizerForImmediatelyResettableGestures.get()];
     1052    [self removeGestureRecognizer:_deferringGestureRecognizerForDelayedResettableGestures.get()];
     1053#endif
    10201054    [self removeGestureRecognizer:_touchEventGestureRecognizer.get()];
    10211055    [self removeGestureRecognizer:_singleTapGestureRecognizer.get()];
     
    10441078- (void)_addDefaultGestureRecognizers
    10451079{
     1080#if ENABLE(IOS_TOUCH_EVENTS)
     1081    [self addGestureRecognizer:_deferringGestureRecognizerForImmediatelyResettableGestures.get()];
     1082    [self addGestureRecognizer:_deferringGestureRecognizerForDelayedResettableGestures.get()];
     1083#endif
    10461084    [self addGestureRecognizer:_touchEventGestureRecognizer.get()];
    10471085    [self addGestureRecognizer:_singleTapGestureRecognizer.get()];
     
    14191457
    14201458    if (_touchEventsCanPreventNativeGestures)
    1421         _page->handleTouchEventSynchronously(nativeWebTouchEvent);
     1459        _page->handlePreventableTouchEvent(nativeWebTouchEvent);
    14221460    else
    1423         _page->handleTouchEventAsynchronously(nativeWebTouchEvent);
     1461        _page->handleUnpreventableTouchEvent(nativeWebTouchEvent);
    14241462
    14251463    if (nativeWebTouchEvent.allTouchPointsAreReleased()) {
     
    15911629- (void)_webTouchEvent:(const WebKit::NativeWebTouchEvent&)touchEvent preventsNativeGestures:(BOOL)preventsNativeGesture
    15921630{
    1593     if (preventsNativeGesture) {
    1594         _longPressCanClick = NO;
    1595 
    1596         _touchEventsCanPreventNativeGestures = NO;
    1597         [_touchEventGestureRecognizer setDefaultPrevented:YES];
    1598     }
    1599 }
     1631    if (!preventsNativeGesture || ![_touchEventGestureRecognizer isDispatchingTouchEvents])
     1632        return;
     1633
     1634    _longPressCanClick = NO;
     1635    _touchEventsCanPreventNativeGestures = NO;
     1636    [_touchEventGestureRecognizer setDefaultPrevented:YES];
     1637}
     1638#endif
     1639
     1640#if ENABLE(IOS_TOUCH_EVENTS)
     1641
     1642- (void)_doneDeferringNativeGestures:(BOOL)preventNativeGestures
     1643{
     1644    [_deferringGestureRecognizerForImmediatelyResettableGestures setDefaultPrevented:preventNativeGestures];
     1645    [_deferringGestureRecognizerForDelayedResettableGestures setDefaultPrevented:preventNativeGestures];
     1646}
     1647
    16001648#endif
    16011649
     
    19732021- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer*)otherGestureRecognizer
    19742022{
     2023#if ENABLE(IOS_TOUCH_EVENTS)
     2024    if (isSamePair(gestureRecognizer, otherGestureRecognizer, _touchEventGestureRecognizer.get(), _deferringGestureRecognizerForImmediatelyResettableGestures.get()))
     2025        return YES;
     2026
     2027    if (isSamePair(gestureRecognizer, otherGestureRecognizer, _touchEventGestureRecognizer.get(), _deferringGestureRecognizerForDelayedResettableGestures.get()))
     2028        return YES;
     2029#endif
     2030
     2031    if ([gestureRecognizer isKindOfClass:WKDeferringGestureRecognizer.class] && [otherGestureRecognizer isKindOfClass:WKDeferringGestureRecognizer.class])
     2032        return YES;
     2033
    19752034    if (isSamePair(gestureRecognizer, otherGestureRecognizer, _highlightLongPressGestureRecognizer.get(), _longPressGestureRecognizer.get()))
    19762035        return YES;
     
    20242083        return YES;
    20252084
     2085    if ([otherGestureRecognizer isKindOfClass:WKDeferringGestureRecognizer.class])
     2086        return [(WKDeferringGestureRecognizer *)otherGestureRecognizer shouldDeferGestureRecognizer:gestureRecognizer];
     2087
     2088    return NO;
     2089}
     2090
     2091- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldBeRequiredToFailByGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
     2092{
     2093    if ([gestureRecognizer isKindOfClass:WKDeferringGestureRecognizer.class])
     2094        return [(WKDeferringGestureRecognizer *)gestureRecognizer shouldDeferGestureRecognizer:otherGestureRecognizer];
     2095
    20262096    return NO;
    20272097}
     
    21062176    if ([self _currentPositionInformationIsValidForRequest:request])
    21072177        return YES;
     2178
     2179    if (!_page->hasRunningProcess())
     2180        return NO;
    21082181
    21092182    auto* connection = _page->process().connection();
     
    41384211{
    41394212    // Reset the double tap gesture recognizer to prevent any double click that is in the process of being recognized.
    4140     [_doubleTapGestureRecognizerForDoubleClick setEnabled:NO];
    4141     [_doubleTapGestureRecognizerForDoubleClick setEnabled:YES];
     4213    [_doubleTapGestureRecognizerForDoubleClick _wk_cancel];
    41424214    // We also need to disable the double-tap gesture recognizers that are enabled for double-tap-to-zoom and which
    41434215    // are enabled when a single tap is first recognized. This avoids tests running in sequence and simulating taps
    41444216    // in the same location to trigger double-tap recognition.
    41454217    [self _setDoubleTapGesturesEnabled:NO];
     4218    [_twoFingerDoubleTapGestureRecognizer _wk_cancel];
    41464219}
    41474220
     
    66866759#pragma mark - WKDeferringGestureRecognizerDelegate
    66876760
    6688 - (BOOL)deferringGestureRecognizer:(WKDeferringGestureRecognizer *)deferringGestureRecognizer shouldDeferGesturesWithEvent:(UIEvent *)event
     6761- (BOOL)deferringGestureRecognizer:(WKDeferringGestureRecognizer *)deferringGestureRecognizer shouldDeferGesturesAfterBeginningTouchesWithEvent:(UIEvent *)event
    66896762{
    66906763    return ![self gestureRecognizer:deferringGestureRecognizer isInterruptingMomentumScrollingWithEvent:event];
    66916764}
    66926765
     6766- (BOOL)deferringGestureRecognizer:(WKDeferringGestureRecognizer *)deferringGestureRecognizer shouldDeferGesturesAfterEndingTouchesWithEvent:(UIEvent *)event
     6767{
     6768    return _page->isHandlingPreventableTouchStart();
     6769}
     6770
    66936771- (BOOL)deferringGestureRecognizer:(WKDeferringGestureRecognizer *)deferringGestureRecognizer shouldDeferOtherGestureRecognizer:(UIGestureRecognizer *)gestureRecognizer
    66946772{
    6695     auto isMultipleTapGesture = [](UIGestureRecognizer *gesture) {
    6696         return [gesture isKindOfClass:UITapGestureRecognizer.class] && [(UITapGestureRecognizer *)gesture numberOfTapsRequired] > 1;
     6773#if ENABLE(IOS_TOUCH_EVENTS)
     6774    auto isOneFingerMultipleTapGesture = [](UIGestureRecognizer *gesture) {
     6775        if (![gesture isKindOfClass:UITapGestureRecognizer.class])
     6776            return NO;
     6777
     6778        UITapGestureRecognizer *tapGesture = (UITapGestureRecognizer *)gesture;
     6779        return tapGesture.numberOfTapsRequired > 1 && tapGesture.numberOfTouchesRequired < 2;
    66976780    };
    66986781
    6699     if (deferringGestureRecognizer == _deferringGestureRecognizerForDelayedResettableGestures)
    6700         return gestureRecognizer != _touchEventGestureRecognizer && isMultipleTapGesture(gestureRecognizer);
    6701 
    6702     if (deferringGestureRecognizer == _deferringGestureRecognizerForImmediatelyResettableGestures)
    6703         return gestureRecognizer != _touchEventGestureRecognizer && !isMultipleTapGesture(gestureRecognizer);
     6782    if (deferringGestureRecognizer == _deferringGestureRecognizerForDelayedResettableGestures) {
     6783        if ([gestureRecognizer isKindOfClass:WKDeferringGestureRecognizer.class])
     6784            return NO;
     6785
     6786        if (gestureRecognizer == _touchEventGestureRecognizer)
     6787            return NO;
     6788
     6789        return isOneFingerMultipleTapGesture(gestureRecognizer);
     6790    }
     6791
     6792    if (deferringGestureRecognizer == _deferringGestureRecognizerForImmediatelyResettableGestures) {
     6793        if ([gestureRecognizer isKindOfClass:WKDeferringGestureRecognizer.class])
     6794            return NO;
     6795
     6796        if (gestureRecognizer == _touchEventGestureRecognizer)
     6797            return NO;
     6798
     6799        return !isOneFingerMultipleTapGesture(gestureRecognizer);
     6800    }
    67046801
    67056802    ASSERT_NOT_REACHED();
     6803#endif
     6804
    67066805    return NO;
    67076806}
  • trunk/Source/WebKit/UIProcess/ios/WKDeferringGestureRecognizer.h

    r253005 r253267  
    3333
    3434@protocol WKDeferringGestureRecognizerDelegate
    35 - (BOOL)deferringGestureRecognizer:(WKDeferringGestureRecognizer *)deferringGestureRecognizer shouldDeferGesturesWithEvent:(UIEvent *)event;
     35- (BOOL)deferringGestureRecognizer:(WKDeferringGestureRecognizer *)deferringGestureRecognizer shouldDeferGesturesAfterEndingTouchesWithEvent:(UIEvent *)event;
     36- (BOOL)deferringGestureRecognizer:(WKDeferringGestureRecognizer *)deferringGestureRecognizer shouldDeferGesturesAfterBeginningTouchesWithEvent:(UIEvent *)event;
    3637- (BOOL)deferringGestureRecognizer:(WKDeferringGestureRecognizer *)deferringGestureRecognizer shouldDeferOtherGestureRecognizer:(UIGestureRecognizer *)gestureRecognizer;
    3738@end
  • trunk/Source/WebKit/UIProcess/ios/WKDeferringGestureRecognizer.mm

    r253005 r253267  
    5050{
    5151    [super touchesBegan:touches withEvent:event];
    52     if ([_deferringGestureDelegate deferringGestureRecognizer:self shouldDeferGesturesWithEvent:event])
    53         self.state = UIGestureRecognizerStatePossible;
    54     else
    55         self.state = UIGestureRecognizerStateFailed;
     52    if ([_deferringGestureDelegate deferringGestureRecognizer:self shouldDeferGesturesAfterBeginningTouchesWithEvent:event])
     53        return;
     54
     55    self.state = UIGestureRecognizerStateFailed;
     56}
     57
     58- (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
     59{
     60    [super touchesEnded:touches withEvent:event];
     61
     62    if (self.state != UIGestureRecognizerStatePossible)
     63        return;
     64
     65    if ([_deferringGestureDelegate deferringGestureRecognizer:self shouldDeferGesturesAfterEndingTouchesWithEvent:event])
     66        return;
     67
     68    self.state = UIGestureRecognizerStateFailed;
    5669}
    5770
  • trunk/Source/WebKit/UIProcess/ios/WKScrollView.mm

    r249585 r253267  
    3131#import "UIKitSPI.h"
    3232#import "VersionChecks.h"
     33#import "WKDeferringGestureRecognizer.h"
    3334#import "WKWebViewInternal.h"
    3435#import <pal/spi/cg/CoreGraphicsSPI.h>
     
    183184{
    184185    return _externalDelegate.getAutoreleased();
     186}
     187
     188- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRequireFailureOfGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
     189{
     190    if ([otherGestureRecognizer isKindOfClass:WKDeferringGestureRecognizer.class])
     191        return [(WKDeferringGestureRecognizer *)otherGestureRecognizer shouldDeferGestureRecognizer:gestureRecognizer];
     192
     193    return NO;
     194}
     195
     196- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldBeRequiredToFailByGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
     197{
     198    if ([gestureRecognizer isKindOfClass:WKDeferringGestureRecognizer.class])
     199        return [(WKDeferringGestureRecognizer *)gestureRecognizer shouldDeferGestureRecognizer:otherGestureRecognizer];
     200
     201    return NO;
    185202}
    186203
  • trunk/Source/WebKit/WebProcess/WebPage/EventDispatcher.cpp

    r250946 r253267  
    165165}
    166166
    167 void EventDispatcher::touchEvent(PageIdentifier pageID, const WebKit::WebTouchEvent& touchEvent)
     167void EventDispatcher::touchEvent(PageIdentifier pageID, const WebKit::WebTouchEvent& touchEvent, Optional<CallbackID> callbackID)
    168168{
    169169    bool updateListWasEmpty;
     
    173173        auto addResult = m_touchEvents.add(pageID, TouchEventQueue());
    174174        if (addResult.isNewEntry)
    175             addResult.iterator->value.append(touchEvent);
     175            addResult.iterator->value.append({ touchEvent, callbackID });
    176176        else {
    177             TouchEventQueue& queuedEvents = addResult.iterator->value;
     177            auto& queuedEvents = addResult.iterator->value;
    178178            ASSERT(!queuedEvents.isEmpty());
    179             const WebTouchEvent& lastTouchEvent = queuedEvents.last();
    180 
     179            auto& lastEventAndCallback = queuedEvents.last();
    181180            // Coalesce touch move events.
    182             if (touchEvent.type() == WebEvent::TouchMove && lastTouchEvent.type() == WebEvent::TouchMove)
    183                 queuedEvents.last() = touchEvent;
     181            if (touchEvent.type() == WebEvent::TouchMove && lastEventAndCallback.first.type() == WebEvent::TouchMove && !callbackID && !lastEventAndCallback.second)
     182                queuedEvents.last() = { touchEvent, WTF::nullopt };
    184183            else
    185                 queuedEvents.append(touchEvent);
     184                queuedEvents.append({ touchEvent, callbackID });
    186185        }
    187186    }
  • trunk/Source/WebKit/WebProcess/WebPage/EventDispatcher.h

    r245796 r253267  
    2727#define EventDispatcher_h
    2828
     29#include "CallbackID.h"
    2930#include "Connection.h"
    30 
    3131#include "WebEvent.h"
    3232#include <WebCore/PageIdentifier.h>
     
    4949namespace WebKit {
    5050
    51 class WebEvent;
    5251class WebPage;
    5352class WebWheelEvent;
     
    6463
    6564#if ENABLE(IOS_TOUCH_EVENTS)
    66     typedef Vector<WebTouchEvent, 1> TouchEventQueue;
     65    using TouchEventQueue = Vector<std::pair<WebTouchEvent, Optional<CallbackID>>, 1>;
    6766
    6867    void clearQueuedTouchEventsForPage(const WebPage&);
     
    8180    void wheelEvent(WebCore::PageIdentifier, const WebWheelEvent&, bool canRubberBandAtLeft, bool canRubberBandAtRight, bool canRubberBandAtTop, bool canRubberBandAtBottom);
    8281#if ENABLE(IOS_TOUCH_EVENTS)
    83     void touchEvent(WebCore::PageIdentifier, const WebTouchEvent&);
     82    void touchEvent(WebCore::PageIdentifier, const WebTouchEvent&, Optional<CallbackID>);
    8483#endif
    8584#if ENABLE(MAC_GESTURE_EVENTS)
  • trunk/Source/WebKit/WebProcess/WebPage/EventDispatcher.messages.in

    r245796 r253267  
    2424    WheelEvent(WebCore::PageIdentifier pageID, WebKit::WebWheelEvent event, bool canRubberBandAtLeft, bool canRubberBandAtRight, bool canRubberBandAtTop, bool canRubberBandAtBottom)
    2525#if ENABLE(IOS_TOUCH_EVENTS)
    26     TouchEvent(WebCore::PageIdentifier pageID, WebKit::WebTouchEvent event)
     26    TouchEvent(WebCore::PageIdentifier pageID, WebKit::WebTouchEvent event, Optional<WebKit::CallbackID> callbackID)
    2727#endif
    2828#if ENABLE(MAC_GESTURE_EVENTS)
  • trunk/Source/WebKit/WebProcess/WebPage/WebPage.h

    r253187 r253267  
    718718
    719719#if PLATFORM(IOS_FAMILY) && ENABLE(IOS_TOUCH_EVENTS)
    720     void dispatchAsynchronousTouchEvents(const Vector<WebTouchEvent, 1>& queue);
     720    void dispatchAsynchronousTouchEvents(const Vector<std::pair<WebTouchEvent, Optional<CallbackID>>, 1>& queue);
    721721#endif
    722722
  • trunk/Source/WebKit/WebProcess/WebPage/ios/WebPageIOS.mm

    r252936 r253267  
    37433743
    37443744#if ENABLE(IOS_TOUCH_EVENTS)
    3745 void WebPage::dispatchAsynchronousTouchEvents(const Vector<WebTouchEvent, 1>& queue)
    3746 {
    3747     bool ignored;
    3748     for (const WebTouchEvent& event : queue)
    3749         dispatchTouchEvent(event, ignored);
     3745void WebPage::dispatchAsynchronousTouchEvents(const Vector<std::pair<WebTouchEvent, Optional<CallbackID>>, 1>& queue)
     3746{
     3747    for (auto& eventAndCallbackID : queue) {
     3748        bool handled;
     3749        dispatchTouchEvent(eventAndCallbackID.first, handled);
     3750        if (eventAndCallbackID.second)
     3751            send(Messages::WebPageProxy::BoolCallback(handled, *eventAndCallbackID.second));
     3752    }
    37503753}
    37513754#endif
Note: See TracChangeset for help on using the changeset viewer.