Title: [273305] trunk/Source/WebKit
Revision
273305
Author
wenson_hs...@apple.com
Date
2021-02-23 06:34:38 -0800 (Tue, 23 Feb 2021)

Log Message

[iOS] Add a watchdog timer to forcibly reset deferring gesture recognizers
https://bugs.webkit.org/show_bug.cgi?id=222216

Reviewed by Simon Fraser.

Add a mechanism to detect and resolve situations where at least one deferring gesture recognizer
(`WKDeferringGestureRecognizer`) is stuck in Possible state after ending touches, but the touch event gesture
recognizer is unable to be reset (i.e. neither possible or recognizing). This state is indicative of a general
class of bugs regarding native gesture deferral, wherein our "gesture gates" never end up being lifted after
ending all touches in the view. One such example of this type of bug was fixed in r272584, where all interaction
(touch events, scrolling, tapping) due to the touch end deferrers never exiting Possible state after long
pressing to present a context menu.

To make these kinds of bugs both easier to diagnose and less fatal in the future, we introduce a watchdog timer
that dispatches after ending all touches on the content view, and checks the consistency of deferring gesture
recognizers in the view; if we detect stuck deferring gestures, we handle this by forcibly resetting the stuck
deferring gestures and logging a fault message.

I manually verified this change by reverting my fix for the aforementioned bug (r272584), and observing both (1)
a system log fault message, and (2) that interactions continued to work even without the fix in r272584, after
the watchdog timer fired. See below for more details.

* SourcesCocoa.txt:
* UIProcess/ios/GestureRecognizerConsistencyEnforcer.h: Copied from Source/WebKit/UIProcess/ios/WKDeferringGestureRecognizer.h.
* UIProcess/ios/GestureRecognizerConsistencyEnforcer.mm: Added.
(WebKit::GestureRecognizerConsistencyEnforcer::GestureRecognizerConsistencyEnforcer):
(WebKit::GestureRecognizerConsistencyEnforcer::beginTracking):
(WebKit::GestureRecognizerConsistencyEnforcer::endTracking):
(WebKit::GestureRecognizerConsistencyEnforcer::reset):
(WebKit::GestureRecognizerConsistencyEnforcer::timerFired):

Add a helper class that wraps a `RunLoop::Timer` for the main runloop, and schedules a 1 second timer after all
deferring gestures have ended; assuming no new touches begin after this timer is scheduled, we'll perform a
consistency check over the content view's gestures and perform a log fault and force deferring gestures to end
if needed.

* UIProcess/ios/WKContentViewInteraction.h:
* UIProcess/ios/WKContentViewInteraction.mm:
(-[WKContentView setUpInteraction]):
(-[WKContentView cleanUpInteraction]):

Make sure that we reset any extant `GestureRecognizerConsistencyEnforcer` if the web process crashes.

(-[WKContentView _removeDefaultGestureRecognizers]):
(-[WKContentView _addDefaultGestureRecognizers]):
(-[WKContentView _webTouchEventsRecognized:]):

Adjust a few compile-time guards so that deferring gesture recognizers exist on the view, even if
`ENABLE_IOS_TOUCH_EVENTS` is off. This makes some of the gesture deferral code throughout this file easier to
reason about in non-internal iOS builds, since we don't need to sprinkle as many `ENABLE(IOS_TOUCH_EVENTS)`
checks throughout this class.

(-[WKContentView touchEventGestureRecognizer]):
(-[WKContentView gestureRecognizerConsistencyEnforcer]):
(-[WKContentView deferringGestures]):
(-[WKContentView gestureRecognizer:shouldRecognizeSimultaneouslyWithGestureRecognizer:]):
(-[WKContentView deferringGestureRecognizer:willBeginTouchesWithEvent:]):

Refactor `-deferringGestureRecognizer:shouldDeferGesturesAfterBeginningTouchesWithEvent:` so that it returns an
enum flag indicating whether the given gesture should be allowed to begin deferring native gestures; by making
this `-deferringGestureRecognizer:willBeginTouchesWithEvent:`, we can now put state management logic here that's
responsible for updating the `GestureRecognizerConsistencyEnforcer`.

(-[WKContentView deferringGestureRecognizer:didTransitionToState:]):
(-[WKContentView deferringGestureRecognizer:didEndTouchesWithEvent:]):
(-[WKContentView _deferringGestures]): Deleted.

Rename this to `-deferringGestures` and expose it as a readonly property, for use by the
`GestureRecognizerConsistencyEnforcer`.

(-[WKContentView deferringGestureRecognizer:shouldDeferGesturesAfterBeginningTouchesWithEvent:]): Deleted.
* UIProcess/ios/WKDeferringGestureRecognizer.h:
* UIProcess/ios/WKDeferringGestureRecognizer.mm:
(-[WKDeferringGestureRecognizer touchesBegan:withEvent:]):
(-[WKDeferringGestureRecognizer setState:]):

Add a delegate hook to inform `WKContentView` when the gesture recognizer state changes.

(-[WKDeferringGestureRecognizer deferringGestureDelegate]): Deleted.
* UIProcess/mac/WKPrintingView.h:

Fix a missing include that results in a build failure due to changed unified sources.

* WebKit.xcodeproj/project.pbxproj:

Modified Paths

Added Paths

Diff

Modified: trunk/Source/WebKit/ChangeLog (273304 => 273305)


--- trunk/Source/WebKit/ChangeLog	2021-02-23 13:58:35 UTC (rev 273304)
+++ trunk/Source/WebKit/ChangeLog	2021-02-23 14:34:38 UTC (rev 273305)
@@ -1,3 +1,90 @@
+2021-02-23  Wenson Hsieh  <wenson_hs...@apple.com>
+
+        [iOS] Add a watchdog timer to forcibly reset deferring gesture recognizers
+        https://bugs.webkit.org/show_bug.cgi?id=222216
+
+        Reviewed by Simon Fraser.
+
+        Add a mechanism to detect and resolve situations where at least one deferring gesture recognizer
+        (`WKDeferringGestureRecognizer`) is stuck in Possible state after ending touches, but the touch event gesture
+        recognizer is unable to be reset (i.e. neither possible or recognizing). This state is indicative of a general
+        class of bugs regarding native gesture deferral, wherein our "gesture gates" never end up being lifted after
+        ending all touches in the view. One such example of this type of bug was fixed in r272584, where all interaction
+        (touch events, scrolling, tapping) due to the touch end deferrers never exiting Possible state after long
+        pressing to present a context menu.
+
+        To make these kinds of bugs both easier to diagnose and less fatal in the future, we introduce a watchdog timer
+        that dispatches after ending all touches on the content view, and checks the consistency of deferring gesture
+        recognizers in the view; if we detect stuck deferring gestures, we handle this by forcibly resetting the stuck
+        deferring gestures and logging a fault message.
+
+        I manually verified this change by reverting my fix for the aforementioned bug (r272584), and observing both (1)
+        a system log fault message, and (2) that interactions continued to work even without the fix in r272584, after
+        the watchdog timer fired. See below for more details.
+
+        * SourcesCocoa.txt:
+        * UIProcess/ios/GestureRecognizerConsistencyEnforcer.h: Copied from Source/WebKit/UIProcess/ios/WKDeferringGestureRecognizer.h.
+        * UIProcess/ios/GestureRecognizerConsistencyEnforcer.mm: Added.
+        (WebKit::GestureRecognizerConsistencyEnforcer::GestureRecognizerConsistencyEnforcer):
+        (WebKit::GestureRecognizerConsistencyEnforcer::beginTracking):
+        (WebKit::GestureRecognizerConsistencyEnforcer::endTracking):
+        (WebKit::GestureRecognizerConsistencyEnforcer::reset):
+        (WebKit::GestureRecognizerConsistencyEnforcer::timerFired):
+
+        Add a helper class that wraps a `RunLoop::Timer` for the main runloop, and schedules a 1 second timer after all
+        deferring gestures have ended; assuming no new touches begin after this timer is scheduled, we'll perform a
+        consistency check over the content view's gestures and perform a log fault and force deferring gestures to end
+        if needed.
+
+        * UIProcess/ios/WKContentViewInteraction.h:
+        * UIProcess/ios/WKContentViewInteraction.mm:
+        (-[WKContentView setUpInteraction]):
+        (-[WKContentView cleanUpInteraction]):
+
+        Make sure that we reset any extant `GestureRecognizerConsistencyEnforcer` if the web process crashes.
+
+        (-[WKContentView _removeDefaultGestureRecognizers]):
+        (-[WKContentView _addDefaultGestureRecognizers]):
+        (-[WKContentView _webTouchEventsRecognized:]):
+
+        Adjust a few compile-time guards so that deferring gesture recognizers exist on the view, even if
+        `ENABLE_IOS_TOUCH_EVENTS` is off. This makes some of the gesture deferral code throughout this file easier to
+        reason about in non-internal iOS builds, since we don't need to sprinkle as many `ENABLE(IOS_TOUCH_EVENTS)`
+        checks throughout this class.
+
+        (-[WKContentView touchEventGestureRecognizer]):
+        (-[WKContentView gestureRecognizerConsistencyEnforcer]):
+        (-[WKContentView deferringGestures]):
+        (-[WKContentView gestureRecognizer:shouldRecognizeSimultaneouslyWithGestureRecognizer:]):
+        (-[WKContentView deferringGestureRecognizer:willBeginTouchesWithEvent:]):
+
+        Refactor `-deferringGestureRecognizer:shouldDeferGesturesAfterBeginningTouchesWithEvent:` so that it returns an
+        enum flag indicating whether the given gesture should be allowed to begin deferring native gestures; by making
+        this `-deferringGestureRecognizer:willBeginTouchesWithEvent:`, we can now put state management logic here that's
+        responsible for updating the `GestureRecognizerConsistencyEnforcer`.
+
+        (-[WKContentView deferringGestureRecognizer:didTransitionToState:]):
+        (-[WKContentView deferringGestureRecognizer:didEndTouchesWithEvent:]):
+        (-[WKContentView _deferringGestures]): Deleted.
+
+        Rename this to `-deferringGestures` and expose it as a readonly property, for use by the
+        `GestureRecognizerConsistencyEnforcer`.
+
+        (-[WKContentView deferringGestureRecognizer:shouldDeferGesturesAfterBeginningTouchesWithEvent:]): Deleted.
+        * UIProcess/ios/WKDeferringGestureRecognizer.h:
+        * UIProcess/ios/WKDeferringGestureRecognizer.mm:
+        (-[WKDeferringGestureRecognizer touchesBegan:withEvent:]):
+        (-[WKDeferringGestureRecognizer setState:]):
+
+        Add a delegate hook to inform `WKContentView` when the gesture recognizer state changes.
+
+        (-[WKDeferringGestureRecognizer deferringGestureDelegate]): Deleted.
+        * UIProcess/mac/WKPrintingView.h:
+
+        Fix a missing include that results in a build failure due to changed unified sources.
+
+        * WebKit.xcodeproj/project.pbxproj:
+
 2021-02-23  Per Arne Vollan  <pvol...@apple.com>
 
         [macOS] Crash under AuxiliaryProcess::initializeSandbox

Modified: trunk/Source/WebKit/SourcesCocoa.txt (273304 => 273305)


--- trunk/Source/WebKit/SourcesCocoa.txt	2021-02-23 13:58:35 UTC (rev 273304)
+++ trunk/Source/WebKit/SourcesCocoa.txt	2021-02-23 14:34:38 UTC (rev 273305)
@@ -458,6 +458,7 @@
 
 UIProcess/ios/AppKitSoftLink.mm
 UIProcess/ios/DragDropInteractionState.mm
+UIProcess/ios/GestureRecognizerConsistencyEnforcer.mm
 UIProcess/ios/InputViewUpdateDeferrer.mm
 UIProcess/ios/PageClientImplIOS.mm
 UIProcess/ios/ProcessAssertionIOS.mm

Copied: trunk/Source/WebKit/UIProcess/ios/GestureRecognizerConsistencyEnforcer.h (from rev 273304, trunk/Source/WebKit/UIProcess/ios/WKDeferringGestureRecognizer.h) (0 => 273305)


--- trunk/Source/WebKit/UIProcess/ios/GestureRecognizerConsistencyEnforcer.h	                        (rev 0)
+++ trunk/Source/WebKit/UIProcess/ios/GestureRecognizerConsistencyEnforcer.h	2021-02-23 14:34:38 UTC (rev 273305)
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2021 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#if PLATFORM(IOS_FAMILY)
+
+#import <wtf/FastMalloc.h>
+#import <wtf/Noncopyable.h>
+#import <wtf/RetainPtr.h>
+#import <wtf/RunLoop.h>
+#import <wtf/WeakObjCPtr.h>
+
+@class WKContentView;
+@class WKDeferringGestureRecognizer;
+
+namespace WebKit {
+
+class GestureRecognizerConsistencyEnforcer {
+    WTF_MAKE_FAST_ALLOCATED;
+    WTF_MAKE_NONCOPYABLE(GestureRecognizerConsistencyEnforcer);
+public:
+    GestureRecognizerConsistencyEnforcer(WKContentView *);
+    ~GestureRecognizerConsistencyEnforcer();
+
+    void beginTracking(WKDeferringGestureRecognizer *);
+    void endTracking(WKDeferringGestureRecognizer *);
+
+    void reset();
+
+private:
+    void timerFired();
+
+    WeakObjCPtr<WKContentView> m_view;
+    RunLoop::Timer<GestureRecognizerConsistencyEnforcer> m_timer;
+    HashSet<RetainPtr<WKDeferringGestureRecognizer>> m_deferringGestureRecognizersWithTouches;
+};
+
+} // namespace WebKit
+
+#endif // PLATFORM(IOS_FAMILY)

Added: trunk/Source/WebKit/UIProcess/ios/GestureRecognizerConsistencyEnforcer.mm (0 => 273305)


--- trunk/Source/WebKit/UIProcess/ios/GestureRecognizerConsistencyEnforcer.mm	                        (rev 0)
+++ trunk/Source/WebKit/UIProcess/ios/GestureRecognizerConsistencyEnforcer.mm	2021-02-23 14:34:38 UTC (rev 273305)
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2021 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#import "config.h"
+#import "GestureRecognizerConsistencyEnforcer.h"
+
+#if PLATFORM(IOS_FAMILY)
+
+#import "Logging.h"
+#import "WKContentViewInteraction.h"
+
+namespace WebKit {
+
+GestureRecognizerConsistencyEnforcer::GestureRecognizerConsistencyEnforcer(WKContentView *view)
+    : m_view(view)
+    , m_timer(RunLoop::main(), this, &GestureRecognizerConsistencyEnforcer::timerFired)
+{
+    ASSERT(m_view);
+}
+
+GestureRecognizerConsistencyEnforcer::~GestureRecognizerConsistencyEnforcer() = default;
+
+void GestureRecognizerConsistencyEnforcer::beginTracking(WKDeferringGestureRecognizer *gesture)
+{
+    m_timer.stop();
+    m_deferringGestureRecognizersWithTouches.add(gesture);
+}
+
+void GestureRecognizerConsistencyEnforcer::endTracking(WKDeferringGestureRecognizer *gesture)
+{
+    if (!m_deferringGestureRecognizersWithTouches.remove(gesture))
+        return;
+
+    if (m_deferringGestureRecognizersWithTouches.isEmpty())
+        m_timer.startOneShot(1_s);
+}
+
+void GestureRecognizerConsistencyEnforcer::reset()
+{
+    m_timer.stop();
+    m_deferringGestureRecognizersWithTouches.clear();
+}
+
+void GestureRecognizerConsistencyEnforcer::timerFired()
+{
+    if (!m_view)
+        return;
+
+    auto strongView = m_view.get();
+    auto possibleDeferringGestures = [NSMutableArray<WKDeferringGestureRecognizer *> array];
+    for (WKDeferringGestureRecognizer *gesture in [strongView deferringGestures]) {
+        if (gesture.state == UIGestureRecognizerStatePossible)
+            [possibleDeferringGestures addObject:gesture];
+    }
+
+    if (!possibleDeferringGestures.count)
+        return;
+
+    auto touchEventState = [strongView touchEventGestureRecognizer].state;
+    if (touchEventState == UIGestureRecognizerStatePossible || touchEventState == UIGestureRecognizerStateBegan || touchEventState == UIGestureRecognizerStateChanged)
+        return;
+
+    for (WKDeferringGestureRecognizer *gesture in possibleDeferringGestures)
+        [gesture setState:UIGestureRecognizerStateEnded];
+
+    RELEASE_LOG_FAULT(ViewGestures, "Touch event gesture recognizer failed to reset after ending gesture deferral: %@", possibleDeferringGestures);
+}
+
+} // namespace WebKit
+
+#endif // PLATFORM(IOS_FAMILY)

Modified: trunk/Source/WebKit/UIProcess/ios/WKContentViewInteraction.h (273304 => 273305)


--- trunk/Source/WebKit/UIProcess/ios/WKContentViewInteraction.h	2021-02-23 13:58:35 UTC (rev 273304)
+++ trunk/Source/WebKit/UIProcess/ios/WKContentViewInteraction.h	2021-02-23 14:34:38 UTC (rev 273305)
@@ -35,6 +35,7 @@
 #import "EditorState.h"
 #import "FocusedElementInformation.h"
 #import "FrameInfoData.h"
+#import "GestureRecognizerConsistencyEnforcer.h"
 #import "GestureTypes.h"
 #import "InteractionInformationAtPosition.h"
 #import "PasteboardAccessIntent.h"
@@ -255,7 +256,6 @@
 @end
 
 @interface WKContentView () {
-#if ENABLE(IOS_TOUCH_EVENTS)
     RetainPtr<WKDeferringGestureRecognizer> _touchStartDeferringGestureRecognizerForImmediatelyResettableGestures;
     RetainPtr<WKDeferringGestureRecognizer> _touchStartDeferringGestureRecognizerForDelayedResettableGestures;
     RetainPtr<WKDeferringGestureRecognizer> _touchStartDeferringGestureRecognizerForSyntheticTapGestures;
@@ -262,7 +262,7 @@
     RetainPtr<WKDeferringGestureRecognizer> _touchEndDeferringGestureRecognizerForImmediatelyResettableGestures;
     RetainPtr<WKDeferringGestureRecognizer> _touchEndDeferringGestureRecognizerForDelayedResettableGestures;
     RetainPtr<WKDeferringGestureRecognizer> _touchEndDeferringGestureRecognizerForSyntheticTapGestures;
-#endif
+    std::unique_ptr<WebKit::GestureRecognizerConsistencyEnforcer> _gestureRecognizerConsistencyEnforcer;
     RetainPtr<UIWebTouchEventsGestureRecognizer> _touchEventGestureRecognizer;
 
     BOOL _touchEventsCanPreventNativeGestures;
@@ -525,6 +525,9 @@
 @property (nonatomic, readonly) UIView *inputAccessoryViewForWebView;
 @property (nonatomic, readonly) BOOL preventsPanningInXAxis;
 @property (nonatomic, readonly) BOOL preventsPanningInYAxis;
+@property (nonatomic, readonly) UIWebTouchEventsGestureRecognizer *touchEventGestureRecognizer;
+@property (nonatomic, readonly) NSArray<WKDeferringGestureRecognizer *> *deferringGestures;
+@property (nonatomic, readonly) WebKit::GestureRecognizerConsistencyEnforcer& gestureRecognizerConsistencyEnforcer;
 
 #if ENABLE(DATALIST_ELEMENT)
 @property (nonatomic, strong) UIView <WKFormControl> *dataListTextSuggestionsInputView;

Modified: trunk/Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm (273304 => 273305)


--- trunk/Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm	2021-02-23 13:58:35 UTC (rev 273304)
+++ trunk/Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm	2021-02-23 14:34:38 UTC (rev 273305)
@@ -773,7 +773,6 @@
     [_touchActionDownSwipeGestureRecognizer setDelegate:self];
     [self addGestureRecognizer:_touchActionDownSwipeGestureRecognizer.get()];
 
-#if ENABLE(IOS_TOUCH_EVENTS)
     _touchStartDeferringGestureRecognizerForImmediatelyResettableGestures = adoptNS([[WKDeferringGestureRecognizer alloc] initWithDeferringGestureDelegate:self]);
     [_touchStartDeferringGestureRecognizerForImmediatelyResettableGestures setName:@"Touch start deferrer (immediate reset)"];
 
@@ -792,12 +791,14 @@
     _touchEndDeferringGestureRecognizerForSyntheticTapGestures = adoptNS([[WKDeferringGestureRecognizer alloc] initWithDeferringGestureDelegate:self]);
     [_touchEndDeferringGestureRecognizerForSyntheticTapGestures setName:@"Touch end deferrer (synthetic tap)"];
 
-    for (WKDeferringGestureRecognizer *gesture in self._deferringGestures) {
+    for (WKDeferringGestureRecognizer *gesture in self.deferringGestures) {
         gesture.delegate = self;
         [self addGestureRecognizer:gesture];
     }
-#endif // ENABLE(IOS_TOUCH_EVENTS)
 
+    if (_gestureRecognizerConsistencyEnforcer)
+        _gestureRecognizerConsistencyEnforcer->reset();
+
     _touchEventGestureRecognizer = adoptNS([[UIWebTouchEventsGestureRecognizer alloc] initWithTarget:self action:@selector(_webTouchEventsRecognized:) touchDelegate:self]);
     [_touchEventGestureRecognizer setDelegate:self];
     [self addGestureRecognizer:_touchEventGestureRecognizer.get()];
@@ -967,13 +968,14 @@
     [_touchEventGestureRecognizer setDelegate:nil];
     [self removeGestureRecognizer:_touchEventGestureRecognizer.get()];
 
-#if ENABLE(IOS_TOUCH_EVENTS)
-    for (WKDeferringGestureRecognizer *gesture in self._deferringGestures) {
+    for (WKDeferringGestureRecognizer *gesture in self.deferringGestures) {
         gesture.delegate = nil;
         [self removeGestureRecognizer:gesture];
     }
-#endif
 
+    if (_gestureRecognizerConsistencyEnforcer)
+        _gestureRecognizerConsistencyEnforcer->reset();
+
 #if HAVE(UIKIT_WITH_MOUSE_SUPPORT)
     [_mouseGestureRecognizer setDelegate:nil];
     [self removeGestureRecognizer:_mouseGestureRecognizer.get()];
@@ -1098,10 +1100,8 @@
 
 - (void)_removeDefaultGestureRecognizers
 {
-#if ENABLE(IOS_TOUCH_EVENTS)
-    for (WKDeferringGestureRecognizer *gesture in self._deferringGestures)
+    for (WKDeferringGestureRecognizer *gesture in self.deferringGestures)
         [self removeGestureRecognizer:gesture];
-#endif
     [self removeGestureRecognizer:_touchEventGestureRecognizer.get()];
     [self removeGestureRecognizer:_singleTapGestureRecognizer.get()];
     [self removeGestureRecognizer:_highlightLongPressGestureRecognizer.get()];
@@ -1125,10 +1125,8 @@
 
 - (void)_addDefaultGestureRecognizers
 {
-#if ENABLE(IOS_TOUCH_EVENTS)
-    for (WKDeferringGestureRecognizer *gesture in self._deferringGestures)
+    for (WKDeferringGestureRecognizer *gesture in self.deferringGestures)
         [self addGestureRecognizer:gesture];
-#endif
     [self addGestureRecognizer:_touchEventGestureRecognizer.get()];
     [self addGestureRecognizer:_singleTapGestureRecognizer.get()];
     [self addGestureRecognizer:_highlightLongPressGestureRecognizer.get()];
@@ -1564,7 +1562,6 @@
         if (nativeWebTouchEvent.isPotentialTap() && self.hasHiddenContentEditable && self._hasFocusedElement && !self.window.keyWindow)
             [self.window makeKeyWindow];
 
-#if ENABLE(IOS_TOUCH_EVENTS)
         auto stopDeferringNativeGesturesIfNeeded = [] (NSArray<WKDeferringGestureRecognizer *> *deferringGestures) {
             for (WKDeferringGestureRecognizer *gesture in deferringGestures) {
                 if (gesture.state == UIGestureRecognizerStatePossible)
@@ -1577,7 +1574,6 @@
 
         if (!_page->isHandlingPreventableTouchEnd())
             stopDeferringNativeGesturesIfNeeded(self._touchEndDeferringGestures);
-#endif // ENABLE(IOS_TOUCH_EVENTS)
     }
 #endif // ENABLE(TOUCH_EVENTS)
 }
@@ -1755,10 +1751,21 @@
 }
 #endif
 
-#if ENABLE(IOS_TOUCH_EVENTS)
+- (UIWebTouchEventsGestureRecognizer *)touchEventGestureRecognizer
+{
+    return _touchEventGestureRecognizer.get();
+}
 
-- (NSArray<WKDeferringGestureRecognizer *> *)_deferringGestures
+- (WebKit::GestureRecognizerConsistencyEnforcer&)gestureRecognizerConsistencyEnforcer
 {
+    if (!_gestureRecognizerConsistencyEnforcer)
+        _gestureRecognizerConsistencyEnforcer = makeUnique<WebKit::GestureRecognizerConsistencyEnforcer>(self);
+
+    return *_gestureRecognizerConsistencyEnforcer;
+}
+
+- (NSArray<WKDeferringGestureRecognizer *> *)deferringGestures
+{
     auto gestures = [NSMutableArray arrayWithCapacity:6];
     [gestures addObjectsFromArray:self._touchStartDeferringGestures];
     [gestures addObjectsFromArray:self._touchEndDeferringGestures];
@@ -1817,8 +1824,6 @@
         || gesture == _touchEndDeferringGestureRecognizerForDelayedResettableGestures;
 }
 
-#endif // ENABLE(IOS_TOUCH_EVENTS)
-
 static NSValue *nsSizeForTapHighlightBorderRadius(WebCore::IntSize borderRadius, CGFloat borderRadiusScale)
 {
     return [NSValue valueWithCGSize:CGSizeMake((borderRadius.width() * borderRadiusScale) + minimumTapHighlightRadius, (borderRadius.height() * borderRadiusScale) + minimumTapHighlightRadius)];
@@ -2221,12 +2226,10 @@
 
 - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer*)otherGestureRecognizer
 {
-#if ENABLE(IOS_TOUCH_EVENTS)
-    for (WKDeferringGestureRecognizer *gesture in self._deferringGestures) {
+    for (WKDeferringGestureRecognizer *gesture in self.deferringGestures) {
         if (isSamePair(gestureRecognizer, otherGestureRecognizer, _touchEventGestureRecognizer.get(), gesture))
             return YES;
     }
-#endif
 
     if ([gestureRecognizer isKindOfClass:WKDeferringGestureRecognizer.class] && [otherGestureRecognizer isKindOfClass:WKDeferringGestureRecognizer.class])
         return YES;
@@ -7554,23 +7557,31 @@
 
 #pragma mark - WKDeferringGestureRecognizerDelegate
 
-- (BOOL)deferringGestureRecognizer:(WKDeferringGestureRecognizer *)deferringGestureRecognizer shouldDeferGesturesAfterBeginningTouchesWithEvent:(UIEvent *)event
+- (WebKit::ShouldDeferGestures)deferringGestureRecognizer:(WKDeferringGestureRecognizer *)deferringGestureRecognizer willBeginTouchesWithEvent:(UIEvent *)event
 {
-    return ![self gestureRecognizer:deferringGestureRecognizer isInterruptingMomentumScrollingWithEvent:event];
+    self.gestureRecognizerConsistencyEnforcer.beginTracking(deferringGestureRecognizer);
+
+    return [self gestureRecognizer:deferringGestureRecognizer isInterruptingMomentumScrollingWithEvent:event] ? WebKit::ShouldDeferGestures::No : WebKit::ShouldDeferGestures::Yes;
 }
 
+- (void)deferringGestureRecognizer:(WKDeferringGestureRecognizer *)deferringGestureRecognizer didTransitionToState:(UIGestureRecognizerState)state
+{
+    if (state == UIGestureRecognizerStateEnded || state == UIGestureRecognizerStateFailed || state == UIGestureRecognizerStateCancelled)
+        self.gestureRecognizerConsistencyEnforcer.endTracking(deferringGestureRecognizer);
+}
+
 - (void)deferringGestureRecognizer:(WKDeferringGestureRecognizer *)deferringGestureRecognizer didEndTouchesWithEvent:(UIEvent *)event
 {
+    self.gestureRecognizerConsistencyEnforcer.endTracking(deferringGestureRecognizer);
+
     if (deferringGestureRecognizer.state != UIGestureRecognizerStatePossible)
         return;
 
-#if ENABLE(IOS_TOUCH_EVENTS)
     if (_page->isHandlingPreventableTouchStart() && [self _isTouchStartDeferringGesture:deferringGestureRecognizer])
         return;
 
     if (_page->isHandlingPreventableTouchEnd() && [self _isTouchEndDeferringGesture:deferringGestureRecognizer])
         return;
-#endif
 
     if ([_touchEventGestureRecognizer state] == UIGestureRecognizerStatePossible)
         return;

Modified: trunk/Source/WebKit/UIProcess/ios/WKDeferringGestureRecognizer.h (273304 => 273305)


--- trunk/Source/WebKit/UIProcess/ios/WKDeferringGestureRecognizer.h	2021-02-23 13:58:35 UTC (rev 273304)
+++ trunk/Source/WebKit/UIProcess/ios/WKDeferringGestureRecognizer.h	2021-02-23 14:34:38 UTC (rev 273305)
@@ -27,12 +27,19 @@
 
 #import <UIKit/UIKit.h>
 
+namespace WebKit {
+
+enum class ShouldDeferGestures : bool { No, Yes };
+
+} // namespace WebKit
+
 @class WKDeferringGestureRecognizer;
 
 @protocol WKDeferringGestureRecognizerDelegate
-- (BOOL)deferringGestureRecognizer:(WKDeferringGestureRecognizer *)deferringGestureRecognizer shouldDeferGesturesAfterBeginningTouchesWithEvent:(UIEvent *)event;
-- (BOOL)deferringGestureRecognizer:(WKDeferringGestureRecognizer *)deferringGestureRecognizer didEndTouchesWithEvent:(UIEvent *)event;
 - (BOOL)deferringGestureRecognizer:(WKDeferringGestureRecognizer *)deferringGestureRecognizer shouldDeferOtherGestureRecognizer:(UIGestureRecognizer *)gestureRecognizer;
+- (WebKit::ShouldDeferGestures)deferringGestureRecognizer:(WKDeferringGestureRecognizer *)deferringGestureRecognizer willBeginTouchesWithEvent:(UIEvent *)event;
+- (void)deferringGestureRecognizer:(WKDeferringGestureRecognizer *)deferringGestureRecognizer didEndTouchesWithEvent:(UIEvent *)event;
+- (void)deferringGestureRecognizer:(WKDeferringGestureRecognizer *)deferringGestureRecognizer didTransitionToState:(UIGestureRecognizerState)state;
 @end
 
 @interface WKDeferringGestureRecognizer : UIGestureRecognizer
@@ -41,8 +48,6 @@
 - (void)setDefaultPrevented:(BOOL)defaultPrevented;
 - (BOOL)shouldDeferGestureRecognizer:(UIGestureRecognizer *)gestureRecognizer;
 
-@property (nonatomic, weak, readonly) id <WKDeferringGestureRecognizerDelegate> deferringGestureDelegate;
-
 @end
 
 #endif // PLATFORM(IOS_FAMILY)

Modified: trunk/Source/WebKit/UIProcess/ios/WKDeferringGestureRecognizer.mm (273304 => 273305)


--- trunk/Source/WebKit/UIProcess/ios/WKDeferringGestureRecognizer.mm	2021-02-23 13:58:35 UTC (rev 273304)
+++ trunk/Source/WebKit/UIProcess/ios/WKDeferringGestureRecognizer.mm	2021-02-23 14:34:38 UTC (rev 273305)
@@ -41,18 +41,13 @@
     return self;
 }
 
-- (id <WKDeferringGestureRecognizerDelegate>)deferringGestureDelegate
-{
-    return _deferringGestureDelegate.get().get();
-}
-
 - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
 {
+    auto shouldDeferGestures = [_deferringGestureDelegate deferringGestureRecognizer:self willBeginTouchesWithEvent:event];
     [super touchesBegan:touches withEvent:event];
-    if ([_deferringGestureDelegate deferringGestureRecognizer:self shouldDeferGesturesAfterBeginningTouchesWithEvent:event])
-        return;
 
-    self.state = UIGestureRecognizerStateFailed;
+    if (shouldDeferGestures == WebKit::ShouldDeferGestures::No)
+        self.state = UIGestureRecognizerStateFailed;
 }
 
 - (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
@@ -86,6 +81,15 @@
     return [_deferringGestureDelegate deferringGestureRecognizer:self shouldDeferOtherGestureRecognizer:gestureRecognizer];
 }
 
+- (void)setState:(UIGestureRecognizerState)state
+{
+    auto previousState = self.state;
+    [super setState:state];
+
+    if (previousState != self.state)
+        [_deferringGestureDelegate deferringGestureRecognizer:self didTransitionToState:state];
+}
+
 @end
 
 #endif // PLATFORM(IOS_FAMILY)

Modified: trunk/Source/WebKit/UIProcess/mac/WKPrintingView.h (273304 => 273305)


--- trunk/Source/WebKit/UIProcess/mac/WKPrintingView.h	2021-02-23 13:58:35 UTC (rev 273304)
+++ trunk/Source/WebKit/UIProcess/mac/WKPrintingView.h	2021-02-23 14:34:38 UTC (rev 273305)
@@ -32,6 +32,7 @@
 #import <wtf/Lock.h>
 #import <wtf/RetainPtr.h>
 #import <wtf/Vector.h>
+#import <wtf/WeakObjCPtr.h>
 
 @class WKPrintingViewData;
 @class PDFDestination;

Modified: trunk/Source/WebKit/WebKit.xcodeproj/project.pbxproj (273304 => 273305)


--- trunk/Source/WebKit/WebKit.xcodeproj/project.pbxproj	2021-02-23 13:58:35 UTC (rev 273304)
+++ trunk/Source/WebKit/WebKit.xcodeproj/project.pbxproj	2021-02-23 14:34:38 UTC (rev 273305)
@@ -2006,6 +2006,7 @@
 		F4A6D6BC254CA3E900B65FAA /* SharedDisplayListHandle.h in Headers */ = {isa = PBXBuildFile; fileRef = F4A6D6BB254CA3E900B65FAA /* SharedDisplayListHandle.h */; };
 		F4BA33F225757E89000A3CE8 /* WKImageExtractionGestureRecognizer.h in Headers */ = {isa = PBXBuildFile; fileRef = F4BA33F025757E89000A3CE8 /* WKImageExtractionGestureRecognizer.h */; };
 		F4CB09E5225D5A0900891487 /* WebsiteMediaSourcePolicy.h in Headers */ = {isa = PBXBuildFile; fileRef = F4CB09E4225D5A0300891487 /* WebsiteMediaSourcePolicy.h */; };
+		F4CF1E9D25E40DCC000F9D73 /* GestureRecognizerConsistencyEnforcer.h in Headers */ = {isa = PBXBuildFile; fileRef = F4CF1E9B25E40DCC000F9D73 /* GestureRecognizerConsistencyEnforcer.h */; };
 		F4D5F51D206087A10038BBA8 /* WKTextInputListViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = F4D5F519206087A00038BBA8 /* WKTextInputListViewController.h */; };
 		F4D5F51F206087A10038BBA8 /* WKQuickboardListViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = F4D5F51B206087A10038BBA8 /* WKQuickboardListViewController.h */; };
 		F4DB54E62319E733009E3155 /* WKHighlightLongPressGestureRecognizer.h in Headers */ = {isa = PBXBuildFile; fileRef = F4DB54E42319E733009E3155 /* WKHighlightLongPressGestureRecognizer.h */; };
@@ -5884,6 +5885,8 @@
 		F4BA33F025757E89000A3CE8 /* WKImageExtractionGestureRecognizer.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = WKImageExtractionGestureRecognizer.h; path = ios/WKImageExtractionGestureRecognizer.h; sourceTree = "<group>"; };
 		F4BA33F125757E89000A3CE8 /* WKImageExtractionGestureRecognizer.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; name = WKImageExtractionGestureRecognizer.mm; path = ios/WKImageExtractionGestureRecognizer.mm; sourceTree = "<group>"; };
 		F4CB09E4225D5A0300891487 /* WebsiteMediaSourcePolicy.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = WebsiteMediaSourcePolicy.h; sourceTree = "<group>"; };
+		F4CF1E9B25E40DCC000F9D73 /* GestureRecognizerConsistencyEnforcer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = GestureRecognizerConsistencyEnforcer.h; path = ios/GestureRecognizerConsistencyEnforcer.h; sourceTree = "<group>"; };
+		F4CF1E9C25E40DCC000F9D73 /* GestureRecognizerConsistencyEnforcer.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = GestureRecognizerConsistencyEnforcer.mm; path = ios/GestureRecognizerConsistencyEnforcer.mm; sourceTree = "<group>"; };
 		F4D5F519206087A00038BBA8 /* WKTextInputListViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = WKTextInputListViewController.h; path = ios/forms/WKTextInputListViewController.h; sourceTree = "<group>"; };
 		F4D5F51A206087A10038BBA8 /* WKTextInputListViewController.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = WKTextInputListViewController.mm; path = ios/forms/WKTextInputListViewController.mm; sourceTree = "<group>"; };
 		F4D5F51B206087A10038BBA8 /* WKQuickboardListViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = WKQuickboardListViewController.h; path = ios/forms/WKQuickboardListViewController.h; sourceTree = "<group>"; };
@@ -7579,6 +7582,8 @@
 				F496A4301F58A272004C1757 /* DragDropInteractionState.mm */,
 				CDCDC99B248FE8DA00A69522 /* EndowmentStateTracker.h */,
 				CDCDC99C248FE8DA00A69522 /* EndowmentStateTracker.mm */,
+				F4CF1E9B25E40DCC000F9D73 /* GestureRecognizerConsistencyEnforcer.h */,
+				F4CF1E9C25E40DCC000F9D73 /* GestureRecognizerConsistencyEnforcer.mm */,
 				2DD45ADC1E5F8972006C355F /* InputViewUpdateDeferrer.h */,
 				2DD45ADD1E5F8972006C355F /* InputViewUpdateDeferrer.mm */,
 				0FCB4E3618BBE044000FCFC9 /* PageClientImplIOS.h */,
@@ -11744,6 +11749,7 @@
 				BC06F42F12DBB9B6002D78DE /* GeolocationPermissionRequestManager.h in Headers */,
 				BC06F44A12DBD1F5002D78DE /* GeolocationPermissionRequestManagerProxy.h in Headers */,
 				BC06F43A12DBCCFB002D78DE /* GeolocationPermissionRequestProxy.h in Headers */,
+				F4CF1E9D25E40DCC000F9D73 /* GestureRecognizerConsistencyEnforcer.h in Headers */,
 				2DA944A41884E4F000ED86DB /* GestureTypes.h in Headers */,
 				4614F13225DED875007006E7 /* GPUProcessConnectionParameters.h in Headers */,
 				F40BBB41257FF46E0067463A /* GPUProcessWakeupMessageArguments.h in Headers */,
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to