WebKit Bugzilla
Attachment 361537 Details for
Bug 194271
: Allow pages to trigger programmatic paste from script on iOS
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Requests
|
Help
|
New Account
|
Log In
Remember
[x]
|
Forgot Password
Login:
[x]
[patch]
Part 2 (tests & minor tweaks)
bug-194271-20190208144507.patch (text/plain), 57.98 KB, created by
Wenson Hsieh
on 2019-02-08 14:45:09 PST
(
hide
)
Description:
Part 2 (tests & minor tweaks)
Filename:
MIME Type:
Creator:
Wenson Hsieh
Created:
2019-02-08 14:45:09 PST
Size:
57.98 KB
patch
obsolete
>Subversion Revision: 241183 >diff --git a/Source/WebCore/ChangeLog b/Source/WebCore/ChangeLog >index 9dd911702c177791d9a39a7ed458e6a2b64c3fea..b70456a80ce4a7e72c4eef59c05f1527c6878576 100644 >--- a/Source/WebCore/ChangeLog >+++ b/Source/WebCore/ChangeLog >@@ -1,3 +1,25 @@ >+2019-02-08 Wenson Hsieh <wenson_hsieh@apple.com> >+ >+ Allow pages to trigger programmatic paste from script on iOS >+ https://bugs.webkit.org/show_bug.cgi?id=194271 >+ <rdar://problem/47808810> >+ >+ Reviewed by NOBODY (OOPS!). >+ >+ Tests: editing/pasteboard/ios/dom-paste-confirmation.html >+ editing/pasteboard/ios/dom-paste-consecutive-confirmations.html >+ editing/pasteboard/ios/dom-paste-rejection.html >+ editing/pasteboard/ios/dom-paste-requires-user-gesture.html >+ >+ * dom/UserGestureIndicator.cpp: >+ (WebCore::UserGestureIndicator::~UserGestureIndicator): >+ >+ Reset a gesture token's DOM paste access when exiting the scope of a user gesture. This prevents DOM paste >+ access permissions from leaking into `setTimeout()` callbacks when we forward user gesture tokens. >+ >+ * dom/UserGestureIndicator.h: >+ (WebCore::UserGestureToken::resetDOMPasteAccess): >+ > 2019-02-07 Wenson Hsieh <wenson_hsieh@apple.com> > > Allow pages to trigger programmatic paste from script on iOS >diff --git a/Source/WebCore/PAL/ChangeLog b/Source/WebCore/PAL/ChangeLog >index 8d7d1d2138f772effa84bba80ef10592f8bf448f..82f585d85b328a94be07c70094a6c488cff36cde 100644 >--- a/Source/WebCore/PAL/ChangeLog >+++ b/Source/WebCore/PAL/ChangeLog >@@ -1,3 +1,15 @@ >+2019-02-08 Wenson Hsieh <wenson_hsieh@apple.com> >+ >+ Allow pages to trigger programmatic paste from script on iOS >+ https://bugs.webkit.org/show_bug.cgi?id=194271 >+ <rdar://problem/47808810> >+ >+ Reviewed by NOBODY (OOPS!). >+ >+ Add some SPI declarations so that we can query for the shared UICalloutBar's arrow direction. >+ >+ * pal/spi/ios/UIKitSPI.h: >+ > 2019-02-07 Chris Dumez <cdumez@apple.com> > > Mark more heap-allocated classes as fast allocated >diff --git a/Source/WebKit/ChangeLog b/Source/WebKit/ChangeLog >index c5539757e7352376c4fdb8e44018e56323c2b0e3..a063dbd12370aa6796be49fc953e9ce0e45455a3 100644 >--- a/Source/WebKit/ChangeLog >+++ b/Source/WebKit/ChangeLog >@@ -1,3 +1,23 @@ >+2019-02-08 Wenson Hsieh <wenson_hsieh@apple.com> >+ >+ Allow pages to trigger programmatic paste from script on iOS >+ https://bugs.webkit.org/show_bug.cgi?id=194271 >+ <rdar://problem/47808810> >+ >+ Reviewed by NOBODY (OOPS!). >+ >+ Cancel the pending DOM paste access handler when the menu is about to hide, rather than when the hiding >+ animation has completed. This ensures that if the page (on behalf of the user) requests DOM paste again during >+ user interaction before the callout bar has finished fading after the previous DOM paste, we won't automatically >+ cancel the incoming DOM paste access request because the callout bar animation finished. >+ >+ This scenario is exercised in the layout test editing/pasteboard/ios/dom-paste-consecutive-confirmations.html. >+ >+ * UIProcess/ios/WKContentViewInteraction.mm: >+ (-[WKContentView setupInteraction]): >+ (-[WKContentView _willHideMenu:]): >+ (-[WKContentView _didHideMenu:]): >+ > 2019-02-07 Wenson Hsieh <wenson_hsieh@apple.com> > > Allow pages to trigger programmatic paste from script on iOS >diff --git a/Source/WebCore/PAL/pal/spi/ios/UIKitSPI.h b/Source/WebCore/PAL/pal/spi/ios/UIKitSPI.h >index e1d46ac955352a4037784cc12c871e61a0737cb7..7fe072f8638f5125c06ea6b05d8b1cdceaf329b6 100644 >--- a/Source/WebCore/PAL/pal/spi/ios/UIKitSPI.h >+++ b/Source/WebCore/PAL/pal/spi/ios/UIKitSPI.h >@@ -134,6 +134,20 @@ typedef enum { > + (UIViewController *)viewControllerForView:(UIView *)view; > @end > >+@interface UICalloutBar : UIView >++ (UICalloutBar *)sharedCalloutBar; >+@property (nonatomic, readonly) UIMenuControllerArrowDirection targetDirection; >+@end >+ >+@interface UIApplicationRotationFollowingWindow : UIWindow >+@end >+ >+@interface UIAutoRotatingWindow : UIApplicationRotationFollowingWindow >+@end >+ >+@interface UITextEffectsWindow : UIAutoRotatingWindow >+@end >+ > NS_ASSUME_NONNULL_END > > #if __IPHONE_OS_VERSION_MIN_REQUIRED >= 110000 >diff --git a/Source/WebCore/dom/UserGestureIndicator.cpp b/Source/WebCore/dom/UserGestureIndicator.cpp >index f691d386d6bad1ae989d489cf095dc3e47380267..35665dd1c31d1d208274da8a938d62ade521a95d 100644 >--- a/Source/WebCore/dom/UserGestureIndicator.cpp >+++ b/Source/WebCore/dom/UserGestureIndicator.cpp >@@ -87,6 +87,9 @@ UserGestureIndicator::~UserGestureIndicator() > if (!isMainThread()) > return; > >+ if (auto token = currentToken()) >+ token->resetDOMPasteAccess(); >+ > currentToken() = m_previousToken; > } > >diff --git a/Source/WebCore/dom/UserGestureIndicator.h b/Source/WebCore/dom/UserGestureIndicator.h >index 3e5ea4306325a245b76de5236ce56604de9df6e6..ddcbdd6490066875de58dd4006b57534c63e3089 100644 >--- a/Source/WebCore/dom/UserGestureIndicator.h >+++ b/Source/WebCore/dom/UserGestureIndicator.h >@@ -66,6 +66,7 @@ public: > > DOMPasteAccessPolicy domPasteAccessPolicy() const { return m_domPasteAccessPolicy; } > void didRequestDOMPasteAccess(bool granted) { m_domPasteAccessPolicy = granted ? DOMPasteAccessPolicy::Granted : DOMPasteAccessPolicy::Denied; } >+ void resetDOMPasteAccess() { m_domPasteAccessPolicy = DOMPasteAccessPolicy::NotRequestedYet; } > > private: > UserGestureToken(ProcessingUserGestureState state, UserGestureType gestureType) >diff --git a/Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm b/Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm >index 62fc7a62348528e050ac982086866927f76aabd5..61abea0fbe7057ce3f268645db1e2908eb1ba94e 100644 >--- a/Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm >+++ b/Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm >@@ -740,6 +740,7 @@ - (void)setupInteraction > #endif > > NSNotificationCenter *center = [NSNotificationCenter defaultCenter]; >+ [center addObserver:self selector:@selector(_willHideMenu:) name:UIMenuControllerWillHideMenuNotification object:nil]; > [center addObserver:self selector:@selector(_didHideMenu:) name:UIMenuControllerDidHideMenuNotification object:nil]; > [center addObserver:self selector:@selector(_keyboardDidRequestDismissal:) name:UIKeyboardPrivateDidRequestDismissalNotification object:nil]; > >@@ -2806,11 +2807,15 @@ - (id)targetForActionForWebView:(SEL)action withSender:(id)sender > return [super targetForAction:action withSender:sender]; > } > >+- (void)_willHideMenu:(NSNotification *)notification >+{ >+ [self _handleDOMPasteRequestWithResult:NO]; >+} >+ > - (void)_didHideMenu:(NSNotification *)notification > { > _showingTextStyleOptions = NO; > [_textSelectionAssistant hideTextStyleOptions]; >- [self _handleDOMPasteRequestWithResult:NO]; > } > > - (void)_keyboardDidRequestDismissal:(NSNotification *)notification >diff --git a/Tools/ChangeLog b/Tools/ChangeLog >index 3014a68edbb701749eb1fb62c5b65052245760c3..169776aaa42b8c4ded5e652f7f9bbaff5b5b8517 100644 >--- a/Tools/ChangeLog >+++ b/Tools/ChangeLog >@@ -1,3 +1,96 @@ >+2019-02-08 Wenson Hsieh <wenson_hsieh@apple.com> >+ >+ Allow pages to trigger programmatic paste from script on iOS >+ https://bugs.webkit.org/show_bug.cgi?id=194271 >+ <rdar://problem/47808810> >+ >+ Reviewed by NOBODY (OOPS!). >+ >+ Add support for interacting with the callout bar on iOS during layout tests. See below for more detail. >+ >+ * DumpRenderTree/ios/UIScriptControllerIOS.mm: >+ (WTR::UIScriptController::platformSetDidShowMenuCallback): >+ (WTR::UIScriptController::platformSetDidHideMenuCallback): >+ (WTR::UIScriptController::rectForMenuAction const): >+ >+ Add new mechanisms to make it possible to interact with and query the state of the callout menu on iOS. This >+ includes determining the rect (in content view coordinates) of the menu's controls, and callbacks to register >+ for when the menu is shown or hidden. >+ >+ * TestRunnerShared/UIScriptContext/Bindings/UIScriptController.idl: >+ * TestRunnerShared/UIScriptContext/UIScriptContext.h: >+ * TestRunnerShared/UIScriptContext/UIScriptController.cpp: >+ (WTR::UIScriptController::setDidShowMenuCallback): >+ (WTR::UIScriptController::didShowMenuCallback const): >+ (WTR::UIScriptController::setDidHideMenuCallback): >+ (WTR::UIScriptController::didHideMenuCallback const): >+ (WTR::UIScriptController::platformSetDidShowMenuCallback): >+ (WTR::UIScriptController::platformSetDidHideMenuCallback): >+ (WTR::UIScriptController::rectForMenuAction const): >+ * TestRunnerShared/UIScriptContext/UIScriptController.h: >+ * WebKitTestRunner/TestController.cpp: >+ (WTR::TestController::resetPreferencesToConsistentValues): >+ (WTR::updateTestOptionsFromTestHeader): >+ * WebKitTestRunner/TestOptions.h: >+ >+ Add a new test option to determine whether DOM paste is enabled. DOM paste is currently enabled everywhere by >+ default, but these new programmatic paste tests require it to be disabled in order for confirmation UI to show. >+ >+ (WTR::TestOptions::hasSameInitializationOptions const): >+ * WebKitTestRunner/UIScriptControllerCocoa.mm: >+ (WTR::UIScriptController::calendarType const): >+ (WTR::UIScriptController::platformUndoManager const): >+ * WebKitTestRunner/cocoa/TestRunnerWKWebView.h: >+ * WebKitTestRunner/cocoa/TestRunnerWKWebView.mm: >+ (-[TestRunnerWKWebView initWithFrame:configuration:]): >+ (-[TestRunnerWKWebView dealloc]): >+ (-[TestRunnerWKWebView _didShowMenu]): >+ (-[TestRunnerWKWebView _didHideMenu]): >+ >+ Listen to when the callout bar is presented and dismissed, and invoke testing callbacks as needed. >+ >+ * WebKitTestRunner/ios/TestControllerIOS.mm: >+ (WTR::handleMenuWillHideNotification): >+ (WTR::handleMenuDidHideNotification): >+ (WTR::TestController::platformInitialize): >+ (WTR::TestController::platformDestroy): >+ (WTR::TestController::platformResetStateToConsistentValues): >+ >+ Additionally ensure that any callout menu presented by a previous layout test is dismissed before running the >+ next test by hiding the callout bar if necessary, and then waiting for the "DidHide" notification. >+ >+ * WebKitTestRunner/ios/UIScriptControllerIOS.mm: >+ (WTR::forEachViewInHierarchy): >+ (WTR::findViewInHierarchyOfType): >+ >+ Move `forEachViewInHierarchy` so that we can use it throughout the file, and then add some additional helper >+ functions that dig through a given view's hierarchy in search of a view of a given class. >+ >+ (WTR::UIScriptController::selectionStartGrabberViewRect const): >+ (WTR::UIScriptController::selectionEndGrabberViewRect const): >+ (WTR::UIScriptController::selectionCaretViewRect const): >+ (WTR::UIScriptController::selectionRangeViewRects const): >+ (WTR::UIScriptController::platformSetDidShowMenuCallback): >+ (WTR::UIScriptController::platformSetDidHideMenuCallback): >+ >+ Tweak these to use `platformContentView` instead of grabbing the content view from WKWebView directly. >+ >+ (WTR::UIScriptController::rectForMenuAction const): >+ >+ Add a new UIScriptController method to get the rect of the action in the contextual menu (on iOS, this is the >+ callout bar) whose label matches the given string. >+ >+ (WTR::UIScriptController::platformContentView const): >+ >+ Add a `platformContentView()` helper on UIScriptController so that we can stop grabbing the value for key >+ "_currentContentView" from various places in this file. Additionally, rewrite `platformUndoManager()` in terms >+ of this new helper, and move the code out from iOS/macOS-specific files into UIScriptControllerCocoa. >+ >+ (WTR::UIScriptController::platformUndoManager const): Deleted. >+ * WebKitTestRunner/mac/UIScriptControllerMac.mm: >+ (WTR::UIScriptController::platformContentView const): >+ (WTR::UIScriptController::platformUndoManager const): Deleted. >+ > 2019-02-07 Jonathan Bedard <jbedard@apple.com> > > webkitpy: Respect --dedicated-simulators flag >diff --git a/Tools/DumpRenderTree/ios/UIScriptControllerIOS.mm b/Tools/DumpRenderTree/ios/UIScriptControllerIOS.mm >index db11ad641562d1d3a3164294b65446df8f53a39c..10cb0e9ec20053f307f2d410e4cd37719d5d1f1c 100644 >--- a/Tools/DumpRenderTree/ios/UIScriptControllerIOS.mm >+++ b/Tools/DumpRenderTree/ios/UIScriptControllerIOS.mm >@@ -311,6 +311,19 @@ void UIScriptController::platformSetDidHideKeyboardCallback() > { > } > >+void UIScriptController::platformSetDidShowMenuCallback() >+{ >+} >+ >+void UIScriptController::platformSetDidHideMenuCallback() >+{ >+} >+ >+JSObjectRef UIScriptController::rectForMenuAction(JSStringRef) const >+{ >+ return nullptr; >+} >+ > void UIScriptController::platformSetDidEndScrollingCallback() > { > } >diff --git a/Tools/TestRunnerShared/UIScriptContext/Bindings/UIScriptController.idl b/Tools/TestRunnerShared/UIScriptContext/Bindings/UIScriptController.idl >index e54467ff448a0a7c37070e9b7d0f18825c943143..ccfb98dc2566ff1cbf17889e7b8b6eb2c49de934 100644 >--- a/Tools/TestRunnerShared/UIScriptContext/Bindings/UIScriptController.idl >+++ b/Tools/TestRunnerShared/UIScriptContext/Bindings/UIScriptController.idl >@@ -221,6 +221,10 @@ interface UIScriptController { > attribute object didHideKeyboardCallback; > readonly attribute boolean isShowingKeyboard; > >+ attribute object didShowMenuCallback; >+ attribute object didHideMenuCallback; >+ object rectForMenuAction(DOMString action); >+ > attribute object willBeginZoomingCallback; > attribute object didEndZoomingCallback; > >diff --git a/Tools/TestRunnerShared/UIScriptContext/UIScriptContext.h b/Tools/TestRunnerShared/UIScriptContext/UIScriptContext.h >index 4ec18293a0331155ff9cb2354ac555ba1408dbbc..8040647a23f237618a3ebd65d76227e8c4a2b6df 100644 >--- a/Tools/TestRunnerShared/UIScriptContext/UIScriptContext.h >+++ b/Tools/TestRunnerShared/UIScriptContext/UIScriptContext.h >@@ -54,6 +54,8 @@ typedef enum { > CallbackTypeDidEndZooming, > CallbackTypeDidShowKeyboard, > CallbackTypeDidHideKeyboard, >+ CallbackTypeDidShowMenu, >+ CallbackTypeDidHideMenu, > CallbackTypeDidEndScrolling, > CallbackTypeDidStartFormControlInteraction, > CallbackTypeDidEndFormControlInteraction, >diff --git a/Tools/TestRunnerShared/UIScriptContext/UIScriptController.cpp b/Tools/TestRunnerShared/UIScriptContext/UIScriptController.cpp >index 4b45d56fdc2cb2ca4dae0f66dd1f527eefe7c39e..05c4727ffddad1e456e52828c9c9e359cd0949ac 100644 >--- a/Tools/TestRunnerShared/UIScriptContext/UIScriptController.cpp >+++ b/Tools/TestRunnerShared/UIScriptContext/UIScriptController.cpp >@@ -205,6 +205,28 @@ JSValueRef UIScriptController::didHideKeyboardCallback() const > return m_context->callbackWithID(CallbackTypeDidHideKeyboard); > } > >+void UIScriptController::setDidShowMenuCallback(JSValueRef callback) >+{ >+ m_context->registerCallback(callback, CallbackTypeDidShowMenu); >+ platformSetDidShowMenuCallback(); >+} >+ >+JSValueRef UIScriptController::didShowMenuCallback() const >+{ >+ return m_context->callbackWithID(CallbackTypeDidShowMenu); >+} >+ >+void UIScriptController::setDidHideMenuCallback(JSValueRef callback) >+{ >+ m_context->registerCallback(callback, CallbackTypeDidHideMenu); >+ platformSetDidHideMenuCallback(); >+} >+ >+JSValueRef UIScriptController::didHideMenuCallback() const >+{ >+ return m_context->callbackWithID(CallbackTypeDidHideMenu); >+} >+ > #if !PLATFORM(COCOA) > > void UIScriptController::zoomToScale(double, JSValueRef) >@@ -489,6 +511,19 @@ void UIScriptController::platformSetDidHideKeyboardCallback() > { > } > >+void UIScriptController::platformSetDidShowMenuCallback() >+{ >+} >+ >+void UIScriptController::platformSetDidHideMenuCallback() >+{ >+} >+ >+JSObjectRef UIScriptController::rectForMenuAction(JSStringRef) const >+{ >+ return nullptr; >+} >+ > void UIScriptController::platformClearAllCallbacks() > { > } >diff --git a/Tools/TestRunnerShared/UIScriptContext/UIScriptController.h b/Tools/TestRunnerShared/UIScriptContext/UIScriptController.h >index 956c345c32f61351cfede1143f4a61c00e66c740..c52ba7b5a6a1bca725d434e8633fa8ddaea83553 100644 >--- a/Tools/TestRunnerShared/UIScriptContext/UIScriptController.h >+++ b/Tools/TestRunnerShared/UIScriptContext/UIScriptController.h >@@ -32,6 +32,8 @@ > #include <wtf/Ref.h> > > OBJC_CLASS NSUndoManager; >+OBJC_CLASS NSView; >+OBJC_CLASS UIView; > > namespace WebCore { > class FloatRect; >@@ -152,6 +154,14 @@ public: > > bool isShowingKeyboard() const; > >+ void setDidHideMenuCallback(JSValueRef); >+ JSValueRef didHideMenuCallback() const; >+ >+ void setDidShowMenuCallback(JSValueRef); >+ JSValueRef didShowMenuCallback() const; >+ >+ JSObjectRef rectForMenuAction(JSStringRef action) const; >+ > void setDidEndScrollingCallback(JSValueRef); > JSValueRef didEndScrollingCallback() const; > >@@ -226,6 +236,8 @@ private: > void platformSetDidEndZoomingCallback(); > void platformSetDidShowKeyboardCallback(); > void platformSetDidHideKeyboardCallback(); >+ void platformSetDidShowMenuCallback(); >+ void platformSetDidHideMenuCallback(); > void platformSetDidEndScrollingCallback(); > void platformClearAllCallbacks(); > void platformPlayBackEventStream(JSStringRef, JSValueRef); >@@ -234,6 +246,13 @@ private: > NSUndoManager *platformUndoManager() const; > #endif > >+#if PLATFORM(IOS_FAMILY) >+ UIView *platformContentView() const; >+#endif >+#if PLATFORM(MAC) >+ NSView *platformContentView() const; >+#endif >+ > JSClassRef wrapperClass() final; > > JSObjectRef objectFromRect(const WebCore::FloatRect&) const; >diff --git a/Tools/WebKitTestRunner/TestController.cpp b/Tools/WebKitTestRunner/TestController.cpp >index 4cc4cccc0f023876de4a00dab367ec8c681ac29b..5673322af6fe5f197c3c2b84d99f61dc935ac237 100644 >--- a/Tools/WebKitTestRunner/TestController.cpp >+++ b/Tools/WebKitTestRunner/TestController.cpp >@@ -745,7 +745,7 @@ void TestController::resetPreferencesToConsistentValues(const TestOptions& optio > WKPreferencesSetJavaScriptRuntimeFlags(preferences, kWKJavaScriptRuntimeFlagsAllEnabled); > WKPreferencesSetJavaScriptCanOpenWindowsAutomatically(preferences, true); > WKPreferencesSetJavaScriptCanAccessClipboard(preferences, true); >- WKPreferencesSetDOMPasteAllowed(preferences, true); >+ WKPreferencesSetDOMPasteAllowed(preferences, options.domPasteAllowed); > WKPreferencesSetUniversalAccessFromFileURLsAllowed(preferences, true); > WKPreferencesSetFileAccessFromFileURLsAllowed(preferences, true); > #if ENABLE(FULLSCREEN_API) >@@ -1262,6 +1262,8 @@ static void updateTestOptionsFromTestHeader(TestOptions& testOptions, const std: > testOptions.applicationManifest = parseStringTestHeaderValueAsRelativePath(value, pathOrURL); > else if (key == "allowCrossOriginSubresourcesToAskForCredentials") > testOptions.allowCrossOriginSubresourcesToAskForCredentials = parseBooleanTestHeaderValue(value); >+ else if (key == "domPasteAllowed") >+ testOptions.domPasteAllowed = parseBooleanTestHeaderValue(value); > else if (key == "enableProcessSwapOnNavigation") > testOptions.enableProcessSwapOnNavigation = parseBooleanTestHeaderValue(value); > else if (key == "enableProcessSwapOnWindowOpen") >diff --git a/Tools/WebKitTestRunner/TestOptions.h b/Tools/WebKitTestRunner/TestOptions.h >index 0ac586eb1eabe5e1901cad638b648328a74ea882..392d8e70b409e88e5924e3fddc0de77628dd91d4 100644 >--- a/Tools/WebKitTestRunner/TestOptions.h >+++ b/Tools/WebKitTestRunner/TestOptions.h >@@ -57,6 +57,7 @@ struct TestOptions { > bool shouldShowTouches { false }; > bool dumpJSConsoleLogInStdErr { false }; > bool allowCrossOriginSubresourcesToAskForCredentials { false }; >+ bool domPasteAllowed { true }; > bool enableProcessSwapOnNavigation { true }; > bool enableProcessSwapOnWindowOpen { false }; > bool enableColorFilter { false }; >@@ -105,6 +106,7 @@ struct TestOptions { > || dumpJSConsoleLogInStdErr != options.dumpJSConsoleLogInStdErr > || applicationManifest != options.applicationManifest > || allowCrossOriginSubresourcesToAskForCredentials != options.allowCrossOriginSubresourcesToAskForCredentials >+ || domPasteAllowed != options.domPasteAllowed > || enableProcessSwapOnNavigation != options.enableProcessSwapOnNavigation > || enableProcessSwapOnWindowOpen != options.enableProcessSwapOnWindowOpen > || enableColorFilter != options.enableColorFilter >diff --git a/Tools/WebKitTestRunner/UIScriptControllerCocoa.mm b/Tools/WebKitTestRunner/UIScriptControllerCocoa.mm >index 702e5185523314be8d975edcea194b841f0fa48e..f12cadb2f820659ef0c3f3572f0037cb8fbaa21c 100644 >--- a/Tools/WebKitTestRunner/UIScriptControllerCocoa.mm >+++ b/Tools/WebKitTestRunner/UIScriptControllerCocoa.mm >@@ -183,8 +183,7 @@ void UIScriptController::setDefaultCalendarType(JSStringRef calendarIdentifier) > JSObjectRef UIScriptController::calendarType() const > { > #if WK_API_ENABLED >- WKWebView *webView = TestController::singleton().mainWebView()->platformView(); >- UIView *contentView = [webView valueForKeyPath:@"_currentContentView"]; >+ UIView *contentView = platformContentView(); > NSString *calendarTypeString = [contentView valueForKeyPath:@"formInputControl.dateTimePickerCalendarType"]; > auto jsContext = m_context->jsContext(); > return JSValueToObject(jsContext, [JSValue valueWithObject:calendarTypeString inContext:[JSContext contextWithJSGlobalContextRef:jsContext]].JSValueRef, nullptr); >@@ -203,4 +202,9 @@ JSRetainPtr<JSStringRef> UIScriptController::firstRedoLabel() const > return JSStringCreateWithCFString((__bridge CFStringRef)platformUndoManager().redoActionName); > } > >+NSUndoManager *UIScriptController::platformUndoManager() const >+{ >+ return platformContentView().undoManager; >+} >+ > } // namespace WTR >diff --git a/Tools/WebKitTestRunner/cocoa/TestRunnerWKWebView.h b/Tools/WebKitTestRunner/cocoa/TestRunnerWKWebView.h >index 5bc2cb7288bd075b31cf7dc08b03360707d369d7..28d57ac2a66ceb10e6150e11b68da0c2c9ae7688 100644 >--- a/Tools/WebKitTestRunner/cocoa/TestRunnerWKWebView.h >+++ b/Tools/WebKitTestRunner/cocoa/TestRunnerWKWebView.h >@@ -43,6 +43,8 @@ > @property (nonatomic, copy) void (^didEndZoomingCallback)(void); > @property (nonatomic, copy) void (^didShowKeyboardCallback)(void); > @property (nonatomic, copy) void (^didHideKeyboardCallback)(void); >+@property (nonatomic, copy) void (^didShowMenuCallback)(void); >+@property (nonatomic, copy) void (^didHideMenuCallback)(void); > @property (nonatomic, copy) void (^didEndScrollingCallback)(void); > @property (nonatomic, copy) void (^rotationDidEndCallback)(void); > @property (nonatomic, copy) NSString *accessibilitySpeakSelectionContent; >diff --git a/Tools/WebKitTestRunner/cocoa/TestRunnerWKWebView.mm b/Tools/WebKitTestRunner/cocoa/TestRunnerWKWebView.mm >index fbfca09d8d351e9417bef8340d2f13a03cd550d0..155fd69a7ecec245af9175e88b0157411f008d43 100644 >--- a/Tools/WebKitTestRunner/cocoa/TestRunnerWKWebView.mm >+++ b/Tools/WebKitTestRunner/cocoa/TestRunnerWKWebView.mm >@@ -55,6 +55,7 @@ @interface TestRunnerWKWebView () <WKUIDelegatePrivate> { > @property (nonatomic, copy) void (^zoomToScaleCompletionHandler)(void); > @property (nonatomic, copy) void (^retrieveSpeakSelectionContentCompletionHandler)(void); > @property (nonatomic, getter=isShowingKeyboard, setter=setIsShowingKeyboard:) BOOL showingKeyboard; >+@property (nonatomic, getter=isShowingMenu, setter=setIsShowingMenu:) BOOL showingMenu; > > @end > >@@ -77,7 +78,8 @@ - (instancetype)initWithFrame:(CGRect)frame configuration:(WKWebViewConfiguratio > NSNotificationCenter* center = [NSNotificationCenter defaultCenter]; > [center addObserver:self selector:@selector(_invokeShowKeyboardCallbackIfNecessary) name:UIKeyboardDidShowNotification object:nil]; > [center addObserver:self selector:@selector(_invokeHideKeyboardCallbackIfNecessary) name:UIKeyboardDidHideNotification object:nil]; >- >+ [center addObserver:self selector:@selector(_didShowMenu) name:UIMenuControllerDidShowMenuNotification object:nil]; >+ [center addObserver:self selector:@selector(_didHideMenu) name:UIMenuControllerDidHideMenuNotification object:nil]; > self.UIDelegate = self; > } > return self; >@@ -95,6 +97,8 @@ - (void)dealloc > self.didEndZoomingCallback = nil; > self.didShowKeyboardCallback = nil; > self.didHideKeyboardCallback = nil; >+ self.didShowMenuCallback = nil; >+ self.didHideMenuCallback = nil; > self.didEndScrollingCallback = nil; > self.rotationDidEndCallback = nil; > >@@ -172,6 +176,26 @@ - (void)_invokeHideKeyboardCallbackIfNecessary > self.didHideKeyboardCallback(); > } > >+- (void)_didShowMenu >+{ >+ if (self.showingMenu) >+ return; >+ >+ self.showingMenu = YES; >+ if (self.didShowMenuCallback) >+ self.didShowMenuCallback(); >+} >+ >+- (void)_didHideMenu >+{ >+ if (!self.showingMenu) >+ return; >+ >+ self.showingMenu = NO; >+ if (self.didHideMenuCallback) >+ self.didHideMenuCallback(); >+} >+ > - (void)scrollViewWillBeginZooming:(UIScrollView *)scrollView withView:(UIView *)view > { > [super scrollViewWillBeginZooming:scrollView withView:view]; >diff --git a/Tools/WebKitTestRunner/ios/TestControllerIOS.mm b/Tools/WebKitTestRunner/ios/TestControllerIOS.mm >index 04011a6b3544837c559851730f699d76987214c3..18ffa5f6ae504955911f6be62c32d951191f6441 100644 >--- a/Tools/WebKitTestRunner/ios/TestControllerIOS.mm >+++ b/Tools/WebKitTestRunner/ios/TestControllerIOS.mm >@@ -53,6 +53,7 @@ static BOOL overrideIsInHardwareKeyboardMode() > namespace WTR { > > static bool isDoneWaitingForKeyboardToDismiss = true; >+static bool isDoneWaitingForMenuToDismiss = true; > > static void handleKeyboardWillHideNotification(CFNotificationCenterRef, void*, CFStringRef, const void*, CFDictionaryRef) > { >@@ -64,6 +65,16 @@ static void handleKeyboardDidHideNotification(CFNotificationCenterRef, void*, CF > isDoneWaitingForKeyboardToDismiss = true; > } > >+static void handleMenuWillHideNotification(CFNotificationCenterRef, void*, CFStringRef, const void*, CFDictionaryRef) >+{ >+ isDoneWaitingForMenuToDismiss = false; >+} >+ >+static void handleMenuDidHideNotification(CFNotificationCenterRef, void*, CFStringRef, const void*, CFDictionaryRef) >+{ >+ isDoneWaitingForMenuToDismiss = true; >+} >+ > void TestController::notifyDone() > { > } >@@ -79,6 +90,8 @@ void TestController::platformInitialize() > auto center = CFNotificationCenterGetLocalCenter(); > CFNotificationCenterAddObserver(center, this, handleKeyboardWillHideNotification, (CFStringRef)UIKeyboardWillHideNotification, nullptr, CFNotificationSuspensionBehaviorDeliverImmediately); > CFNotificationCenterAddObserver(center, this, handleKeyboardDidHideNotification, (CFStringRef)UIKeyboardDidHideNotification, nullptr, CFNotificationSuspensionBehaviorDeliverImmediately); >+ CFNotificationCenterAddObserver(center, this, handleMenuWillHideNotification, (CFStringRef)UIMenuControllerWillHideMenuNotification, nullptr, CFNotificationSuspensionBehaviorDeliverImmediately); >+ CFNotificationCenterAddObserver(center, this, handleMenuDidHideNotification, (CFStringRef)UIMenuControllerDidHideMenuNotification, nullptr, CFNotificationSuspensionBehaviorDeliverImmediately); > > // Override the implementation of +[UIKeyboard isInHardwareKeyboardMode] to ensure that test runs are deterministic > // regardless of whether a hardware keyboard is attached. We intentionally never restore the original implementation. >@@ -92,6 +105,8 @@ void TestController::platformDestroy() > auto center = CFNotificationCenterGetLocalCenter(); > CFNotificationCenterRemoveObserver(center, this, (CFStringRef)UIKeyboardWillHideNotification, nullptr); > CFNotificationCenterRemoveObserver(center, this, (CFStringRef)UIKeyboardDidHideNotification, nullptr); >+ CFNotificationCenterRemoveObserver(center, this, (CFStringRef)UIMenuControllerWillHideMenuNotification, nullptr); >+ CFNotificationCenterRemoveObserver(center, this, (CFStringRef)UIMenuControllerDidHideMenuNotification, nullptr); > } > > void TestController::initializeInjectedBundlePath() >@@ -115,6 +130,7 @@ void TestController::platformResetStateToConsistentValues(const TestOptions& opt > { > cocoaResetStateToConsistentValues(options); > >+ UIMenuController.sharedMenuController.menuVisible = NO; > [[UIApplication sharedApplication] _cancelAllTouches]; > [[UIDevice currentDevice] setOrientation:UIDeviceOrientationPortrait animated:NO]; > >@@ -141,6 +157,7 @@ void TestController::platformResetStateToConsistentValues(const TestOptions& opt > } > > runUntil(isDoneWaitingForKeyboardToDismiss, m_currentInvocation->shortTimeout()); >+ runUntil(isDoneWaitingForMenuToDismiss, m_currentInvocation->shortTimeout()); > > if (shouldRestoreFirstResponder) > [mainWebView()->platformView() becomeFirstResponder]; >diff --git a/Tools/WebKitTestRunner/ios/UIScriptControllerIOS.mm b/Tools/WebKitTestRunner/ios/UIScriptControllerIOS.mm >index 4952765a0acfd3de3f182be28e2fe8e19395683b..f22043de5f4b2b540c07e5b63a1741865b4e0fd2 100644 >--- a/Tools/WebKitTestRunner/ios/UIScriptControllerIOS.mm >+++ b/Tools/WebKitTestRunner/ios/UIScriptControllerIOS.mm >@@ -63,7 +63,45 @@ static NSDictionary *toNSDictionary(CGRect rect) > @"height": @(rect.size.height) > }; > } >+ >+static BOOL forEachViewInHierarchy(UIView *view, void(^mapFunction)(UIView *subview, BOOL *stop)) >+{ >+ BOOL stop = NO; >+ mapFunction(view, &stop); >+ if (stop) >+ return YES; > >+ for (UIView *subview in view.subviews) { >+ stop = forEachViewInHierarchy(subview, mapFunction); >+ if (stop) >+ break; >+ } >+ return stop; >+} >+ >+static UIView *findViewInHierarchyOfType(UIView *view, Class viewClass) >+{ >+ __block RetainPtr<UIView> foundView; >+ forEachViewInHierarchy(view, ^(UIView *subview, BOOL *stop) { >+ if (![subview isKindOfClass:viewClass]) >+ return; >+ >+ foundView = subview; >+ *stop = YES; >+ }); >+ return foundView.autorelease(); >+} >+ >+static NSArray<UIView *> *findAllViewsInHierarchyOfType(UIView *view, Class viewClass) >+{ >+ __block RetainPtr<NSMutableArray> views = adoptNS([[NSMutableArray alloc] init]); >+ forEachViewInHierarchy(view, ^(UIView *subview, BOOL *stop) { >+ if ([subview isKindOfClass:viewClass]) >+ [views addObject:subview]; >+ }); >+ return views.autorelease(); >+} >+ > void UIScriptController::checkForOutstandingCallbacks() > { > if (![[HIDEventGenerator sharedHIDEventGenerator] checkForOutstandingCallbacks]) >@@ -589,8 +627,7 @@ JSObjectRef UIScriptController::textSelectionCaretRect() const > > JSObjectRef UIScriptController::selectionStartGrabberViewRect() const > { >- WKWebView *webView = TestController::singleton().mainWebView()->platformView(); >- UIView *contentView = [webView valueForKeyPath:@"_currentContentView"]; >+ UIView *contentView = platformContentView(); > UIView *selectionRangeView = [contentView valueForKeyPath:@"interactionAssistant.selectionView.rangeView"]; > auto frameInContentCoordinates = [selectionRangeView convertRect:[[selectionRangeView valueForKeyPath:@"startGrabber"] frame] toView:contentView]; > frameInContentCoordinates = CGRectIntersection(contentView.bounds, frameInContentCoordinates); >@@ -600,8 +637,7 @@ JSObjectRef UIScriptController::selectionStartGrabberViewRect() const > > JSObjectRef UIScriptController::selectionEndGrabberViewRect() const > { >- WKWebView *webView = TestController::singleton().mainWebView()->platformView(); >- UIView *contentView = [webView valueForKeyPath:@"_currentContentView"]; >+ UIView *contentView = platformContentView(); > UIView *selectionRangeView = [contentView valueForKeyPath:@"interactionAssistant.selectionView.rangeView"]; > auto frameInContentCoordinates = [selectionRangeView convertRect:[[selectionRangeView valueForKeyPath:@"endGrabber"] frame] toView:contentView]; > frameInContentCoordinates = CGRectIntersection(contentView.bounds, frameInContentCoordinates); >@@ -611,8 +647,7 @@ JSObjectRef UIScriptController::selectionEndGrabberViewRect() const > > JSObjectRef UIScriptController::selectionCaretViewRect() const > { >- WKWebView *webView = TestController::singleton().mainWebView()->platformView(); >- UIView *contentView = [webView valueForKeyPath:@"_currentContentView"]; >+ UIView *contentView = platformContentView(); > UIView *caretView = [contentView valueForKeyPath:@"interactionAssistant.selectionView.caretView"]; > auto rectInContentViewCoordinates = CGRectIntersection([caretView convertRect:caretView.bounds toView:contentView], contentView.bounds); > return JSValueToObject(m_context->jsContext(), [JSValue valueWithObject:toNSDictionary(rectInContentViewCoordinates) inContext:[JSContext contextWithJSGlobalContextRef:m_context->jsContext()]].JSValueRef, nullptr); >@@ -620,8 +655,7 @@ JSObjectRef UIScriptController::selectionCaretViewRect() const > > JSObjectRef UIScriptController::selectionRangeViewRects() const > { >- WKWebView *webView = TestController::singleton().mainWebView()->platformView(); >- UIView *contentView = [webView valueForKeyPath:@"_currentContentView"]; >+ UIView *contentView = platformContentView(); > UIView *rangeView = [contentView valueForKeyPath:@"interactionAssistant.selectionView.rangeView"]; > auto rectsAsDictionaries = adoptNS([[NSMutableArray alloc] init]); > NSArray *textRectInfoArray = [rangeView valueForKeyPath:@"rects"]; >@@ -785,6 +819,58 @@ void UIScriptController::platformSetDidHideKeyboardCallback() > }; > } > >+void UIScriptController::platformSetDidShowMenuCallback() >+{ >+ TestController::singleton().mainWebView()->platformView().didShowMenuCallback = ^{ >+ if (!m_context) >+ return; >+ m_context->fireCallback(CallbackTypeDidShowMenu); >+ }; >+} >+ >+void UIScriptController::platformSetDidHideMenuCallback() >+{ >+ TestController::singleton().mainWebView()->platformView().didHideMenuCallback = ^{ >+ if (!m_context) >+ return; >+ m_context->fireCallback(CallbackTypeDidHideMenu); >+ }; >+} >+ >+JSObjectRef UIScriptController::rectForMenuAction(JSStringRef jsAction) const >+{ >+ auto action = adoptCF(JSStringCopyCFString(kCFAllocatorDefault, jsAction)); >+ >+ UIWindow *windowForButton = nil; >+ UIButton *buttonForAction = nil; >+ for (UIWindow *window in UIApplication.sharedApplication.windows) { >+ if (![window isKindOfClass:[UITextEffectsWindow class]]) >+ continue; >+ >+ UICalloutBar *calloutBar = (UICalloutBar *)findViewInHierarchyOfType(window, UICalloutBar.class); >+ if (!calloutBar) >+ continue; >+ >+ for (UIButton *button in findAllViewsInHierarchyOfType(calloutBar, UIButton.class)) { >+ NSString *buttonTitle = [button titleForState:UIControlStateNormal]; >+ if (!buttonTitle.length) >+ continue; >+ >+ if (CFStringCompare((__bridge CFStringRef)buttonTitle, action.get(), 0) != kCFCompareEqualTo) >+ continue; >+ >+ buttonForAction = button; >+ windowForButton = window; >+ } >+ } >+ >+ if (!buttonForAction) >+ return nullptr; >+ >+ CGRect rectInRootViewCoordinates = [buttonForAction convertRect:buttonForAction.bounds toView:platformContentView()]; >+ return m_context->objectFromRect(WebCore::FloatRect(rectInRootViewCoordinates.origin.x, rectInRootViewCoordinates.origin.y, rectInRootViewCoordinates.size.width, rectInRootViewCoordinates.size.height)); >+} >+ > void UIScriptController::platformSetDidEndScrollingCallback() > { > TestRunnerWKWebView *webView = TestController::singleton().mainWebView()->platformView(); >@@ -830,21 +916,6 @@ void UIScriptController::completeBackSwipe(JSValueRef callback) > [webView _completeBackSwipeForTesting]; > } > >-static BOOL forEachViewInHierarchy(UIView *view, void(^mapFunction)(UIView *subview, BOOL *stop)) >-{ >- BOOL stop = NO; >- mapFunction(view, &stop); >- if (stop) >- return YES; >- >- for (UIView *subview in view.subviews) { >- stop = forEachViewInHierarchy(subview, mapFunction); >- if (stop) >- break; >- } >- return stop; >-} >- > bool UIScriptController::isShowingDataListSuggestions() const > { > Class remoteKeyboardWindowClass = NSClassFromString(@"UIRemoteKeyboardWindow"); >@@ -949,9 +1020,9 @@ JSObjectRef UIScriptController::attachmentInfo(JSStringRef jsAttachmentIdentifie > return JSValueToObject(m_context->jsContext(), [JSValue valueWithObject:attachmentInfoDictionary inContext:[JSContext contextWithJSGlobalContextRef:m_context->jsContext()]].JSValueRef, nullptr); > } > >-NSUndoManager *UIScriptController::platformUndoManager() const >+UIView *UIScriptController::platformContentView() const > { >- return [(UIView *)[TestController::singleton().mainWebView()->platformView() valueForKeyPath:@"_currentContentView"] undoManager]; >+ return [TestController::singleton().mainWebView()->platformView() valueForKeyPath:@"_currentContentView"]; > } > > } >diff --git a/Tools/WebKitTestRunner/mac/UIScriptControllerMac.mm b/Tools/WebKitTestRunner/mac/UIScriptControllerMac.mm >index 757fa06bd78f1833f13b38cbc9b030f93419763f..09a26e110061bd9fd0488ee4b12c83ea25fda67c 100644 >--- a/Tools/WebKitTestRunner/mac/UIScriptControllerMac.mm >+++ b/Tools/WebKitTestRunner/mac/UIScriptControllerMac.mm >@@ -209,9 +209,9 @@ void UIScriptController::toggleCapsLock(JSValueRef callback) > doAsyncTask(callback); > } > >-NSUndoManager *UIScriptController::platformUndoManager() const >+NSView *UIScriptController::platformContentView() const > { >- return TestController::singleton().mainWebView()->platformView().undoManager; >+ return TestController::singleton().mainWebView()->platformView(); > } > > } // namespace WTR >diff --git a/LayoutTests/ChangeLog b/LayoutTests/ChangeLog >index cabed6b27dc78adb8cee1ab4461bbd74ed0b4d5e..502439a5adb2bac5dc4f311130e3e40c7a22d4e8 100644 >--- a/LayoutTests/ChangeLog >+++ b/LayoutTests/ChangeLog >@@ -1,3 +1,47 @@ >+2019-02-08 Wenson Hsieh <wenson_hsieh@apple.com> >+ >+ Allow pages to trigger programmatic paste from script on iOS >+ https://bugs.webkit.org/show_bug.cgi?id=194271 >+ <rdar://problem/47808810> >+ >+ Reviewed by NOBODY (OOPS!). >+ >+ Add new tests to exercise programmatic pasting. >+ >+ * TestExpectations: >+ * editing/pasteboard/ios/dom-paste-confirmation-expected.txt: Added. >+ * editing/pasteboard/ios/dom-paste-confirmation.html: Added. >+ >+ Verify that the user can tap "Paste" to allow programmatic pasting. >+ >+ * editing/pasteboard/ios/dom-paste-consecutive-confirmations-expected.txt: Added. >+ * editing/pasteboard/ios/dom-paste-consecutive-confirmations.html: Added. >+ >+ Verify that DOM paste access isn't carried over when using `setTimeout` in a user gesture event handler. >+ >+ * editing/pasteboard/ios/dom-paste-rejection-expected.txt: Added. >+ * editing/pasteboard/ios/dom-paste-rejection.html: Added. >+ >+ Verify that resigning first responder dismisses the callout bar and does not allow programmatic pasting. >+ >+ * editing/pasteboard/ios/dom-paste-requires-user-gesture-expected.txt: Added. >+ * editing/pasteboard/ios/dom-paste-requires-user-gesture.html: Added. >+ >+ Verify that user gesture is required to present the callout menu for a programmatic paste request. >+ >+ * editing/pasteboard/ios/resources/dom-paste-helper.js: Added. >+ (return.new.Promise.): >+ (async._waitForOrTriggerPasteMenu): >+ (async.triggerPasteMenuAfterTapAt): >+ (async.waitForPasteMenu): >+ >+ Add helpers to summon, wait for, and interact with the callout bar when the page attempts to trigger a paste. >+ >+ * platform/ios-wk2/TestExpectations: >+ * platform/win/TestExpectations: >+ >+ Skip editing/pasteboard/ios by default, and enable it only in the modern WebKit port of iOS. >+ > 2019-02-07 Justin Fan <justin_fan@apple.com> > > [Web GPU] GPUDevice::createTexture implementation prototype >diff --git a/LayoutTests/TestExpectations b/LayoutTests/TestExpectations >index fe88082b85ecc0610bcfc844c34f6c2d3d8aaac0..e5f294a499d2baed35cf71523fbbd9d8890b64ac 100644 >--- a/LayoutTests/TestExpectations >+++ b/LayoutTests/TestExpectations >@@ -52,6 +52,7 @@ http/tests/cookies/same-site [ Skip ] > system-preview [ Skip ] > editing/images [ Skip ] > pointerevents/ios [ Skip ] >+editing/pasteboard/ios [ Skip ] > editing/pasteboard/mac [ Skip ] > > # window.showModalDialog is only tested in DumpRenderTree on Mac. >diff --git a/LayoutTests/editing/pasteboard/ios/dom-paste-confirmation-expected.txt b/LayoutTests/editing/pasteboard/ios/dom-paste-confirmation-expected.txt >new file mode 100644 >index 0000000000000000000000000000000000000000..1cc3a9fb607f300b8d4549f2c56d5bffb91a70f5 >--- /dev/null >+++ b/LayoutTests/editing/pasteboard/ios/dom-paste-confirmation-expected.txt >@@ -0,0 +1,18 @@ >+Click here to copy >+Click here to copy >+Click here to copy >+Verifies that a callout is shown when the page programmatically triggers paste, and that tapping the callout allows the paste to happen. To manually test, tap the text on the top, tap the editable area below, and then select 'Paste' in the resulting callout menu. The text 'Click here to copy' should be pasted twice in the editable area. >+ >+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE". >+ >+PASS document.queryCommandSupported('Paste') is true >+PASS document.queryCommandEnabled('Paste') is true >+PASS event.clipboardData.getData('text/plain') is "Click here to copy" >+PASS document.execCommand('Paste') is true >+PASS event.clipboardData.getData('text/plain') is "Click here to copy" >+PASS document.execCommand('Paste') is true >+PASS editor.textContent is "Click here to copyClick here to copy" >+PASS successfullyParsed is true >+ >+TEST COMPLETE >+ >diff --git a/LayoutTests/editing/pasteboard/ios/dom-paste-confirmation.html b/LayoutTests/editing/pasteboard/ios/dom-paste-confirmation.html >new file mode 100644 >index 0000000000000000000000000000000000000000..bba0732bba86783c6c45949cbf25d38850aa1ee5 >--- /dev/null >+++ b/LayoutTests/editing/pasteboard/ios/dom-paste-confirmation.html >@@ -0,0 +1,73 @@ >+<!DOCTYPE html> <!-- webkit-test-runner [ domPasteAllowed=false useFlexibleViewport=true ] --> >+<html> >+<meta name="viewport" content="width=device-width, initial-scale=1"> >+<head> >+<script src="./resources/dom-paste-helper.js"></script> >+<script src="../../../resources/js-test.js"></script> >+<script src="../../../resources/ui-helper.js"></script> >+<style> >+body { >+ margin: 0; >+} >+ >+#copy, #editor { >+ text-align: center; >+} >+ >+#copy { >+ font-size: 40px; >+ width: 100%; >+ height: 50px; >+ border: 1px dashed black; >+} >+ >+#editor { >+ width: 100%; >+ height: 100px; >+ border: 1px dashed silver; >+} >+</style> >+</head> >+<body> >+<div id="editor" contenteditable></div> >+<div id="copy">Click here to copy</div> >+<div id="description"></div> >+<div id="console"></div> >+<script> >+jsTestIsAsync = true; >+ >+const copy = document.getElementById("copy"); >+const editor = document.getElementById("editor"); >+ >+description("Verifies that a callout is shown when the page programmatically triggers paste, and that tapping the callout allows the paste to happen. To manually test, tap the text on the top, tap the editable area below, and then select 'Paste' in the resulting callout menu. The text 'Click here to copy' should be pasted <strong><em>twice</em></strong> in the editable area."); >+ >+copy.addEventListener("click", () => { >+ getSelection().selectAllChildren(copy); >+ document.execCommand("Copy"); >+ getSelection().removeAllRanges(); >+}); >+ >+editor.addEventListener("paste", event => shouldBeEqualToString("event.clipboardData.getData('text/plain')", "Click here to copy")); >+editor.addEventListener("click", event => { >+ getSelection().setPosition(editor); >+ shouldBe("document.queryCommandSupported('Paste')", "true"); >+ shouldBe("document.queryCommandEnabled('Paste')", "true"); >+ shouldBe("document.execCommand('Paste')", "true"); >+ document.execCommand('InsertParagraph'); >+ shouldBe("document.execCommand('Paste')", "true"); >+ shouldBeEqualToString("editor.textContent", "Click here to copyClick here to copy"); >+ event.preventDefault(); >+ editor.blur(); >+}); >+ >+(async () => { >+ if (!window.testRunner) >+ return; >+ >+ await UIHelper.activateAt(160, 125); >+ await triggerPasteMenuAfterTapAt(160, 50); >+ finishJSTest(); >+})(); >+</script> >+</body> >+</html> >diff --git a/LayoutTests/editing/pasteboard/ios/dom-paste-consecutive-confirmations-expected.txt b/LayoutTests/editing/pasteboard/ios/dom-paste-consecutive-confirmations-expected.txt >new file mode 100644 >index 0000000000000000000000000000000000000000..cf407aa55fce85a22e3d6d8db32d56ad306a0a44 >--- /dev/null >+++ b/LayoutTests/editing/pasteboard/ios/dom-paste-consecutive-confirmations-expected.txt >@@ -0,0 +1,13 @@ >+Click here to copy >+Verifies that no callout is shown when the page programmatically triggers paste on a timer after user interaction. To test manually, click the text near the top of the page to copy, and then click the editable area to trigger two programmatic pastes with the callout bar. Check that permissions for the first programmatic paste do not affect the second programmatic paste, since it is performed on a zero-delay timer. >+ >+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE". >+ >+PASS document.execCommand('Paste') is true >+PASS editor.textContent is "Click here to copy" >+PASS document.execCommand('Paste') is true >+PASS editor.textContent is "Click here to copy" >+PASS successfullyParsed is true >+ >+TEST COMPLETE >+ >diff --git a/LayoutTests/editing/pasteboard/ios/dom-paste-consecutive-confirmations.html b/LayoutTests/editing/pasteboard/ios/dom-paste-consecutive-confirmations.html >new file mode 100644 >index 0000000000000000000000000000000000000000..037e0aaa581e491c13c8b2448ffa132c701afeb5 >--- /dev/null >+++ b/LayoutTests/editing/pasteboard/ios/dom-paste-consecutive-confirmations.html >@@ -0,0 +1,93 @@ >+<!DOCTYPE html> <!-- webkit-test-runner [ domPasteAllowed=false useFlexibleViewport=true ] --> >+<html> >+<meta name="viewport" content="width=device-width, initial-scale=1"> >+<head> >+<script src="./resources/dom-paste-helper.js"></script> >+<script src="../../../resources/js-test.js"></script> >+<script src="../../../resources/ui-helper.js"></script> >+<style> >+body { >+ margin: 0; >+} >+ >+#copy, #editor { >+ text-align: center; >+} >+ >+#copy { >+ font-size: 40px; >+ width: 100%; >+ height: 50px; >+ border: 1px dashed black; >+} >+ >+#editor { >+ width: 100%; >+ height: 100px; >+ border: 1px dashed silver; >+} >+</style> >+</head> >+<body> >+<div id="editor" contenteditable></div> >+<div id="copy">Click here to copy</div> >+<div id="description"></div> >+<div id="console"></div> >+<script> >+jsTestIsAsync = true; >+ >+const copy = document.getElementById("copy"); >+const editor = document.getElementById("editor"); >+ >+description("Verifies that no callout is shown when the page programmatically triggers paste on a timer after user interaction. To test manually, click the text near the top of the page to copy, and then click the editable area to trigger two programmatic pastes with the callout bar. Check that permissions for the first programmatic paste do not affect the second programmatic paste, since it is performed on a zero-delay timer."); >+ >+async function waitForAndTapPasteMenuTwice() { >+ return new Promise(resolve => { >+ testRunner.runUIScript(` >+ (() => { >+ doneCount = 0; >+ function incrementProgress() { >+ if (++doneCount === 4) >+ uiController.uiScriptComplete(); >+ } >+ >+ uiController.didHideMenuCallback = incrementProgress; >+ uiController.didShowMenuCallback = () => { >+ const rect = uiController.rectForMenuAction("Paste"); >+ uiController.singleTapAtPoint(rect.left + rect.width / 2, rect.top + rect.height / 2, incrementProgress); >+ }; >+ })()`, resolve); >+ }); >+} >+ >+copy.addEventListener("click", () => { >+ getSelection().selectAllChildren(copy); >+ document.execCommand("Copy"); >+ getSelection().removeAllRanges(); >+}); >+ >+function paste() { >+ getSelection().setPosition(editor); >+ shouldBe("document.execCommand('Paste')", "true"); >+ shouldBeEqualToString("editor.textContent", "Click here to copy"); >+ editor.textContent = ""; >+ getSelection().removeAllRanges(editor); >+} >+ >+editor.addEventListener("click", event => { >+ event.preventDefault(); >+ paste(); >+ setTimeout(paste, 0); >+}); >+ >+(async () => { >+ if (!window.testRunner || !window.internals) >+ return; >+ >+ waitForAndTapPasteMenuTwice().then(finishJSTest); >+ await UIHelper.activateAt(160, 125); >+ await UIHelper.activateAt(160, 50); >+})(); >+</script> >+</body> >+</html> >diff --git a/LayoutTests/editing/pasteboard/ios/dom-paste-rejection-expected.txt b/LayoutTests/editing/pasteboard/ios/dom-paste-rejection-expected.txt >new file mode 100644 >index 0000000000000000000000000000000000000000..aa583cfd41ef03fba72a663a2d0613bef61011dc >--- /dev/null >+++ b/LayoutTests/editing/pasteboard/ios/dom-paste-rejection-expected.txt >@@ -0,0 +1,14 @@ >+Click here to copy >+Verifies that a callout is shown when the page programmatically triggers paste, and that dismissing the callout prevents the paste from happening. To manually test, tap the text on the top, tap the editable area below, and then dismiss the resulting callout menu by scrolling or tapping elsewhere. The text 'Click here to copy' should not be pasted, and the callout bar should disappear. >+ >+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE". >+ >+PASS document.queryCommandSupported('Paste') is true >+PASS document.queryCommandEnabled('Paste') is true >+PASS document.execCommand('Paste') is false >+PASS document.execCommand('Paste') is false >+PASS editor.textContent is "" >+PASS successfullyParsed is true >+ >+TEST COMPLETE >+ >diff --git a/LayoutTests/editing/pasteboard/ios/dom-paste-rejection.html b/LayoutTests/editing/pasteboard/ios/dom-paste-rejection.html >new file mode 100644 >index 0000000000000000000000000000000000000000..9e61cd8d04b89592b5dc8b81f9c4cdf42d75d66b >--- /dev/null >+++ b/LayoutTests/editing/pasteboard/ios/dom-paste-rejection.html >@@ -0,0 +1,72 @@ >+<!DOCTYPE html> <!-- webkit-test-runner [ domPasteAllowed=false useFlexibleViewport=true ] --> >+<html> >+<meta name="viewport" content="width=device-width, initial-scale=1"> >+<head> >+<script src="./resources/dom-paste-helper.js"></script> >+<script src="../../../resources/js-test.js"></script> >+<script src="../../../resources/ui-helper.js"></script> >+<style> >+body { >+ margin: 0; >+} >+ >+#copy, #editor { >+ text-align: center; >+} >+ >+#copy { >+ font-size: 40px; >+ width: 100%; >+ height: 50px; >+ border: 1px dashed black; >+} >+ >+#editor { >+ width: 100%; >+ height: 100px; >+ border: 1px dashed silver; >+} >+</style> >+</head> >+<body> >+<div id="editor" contenteditable></div> >+<div id="copy">Click here to copy</div> >+<div id="description"></div> >+<div id="console"></div> >+<script> >+jsTestIsAsync = true; >+ >+const copy = document.getElementById("copy"); >+const editor = document.getElementById("editor"); >+ >+description("Verifies that a callout is shown when the page programmatically triggers paste, and that dismissing the callout prevents the paste from happening. To manually test, tap the text on the top, tap the editable area below, and then dismiss the resulting callout menu by scrolling or tapping elsewhere. The text 'Click here to copy' should <strong>not</strong> be pasted, and the callout bar should disappear."); >+ >+copy.addEventListener("click", () => { >+ getSelection().selectAllChildren(copy); >+ document.execCommand("Copy"); >+ getSelection().removeAllRanges(); >+}); >+ >+editor.addEventListener("paste", event => shouldBeEqualToString("event.clipboardData.getData('text/plain')", "Click here to copy")); >+editor.addEventListener("click", event => { >+ getSelection().setPosition(editor); >+ shouldBe("document.queryCommandSupported('Paste')", "true"); >+ shouldBe("document.queryCommandEnabled('Paste')", "true"); >+ shouldBe("document.execCommand('Paste')", "false"); >+ shouldBe("document.execCommand('Paste')", "false"); >+ shouldBeEqualToString("editor.textContent", ""); >+ event.preventDefault(); >+ editor.blur(); >+}); >+ >+(async () => { >+ if (!window.testRunner) >+ return; >+ >+ await UIHelper.activateAt(160, 125); >+ await triggerPasteMenuAfterTapAt(160, 50, false); >+ finishJSTest(); >+})(); >+</script> >+</body> >+</html> >diff --git a/LayoutTests/editing/pasteboard/ios/dom-paste-requires-user-gesture-expected.txt b/LayoutTests/editing/pasteboard/ios/dom-paste-requires-user-gesture-expected.txt >new file mode 100644 >index 0000000000000000000000000000000000000000..67aaeca32ae25f8662d14c940df6139f7eebebed >--- /dev/null >+++ b/LayoutTests/editing/pasteboard/ios/dom-paste-requires-user-gesture-expected.txt >@@ -0,0 +1,13 @@ >+Click here to copy >+Click here to copy >+Verifies that no callout is shown when the page programmatically triggers paste outside the scope of user interaction. This test requires WebKitTestRunner. >+ >+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE". >+ >+PASS document.execCommand('Paste') is true >+PASS document.execCommand('Paste') is false >+PASS editor.textContent is "Click here to copy" >+PASS successfullyParsed is true >+ >+TEST COMPLETE >+ >diff --git a/LayoutTests/editing/pasteboard/ios/dom-paste-requires-user-gesture.html b/LayoutTests/editing/pasteboard/ios/dom-paste-requires-user-gesture.html >new file mode 100644 >index 0000000000000000000000000000000000000000..5223f63e75fa7a8f65e4499191bedb56d9dec8a2 >--- /dev/null >+++ b/LayoutTests/editing/pasteboard/ios/dom-paste-requires-user-gesture.html >@@ -0,0 +1,62 @@ >+<!DOCTYPE html> <!-- webkit-test-runner [ domPasteAllowed=false useFlexibleViewport=true ] --> >+<html> >+<meta name="viewport" content="width=device-width, initial-scale=1"> >+<head> >+<script src="./resources/dom-paste-helper.js"></script> >+<script src="../../../resources/js-test.js"></script> >+<script src="../../../resources/ui-helper.js"></script> >+<style> >+body { >+ margin: 0; >+} >+ >+#copy, #editor { >+ text-align: center; >+} >+ >+#copy { >+ font-size: 40px; >+ width: 100%; >+ height: 50px; >+ border: 1px dashed black; >+} >+ >+#editor { >+ width: 100%; >+ height: 100px; >+ border: 1px dashed silver; >+} >+</style> >+</head> >+<body> >+<div id="editor" contenteditable></div> >+<div id="copy">Click here to copy</div> >+<div id="description"></div> >+<div id="console"></div> >+<script> >+jsTestIsAsync = true; >+ >+const copy = document.getElementById("copy"); >+const editor = document.getElementById("editor"); >+ >+description("Verifies that no callout is shown when the page programmatically triggers paste outside the scope of user interaction. This test requires WebKitTestRunner."); >+ >+copy.addEventListener("click", () => { >+ getSelection().selectAllChildren(copy); >+ document.execCommand("Copy"); >+ getSelection().removeAllRanges(); >+}); >+ >+UIHelper.activateAt(160, 125).then(() => { >+ editor.focus(); >+ waitForPasteMenu().then(finishJSTest); >+ >+ UIHelper.ensurePresentationUpdate().then(() => { >+ internals.withUserGesture(() => shouldBe("document.execCommand('Paste')", "true")); >+ shouldBe("document.execCommand('Paste')", "false"); >+ shouldBeEqualToString("editor.textContent", "Click here to copy"); >+ }); >+}); >+</script> >+</body> >+</html> >diff --git a/LayoutTests/editing/pasteboard/ios/resources/dom-paste-helper.js b/LayoutTests/editing/pasteboard/ios/resources/dom-paste-helper.js >new file mode 100644 >index 0000000000000000000000000000000000000000..bbcdb4fae56da0bc5b60fbdcb716291d372b0223 >--- /dev/null >+++ b/LayoutTests/editing/pasteboard/ios/resources/dom-paste-helper.js >@@ -0,0 +1,35 @@ >+ >+async function _waitForOrTriggerPasteMenu(x, y, proceedWithPaste, shouldTap) { >+ return new Promise(resolve => { >+ testRunner.runUIScript(` >+ (() => { >+ doneCount = 0; >+ function checkDone() { >+ if (++doneCount === (${shouldTap} ? 3 : 2)) >+ uiController.uiScriptComplete(); >+ } >+ >+ uiController.didHideMenuCallback = checkDone; >+ uiController.didShowMenuCallback = () => { >+ if (${proceedWithPaste}) { >+ const rect = uiController.rectForMenuAction("Paste"); >+ uiController.singleTapAtPoint(rect.left + rect.width / 2, rect.top + rect.height / 2, checkDone); >+ } else { >+ uiController.resignFirstResponder(); >+ checkDone(); >+ } >+ }; >+ >+ if (${shouldTap}) >+ uiController.singleTapAtPoint(${x}, ${y}, checkDone); >+ })()`, resolve); >+ }); >+} >+ >+async function triggerPasteMenuAfterTapAt(x, y, proceedWithPaste = true) { >+ return _waitForOrTriggerPasteMenu(x, y, proceedWithPaste, true); >+} >+ >+async function waitForPasteMenu(proceedWithPaste = true) { >+ return _waitForOrTriggerPasteMenu(null, null, proceedWithPaste, false); >+} >diff --git a/LayoutTests/platform/ios-wk2/TestExpectations b/LayoutTests/platform/ios-wk2/TestExpectations >index 8dc1fb3fe08ddb2d20856bd78e92089b352f3e05..59a5103fbc2c1378670a4f7d0a1117a9f4bdc957 100644 >--- a/LayoutTests/platform/ios-wk2/TestExpectations >+++ b/LayoutTests/platform/ios-wk2/TestExpectations >@@ -15,6 +15,7 @@ scrollingcoordinator [ Pass ] > fast/web-share [ Pass ] > editing/find [ Pass ] > editing/input/ios [ Pass ] >+editing/pasteboard/ios [ Pass ] > editing/undo-manager [ Pass ] > > editing/selection/character-granularity-rect.html [ Failure ] >diff --git a/LayoutTests/platform/win/TestExpectations b/LayoutTests/platform/win/TestExpectations >index 3230bf0469a649e9fad7767ce2b6152db4f528ee..ffec15ae231e10adac03b3db2c8987d629a3736f 100644 >--- a/LayoutTests/platform/win/TestExpectations >+++ b/LayoutTests/platform/win/TestExpectations >@@ -1157,6 +1157,7 @@ media/track/track-in-band-cues-added-once.html [ Skip ] # Timeout > ###### Pasteboard > ###### These tests are very flaky. > editing/pasteboard/ [ Pass Failure ] >+editing/pasteboard/ios [ Skip ] > [ Debug ] editing/pasteboard/copy-crash.html [ Skip ] # Debug Assertion > [ Debug ] editing/pasteboard/copy-crash-with-extraneous-attribute.html [ Skip ] # Debug Assertion > [ Debug ] editing/pasteboard/testcase-9507.html [ Skip ] # Debug Assertion
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Formatted Diff
|
Diff
Attachments on
bug 194271
:
361202
|
361212
|
361252
|
361305
|
361306
|
361316
|
361350
|
361351
|
361397
|
361398
|
361537
|
361547
|
361582
|
361702
|
361837
|
361847