Title: [287369] branches/safari-612-branch
Revision
287369
Author
kocsen_ch...@apple.com
Date
2021-12-22 11:44:09 -0800 (Wed, 22 Dec 2021)

Log Message

Cherry-pick r287366. rdar://problem/84379650

    [iOS] Scroll view pinch zoom gesture sometimes fails to recognize in WKWebView
    https://bugs.webkit.org/show_bug.cgi?id=234584
    rdar://84379650

    Reviewed by Simon Fraser.

    Source/WebKit:

    WKWebView may get into a state where the pinch zoom gesture recognizer on its scroll view sometimes fails to
    transition to Changed state (and invoke its action, which sets the zoomScale of the scroll view); this happens
    because the scroll view pinch gesture requires the touch start deferring gesture recognizer (TSDG) to fail, but
    it's possible for TSDG to remain stuck in Possible state over the duration of a gesture after beginning a touch
    over content with either passive touch event listeners, or no touch event listeners.

    In the case where the TSDG is stuck in Possible state, we observe the following sequence of events (let's
    suppose the user is starting a touch over a passive touch event listener on a web page):

    1.      The UITouch is delivered to the web touch event gesture recognizer, which fires the action
            (`-_webTouchEventsRecognized:`). We observe that we're over an async touch event handling region (i.e.
            passive or no touch event listeners), so we immediately "lift the gesture gate" by transitioning all
            deferring gesture recognizers to Failed state, (with the intent that they won't prevent native gestures
            from recognizing).

    2.      A UITouch is then delivered to the TSDG in the same runloop as UIKit continues to deliver the touch
            event to all gestures in the `NSSet` of gesture recognizers on the window. Receiving the UITouch causes
            TSDG (which we already set to Failed state in step (1)) to internally reset and transition back to
            Possible state, underneath WebKit.

    3.      TSDG is now in possible state after the gesture has begun, but we've already tried to unblock native
            gestures. When performing the second touch of the pinch zoom gesture, the pinch zoom gesture fails to
            commence because it's stuck waiting for TSDG to fail.

    In the normal (working) scenario, step (2) happens before step (1); this ensures that TSDG is set to Failed
    state and remains in Failed state over the course of the gesture, thereby preventing the bug from happening. The
    order in which (1) and (2) happen is dependent on the order in which the web touch event gesture and TSDG are
    iterated in UIKit's `NSSet` of gestures, which explains why this bug only reproduces some of the time.

    This patch mitigates this by adding a mechanism to keep track of touch start deferrers that we've already
    transitioned to Failed state during the course of a gesture, and uses this information to avoid adding failure
    requirements to these deferring gestures that have already been "ungated". This ensures that even if the
    deferring gesture resets to Possible state from underneath WebKit, we still avoid pushing out native gesture
    recognition due to these deferring gestures.

    As an aside, I initially attempted to avoid having TSDG transition back to Possible state altogether in this
    scenario, but this doesn't seem to be avoidable (short of overriding the SPI method `-_resetGestureRecognizer`
    and not calling the superclass, which does not seem to be supported behavior).

    Test: fast/events/touch/ios/pinch-zoom-with-passive-touch-event-listeners.html

    * UIProcess/ios/WKContentViewInteraction.h:
    * UIProcess/ios/WKContentViewInteraction.mm:
    (-[WKContentView _webTouchEventsRecognized:]):
    (-[WKContentView _doneDeferringTouchStart:]):
    (-[WKContentView deferringGestureRecognizer:shouldDeferOtherGestureRecognizer:]):

    LayoutTests:

    Add a test to exercise the fix. Note that this test will only fail without the fix *some* of the time, since it
    depends entirely on the order in which two gestures appear in an Objective-C hash datastructure (see WebKit
    ChangeLog for more details).

    * fast/events/touch/ios/pinch-zoom-with-passive-touch-event-listeners-expected.txt: Added.
    * fast/events/touch/ios/pinch-zoom-with-passive-touch-event-listeners.html: Added.

    git-svn-id: https://svn.webkit.org/repository/webkit/trunk@287366 268f45cc-cd09-0410-ab3c-d52691b4dbfc

Modified Paths

Added Paths

Diff

Modified: branches/safari-612-branch/LayoutTests/ChangeLog (287368 => 287369)


--- branches/safari-612-branch/LayoutTests/ChangeLog	2021-12-22 19:39:36 UTC (rev 287368)
+++ branches/safari-612-branch/LayoutTests/ChangeLog	2021-12-22 19:44:09 UTC (rev 287369)
@@ -1,3 +1,89 @@
+2021-12-22  Alan Coon  <alanc...@apple.com>
+
+        Cherry-pick r287366. rdar://problem/84379650
+
+    [iOS] Scroll view pinch zoom gesture sometimes fails to recognize in WKWebView
+    https://bugs.webkit.org/show_bug.cgi?id=234584
+    rdar://84379650
+    
+    Reviewed by Simon Fraser.
+    
+    Source/WebKit:
+    
+    WKWebView may get into a state where the pinch zoom gesture recognizer on its scroll view sometimes fails to
+    transition to Changed state (and invoke its action, which sets the zoomScale of the scroll view); this happens
+    because the scroll view pinch gesture requires the touch start deferring gesture recognizer (TSDG) to fail, but
+    it's possible for TSDG to remain stuck in Possible state over the duration of a gesture after beginning a touch
+    over content with either passive touch event listeners, or no touch event listeners.
+    
+    In the case where the TSDG is stuck in Possible state, we observe the following sequence of events (let's
+    suppose the user is starting a touch over a passive touch event listener on a web page):
+    
+    1.      The UITouch is delivered to the web touch event gesture recognizer, which fires the action
+            (`-_webTouchEventsRecognized:`). We observe that we're over an async touch event handling region (i.e.
+            passive or no touch event listeners), so we immediately "lift the gesture gate" by transitioning all
+            deferring gesture recognizers to Failed state, (with the intent that they won't prevent native gestures
+            from recognizing).
+    
+    2.      A UITouch is then delivered to the TSDG in the same runloop as UIKit continues to deliver the touch
+            event to all gestures in the `NSSet` of gesture recognizers on the window. Receiving the UITouch causes
+            TSDG (which we already set to Failed state in step (1)) to internally reset and transition back to
+            Possible state, underneath WebKit.
+    
+    3.      TSDG is now in possible state after the gesture has begun, but we've already tried to unblock native
+            gestures. When performing the second touch of the pinch zoom gesture, the pinch zoom gesture fails to
+            commence because it's stuck waiting for TSDG to fail.
+    
+    In the normal (working) scenario, step (2) happens before step (1); this ensures that TSDG is set to Failed
+    state and remains in Failed state over the course of the gesture, thereby preventing the bug from happening. The
+    order in which (1) and (2) happen is dependent on the order in which the web touch event gesture and TSDG are
+    iterated in UIKit's `NSSet` of gestures, which explains why this bug only reproduces some of the time.
+    
+    This patch mitigates this by adding a mechanism to keep track of touch start deferrers that we've already
+    transitioned to Failed state during the course of a gesture, and uses this information to avoid adding failure
+    requirements to these deferring gestures that have already been "ungated". This ensures that even if the
+    deferring gesture resets to Possible state from underneath WebKit, we still avoid pushing out native gesture
+    recognition due to these deferring gestures.
+    
+    As an aside, I initially attempted to avoid having TSDG transition back to Possible state altogether in this
+    scenario, but this doesn't seem to be avoidable (short of overriding the SPI method `-_resetGestureRecognizer`
+    and not calling the superclass, which does not seem to be supported behavior).
+    
+    Test: fast/events/touch/ios/pinch-zoom-with-passive-touch-event-listeners.html
+    
+    * UIProcess/ios/WKContentViewInteraction.h:
+    * UIProcess/ios/WKContentViewInteraction.mm:
+    (-[WKContentView _webTouchEventsRecognized:]):
+    (-[WKContentView _doneDeferringTouchStart:]):
+    (-[WKContentView deferringGestureRecognizer:shouldDeferOtherGestureRecognizer:]):
+    
+    LayoutTests:
+    
+    Add a test to exercise the fix. Note that this test will only fail without the fix *some* of the time, since it
+    depends entirely on the order in which two gestures appear in an Objective-C hash datastructure (see WebKit
+    ChangeLog for more details).
+    
+    * fast/events/touch/ios/pinch-zoom-with-passive-touch-event-listeners-expected.txt: Added.
+    * fast/events/touch/ios/pinch-zoom-with-passive-touch-event-listeners.html: Added.
+    
+    
+    git-svn-id: https://svn.webkit.org/repository/webkit/trunk@287366 268f45cc-cd09-0410-ab3c-d52691b4dbfc
+
+    2021-12-22  Wenson Hsieh  <wenson_hs...@apple.com>
+
+            [iOS] Scroll view pinch zoom gesture sometimes fails to recognize in WKWebView
+            https://bugs.webkit.org/show_bug.cgi?id=234584
+            rdar://84379650
+
+            Reviewed by Simon Fraser.
+
+            Add a test to exercise the fix. Note that this test will only fail without the fix *some* of the time, since it
+            depends entirely on the order in which two gestures appear in an Objective-C hash datastructure (see WebKit
+            ChangeLog for more details).
+
+            * fast/events/touch/ios/pinch-zoom-with-passive-touch-event-listeners-expected.txt: Added.
+            * fast/events/touch/ios/pinch-zoom-with-passive-touch-event-listeners.html: Added.
+
 2021-12-21  Alan Coon  <alanc...@apple.com>
 
         Cherry-pick r287067. rdar://problem/86276497

Added: branches/safari-612-branch/LayoutTests/fast/events/touch/ios/pinch-zoom-with-passive-touch-event-listeners-expected.txt (0 => 287369)


--- branches/safari-612-branch/LayoutTests/fast/events/touch/ios/pinch-zoom-with-passive-touch-event-listeners-expected.txt	                        (rev 0)
+++ branches/safari-612-branch/LayoutTests/fast/events/touch/ios/pinch-zoom-with-passive-touch-event-listeners-expected.txt	2021-12-22 19:44:09 UTC (rev 287369)
@@ -0,0 +1,9 @@
+Verifies that the pinch zoom gesture is recognized in passive touch event regions. To manually run the test, pinch over the red square to zoom in.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+PASS Successfully zoomed in by pinching
+PASS successfullyParsed is true
+
+TEST COMPLETE
+

Added: branches/safari-612-branch/LayoutTests/fast/events/touch/ios/pinch-zoom-with-passive-touch-event-listeners.html (0 => 287369)


--- branches/safari-612-branch/LayoutTests/fast/events/touch/ios/pinch-zoom-with-passive-touch-event-listeners.html	                        (rev 0)
+++ branches/safari-612-branch/LayoutTests/fast/events/touch/ios/pinch-zoom-with-passive-touch-event-listeners.html	2021-12-22 19:44:09 UTC (rev 287369)
@@ -0,0 +1,141 @@
+<!DOCTYPE html> <!-- webkit-test-runner [ useFlexibleViewport=true ] -->
+<html>
+<meta name="viewport" content="width=device-width, initial-scale=1">
+<head>
+<script src=""
+<script src=""
+<style>
+html, body {
+    width: 100%;
+    height: 100%;
+    margin: 0;
+}
+
+#box {
+    top: 100px;
+    left: 10px;
+    width: 300px;
+    height: 300px;
+    background-color: tomato;
+    position: absolute;
+}
+</style>
+</head>
+<body>
+<div id="box"></div>
+<pre id="description"></pre>
+<pre id="console"></pre>
+</body>
+<script>
+jsTestIsAsync = true;
+description("Verifies that the pinch zoom gesture is recognized in passive touch event regions. To manually run the test, pinch over the red square to zoom in.");
+document.getElementById("box").addEventListener("touchstart", () => { }, { passive: true });
+
+async function pinchZoom() {
+    await UIHelper.sendEventStream({
+        events: [
+            {
+                interpolate : "linear",
+                timestep : 0.01,
+                coordinateSpace : "content",
+                startEvent : {
+                    inputType : "hand",
+                    timeOffset : 0,
+                    touches : [{ inputType : "finger", phase : "began", id : 1, x : 140, y : 200, pressure : 0 }]
+                },
+                endEvent : {
+                    inputType : "hand",
+                    timeOffset : 0.01,
+                    touches : [{ inputType : "finger", phase : "began", id : 1, x : 140, y : 200, pressure : 0 }]
+                }
+            },
+            {
+                interpolate : "linear",
+                timestep : 0.01,
+                coordinateSpace : "content",
+                startEvent : {
+                    inputType : "hand",
+                    timeOffset : 0.01,
+                    touches : [{ inputType : "finger", phase : "moved", id : 1, x : 140, y : 200, pressure : 0 }]
+                },
+                endEvent : {
+                    inputType : "hand",
+                    timeOffset : 0.49,
+                    touches : [{ inputType : "finger", phase : "moved", id : 1, x : 140, y : 100, pressure : 0 }]
+                }
+            },
+            {
+                interpolate : "linear",
+                timestep : 0.01,
+                coordinateSpace : "content",
+                startEvent : {
+                    inputType : "hand",
+                    timeOffset : 0.49,
+                    touches : [
+                        { inputType : "finger", phase : "stationary", id : 1, x : 140, y : 100, pressure : 0 },
+                        { inputType : "finger", phase : "began", id : 2, x : 140, y : 200, pressure : 0 }
+                    ]
+                },
+                endEvent : {
+                    inputType : "hand",
+                    timeOffset : 0.5,
+                    touches : [
+                        { inputType : "finger", phase : "stationary", id : 1, x : 140, y : 100, pressure : 0 },
+                        { inputType : "finger", phase : "began", id : 2, x : 140, y : 200, pressure : 0 }
+                    ]
+                }
+            },
+            {
+                interpolate : "linear",
+                timestep : 0.01,
+                coordinateSpace : "content",
+                startEvent : {
+                    inputType : "hand",
+                    timeOffset : 0.5,
+                    touches : [
+                        { inputType : "finger", phase : "moved", id : 1, x : 140, y : 100, pressure : 0 },
+                        { inputType : "finger", phase : "moved", id : 2, x : 140, y : 200, pressure : 0 }
+                    ]
+                },
+                endEvent : {
+                    inputType : "hand",
+                    timeOffset : 0.99,
+                    touches : [
+                        { inputType : "finger", phase : "moved", id : 1, x : 140, y : 20, pressure : 0 },
+                        { inputType : "finger", phase : "moved", id : 2, x : 140, y : 300, pressure : 0 }
+                    ]
+                }
+            },
+            {
+                interpolate : "linear",
+                timestep : 0.01,
+                coordinateSpace : "content",
+                startEvent : {
+                    inputType : "hand",
+                    timeOffset : 0.99,
+                    touches : [
+                        { inputType : "finger", phase : "ended", id : 1, x : 140, y : 20, pressure : 0 },
+                        { inputType : "finger", phase : "ended", id : 2, x : 140, y : 300, pressure : 0 }
+                    ]
+                },
+                endEvent : {
+                    inputType : "hand",
+                    timeOffset : 1,
+                    touches : [
+                        { inputType : "finger", phase : "ended", id : 1, x : 140, y : 20, pressure : 0 },
+                        { inputType : "finger", phase : "ended", id : 2, x : 140, y : 300, pressure : 0 }
+                    ]
+                }
+            }
+        ]
+    });
+}
+
+addEventListener("load", async () => {
+    while (visualViewport.scale <= 1)
+        await pinchZoom();
+    testPassed("Successfully zoomed in by pinching");
+    finishJSTest();
+});
+</script>
+</html>
\ No newline at end of file

Modified: branches/safari-612-branch/Source/WebKit/ChangeLog (287368 => 287369)


--- branches/safari-612-branch/Source/WebKit/ChangeLog	2021-12-22 19:39:36 UTC (rev 287368)
+++ branches/safari-612-branch/Source/WebKit/ChangeLog	2021-12-22 19:44:09 UTC (rev 287369)
@@ -1,3 +1,129 @@
+2021-12-22  Alan Coon  <alanc...@apple.com>
+
+        Cherry-pick r287366. rdar://problem/84379650
+
+    [iOS] Scroll view pinch zoom gesture sometimes fails to recognize in WKWebView
+    https://bugs.webkit.org/show_bug.cgi?id=234584
+    rdar://84379650
+    
+    Reviewed by Simon Fraser.
+    
+    Source/WebKit:
+    
+    WKWebView may get into a state where the pinch zoom gesture recognizer on its scroll view sometimes fails to
+    transition to Changed state (and invoke its action, which sets the zoomScale of the scroll view); this happens
+    because the scroll view pinch gesture requires the touch start deferring gesture recognizer (TSDG) to fail, but
+    it's possible for TSDG to remain stuck in Possible state over the duration of a gesture after beginning a touch
+    over content with either passive touch event listeners, or no touch event listeners.
+    
+    In the case where the TSDG is stuck in Possible state, we observe the following sequence of events (let's
+    suppose the user is starting a touch over a passive touch event listener on a web page):
+    
+    1.      The UITouch is delivered to the web touch event gesture recognizer, which fires the action
+            (`-_webTouchEventsRecognized:`). We observe that we're over an async touch event handling region (i.e.
+            passive or no touch event listeners), so we immediately "lift the gesture gate" by transitioning all
+            deferring gesture recognizers to Failed state, (with the intent that they won't prevent native gestures
+            from recognizing).
+    
+    2.      A UITouch is then delivered to the TSDG in the same runloop as UIKit continues to deliver the touch
+            event to all gestures in the `NSSet` of gesture recognizers on the window. Receiving the UITouch causes
+            TSDG (which we already set to Failed state in step (1)) to internally reset and transition back to
+            Possible state, underneath WebKit.
+    
+    3.      TSDG is now in possible state after the gesture has begun, but we've already tried to unblock native
+            gestures. When performing the second touch of the pinch zoom gesture, the pinch zoom gesture fails to
+            commence because it's stuck waiting for TSDG to fail.
+    
+    In the normal (working) scenario, step (2) happens before step (1); this ensures that TSDG is set to Failed
+    state and remains in Failed state over the course of the gesture, thereby preventing the bug from happening. The
+    order in which (1) and (2) happen is dependent on the order in which the web touch event gesture and TSDG are
+    iterated in UIKit's `NSSet` of gestures, which explains why this bug only reproduces some of the time.
+    
+    This patch mitigates this by adding a mechanism to keep track of touch start deferrers that we've already
+    transitioned to Failed state during the course of a gesture, and uses this information to avoid adding failure
+    requirements to these deferring gestures that have already been "ungated". This ensures that even if the
+    deferring gesture resets to Possible state from underneath WebKit, we still avoid pushing out native gesture
+    recognition due to these deferring gestures.
+    
+    As an aside, I initially attempted to avoid having TSDG transition back to Possible state altogether in this
+    scenario, but this doesn't seem to be avoidable (short of overriding the SPI method `-_resetGestureRecognizer`
+    and not calling the superclass, which does not seem to be supported behavior).
+    
+    Test: fast/events/touch/ios/pinch-zoom-with-passive-touch-event-listeners.html
+    
+    * UIProcess/ios/WKContentViewInteraction.h:
+    * UIProcess/ios/WKContentViewInteraction.mm:
+    (-[WKContentView _webTouchEventsRecognized:]):
+    (-[WKContentView _doneDeferringTouchStart:]):
+    (-[WKContentView deferringGestureRecognizer:shouldDeferOtherGestureRecognizer:]):
+    
+    LayoutTests:
+    
+    Add a test to exercise the fix. Note that this test will only fail without the fix *some* of the time, since it
+    depends entirely on the order in which two gestures appear in an Objective-C hash datastructure (see WebKit
+    ChangeLog for more details).
+    
+    * fast/events/touch/ios/pinch-zoom-with-passive-touch-event-listeners-expected.txt: Added.
+    * fast/events/touch/ios/pinch-zoom-with-passive-touch-event-listeners.html: Added.
+    
+    
+    git-svn-id: https://svn.webkit.org/repository/webkit/trunk@287366 268f45cc-cd09-0410-ab3c-d52691b4dbfc
+
+    2021-12-22  Wenson Hsieh  <wenson_hs...@apple.com>
+
+            [iOS] Scroll view pinch zoom gesture sometimes fails to recognize in WKWebView
+            https://bugs.webkit.org/show_bug.cgi?id=234584
+            rdar://84379650
+
+            Reviewed by Simon Fraser.
+
+            WKWebView may get into a state where the pinch zoom gesture recognizer on its scroll view sometimes fails to
+            transition to Changed state (and invoke its action, which sets the zoomScale of the scroll view); this happens
+            because the scroll view pinch gesture requires the touch start deferring gesture recognizer (TSDG) to fail, but
+            it's possible for TSDG to remain stuck in Possible state over the duration of a gesture after beginning a touch
+            over content with either passive touch event listeners, or no touch event listeners.
+
+            In the case where the TSDG is stuck in Possible state, we observe the following sequence of events (let's
+            suppose the user is starting a touch over a passive touch event listener on a web page):
+
+            1.      The UITouch is delivered to the web touch event gesture recognizer, which fires the action
+                    (`-_webTouchEventsRecognized:`). We observe that we're over an async touch event handling region (i.e.
+                    passive or no touch event listeners), so we immediately "lift the gesture gate" by transitioning all
+                    deferring gesture recognizers to Failed state, (with the intent that they won't prevent native gestures
+                    from recognizing).
+
+            2.      A UITouch is then delivered to the TSDG in the same runloop as UIKit continues to deliver the touch
+                    event to all gestures in the `NSSet` of gesture recognizers on the window. Receiving the UITouch causes
+                    TSDG (which we already set to Failed state in step (1)) to internally reset and transition back to
+                    Possible state, underneath WebKit.
+
+            3.      TSDG is now in possible state after the gesture has begun, but we've already tried to unblock native
+                    gestures. When performing the second touch of the pinch zoom gesture, the pinch zoom gesture fails to
+                    commence because it's stuck waiting for TSDG to fail.
+
+            In the normal (working) scenario, step (2) happens before step (1); this ensures that TSDG is set to Failed
+            state and remains in Failed state over the course of the gesture, thereby preventing the bug from happening. The
+            order in which (1) and (2) happen is dependent on the order in which the web touch event gesture and TSDG are
+            iterated in UIKit's `NSSet` of gestures, which explains why this bug only reproduces some of the time.
+
+            This patch mitigates this by adding a mechanism to keep track of touch start deferrers that we've already
+            transitioned to Failed state during the course of a gesture, and uses this information to avoid adding failure
+            requirements to these deferring gestures that have already been "ungated". This ensures that even if the
+            deferring gesture resets to Possible state from underneath WebKit, we still avoid pushing out native gesture
+            recognition due to these deferring gestures.
+
+            As an aside, I initially attempted to avoid having TSDG transition back to Possible state altogether in this
+            scenario, but this doesn't seem to be avoidable (short of overriding the SPI method `-_resetGestureRecognizer`
+            and not calling the superclass, which does not seem to be supported behavior).
+
+            Test: fast/events/touch/ios/pinch-zoom-with-passive-touch-event-listeners.html
+
+            * UIProcess/ios/WKContentViewInteraction.h:
+            * UIProcess/ios/WKContentViewInteraction.mm:
+            (-[WKContentView _webTouchEventsRecognized:]):
+            (-[WKContentView _doneDeferringTouchStart:]):
+            (-[WKContentView deferringGestureRecognizer:shouldDeferOtherGestureRecognizer:]):
+
 2021-12-17  Russell Epstein  <repst...@apple.com>
 
         Apply patch. rdar://problem/86635432

Modified: branches/safari-612-branch/Source/WebKit/UIProcess/ios/WKContentViewInteraction.h (287368 => 287369)


--- branches/safari-612-branch/Source/WebKit/UIProcess/ios/WKContentViewInteraction.h	2021-12-22 19:39:36 UTC (rev 287368)
+++ branches/safari-612-branch/Source/WebKit/UIProcess/ios/WKContentViewInteraction.h	2021-12-22 19:44:09 UTC (rev 287369)
@@ -64,6 +64,7 @@
 #import <wtf/CompletionHandler.h>
 #import <wtf/Forward.h>
 #import <wtf/Function.h>
+#import <wtf/HashSet.h>
 #import <wtf/ObjectIdentifier.h>
 #import <wtf/OptionSet.h>
 #import <wtf/Vector.h>
@@ -259,6 +260,7 @@
     RetainPtr<WKDeferringGestureRecognizer> _touchEndDeferringGestureRecognizerForImmediatelyResettableGestures;
     RetainPtr<WKDeferringGestureRecognizer> _touchEndDeferringGestureRecognizerForDelayedResettableGestures;
     RetainPtr<WKDeferringGestureRecognizer> _touchEndDeferringGestureRecognizerForSyntheticTapGestures;
+    std::optional<HashSet<RetainPtr<WKDeferringGestureRecognizer>>> _failedTouchStartDeferringGestures;
 #if ENABLE(IMAGE_ANALYSIS)
     RetainPtr<WKDeferringGestureRecognizer> _imageAnalysisDeferringGestureRecognizer;
 #endif

Modified: branches/safari-612-branch/Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm (287368 => 287369)


--- branches/safari-612-branch/Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm	2021-12-22 19:39:36 UTC (rev 287368)
+++ branches/safari-612-branch/Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm	2021-12-22 19:44:09 UTC (rev 287369)
@@ -1699,6 +1699,9 @@
 
     _lastInteractionLocation = lastTouchEvent->locationInDocumentCoordinates;
     if (lastTouchEvent->type == UIWebTouchEventTouchBegin) {
+        if (!_failedTouchStartDeferringGestures)
+            _failedTouchStartDeferringGestures = { { } };
+
         [self _handleDOMPasteRequestWithResult:WebCore::DOMPasteAccessResponse::DeniedForGesture];
         _layerTreeTransactionIdAtLastInteractionStart = downcast<WebKit::RemoteLayerTreeDrawingAreaProxy>(*_page->drawingArea()).lastCommittedLayerTreeTransactionID();
 
@@ -1744,6 +1747,8 @@
 
         if (!_page->isHandlingPreventableTouchEnd())
             stopDeferringNativeGesturesIfNeeded(self._touchEndDeferringGestures);
+
+        _failedTouchStartDeferringGestures = std::nullopt;
     }
 #endif // ENABLE(TOUCH_EVENTS)
 }
@@ -1989,8 +1994,11 @@
 
 - (void)_doneDeferringTouchStart:(BOOL)preventNativeGestures
 {
-    for (WKDeferringGestureRecognizer *gesture in self._touchStartDeferringGestures)
-        [gesture endDeferral:preventNativeGestures ? WebKit::ShouldPreventGestures::Yes : WebKit::ShouldPreventGestures::No];
+    for (WKDeferringGestureRecognizer *gestureRecognizer in self._touchStartDeferringGestures) {
+        [gestureRecognizer endDeferral:preventNativeGestures ? WebKit::ShouldPreventGestures::Yes : WebKit::ShouldPreventGestures::No];
+        if (_failedTouchStartDeferringGestures && !preventNativeGestures)
+            _failedTouchStartDeferringGestures->add(gestureRecognizer);
+    }
 }
 
 - (void)_doneDeferringTouchEnd:(BOOL)preventNativeGestures
@@ -8041,6 +8049,15 @@
     if ([self _touchEventsMustRequireGestureRecognizerToFail:gestureRecognizer])
         return NO;
 
+    if (_failedTouchStartDeferringGestures && _failedTouchStartDeferringGestures->contains(deferringGestureRecognizer)
+        && deferringGestureRecognizer.state == UIGestureRecognizerStatePossible) {
+        // This deferring gesture no longer has an oppportunity to defer native gestures (either because the touch region did not have any
+        // active touch event listeners, or because any active touch event listeners on the page have already executed, and did not prevent
+        // default). UIKit may have already reset the gesture to Possible state underneath us, in which case we still need to treat it as
+        // if it has already failed; otherwise, we will incorrectly defer other gestures in the web view, such as scroll view pinching.
+        return NO;
+    }
+
     auto webView = _webView.getAutoreleased();
     auto view = gestureRecognizer.view;
     BOOL gestureIsInstalledOnOrUnderWebView = NO;
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to