- Revision
- 261530
- Author
- [email protected]
- Date
- 2020-05-11 17:22:29 -0700 (Mon, 11 May 2020)
Log Message
Cherry-pick r260979. rdar://problem/62978869
REGRESSION: Double tap dispatches one click event on iOS 13.4 when it would dispatch two on iOS 13.3
https://bugs.webkit.org/show_bug.cgi?id=211179
<rdar://problem/62594779>
Reviewed by Tim Horton.
Source/WebKit:
https://trac.webkit.org/r253267 introduced deferring gesture recognizers as a way to handle preventable (non-
passive) touchstart events without blocking the UI process. These deferring gesture recognizers work by having
other gesture recognizers at or below WKWebView (with few exceptions) require the failure of these deferring
gestures. These gestures transition to possible state when beginning a touch inside a non-passive touch event
handling region, and transition to either failed or ended state (depending on whether `preventDefault()` was
called) after the web content process finished handling the touch event.
However, this means that the resulting dependency graph now has an edge between each gesture under WKWebView and
one of the deferring gesture recognizers, due to these new failure requirements. Since gestures that have been
recognized or have failed don't get reset until all other gestures in the same dependency subgraph have also
recognized or failed, some gestures (such as the synthetic single tap gesture recognizer in this bug) might not
be resetting as soon after ending as they did before, since they may be connected to other gesture recognizers
that are still in possible state by way of the failure requirements added by the new deferring gestures.
I was already aware of this problem in r253267, and attempted to solve this by bisecting the gesture dependency
graph into two subgraphs: one containing all the one-finger multi-tap gestures that are reset after a lengthy
delay, and another containing everything else. To do this, I added two different deferring gesture recognizers:
one for immediately resettable gestures (meant for gestures in the latter subgraph), and another for gestures
that are reset after a delay (meant for gestures in the former subgraph).
Unfortunately, unrelated changes around text interactions in UIKit in iOS 13.4 caused the tap-and-a-half
gesture for selecting a range of text to now have a delayed reset; this means that gestures in the "immediately
resettable" gesture subgraph are all forced to wait on the tap-and-a-half text interaction gesture before
resetting, which causes the bug here, since the synthetic single tap gesture is in this "immediately resettable"
gesture subgraph.
To mitigate this, this patch pushes the tap-and-a-half text selection gesture, along with the loupe gesture,
context menu relationship gesture, and drag lift gestures (i.e. the other gestures that are connected to the
tap-and-a-half gesture via failure requirements) out of the "immediately resettable" subgraph, and into the
"delayed" subgraph.
Test: fast/events/touch/ios/two-click-events-after-double-tap-with-touch-handler.html
* Platform/spi/ios/UIKitSPI.h:
* UIProcess/ios/WKContentViewInteraction.mm:
(-[WKContentView deferringGestureRecognizer:shouldDeferOtherGestureRecognizer:]):
LayoutTests:
Add a new layout test to verify that double tapping a button with fast-clicking enabled (i.e. in a device-width
viewport) fires two click events.
* fast/events/touch/ios/two-click-events-after-double-tap-with-touch-handler-expected.txt: Added.
* fast/events/touch/ios/two-click-events-after-double-tap-with-touch-handler.html: Added.
git-svn-id: https://svn.webkit.org/repository/webkit/trunk@260979 268f45cc-cd09-0410-ab3c-d52691b4dbfc
Modified Paths
Added Paths
Diff
Modified: branches/safari-609-branch/LayoutTests/ChangeLog (261529 => 261530)
--- branches/safari-609-branch/LayoutTests/ChangeLog 2020-05-12 00:22:26 UTC (rev 261529)
+++ branches/safari-609-branch/LayoutTests/ChangeLog 2020-05-12 00:22:29 UTC (rev 261530)
@@ -1,5 +1,78 @@
2020-05-11 Alan Coon <[email protected]>
+ Cherry-pick r260979. rdar://problem/62978869
+
+ REGRESSION: Double tap dispatches one click event on iOS 13.4 when it would dispatch two on iOS 13.3
+ https://bugs.webkit.org/show_bug.cgi?id=211179
+ <rdar://problem/62594779>
+
+ Reviewed by Tim Horton.
+
+ Source/WebKit:
+
+ https://trac.webkit.org/r253267 introduced deferring gesture recognizers as a way to handle preventable (non-
+ passive) touchstart events without blocking the UI process. These deferring gesture recognizers work by having
+ other gesture recognizers at or below WKWebView (with few exceptions) require the failure of these deferring
+ gestures. These gestures transition to possible state when beginning a touch inside a non-passive touch event
+ handling region, and transition to either failed or ended state (depending on whether `preventDefault()` was
+ called) after the web content process finished handling the touch event.
+
+ However, this means that the resulting dependency graph now has an edge between each gesture under WKWebView and
+ one of the deferring gesture recognizers, due to these new failure requirements. Since gestures that have been
+ recognized or have failed don't get reset until all other gestures in the same dependency subgraph have also
+ recognized or failed, some gestures (such as the synthetic single tap gesture recognizer in this bug) might not
+ be resetting as soon after ending as they did before, since they may be connected to other gesture recognizers
+ that are still in possible state by way of the failure requirements added by the new deferring gestures.
+
+ I was already aware of this problem in r253267, and attempted to solve this by bisecting the gesture dependency
+ graph into two subgraphs: one containing all the one-finger multi-tap gestures that are reset after a lengthy
+ delay, and another containing everything else. To do this, I added two different deferring gesture recognizers:
+ one for immediately resettable gestures (meant for gestures in the latter subgraph), and another for gestures
+ that are reset after a delay (meant for gestures in the former subgraph).
+
+ Unfortunately, unrelated changes around text interactions in UIKit in iOS 13.4 caused the tap-and-a-half
+ gesture for selecting a range of text to now have a delayed reset; this means that gestures in the "immediately
+ resettable" gesture subgraph are all forced to wait on the tap-and-a-half text interaction gesture before
+ resetting, which causes the bug here, since the synthetic single tap gesture is in this "immediately resettable"
+ gesture subgraph.
+
+ To mitigate this, this patch pushes the tap-and-a-half text selection gesture, along with the loupe gesture,
+ context menu relationship gesture, and drag lift gestures (i.e. the other gestures that are connected to the
+ tap-and-a-half gesture via failure requirements) out of the "immediately resettable" subgraph, and into the
+ "delayed" subgraph.
+
+ Test: fast/events/touch/ios/two-click-events-after-double-tap-with-touch-handler.html
+
+ * Platform/spi/ios/UIKitSPI.h:
+ * UIProcess/ios/WKContentViewInteraction.mm:
+ (-[WKContentView deferringGestureRecognizer:shouldDeferOtherGestureRecognizer:]):
+
+ LayoutTests:
+
+ Add a new layout test to verify that double tapping a button with fast-clicking enabled (i.e. in a device-width
+ viewport) fires two click events.
+
+ * fast/events/touch/ios/two-click-events-after-double-tap-with-touch-handler-expected.txt: Added.
+ * fast/events/touch/ios/two-click-events-after-double-tap-with-touch-handler.html: Added.
+
+ git-svn-id: https://svn.webkit.org/repository/webkit/trunk@260979 268f45cc-cd09-0410-ab3c-d52691b4dbfc
+
+ 2020-04-30 Wenson Hsieh <[email protected]>
+
+ REGRESSION: Double tap dispatches one click event on iOS 13.4 when it would dispatch two on iOS 13.3
+ https://bugs.webkit.org/show_bug.cgi?id=211179
+ <rdar://problem/62594779>
+
+ Reviewed by Tim Horton.
+
+ Add a new layout test to verify that double tapping a button with fast-clicking enabled (i.e. in a device-width
+ viewport) fires two click events.
+
+ * fast/events/touch/ios/two-click-events-after-double-tap-with-touch-handler-expected.txt: Added.
+ * fast/events/touch/ios/two-click-events-after-double-tap-with-touch-handler.html: Added.
+
+2020-05-11 Alan Coon <[email protected]>
+
Cherry-pick r259669. rdar://problem/63111225
Preventing touch events should not prevent gestures installed above WKWebView from recognizing
Added: branches/safari-609-branch/LayoutTests/fast/events/touch/ios/two-click-events-after-double-tap-with-touch-handler-expected.txt (0 => 261530)
--- branches/safari-609-branch/LayoutTests/fast/events/touch/ios/two-click-events-after-double-tap-with-touch-handler-expected.txt (rev 0)
+++ branches/safari-609-branch/LayoutTests/fast/events/touch/ios/two-click-events-after-double-tap-with-touch-handler-expected.txt 2020-05-12 00:22:29 UTC (rev 261530)
@@ -0,0 +1,13 @@
+This test verifies that double-tapping a button dispatches two click events when fast-clicking is active. To manually run the test, double tap the button; two 'Clicked' messages should appear.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS Clicked
+PASS Clicked
+PASS clickCount became 2
+PASS touchStartCount is 2
+PASS successfullyParsed is true
+
+TEST COMPLETE
+Double tap here
Added: branches/safari-609-branch/LayoutTests/fast/events/touch/ios/two-click-events-after-double-tap-with-touch-handler.html (0 => 261530)
--- branches/safari-609-branch/LayoutTests/fast/events/touch/ios/two-click-events-after-double-tap-with-touch-handler.html (rev 0)
+++ branches/safari-609-branch/LayoutTests/fast/events/touch/ios/two-click-events-after-double-tap-with-touch-handler.html 2020-05-12 00:22:29 UTC (rev 261530)
@@ -0,0 +1,43 @@
+<!-- webkit-test-runner [ useFlexibleViewport=true ] -->
+<!DOCTYPE html>
+<meta charset="utf-8">
+<meta name="viewport" content="initial-scale=1, user-scalable=no, width=device-width">
+<html>
+<head>
+ <script src=""
+ <script src=""
+ <style>
+ button {
+ width: 100%;
+ height: 100px;
+ position: absolute;
+ top: 100px;
+ left: 0;
+ }
+ </style>
+</head>
+<body>
+<button>Double tap here</button>
+</body>
+<script>
+jsTestIsAsync = true;
+touchStartCount = 0;
+clickCount = 0;
+
+const button = document.querySelector("button");
+button.addEventListener("touchstart", () => ++touchStartCount);
+button.addEventListener("click", () => {
+ testPassed("Clicked");
+ ++clickCount;
+});
+
+addEventListener("load", async () => {
+ description("This test verifies that double-tapping a button dispatches two click events when fast-clicking is active. To manually run the test, double tap the button; two 'Clicked' messages should appear.");
+
+ await UIHelper.humanSpeedDoubleTapAt(50, 150);
+ await new Promise(resolve => shouldBecomeEqual("clickCount", "2", resolve));
+ shouldBe("touchStartCount", "2");
+ finishJSTest();
+});
+</script>
+</html>
Modified: branches/safari-609-branch/Source/WebKit/ChangeLog (261529 => 261530)
--- branches/safari-609-branch/Source/WebKit/ChangeLog 2020-05-12 00:22:26 UTC (rev 261529)
+++ branches/safari-609-branch/Source/WebKit/ChangeLog 2020-05-12 00:22:29 UTC (rev 261530)
@@ -1,5 +1,109 @@
2020-05-11 Alan Coon <[email protected]>
+ Cherry-pick r260979. rdar://problem/62978869
+
+ REGRESSION: Double tap dispatches one click event on iOS 13.4 when it would dispatch two on iOS 13.3
+ https://bugs.webkit.org/show_bug.cgi?id=211179
+ <rdar://problem/62594779>
+
+ Reviewed by Tim Horton.
+
+ Source/WebKit:
+
+ https://trac.webkit.org/r253267 introduced deferring gesture recognizers as a way to handle preventable (non-
+ passive) touchstart events without blocking the UI process. These deferring gesture recognizers work by having
+ other gesture recognizers at or below WKWebView (with few exceptions) require the failure of these deferring
+ gestures. These gestures transition to possible state when beginning a touch inside a non-passive touch event
+ handling region, and transition to either failed or ended state (depending on whether `preventDefault()` was
+ called) after the web content process finished handling the touch event.
+
+ However, this means that the resulting dependency graph now has an edge between each gesture under WKWebView and
+ one of the deferring gesture recognizers, due to these new failure requirements. Since gestures that have been
+ recognized or have failed don't get reset until all other gestures in the same dependency subgraph have also
+ recognized or failed, some gestures (such as the synthetic single tap gesture recognizer in this bug) might not
+ be resetting as soon after ending as they did before, since they may be connected to other gesture recognizers
+ that are still in possible state by way of the failure requirements added by the new deferring gestures.
+
+ I was already aware of this problem in r253267, and attempted to solve this by bisecting the gesture dependency
+ graph into two subgraphs: one containing all the one-finger multi-tap gestures that are reset after a lengthy
+ delay, and another containing everything else. To do this, I added two different deferring gesture recognizers:
+ one for immediately resettable gestures (meant for gestures in the latter subgraph), and another for gestures
+ that are reset after a delay (meant for gestures in the former subgraph).
+
+ Unfortunately, unrelated changes around text interactions in UIKit in iOS 13.4 caused the tap-and-a-half
+ gesture for selecting a range of text to now have a delayed reset; this means that gestures in the "immediately
+ resettable" gesture subgraph are all forced to wait on the tap-and-a-half text interaction gesture before
+ resetting, which causes the bug here, since the synthetic single tap gesture is in this "immediately resettable"
+ gesture subgraph.
+
+ To mitigate this, this patch pushes the tap-and-a-half text selection gesture, along with the loupe gesture,
+ context menu relationship gesture, and drag lift gestures (i.e. the other gestures that are connected to the
+ tap-and-a-half gesture via failure requirements) out of the "immediately resettable" subgraph, and into the
+ "delayed" subgraph.
+
+ Test: fast/events/touch/ios/two-click-events-after-double-tap-with-touch-handler.html
+
+ * Platform/spi/ios/UIKitSPI.h:
+ * UIProcess/ios/WKContentViewInteraction.mm:
+ (-[WKContentView deferringGestureRecognizer:shouldDeferOtherGestureRecognizer:]):
+
+ LayoutTests:
+
+ Add a new layout test to verify that double tapping a button with fast-clicking enabled (i.e. in a device-width
+ viewport) fires two click events.
+
+ * fast/events/touch/ios/two-click-events-after-double-tap-with-touch-handler-expected.txt: Added.
+ * fast/events/touch/ios/two-click-events-after-double-tap-with-touch-handler.html: Added.
+
+ git-svn-id: https://svn.webkit.org/repository/webkit/trunk@260979 268f45cc-cd09-0410-ab3c-d52691b4dbfc
+
+ 2020-04-30 Wenson Hsieh <[email protected]>
+
+ REGRESSION: Double tap dispatches one click event on iOS 13.4 when it would dispatch two on iOS 13.3
+ https://bugs.webkit.org/show_bug.cgi?id=211179
+ <rdar://problem/62594779>
+
+ Reviewed by Tim Horton.
+
+ https://trac.webkit.org/r253267 introduced deferring gesture recognizers as a way to handle preventable (non-
+ passive) touchstart events without blocking the UI process. These deferring gesture recognizers work by having
+ other gesture recognizers at or below WKWebView (with few exceptions) require the failure of these deferring
+ gestures. These gestures transition to possible state when beginning a touch inside a non-passive touch event
+ handling region, and transition to either failed or ended state (depending on whether `preventDefault()` was
+ called) after the web content process finished handling the touch event.
+
+ However, this means that the resulting dependency graph now has an edge between each gesture under WKWebView and
+ one of the deferring gesture recognizers, due to these new failure requirements. Since gestures that have been
+ recognized or have failed don't get reset until all other gestures in the same dependency subgraph have also
+ recognized or failed, some gestures (such as the synthetic single tap gesture recognizer in this bug) might not
+ be resetting as soon after ending as they did before, since they may be connected to other gesture recognizers
+ that are still in possible state by way of the failure requirements added by the new deferring gestures.
+
+ I was already aware of this problem in r253267, and attempted to solve this by bisecting the gesture dependency
+ graph into two subgraphs: one containing all the one-finger multi-tap gestures that are reset after a lengthy
+ delay, and another containing everything else. To do this, I added two different deferring gesture recognizers:
+ one for immediately resettable gestures (meant for gestures in the latter subgraph), and another for gestures
+ that are reset after a delay (meant for gestures in the former subgraph).
+
+ Unfortunately, unrelated changes around text interactions in UIKit in iOS 13.4 caused the tap-and-a-half
+ gesture for selecting a range of text to now have a delayed reset; this means that gestures in the "immediately
+ resettable" gesture subgraph are all forced to wait on the tap-and-a-half text interaction gesture before
+ resetting, which causes the bug here, since the synthetic single tap gesture is in this "immediately resettable"
+ gesture subgraph.
+
+ To mitigate this, this patch pushes the tap-and-a-half text selection gesture, along with the loupe gesture,
+ context menu relationship gesture, and drag lift gestures (i.e. the other gestures that are connected to the
+ tap-and-a-half gesture via failure requirements) out of the "immediately resettable" subgraph, and into the
+ "delayed" subgraph.
+
+ Test: fast/events/touch/ios/two-click-events-after-double-tap-with-touch-handler.html
+
+ * Platform/spi/ios/UIKitSPI.h:
+ * UIProcess/ios/WKContentViewInteraction.mm:
+ (-[WKContentView deferringGestureRecognizer:shouldDeferOtherGestureRecognizer:]):
+
+2020-05-11 Alan Coon <[email protected]>
+
Cherry-pick r259669. rdar://problem/63111225
Preventing touch events should not prevent gestures installed above WKWebView from recognizing
Modified: branches/safari-609-branch/Source/WebKit/Platform/spi/ios/UIKitSPI.h (261529 => 261530)
--- branches/safari-609-branch/Source/WebKit/Platform/spi/ios/UIKitSPI.h 2020-05-12 00:22:26 UTC (rev 261529)
+++ branches/safari-609-branch/Source/WebKit/Platform/spi/ios/UIKitSPI.h 2020-05-12 00:22:29 UTC (rev 261530)
@@ -1097,6 +1097,12 @@
@interface UITargetedPreview ()
@property (nonatomic, strong, setter=_setOverridePositionTrackingView:) UIView *overridePositionTrackingView;
@end
+
+@interface UIContextMenuInteraction ()
+@property (nonatomic, readonly) UIGestureRecognizer *gestureRecognizerForFailureRelationships;
+- (void)_presentMenuAtLocation:(CGPoint)location;
+@end
+
#endif // USE(UICONTEXTMENU)
#if HAVE(LINK_PREVIEW) && USE(UICONTEXTMENU)
@@ -1121,6 +1127,12 @@
@property (nonatomic, readonly) BOOL inGesture;
@end
+#if ENABLE(DRAG_SUPPORT)
+@interface UIDragInteraction (IPI)
+@property (nonatomic, readonly, getter=_initiationDriver) id initiationDriver;
+@end
+#endif // ENABLE(DRAG_SUPPORT)
+
#if HAVE(LINK_PREVIEW) && USE(UICONTEXTMENU)
@interface UIContextMenuConfiguration (IPI)
@property (nonatomic, copy) UIContextMenuContentPreviewProvider previewProvider;
Modified: branches/safari-609-branch/Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm (261529 => 261530)
--- branches/safari-609-branch/Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm 2020-05-12 00:22:26 UTC (rev 261529)
+++ branches/safari-609-branch/Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm 2020-05-12 00:22:29 UTC (rev 261530)
@@ -6832,6 +6832,7 @@
- (BOOL)deferringGestureRecognizer:(WKDeferringGestureRecognizer *)deferringGestureRecognizer shouldDeferOtherGestureRecognizer:(UIGestureRecognizer *)gestureRecognizer
{
+#if ENABLE(IOS_TOUCH_EVENTS)
if ([_webView _isNavigationSwipeGestureRecognizer:gestureRecognizer])
return NO;
@@ -6849,39 +6850,46 @@
if (!gestureIsInstalledOnOrUnderWebView)
return NO;
-#if ENABLE(IOS_TOUCH_EVENTS)
- auto isOneFingerMultipleTapGesture = [](UIGestureRecognizer *gesture) -> BOOL {
- if (![gesture isKindOfClass:UITapGestureRecognizer.class])
- return NO;
+ if ([gestureRecognizer isKindOfClass:WKDeferringGestureRecognizer.class])
+ return NO;
- UITapGestureRecognizer *tapGesture = (UITapGestureRecognizer *)gesture;
- return tapGesture.numberOfTapsRequired > 1 && tapGesture.numberOfTouchesRequired < 2;
- };
+ if (gestureRecognizer == _touchEventGestureRecognizer)
+ return NO;
- if (deferringGestureRecognizer == _deferringGestureRecognizerForDelayedResettableGestures) {
- if ([gestureRecognizer isKindOfClass:WKDeferringGestureRecognizer.class])
- return NO;
+ auto mayDelayResetOfContainingSubgraph = [&](UIGestureRecognizer *gesture) -> BOOL {
+#if USE(UICONTEXTMENU)
+ if (gesture == [_contextMenuInteraction gestureRecognizerForFailureRelationships])
+ return YES;
+#endif
- if (gestureRecognizer == _touchEventGestureRecognizer)
- return NO;
+#if ENABLE(DRAG_SUPPORT)
+ if (gesture.delegate == [_dragInteraction _initiationDriver])
+ return YES;
+#endif
- return isOneFingerMultipleTapGesture(gestureRecognizer);
- }
+ if ([gesture isKindOfClass:tapAndAHalfRecognizerClass()])
+ return YES;
- if (deferringGestureRecognizer == _deferringGestureRecognizerForImmediatelyResettableGestures) {
- if ([gestureRecognizer isKindOfClass:WKDeferringGestureRecognizer.class])
- return NO;
+ if (gesture == [_textInteractionAssistant loupeGesture])
+ return YES;
- if (gestureRecognizer == _touchEventGestureRecognizer)
- return NO;
+ if ([gesture isKindOfClass:UITapGestureRecognizer.class]) {
+ UITapGestureRecognizer *tapGesture = (UITapGestureRecognizer *)gesture;
+ return tapGesture.numberOfTapsRequired > 1 && tapGesture.numberOfTouchesRequired < 2;
+ }
- return !isOneFingerMultipleTapGesture(gestureRecognizer);
- }
+ return NO;
+ };
- ASSERT_NOT_REACHED();
-#endif
+ if (mayDelayResetOfContainingSubgraph(gestureRecognizer))
+ return deferringGestureRecognizer == _deferringGestureRecognizerForDelayedResettableGestures;
+ return deferringGestureRecognizer == _deferringGestureRecognizerForImmediatelyResettableGestures;
+#else
+ UNUSED_PARAM(deferringGestureRecognizer);
+ UNUSED_PARAM(gestureRecognizer);
return NO;
+#endif
}
#if ENABLE(DRAG_SUPPORT)