Allow pages to trigger programmatic paste from script on iOS
authorwenson_hsieh@apple.com <wenson_hsieh@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 12 Feb 2019 23:18:30 +0000 (23:18 +0000)
committerwenson_hsieh@apple.com <wenson_hsieh@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 12 Feb 2019 23:18:30 +0000 (23:18 +0000)
commitf942093ea9748b57c4aeeb4179cbcc567a963cff
tree0a7b1abd43d8f8a5dd0f72efed2fa2606731df06
parent3c54b2bbc1a9af8e73c15b22701e7111a9d78fc5
Allow pages to trigger programmatic paste from script on iOS
https://bugs.webkit.org/show_bug.cgi?id=194271
<rdar://problem/47808810>

Reviewed by Tim Horton.

Source/WebCore:

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):

Source/WebKit:

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.

* Platform/spi/ios/UIKitSPI.h:
* UIProcess/ios/WKContentViewInteraction.mm:
(-[WKContentView setupInteraction]):
(-[WKContentView _willHideMenu:]):
(-[WKContentView _didHideMenu:]):

Tools:

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.

LayoutTests:

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.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@241322 268f45cc-cd09-0410-ab3c-d52691b4dbfc
33 files changed:
LayoutTests/ChangeLog
LayoutTests/TestExpectations
LayoutTests/editing/pasteboard/ios/dom-paste-confirmation-expected.txt [new file with mode: 0644]
LayoutTests/editing/pasteboard/ios/dom-paste-confirmation.html [new file with mode: 0644]
LayoutTests/editing/pasteboard/ios/dom-paste-consecutive-confirmations-expected.txt [new file with mode: 0644]
LayoutTests/editing/pasteboard/ios/dom-paste-consecutive-confirmations.html [new file with mode: 0644]
LayoutTests/editing/pasteboard/ios/dom-paste-rejection-expected.txt [new file with mode: 0644]
LayoutTests/editing/pasteboard/ios/dom-paste-rejection.html [new file with mode: 0644]
LayoutTests/editing/pasteboard/ios/dom-paste-requires-user-gesture-expected.txt [new file with mode: 0644]
LayoutTests/editing/pasteboard/ios/dom-paste-requires-user-gesture.html [new file with mode: 0644]
LayoutTests/editing/pasteboard/ios/resources/dom-paste-helper.js [new file with mode: 0644]
LayoutTests/platform/ios-wk2/TestExpectations
LayoutTests/platform/win/TestExpectations
Source/WebCore/ChangeLog
Source/WebCore/dom/UserGestureIndicator.cpp
Source/WebCore/dom/UserGestureIndicator.h
Source/WebKit/ChangeLog
Source/WebKit/Platform/spi/ios/UIKitSPI.h
Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm
Tools/ChangeLog
Tools/DumpRenderTree/ios/UIScriptControllerIOS.mm
Tools/TestRunnerShared/UIScriptContext/Bindings/UIScriptController.idl
Tools/TestRunnerShared/UIScriptContext/UIScriptContext.h
Tools/TestRunnerShared/UIScriptContext/UIScriptController.cpp
Tools/TestRunnerShared/UIScriptContext/UIScriptController.h
Tools/WebKitTestRunner/TestController.cpp
Tools/WebKitTestRunner/TestOptions.h
Tools/WebKitTestRunner/UIScriptControllerCocoa.mm
Tools/WebKitTestRunner/cocoa/TestRunnerWKWebView.h
Tools/WebKitTestRunner/cocoa/TestRunnerWKWebView.mm
Tools/WebKitTestRunner/ios/TestControllerIOS.mm
Tools/WebKitTestRunner/ios/UIScriptControllerIOS.mm
Tools/WebKitTestRunner/mac/UIScriptControllerMac.mm