- Revision
- 259669
- Author
- [email protected]
- Date
- 2020-04-07 14:00:49 -0700 (Tue, 07 Apr 2020)
Log Message
Preventing touch events should not prevent gestures installed above WKWebView from recognizing
https://bugs.webkit.org/show_bug.cgi?id=210080
<rdar://problem/61365814>
Reviewed by Tim Horton.
Source/WebKit:
Makes a small adjustment to native gesture deferral logic, so that gestures installed above WKWebView (in the
view hierarchy) are not prevented from recognizing by WKDeferringGestureRecognizer. This makes it possible for
WebKit clients to install custom gestures outside of WKWebView that cannot be prevented by web content, without
having to create a separate window and pass touches through to the WKWebView.
Test: fast/events/touch/ios/prevent-default-with-window-tap-gesture.html
* UIProcess/ios/WKContentViewInteraction.mm:
(-[WKContentView deferringGestureRecognizer:shouldDeferOtherGestureRecognizer:]):
Tools:
Add a UIScriptController helper method that allows a test to install a tap gesture recognizer on the UIWindow
containing the web view. This method additionally takes a _javascript_ callback, which is invoked when the tap
gesture is recognized.
* TestRunnerShared/UIScriptContext/Bindings/UIScriptController.idl:
* TestRunnerShared/UIScriptContext/UIScriptContext.h:
* TestRunnerShared/UIScriptContext/UIScriptController.h:
(WTR::UIScriptController::installTapGestureOnWindow):
* WebKitTestRunner/cocoa/TestRunnerWKWebView.h:
* WebKitTestRunner/cocoa/TestRunnerWKWebView.mm:
(-[TestRunnerWKWebView resetInteractionCallbacks]):
(-[TestRunnerWKWebView didRecognizeTapOnWindow]):
(-[TestRunnerWKWebView windowTapRecognizedCallback]):
(-[TestRunnerWKWebView setWindowTapRecognizedCallback:]):
(-[TestRunnerWKWebView willMoveToWindow:]):
(-[TestRunnerWKWebView didMoveToWindow]):
(-[TestRunnerWKWebView gestureRecognizer:shouldRecognizeSimultaneouslyWithGestureRecognizer:]):
* WebKitTestRunner/ios/UIScriptControllerIOS.h:
* WebKitTestRunner/ios/UIScriptControllerIOS.mm:
(WTR::UIScriptControllerIOS::installTapGestureOnWindow):
LayoutTests:
Add a new layout test to verify that calling preventDefault() on touchstart doesn't prevent gesture recognizers
installed above the WKWebView from recognizing. To do this, we use the new UIScriptController method to add a
gesture recognizer to the window containing the web view, and then simulate a tap over an element that prevents
the touchstart event.
* fast/events/touch/ios/prevent-default-with-window-tap-gesture-expected.txt: Added.
* fast/events/touch/ios/prevent-default-with-window-tap-gesture.html: Added.
* resources/ui-helper.js:
(window.UIHelper.async activateElementAfterInstallingTapGestureOnWindow.return.new.Promise.):
(window.UIHelper.async activateElementAfterInstallingTapGestureOnWindow.return.new.Promise):
(window.UIHelper.async activateElementAfterInstallingTapGestureOnWindow):
(window.UIHelper):
Modified Paths
Added Paths
Diff
Modified: trunk/LayoutTests/ChangeLog (259668 => 259669)
--- trunk/LayoutTests/ChangeLog 2020-04-07 20:49:06 UTC (rev 259668)
+++ trunk/LayoutTests/ChangeLog 2020-04-07 21:00:49 UTC (rev 259669)
@@ -1,3 +1,24 @@
+2020-04-07 Wenson Hsieh <[email protected]>
+
+ Preventing touch events should not prevent gestures installed above WKWebView from recognizing
+ https://bugs.webkit.org/show_bug.cgi?id=210080
+ <rdar://problem/61365814>
+
+ Reviewed by Tim Horton.
+
+ Add a new layout test to verify that calling preventDefault() on touchstart doesn't prevent gesture recognizers
+ installed above the WKWebView from recognizing. To do this, we use the new UIScriptController method to add a
+ gesture recognizer to the window containing the web view, and then simulate a tap over an element that prevents
+ the touchstart event.
+
+ * fast/events/touch/ios/prevent-default-with-window-tap-gesture-expected.txt: Added.
+ * fast/events/touch/ios/prevent-default-with-window-tap-gesture.html: Added.
+ * resources/ui-helper.js:
+ (window.UIHelper.async activateElementAfterInstallingTapGestureOnWindow.return.new.Promise.):
+ (window.UIHelper.async activateElementAfterInstallingTapGestureOnWindow.return.new.Promise):
+ (window.UIHelper.async activateElementAfterInstallingTapGestureOnWindow):
+ (window.UIHelper):
+
2020-04-07 Truitt Savell <[email protected]>
Fix my conflict Markers
Added: trunk/LayoutTests/fast/events/touch/ios/prevent-default-with-window-tap-gesture-expected.txt (0 => 259669)
--- trunk/LayoutTests/fast/events/touch/ios/prevent-default-with-window-tap-gesture-expected.txt (rev 0)
+++ trunk/LayoutTests/fast/events/touch/ios/prevent-default-with-window-tap-gesture-expected.txt 2020-04-07 21:00:49 UTC (rev 259669)
@@ -0,0 +1,11 @@
+This test verifies that a gesture recognizer installed on the window containing the web view is not prevented from recognizing if the web page prevents default on touchstart. This test requires WebKitTestRunner.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS Tap gesture on window recognized
+PASS receivedTouchStart is true
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
Added: trunk/LayoutTests/fast/events/touch/ios/prevent-default-with-window-tap-gesture.html (0 => 259669)
--- trunk/LayoutTests/fast/events/touch/ios/prevent-default-with-window-tap-gesture.html (rev 0)
+++ trunk/LayoutTests/fast/events/touch/ios/prevent-default-with-window-tap-gesture.html 2020-04-07 21:00:49 UTC (rev 259669)
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script src=""
+<script src=""
+<style>
+ #target {
+ border: solid 1px tomato;
+ width: 200px;
+ height: 200px;
+ }
+</style>
+<script>
+ jsTestIsAsync = true;
+
+ addEventListener("load", async () => {
+ description("This test verifies that a gesture recognizer installed on the window containing the web view is not prevented from recognizing if the web page prevents default on touchstart. This test requires WebKitTestRunner.");
+
+ receivedTouchStart = false;
+ const target = document.getElementById("target");
+ target.addEventListener("touchstart", event => {
+ receivedTouchStart = true;
+ event.preventDefault();
+ });
+ await UIHelper.activateElementAfterInstallingTapGestureOnWindow(target);
+
+ testPassed("Tap gesture on window recognized");
+ shouldBeTrue("receivedTouchStart");
+ finishJSTest();
+ });
+</script>
+</head>
+<body>
+ <div id="target"></div>
+</body>
+</html>
\ No newline at end of file
Modified: trunk/LayoutTests/resources/ui-helper.js (259668 => 259669)
--- trunk/LayoutTests/resources/ui-helper.js 2020-04-07 20:49:06 UTC (rev 259668)
+++ trunk/LayoutTests/resources/ui-helper.js 2020-04-07 21:00:49 UTC (rev 259669)
@@ -1148,6 +1148,27 @@
})();`, resolve);
});
}
+
+ static async activateElementAfterInstallingTapGestureOnWindow(element)
+ {
+ if (!this.isWebKit2() || !this.isIOSFamily())
+ return activateElement(element);
+
+ const x = element.offsetLeft + element.offsetWidth / 2;
+ const y = element.offsetTop + element.offsetHeight / 2;
+ return new Promise(resolve => {
+ testRunner.runUIScript(`
+ (function() {
+ let progress = 0;
+ function incrementProgress() {
+ if (++progress == 2)
+ uiController.uiScriptComplete();
+ }
+ uiController.installTapGestureOnWindow(incrementProgress);
+ uiController.singleTapAtPoint(${x}, ${y}, incrementProgress);
+ })();`, resolve);
+ });
+ }
}
UIHelper.EventStreamBuilder = class {
Modified: trunk/Source/WebKit/ChangeLog (259668 => 259669)
--- trunk/Source/WebKit/ChangeLog 2020-04-07 20:49:06 UTC (rev 259668)
+++ trunk/Source/WebKit/ChangeLog 2020-04-07 21:00:49 UTC (rev 259669)
@@ -1,3 +1,21 @@
+2020-04-07 Wenson Hsieh <[email protected]>
+
+ Preventing touch events should not prevent gestures installed above WKWebView from recognizing
+ https://bugs.webkit.org/show_bug.cgi?id=210080
+ <rdar://problem/61365814>
+
+ Reviewed by Tim Horton.
+
+ Makes a small adjustment to native gesture deferral logic, so that gestures installed above WKWebView (in the
+ view hierarchy) are not prevented from recognizing by WKDeferringGestureRecognizer. This makes it possible for
+ WebKit clients to install custom gestures outside of WKWebView that cannot be prevented by web content, without
+ having to create a separate window and pass touches through to the WKWebView.
+
+ Test: fast/events/touch/ios/prevent-default-with-window-tap-gesture.html
+
+ * UIProcess/ios/WKContentViewInteraction.mm:
+ (-[WKContentView deferringGestureRecognizer:shouldDeferOtherGestureRecognizer:]):
+
2020-04-07 Brian Burg <[email protected]>
Web Automation: Automation.inspectBrowsingContext should bring Web Inspector to front automatically
Modified: trunk/Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm (259668 => 259669)
--- trunk/Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm 2020-04-07 20:49:06 UTC (rev 259668)
+++ trunk/Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm 2020-04-07 21:00:49 UTC (rev 259669)
@@ -6939,6 +6939,20 @@
if ([_webView _isNavigationSwipeGestureRecognizer:gestureRecognizer])
return NO;
+ auto webView = _webView.getAutoreleased();
+ auto view = gestureRecognizer.view;
+ BOOL gestureIsInstalledOnOrUnderWebView = NO;
+ while (view) {
+ if (view == webView) {
+ gestureIsInstalledOnOrUnderWebView = YES;
+ break;
+ }
+ view = view.superview;
+ }
+
+ if (!gestureIsInstalledOnOrUnderWebView)
+ return NO;
+
#if ENABLE(IOS_TOUCH_EVENTS)
auto isOneFingerMultipleTapGesture = [](UIGestureRecognizer *gesture) -> BOOL {
if (![gesture isKindOfClass:UITapGestureRecognizer.class])
Modified: trunk/Tools/ChangeLog (259668 => 259669)
--- trunk/Tools/ChangeLog 2020-04-07 20:49:06 UTC (rev 259668)
+++ trunk/Tools/ChangeLog 2020-04-07 21:00:49 UTC (rev 259669)
@@ -1,3 +1,32 @@
+2020-04-07 Wenson Hsieh <[email protected]>
+
+ Preventing touch events should not prevent gestures installed above WKWebView from recognizing
+ https://bugs.webkit.org/show_bug.cgi?id=210080
+ <rdar://problem/61365814>
+
+ Reviewed by Tim Horton.
+
+ Add a UIScriptController helper method that allows a test to install a tap gesture recognizer on the UIWindow
+ containing the web view. This method additionally takes a _javascript_ callback, which is invoked when the tap
+ gesture is recognized.
+
+ * TestRunnerShared/UIScriptContext/Bindings/UIScriptController.idl:
+ * TestRunnerShared/UIScriptContext/UIScriptContext.h:
+ * TestRunnerShared/UIScriptContext/UIScriptController.h:
+ (WTR::UIScriptController::installTapGestureOnWindow):
+ * WebKitTestRunner/cocoa/TestRunnerWKWebView.h:
+ * WebKitTestRunner/cocoa/TestRunnerWKWebView.mm:
+ (-[TestRunnerWKWebView resetInteractionCallbacks]):
+ (-[TestRunnerWKWebView didRecognizeTapOnWindow]):
+ (-[TestRunnerWKWebView windowTapRecognizedCallback]):
+ (-[TestRunnerWKWebView setWindowTapRecognizedCallback:]):
+ (-[TestRunnerWKWebView willMoveToWindow:]):
+ (-[TestRunnerWKWebView didMoveToWindow]):
+ (-[TestRunnerWKWebView gestureRecognizer:shouldRecognizeSimultaneouslyWithGestureRecognizer:]):
+ * WebKitTestRunner/ios/UIScriptControllerIOS.h:
+ * WebKitTestRunner/ios/UIScriptControllerIOS.mm:
+ (WTR::UIScriptControllerIOS::installTapGestureOnWindow):
+
2020-04-07 Alexey Shvayka <[email protected]>
test262/Runner.pm ignores "async" flag
Modified: trunk/Tools/TestRunnerShared/UIScriptContext/Bindings/UIScriptController.idl (259668 => 259669)
--- trunk/Tools/TestRunnerShared/UIScriptContext/Bindings/UIScriptController.idl 2020-04-07 20:49:06 UTC (rev 259668)
+++ trunk/Tools/TestRunnerShared/UIScriptContext/Bindings/UIScriptController.idl 2020-04-07 21:00:49 UTC (rev 259669)
@@ -322,6 +322,8 @@
void removeViewFromWindow(object callback);
void addViewToWindow(object callback);
+ void installTapGestureOnWindow(object callback);
+
void overridePreference(DOMString preference, DOMString value);
void setSafeAreaInsets(double top, double right, double bottom, double left);
Modified: trunk/Tools/TestRunnerShared/UIScriptContext/UIScriptContext.h (259668 => 259669)
--- trunk/Tools/TestRunnerShared/UIScriptContext/UIScriptContext.h 2020-04-07 20:49:06 UTC (rev 259668)
+++ trunk/Tools/TestRunnerShared/UIScriptContext/UIScriptContext.h 2020-04-07 21:00:49 UTC (rev 259669)
@@ -64,6 +64,7 @@
CallbackTypeDidShowContextMenu,
CallbackTypeDidDismissContextMenu,
CallbackTypeWillCreateNewPage,
+ CallbackTypeWindowTapRecognized,
CallbackTypeNonPersistent = firstNonPersistentCallbackID
} CallbackType;
Modified: trunk/Tools/TestRunnerShared/UIScriptContext/UIScriptController.h (259668 => 259669)
--- trunk/Tools/TestRunnerShared/UIScriptContext/UIScriptController.h 2020-04-07 20:49:06 UTC (rev 259668)
+++ trunk/Tools/TestRunnerShared/UIScriptContext/UIScriptController.h 2020-04-07 21:00:49 UTC (rev 259669)
@@ -117,6 +117,8 @@
virtual void removeViewFromWindow(JSValueRef) { notImplemented(); }
virtual void addViewToWindow(JSValueRef) { notImplemented(); }
+ virtual void installTapGestureOnWindow(JSValueRef) { notImplemented(); }
+
// Compositing
virtual JSObjectRef propertiesOfLayerWithID(uint64_t layerID) const { notImplemented(); return nullptr; }
Modified: trunk/Tools/WebKitTestRunner/cocoa/TestRunnerWKWebView.h (259668 => 259669)
--- trunk/Tools/WebKitTestRunner/cocoa/TestRunnerWKWebView.h 2020-04-07 20:49:06 UTC (rev 259668)
+++ trunk/Tools/WebKitTestRunner/cocoa/TestRunnerWKWebView.h 2020-04-07 21:00:49 UTC (rev 259669)
@@ -45,6 +45,7 @@
@property (nonatomic, copy) void (^didDismissPopoverCallback)(void);
@property (nonatomic, copy) void (^didEndScrollingCallback)(void);
@property (nonatomic, copy) void (^rotationDidEndCallback)(void);
+@property (nonatomic, copy) void (^windowTapRecognizedCallback)(void);
@property (nonatomic, copy) NSString *accessibilitySpeakSelectionContent;
- (void)setAllowedMenuActions:(NSArray<NSString *> *)actions;
Modified: trunk/Tools/WebKitTestRunner/cocoa/TestRunnerWKWebView.mm (259668 => 259669)
--- trunk/Tools/WebKitTestRunner/cocoa/TestRunnerWKWebView.mm 2020-04-07 20:49:06 UTC (rev 259668)
+++ trunk/Tools/WebKitTestRunner/cocoa/TestRunnerWKWebView.mm 2020-04-07 21:00:49 UTC (rev 259669)
@@ -55,12 +55,20 @@
BlockPtr<void()> callback;
};
-@interface TestRunnerWKWebView () <WKUIDelegatePrivate> {
+@interface TestRunnerWKWebView () <WKUIDelegatePrivate
+#if PLATFORM(IOS_FAMILY)
+ , UIGestureRecognizerDelegate
+#endif
+> {
RetainPtr<NSNumber> m_stableStateOverride;
BOOL _isInteractingWithFormControl;
BOOL _scrollingUpdatesDisabled;
Optional<CustomMenuActionInfo> _customMenuActionInfo;
RetainPtr<NSArray<NSString *>> _allowedMenuActions;
+#if PLATFORM(IOS_FAMILY)
+ RetainPtr<UITapGestureRecognizer> _windowTapGestureRecognizer;
+ BlockPtr<void()> _windowTapRecognizedCallback;
+#endif
}
@property (nonatomic, copy) void (^zoomToScaleCompletionHandler)(void);
@@ -172,6 +180,7 @@
self.didDismissPopoverCallback = nil;
self.didEndScrollingCallback = nil;
self.rotationDidEndCallback = nil;
+ self.windowTapRecognizedCallback = nil;
#endif // PLATFORM(IOS_FAMILY)
}
@@ -409,6 +418,50 @@
self.rotationDidEndCallback();
}
+- (void)didRecognizeTapOnWindow
+{
+ ASSERT(self.windowTapRecognizedCallback);
+ if (self.windowTapRecognizedCallback)
+ self.windowTapRecognizedCallback();
+}
+
+- (void(^)())windowTapRecognizedCallback
+{
+ return _windowTapRecognizedCallback.get();
+}
+
+- (void)setWindowTapRecognizedCallback:(void(^)())windowTapRecognizedCallback
+{
+ _windowTapRecognizedCallback = windowTapRecognizedCallback;
+
+ if (windowTapRecognizedCallback && !_windowTapGestureRecognizer) {
+ ASSERT(self.window);
+ _windowTapGestureRecognizer = adoptNS([[UITapGestureRecognizer alloc] init]);
+ [_windowTapGestureRecognizer setDelegate:self];
+ [_windowTapGestureRecognizer addTarget:self action:@selector(didRecognizeTapOnWindow)];
+ [self.window addGestureRecognizer:_windowTapGestureRecognizer.get()];
+ } else if (!windowTapRecognizedCallback && _windowTapGestureRecognizer) {
+ [self.window removeGestureRecognizer:_windowTapGestureRecognizer.get()];
+ _windowTapGestureRecognizer = nil;
+ }
+}
+
+- (void)willMoveToWindow:(UIWindow *)window
+{
+ [super willMoveToWindow:window];
+
+ if (_windowTapGestureRecognizer)
+ [self.window removeGestureRecognizer:_windowTapGestureRecognizer.get()];
+}
+
+- (void)didMoveToWindow
+{
+ [super didMoveToWindow];
+
+ if (_windowTapGestureRecognizer)
+ [self.window addGestureRecognizer:_windowTapGestureRecognizer.get()];
+}
+
- (void)_accessibilityDidGetSpeakSelectionContent:(NSString *)content
{
self.accessibilitySpeakSelectionContent = content;
@@ -449,6 +502,13 @@
[self _invokeHideKeyboardCallbackIfNecessary];
}
+#pragma mark - UIGestureRecognizerDelegate
+
+- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
+{
+ return gestureRecognizer == _windowTapGestureRecognizer;
+}
+
#endif // PLATFORM(IOS_FAMILY)
@end
Modified: trunk/Tools/WebKitTestRunner/ios/UIScriptControllerIOS.h (259668 => 259669)
--- trunk/Tools/WebKitTestRunner/ios/UIScriptControllerIOS.h 2020-04-07 20:49:06 UTC (rev 259668)
+++ trunk/Tools/WebKitTestRunner/ios/UIScriptControllerIOS.h 2020-04-07 21:00:49 UTC (rev 259669)
@@ -140,6 +140,7 @@
void setHardwareKeyboardAttached(bool) override;
void setAllowsViewportShrinkToFit(bool) override;
void copyText(JSStringRef) override;
+ void installTapGestureOnWindow(JSValueRef) override;
void setDidStartFormControlInteractionCallback(JSValueRef) override;
void setDidEndFormControlInteractionCallback(JSValueRef) override;
Modified: trunk/Tools/WebKitTestRunner/ios/UIScriptControllerIOS.mm (259668 => 259669)
--- trunk/Tools/WebKitTestRunner/ios/UIScriptControllerIOS.mm 2020-04-07 20:49:06 UTC (rev 259668)
+++ trunk/Tools/WebKitTestRunner/ios/UIScriptControllerIOS.mm 2020-04-07 21:00:49 UTC (rev 259669)
@@ -1263,6 +1263,16 @@
UIPasteboard.generalPasteboard.string = text->string();
}
+void UIScriptControllerIOS::installTapGestureOnWindow(JSValueRef callback)
+{
+ m_context->registerCallback(callback, CallbackTypeWindowTapRecognized);
+ webView().windowTapRecognizedCallback = makeBlockPtr([this, strongThis = makeRef(*this)] {
+ if (!m_context)
+ return;
+ m_context->fireCallback(CallbackTypeWindowTapRecognized);
+ }).get();
}
+}
+
#endif // PLATFORM(IOS_FAMILY)