[iOS] Programmatic paste access should be granted when copying and pasting within...
authorwenson_hsieh@apple.com <wenson_hsieh@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sat, 2 Mar 2019 21:50:43 +0000 (21:50 +0000)
committerwenson_hsieh@apple.com <wenson_hsieh@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sat, 2 Mar 2019 21:50:43 +0000 (21:50 +0000)
https://bugs.webkit.org/show_bug.cgi?id=195053
<rdar://problem/48134710>

Reviewed by Ryosuke Niwa.

Source/WebCore:

Plumb the document pasteboard identifier through the client when making a DOM paste access request. See WebKit
ChangeLog for more details.

Test: editing/pasteboard/ios/dom-paste-same-origin.html

* WebCore.xcodeproj/project.pbxproj:
* dom/DOMPasteAccess.h: Renamed from Source/WebCore/dom/DOMPasteAccessPolicy.h.

Introduce DOMPasteAccessResponse, which is either DeniedForGesture, GrantedForCommand, or GrantedForGesture. In
particular, when pasteboard identifiers match, we only grant access for the current paste command, rather than
throughout the user gesture.

* dom/UserGestureIndicator.h:
(WebCore::UserGestureToken::didRequestDOMPasteAccess):
* loader/EmptyClients.cpp:
* page/EditorClient.h:
* page/Frame.cpp:
(WebCore::Frame::requestDOMPasteAccess):

Source/WebKit:

Add support for automatically granting programmatic pasteboard access when the pasteboard identifier of the
document requesting programmatic paste matches the identifier in the custom pasteboard data blob in the
UIPasteboard. To do this, we send the pasteboard identifier of the document requesting the DOM paste to the UI
process, and check this against the pasteboard identifiers for each item on the platform pasteboard. If all
items in the platform pasteboard match the given pasteboard identifier (since we don't support writing multiple
pasteboard items via bindings, this should only be a single item), we skip showing the paste callout and
immediately invoke the programmatic paste handler.

* Scripts/webkit/messages.py:
* UIProcess/API/gtk/PageClientImpl.cpp:
(WebKit::PageClientImpl::requestDOMPasteAccess):
* UIProcess/API/gtk/PageClientImpl.h:
* UIProcess/API/wpe/PageClientImpl.cpp:
(WebKit::PageClientImpl::requestDOMPasteAccess):
* UIProcess/API/wpe/PageClientImpl.h:
* UIProcess/PageClient.h:
* UIProcess/WebPageProxy.cpp:
(WebKit::WebPageProxy::requestDOMPasteAccess):
* UIProcess/WebPageProxy.h:
* UIProcess/WebPageProxy.messages.in:

Add plumbing to deliver the pasteboard identifier to the client when requesting DOM paste.

* UIProcess/ios/PageClientImplIOS.h:
* UIProcess/ios/PageClientImplIOS.mm:
(WebKit::PageClientImpl::requestDOMPasteAccess):
* UIProcess/ios/WKContentViewInteraction.h:
* UIProcess/ios/WKContentViewInteraction.mm:
(-[WKContentView cleanupInteraction]):
(-[WKContentView resignFirstResponderForWebView]):
(-[WKContentView _webTouchEventsRecognized:]):
(-[WKContentView _willHideMenu:]):
(-[WKContentView pasteForWebView:]):
(-[WKContentView _handleDOMPasteRequestWithResult:]):
(-[WKContentView _willPerformAction:sender:]):
(-[WKContentView _didPerformAction:sender:]):
(-[WKContentView handleKeyWebEvent:withCompletionHandler:]):
(allPasteboardItemOriginsMatchOrigin):
(-[WKContentView _requestDOMPasteAccessWithElementRect:originIdentifier:completionHandler:]):
(-[WKContentView _requestDOMPasteAccessWithElementRect:completionHandler:]): Deleted.

Bail early in the case where all items' pasteboard identifiers (read via custom pasteboard data) match. When
this happens, we only grant access for the current paste command, rather than granting access for the user
gesture token.

* UIProcess/mac/PageClientImplMac.h:
* UIProcess/win/PageClientImpl.cpp:
(WebKit::PageClientImpl::requestDOMPasteAccess):
* UIProcess/win/PageClientImpl.h:
* WebProcess/WebCoreSupport/WebEditorClient.cpp:
(WebKit::WebEditorClient::requestDOMPasteAccess):
* WebProcess/WebCoreSupport/WebEditorClient.h:
* WebProcess/WebPage/WebPage.cpp:
(WebKit::WebPage::requestDOMPasteAccess):
* WebProcess/WebPage/WebPage.h:

Source/WebKitLegacy/mac:

* WebCoreSupport/WebEditorClient.h:

Source/WebKitLegacy/win:

* WebCoreSupport/WebEditorClient.h:

LayoutTests:

Adjust several existing DOM paste tests to copy text from a child frame that has a different origin as the main
frame, such that we'll trigger the paste callout menu when performing a programmatic paste. Also add a new
layout test that copies and programmatically pastes within the same document, to verify that no paste callout is
displayed and the paste is allowed.

* editing/pasteboard/ios/dom-paste-confirmation-expected.txt:
* editing/pasteboard/ios/dom-paste-confirmation.html:
* editing/pasteboard/ios/dom-paste-consecutive-confirmations-expected.txt:
* editing/pasteboard/ios/dom-paste-consecutive-confirmations.html:
* editing/pasteboard/ios/dom-paste-rejection-expected.txt:
* editing/pasteboard/ios/dom-paste-rejection.html:
* editing/pasteboard/ios/dom-paste-requires-user-gesture-expected.txt:
* editing/pasteboard/ios/dom-paste-requires-user-gesture.html:
* editing/pasteboard/ios/dom-paste-same-origin-expected.txt: Copied from LayoutTests/editing/pasteboard/ios/dom-paste-confirmation-expected.txt.
* editing/pasteboard/ios/dom-paste-same-origin.html: Copied from LayoutTests/editing/pasteboard/ios/dom-paste-confirmation.html.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@242317 268f45cc-cd09-0410-ab3c-d52691b4dbfc

43 files changed:
LayoutTests/ChangeLog
LayoutTests/editing/pasteboard/ios/dom-paste-confirmation-expected.txt
LayoutTests/editing/pasteboard/ios/dom-paste-confirmation.html
LayoutTests/editing/pasteboard/ios/dom-paste-consecutive-confirmations-expected.txt
LayoutTests/editing/pasteboard/ios/dom-paste-consecutive-confirmations.html
LayoutTests/editing/pasteboard/ios/dom-paste-rejection-expected.txt
LayoutTests/editing/pasteboard/ios/dom-paste-rejection.html
LayoutTests/editing/pasteboard/ios/dom-paste-requires-user-gesture-expected.txt
LayoutTests/editing/pasteboard/ios/dom-paste-requires-user-gesture.html
LayoutTests/editing/pasteboard/ios/dom-paste-same-origin-expected.txt [new file with mode: 0644]
LayoutTests/editing/pasteboard/ios/dom-paste-same-origin.html [new file with mode: 0644]
Source/WebCore/ChangeLog
Source/WebCore/WebCore.xcodeproj/project.pbxproj
Source/WebCore/dom/DOMPasteAccess.h [moved from Source/WebCore/dom/DOMPasteAccessPolicy.h with 73% similarity]
Source/WebCore/dom/UserGestureIndicator.h
Source/WebCore/loader/EmptyClients.cpp
Source/WebCore/page/EditorClient.h
Source/WebCore/page/Frame.cpp
Source/WebKit/ChangeLog
Source/WebKit/Scripts/webkit/messages.py
Source/WebKit/UIProcess/API/gtk/PageClientImpl.cpp
Source/WebKit/UIProcess/API/gtk/PageClientImpl.h
Source/WebKit/UIProcess/API/wpe/PageClientImpl.cpp
Source/WebKit/UIProcess/API/wpe/PageClientImpl.h
Source/WebKit/UIProcess/PageClient.h
Source/WebKit/UIProcess/WebPageProxy.cpp
Source/WebKit/UIProcess/WebPageProxy.h
Source/WebKit/UIProcess/WebPageProxy.messages.in
Source/WebKit/UIProcess/ios/PageClientImplIOS.h
Source/WebKit/UIProcess/ios/PageClientImplIOS.mm
Source/WebKit/UIProcess/ios/WKContentViewInteraction.h
Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm
Source/WebKit/UIProcess/mac/PageClientImplMac.h
Source/WebKit/UIProcess/win/PageClientImpl.cpp
Source/WebKit/UIProcess/win/PageClientImpl.h
Source/WebKit/WebProcess/WebCoreSupport/WebEditorClient.cpp
Source/WebKit/WebProcess/WebCoreSupport/WebEditorClient.h
Source/WebKit/WebProcess/WebPage/WebPage.cpp
Source/WebKit/WebProcess/WebPage/WebPage.h
Source/WebKitLegacy/mac/ChangeLog
Source/WebKitLegacy/mac/WebCoreSupport/WebEditorClient.h
Source/WebKitLegacy/win/ChangeLog
Source/WebKitLegacy/win/WebCoreSupport/WebEditorClient.h

index 034af45..cd61ff5 100644 (file)
@@ -1,3 +1,27 @@
+2019-03-02  Wenson Hsieh  <wenson_hsieh@apple.com>
+
+        [iOS] Programmatic paste access should be granted when copying and pasting within the same origin
+        https://bugs.webkit.org/show_bug.cgi?id=195053
+        <rdar://problem/48134710>
+
+        Reviewed by Ryosuke Niwa.
+
+        Adjust several existing DOM paste tests to copy text from a child frame that has a different origin as the main
+        frame, such that we'll trigger the paste callout menu when performing a programmatic paste. Also add a new
+        layout test that copies and programmatically pastes within the same document, to verify that no paste callout is
+        displayed and the paste is allowed.
+
+        * editing/pasteboard/ios/dom-paste-confirmation-expected.txt:
+        * editing/pasteboard/ios/dom-paste-confirmation.html:
+        * editing/pasteboard/ios/dom-paste-consecutive-confirmations-expected.txt:
+        * editing/pasteboard/ios/dom-paste-consecutive-confirmations.html:
+        * editing/pasteboard/ios/dom-paste-rejection-expected.txt:
+        * editing/pasteboard/ios/dom-paste-rejection.html:
+        * editing/pasteboard/ios/dom-paste-requires-user-gesture-expected.txt:
+        * editing/pasteboard/ios/dom-paste-requires-user-gesture.html:
+        * editing/pasteboard/ios/dom-paste-same-origin-expected.txt: Copied from LayoutTests/editing/pasteboard/ios/dom-paste-confirmation-expected.txt.
+        * editing/pasteboard/ios/dom-paste-same-origin.html: Copied from LayoutTests/editing/pasteboard/ios/dom-paste-confirmation.html.
+
 2019-03-02  Simon Fraser  <simon.fraser@apple.com>
 
         REGRESSION (r242132): Incorrect positioning with multiple position:fixed elements
index 1e52289..37c783a 100644 (file)
@@ -1,6 +1,6 @@
 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 bottom, tap the editable area above, 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".
index 74c589a..00c233d 100644 (file)
@@ -10,12 +10,7 @@ body {
     margin: 0;
 }
 
-#copy, #editor {
-    text-align: center;
-}
-
 #copy {
-    font-size: 40px;
     width: 100%;
     height: 50px;
     border: 1px dashed black;
@@ -25,28 +20,29 @@ body {
     width: 100%;
     height: 100px;
     border: 1px dashed silver;
+    text-align: center;
 }
 </style>
 </head>
 <body>
 <div id="editor" contenteditable></div>
-<div id="copy">Click here to copy</div>
+<iframe id="copy" src="data:text/html,<div id='copy' style='font-size: 40px; text-align: center;'>Click here to copy</div>
+    <script>
+    copy.addEventListener('click', () => {
+        getSelection().selectAllChildren(copy);
+        document.execCommand('Copy');
+        getSelection().removeAllRanges();
+    });
+    </script>"></iframe>
 <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 bottom, tap the editable area above, 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);
@@ -60,14 +56,14 @@ editor.addEventListener("click", event => {
     editor.blur();
 });
 
-(async () => {
+addEventListener("load", async () => {
     if (!window.testRunner)
         return;
 
     await UIHelper.activateAt(160, 125);
     await triggerPasteMenuAfterTapAt(160, 50);
     finishJSTest();
-})();
+});
 </script>
 </body>
 </html>
index ad36273..056b480 100644 (file)
@@ -1,4 +1,4 @@
-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 on the bottom to copy, and then click the editable area above 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".
index 7971a58..5ceebbd 100644 (file)
@@ -10,12 +10,7 @@ body {
     margin: 0;
 }
 
-#copy, #editor {
-    text-align: center;
-}
-
 #copy {
-    font-size: 40px;
     width: 100%;
     height: 50px;
     border: 1px dashed black;
@@ -25,18 +20,25 @@ body {
     width: 100%;
     height: 100px;
     border: 1px dashed silver;
+    text-align: center;
 }
 </style>
 </head>
 <body>
 <div id="editor" contenteditable></div>
-<div id="copy">Click here to copy</div>
+<iframe id="copy" src="data:text/html,<div id='copy' style='font-size: 40px; text-align: center;'>Click here to copy</div>
+    <script>
+    copy.addEventListener('click', () => {
+        getSelection().selectAllChildren(copy);
+        document.execCommand('Copy');
+        getSelection().removeAllRanges();
+    });
+    </script>"></iframe>
 <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 on the bottom to copy, and then click the editable area above 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.");
@@ -60,12 +62,6 @@ async function waitForAndTapPasteMenuTwice() {
     });
 }
 
-copy.addEventListener("click", () => {
-    getSelection().selectAllChildren(copy);
-    document.execCommand("Copy");
-    getSelection().removeAllRanges();
-});
-
 function paste() {
     getSelection().setPosition(editor);
     shouldBe("document.execCommand('Paste')", "true");
@@ -80,14 +76,14 @@ editor.addEventListener("click", event => {
     setTimeout(paste, 0);
 });
 
-(async () => {
+addEventListener("load", async () => {
     if (!window.testRunner || !window.internals)
         return;
 
     waitForAndTapPasteMenuTwice().then(finishJSTest);
     await UIHelper.activateAt(160, 125);
     await UIHelper.activateAt(160, 50);
-})();
+});
 </script>
 </body>
 </html>
index bccad17..c3adad7 100644 (file)
@@ -1,4 +1,4 @@
-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 bottom, tap the editable area above, 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".
index 7515baa..73d2f8f 100644 (file)
@@ -10,12 +10,7 @@ body {
     margin: 0;
 }
 
-#copy, #editor {
-    text-align: center;
-}
-
 #copy {
-    font-size: 40px;
     width: 100%;
     height: 50px;
     border: 1px dashed black;
@@ -25,28 +20,29 @@ body {
     width: 100%;
     height: 100px;
     border: 1px dashed silver;
+    text-align: center;
 }
 </style>
 </head>
 <body>
 <div id="editor" contenteditable></div>
-<div id="copy">Click here to copy</div>
+<iframe id="copy" src="data:text/html,<div id='copy' style='font-size: 40px; text-align: center;'>Click here to copy</div>
+    <script>
+    copy.addEventListener('click', () => {
+        getSelection().selectAllChildren(copy);
+        document.execCommand('Copy');
+        getSelection().removeAllRanges();
+    });
+    </script>"></iframe>
 <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 bottom, tap the editable area above, 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);
@@ -59,14 +55,14 @@ editor.addEventListener("click", event => {
     editor.blur();
 });
 
-(async () => {
+addEventListener("load", async () => {
     if (!window.testRunner)
         return;
 
     await UIHelper.activateAt(160, 125);
     await triggerPasteMenuAfterTapAt(160, 50, false);
     finishJSTest();
-})();
+});
 </script>
 </body>
 </html>
index 67aaeca..f100ba6 100644 (file)
@@ -1,5 +1,5 @@
 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".
index 5223f63..bdf489b 100644 (file)
@@ -10,12 +10,7 @@ body {
     margin: 0;
 }
 
-#copy, #editor {
-    text-align: center;
-}
-
 #copy {
-    font-size: 40px;
     width: 100%;
     height: 50px;
     border: 1px dashed black;
@@ -25,36 +20,39 @@ body {
     width: 100%;
     height: 100px;
     border: 1px dashed silver;
+    text-align: center;
 }
 </style>
 </head>
 <body>
 <div id="editor" contenteditable></div>
-<div id="copy">Click here to copy</div>
+<iframe id="copy" src="data:text/html,<div id='copy' style='font-size: 40px; text-align: center;'>Click here to copy</div>
+    <script>
+    copy.addEventListener('click', () => {
+        getSelection().selectAllChildren(copy);
+        document.execCommand('Copy');
+        getSelection().removeAllRanges();
+    });
+    </script>"></iframe>
 <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);
+addEventListener("load", async () => {
+    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");
+        UIHelper.ensurePresentationUpdate().then(() => {
+            internals.withUserGesture(() => shouldBe("document.execCommand('Paste')", "true"));
+            shouldBe("document.execCommand('Paste')", "false");
+            shouldBeEqualToString("editor.textContent", "Click here to copy");
+        });
     });
 });
 </script>
diff --git a/LayoutTests/editing/pasteboard/ios/dom-paste-same-origin-expected.txt b/LayoutTests/editing/pasteboard/ios/dom-paste-same-origin-expected.txt
new file mode 100644 (file)
index 0000000..6aab17b
--- /dev/null
@@ -0,0 +1,18 @@
+Click here to copy
+Click here to copy
+Click here to copy
+Verifies that programmatic paste is allowed when copied data is from the same origin. To manually test, tap the text on the bottom to programmatically copy, and then tap the editable area and check that the text is pasted twice.
+
+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-same-origin.html b/LayoutTests/editing/pasteboard/ios/dom-paste-same-origin.html
new file mode 100644 (file)
index 0000000..e96a239
--- /dev/null
@@ -0,0 +1,71 @@
+<!DOCTYPE html> <!-- webkit-test-runner [ domPasteAllowed=false useFlexibleViewport=true ] -->
+<html>
+<meta name="viewport" content="width=device-width, initial-scale=1">
+<head>
+<script src="../../../resources/js-test.js"></script>
+<script src="../../../resources/ui-helper.js"></script>
+<style>
+body {
+    margin: 0;
+}
+
+#copy, #editor {
+    width: 100%;
+    text-align: center;
+}
+
+#copy {
+    height: 50px;
+    border: 1px dashed black;
+    font-size: 40px;
+}
+
+#editor {
+    height: 100px;
+    border: 1px dashed silver;
+}
+</style>
+</head>
+<body>
+<div id="editor" contenteditable></div>
+<div id='copy' style=''>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 programmatic paste is allowed when copied data is from the same origin. To manually test, tap the text on the bottom to programmatically copy, and then tap the editable area and check that the text is pasted <em>twice</em>.");
+
+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();
+    finishJSTest();
+});
+
+addEventListener("load", async () => {
+    if (!window.testRunner)
+        return;
+
+    await UIHelper.activateAt(160, 125);
+    await UIHelper.activateAt(160, 50);
+});
+</script>
+</body>
+</html>
index 4d72923..246a417 100644 (file)
@@ -1,3 +1,30 @@
+2019-03-02  Wenson Hsieh  <wenson_hsieh@apple.com>
+
+        [iOS] Programmatic paste access should be granted when copying and pasting within the same origin
+        https://bugs.webkit.org/show_bug.cgi?id=195053
+        <rdar://problem/48134710>
+
+        Reviewed by Ryosuke Niwa.
+
+        Plumb the document pasteboard identifier through the client when making a DOM paste access request. See WebKit
+        ChangeLog for more details.
+
+        Test: editing/pasteboard/ios/dom-paste-same-origin.html
+
+        * WebCore.xcodeproj/project.pbxproj:
+        * dom/DOMPasteAccess.h: Renamed from Source/WebCore/dom/DOMPasteAccessPolicy.h.
+
+        Introduce DOMPasteAccessResponse, which is either DeniedForGesture, GrantedForCommand, or GrantedForGesture. In
+        particular, when pasteboard identifiers match, we only grant access for the current paste command, rather than
+        throughout the user gesture.
+
+        * dom/UserGestureIndicator.h:
+        (WebCore::UserGestureToken::didRequestDOMPasteAccess):
+        * loader/EmptyClients.cpp:
+        * page/EditorClient.h:
+        * page/Frame.cpp:
+        (WebCore::Frame::requestDOMPasteAccess):
+
 2019-02-27  Darin Adler  <darin@apple.com>
 
         Fixed makeString(float) to do shortest-form serialization without first converting to double
index 2441959..4c03cf0 100644 (file)
                F48D2A7E2157182600C6752B /* FontAttributes.h in Headers */ = {isa = PBXBuildFile; fileRef = F48D2A712156DC0A00C6752B /* FontAttributes.h */; settings = {ATTRIBUTES = (Private, ); }; };
                F48D2AA52159740D00C6752B /* ColorCocoa.h in Headers */ = {isa = PBXBuildFile; fileRef = F48D2AA32159740D00C6752B /* ColorCocoa.h */; };
                F49786881FF45FA500E060AB /* PasteboardItemInfo.h in Headers */ = {isa = PBXBuildFile; fileRef = F49786871FF45FA500E060AB /* PasteboardItemInfo.h */; settings = {ATTRIBUTES = (Private, ); }; };
-               F4B422C4220C0568009E1E7D /* DOMPasteAccessPolicy.h in Headers */ = {isa = PBXBuildFile; fileRef = F4B422C2220C0000009E1E7D /* DOMPasteAccessPolicy.h */; settings = {ATTRIBUTES = (Private, ); }; };
+               F4B422C4220C0568009E1E7D /* DOMPasteAccess.h in Headers */ = {isa = PBXBuildFile; fileRef = F4B422C2220C0000009E1E7D /* DOMPasteAccess.h */; settings = {ATTRIBUTES = (Private, ); }; };
                F4BFB9851E1DDF9B00862C24 /* DumpEditingHistory.js in Copy Scripts */ = {isa = PBXBuildFile; fileRef = F48389831E1DDF2B0076B7EA /* DumpEditingHistory.js */; };
                F4BFB9861E1DDF9B00862C24 /* EditingHistoryUtil.js in Copy Scripts */ = {isa = PBXBuildFile; fileRef = F48389841E1DDF2B0076B7EA /* EditingHistoryUtil.js */; };
                F4D43D662188038B00ECECAC /* SerializedAttachmentData.h in Headers */ = {isa = PBXBuildFile; fileRef = F4D43D64218802E600ECECAC /* SerializedAttachmentData.h */; settings = {ATTRIBUTES = (Private, ); }; };
                F48D2AA42159740D00C6752B /* ColorCocoa.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = ColorCocoa.mm; sourceTree = "<group>"; };
                F49786871FF45FA500E060AB /* PasteboardItemInfo.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PasteboardItemInfo.h; sourceTree = "<group>"; };
                F49E98E421DEE6C1009AE55E /* EditAction.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = EditAction.cpp; sourceTree = "<group>"; };
-               F4B422C2220C0000009E1E7D /* DOMPasteAccessPolicy.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DOMPasteAccessPolicy.h; sourceTree = "<group>"; };
+               F4B422C2220C0000009E1E7D /* DOMPasteAccess.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DOMPasteAccess.h; sourceTree = "<group>"; };
                F4D43D64218802E600ECECAC /* SerializedAttachmentData.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SerializedAttachmentData.h; sourceTree = "<group>"; };
                F4D9817D2195FBF6008230FC /* ChangeListTypeCommand.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ChangeListTypeCommand.h; sourceTree = "<group>"; };
                F4D9817E2195FBF6008230FC /* ChangeListTypeCommand.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = ChangeListTypeCommand.cpp; sourceTree = "<group>"; };
                                A8185F3609765765005826D9 /* DOMImplementation.cpp */,
                                A8185F3309765765005826D9 /* DOMImplementation.h */,
                                93EEC1E909C2877700C515D1 /* DOMImplementation.idl */,
-                               F4B422C2220C0000009E1E7D /* DOMPasteAccessPolicy.h */,
+                               F4B422C2220C0000009E1E7D /* DOMPasteAccess.h */,
                                0F4966991DB408C100A274BB /* DOMPoint.h */,
                                0F49669A1DB408C100A274BB /* DOMPoint.idl */,
                                0F4966A21DB4091000A274BB /* DOMPointInit.h */,
                                A9C6E4E40D745E05006442E9 /* DOMMimeType.h in Headers */,
                                A9C6E4E80D745E18006442E9 /* DOMMimeTypeArray.h in Headers */,
                                1ACE53E80A8D18E70022947D /* DOMParser.h in Headers */,
-                               F4B422C4220C0568009E1E7D /* DOMPasteAccessPolicy.h in Headers */,
+                               F4B422C4220C0568009E1E7D /* DOMPasteAccess.h in Headers */,
                                7A54881714E432A1006AE05A /* DOMPatchSupport.h in Headers */,
                                A9C6E4EC0D745E2B006442E9 /* DOMPlugin.h in Headers */,
                                A9C6E4F00D745E38006442E9 /* DOMPluginArray.h in Headers */,
similarity index 73%
rename from Source/WebCore/dom/DOMPasteAccessPolicy.h
rename to Source/WebCore/dom/DOMPasteAccess.h
index 80018b9..8bfc167 100644 (file)
@@ -25,6 +25,8 @@
 
 #pragma once
 
+#include <wtf/Forward.h>
+
 namespace WebCore {
 
 enum class DOMPasteAccessPolicy : uint8_t {
@@ -33,4 +35,23 @@ enum class DOMPasteAccessPolicy : uint8_t {
     Granted
 };
 
-}
+enum class DOMPasteAccessResponse : uint8_t {
+    DeniedForGesture,
+    GrantedForCommand,
+    GrantedForGesture
+};
+
+} // namespace WebCore
+
+namespace WTF {
+
+template<> struct EnumTraits<WebCore::DOMPasteAccessResponse> {
+    using values = EnumValues<
+        WebCore::DOMPasteAccessResponse,
+        WebCore::DOMPasteAccessResponse::DeniedForGesture,
+        WebCore::DOMPasteAccessResponse::GrantedForCommand,
+        WebCore::DOMPasteAccessResponse::GrantedForGesture
+    >;
+};
+
+} // namespace WTF
index ddcbdd6..15ebedf 100644 (file)
@@ -25,7 +25,7 @@
 
 #pragma once
 
-#include "DOMPasteAccessPolicy.h"
+#include "DOMPasteAccess.h"
 #include <wtf/Function.h>
 #include <wtf/Noncopyable.h>
 #include <wtf/Optional.h>
@@ -65,7 +65,19 @@ public:
     }
 
     DOMPasteAccessPolicy domPasteAccessPolicy() const { return m_domPasteAccessPolicy; }
-    void didRequestDOMPasteAccess(bool granted) { m_domPasteAccessPolicy = granted ? DOMPasteAccessPolicy::Granted : DOMPasteAccessPolicy::Denied; }
+    void didRequestDOMPasteAccess(DOMPasteAccessResponse response)
+    {
+        switch (response) {
+        case DOMPasteAccessResponse::DeniedForGesture:
+            m_domPasteAccessPolicy = DOMPasteAccessPolicy::Denied;
+            break;
+        case DOMPasteAccessResponse::GrantedForCommand:
+            break;
+        case DOMPasteAccessResponse::GrantedForGesture:
+            m_domPasteAccessPolicy = DOMPasteAccessPolicy::Granted;
+            break;
+        }
+    }
     void resetDOMPasteAccess() { m_domPasteAccessPolicy = DOMPasteAccessPolicy::NotRequestedYet; }
 
 private:
index a43a1bb..3eb676e 100644 (file)
@@ -34,6 +34,7 @@
 #include "ColorChooser.h"
 #include "ContextMenuClient.h"
 #include "CookieJar.h"
+#include "DOMPasteAccess.h"
 #include "DataListSuggestionPicker.h"
 #include "DatabaseProvider.h"
 #include "DiagnosticLoggingClient.h"
@@ -189,7 +190,7 @@ private:
     void registerRedoStep(UndoStep&) final;
     void clearUndoRedoOperations() final { }
 
-    bool requestDOMPasteAccess() final { return false; }
+    DOMPasteAccessResponse requestDOMPasteAccess(const String&) final { return DOMPasteAccessResponse::DeniedForGesture; }
 
     bool canCopyCut(Frame*, bool defaultValue) const final { return defaultValue; }
     bool canPaste(Frame*, bool defaultValue) const final { return defaultValue; }
index bc6584b..1d1c8d0 100644 (file)
@@ -36,6 +36,8 @@
 
 namespace WebCore {
 
+enum class DOMPasteAccessResponse : uint8_t;
+
 class DocumentFragment;
 class Element;
 class Frame;
@@ -97,7 +99,7 @@ public:
     virtual void requestCandidatesForSelection(const VisibleSelection&) { }
     virtual void handleAcceptedCandidateWithSoftSpaces(TextCheckingResult) { }
 
-    virtual bool requestDOMPasteAccess() = 0;
+    virtual DOMPasteAccessResponse requestDOMPasteAccess(const String& originIdentifier) = 0;
 
     // Notify an input method that a composition was voluntarily discarded by WebCore, so that it could clean up too.
     // This function is not called when a composition is closed per a request from an input method.
index 6784380..4fe2bc9 100644 (file)
@@ -681,9 +681,15 @@ bool Frame::requestDOMPasteAccess()
         if (!client)
             return false;
 
-        bool granted = client->requestDOMPasteAccess();
-        gestureToken->didRequestDOMPasteAccess(granted);
-        return granted;
+        auto response = client->requestDOMPasteAccess(m_doc->originIdentifierForPasteboard());
+        gestureToken->didRequestDOMPasteAccess(response);
+        switch (response) {
+        case DOMPasteAccessResponse::GrantedForCommand:
+        case DOMPasteAccessResponse::GrantedForGesture:
+            return true;
+        case DOMPasteAccessResponse::DeniedForGesture:
+            return false;
+        }
     }
     }
 
index 3446c0d..fa8d986 100644 (file)
@@ -1,3 +1,67 @@
+2019-03-02  Wenson Hsieh  <wenson_hsieh@apple.com>
+
+        [iOS] Programmatic paste access should be granted when copying and pasting within the same origin
+        https://bugs.webkit.org/show_bug.cgi?id=195053
+        <rdar://problem/48134710>
+
+        Reviewed by Ryosuke Niwa.
+
+        Add support for automatically granting programmatic pasteboard access when the pasteboard identifier of the
+        document requesting programmatic paste matches the identifier in the custom pasteboard data blob in the
+        UIPasteboard. To do this, we send the pasteboard identifier of the document requesting the DOM paste to the UI
+        process, and check this against the pasteboard identifiers for each item on the platform pasteboard. If all
+        items in the platform pasteboard match the given pasteboard identifier (since we don't support writing multiple
+        pasteboard items via bindings, this should only be a single item), we skip showing the paste callout and
+        immediately invoke the programmatic paste handler.
+
+        * Scripts/webkit/messages.py:
+        * UIProcess/API/gtk/PageClientImpl.cpp:
+        (WebKit::PageClientImpl::requestDOMPasteAccess):
+        * UIProcess/API/gtk/PageClientImpl.h:
+        * UIProcess/API/wpe/PageClientImpl.cpp:
+        (WebKit::PageClientImpl::requestDOMPasteAccess):
+        * UIProcess/API/wpe/PageClientImpl.h:
+        * UIProcess/PageClient.h:
+        * UIProcess/WebPageProxy.cpp:
+        (WebKit::WebPageProxy::requestDOMPasteAccess):
+        * UIProcess/WebPageProxy.h:
+        * UIProcess/WebPageProxy.messages.in:
+
+        Add plumbing to deliver the pasteboard identifier to the client when requesting DOM paste.
+
+        * UIProcess/ios/PageClientImplIOS.h:
+        * UIProcess/ios/PageClientImplIOS.mm:
+        (WebKit::PageClientImpl::requestDOMPasteAccess):
+        * UIProcess/ios/WKContentViewInteraction.h:
+        * UIProcess/ios/WKContentViewInteraction.mm:
+        (-[WKContentView cleanupInteraction]):
+        (-[WKContentView resignFirstResponderForWebView]):
+        (-[WKContentView _webTouchEventsRecognized:]):
+        (-[WKContentView _willHideMenu:]):
+        (-[WKContentView pasteForWebView:]):
+        (-[WKContentView _handleDOMPasteRequestWithResult:]):
+        (-[WKContentView _willPerformAction:sender:]):
+        (-[WKContentView _didPerformAction:sender:]):
+        (-[WKContentView handleKeyWebEvent:withCompletionHandler:]):
+        (allPasteboardItemOriginsMatchOrigin):
+        (-[WKContentView _requestDOMPasteAccessWithElementRect:originIdentifier:completionHandler:]):
+        (-[WKContentView _requestDOMPasteAccessWithElementRect:completionHandler:]): Deleted.
+
+        Bail early in the case where all items' pasteboard identifiers (read via custom pasteboard data) match. When
+        this happens, we only grant access for the current paste command, rather than granting access for the user
+        gesture token.
+
+        * UIProcess/mac/PageClientImplMac.h:
+        * UIProcess/win/PageClientImpl.cpp:
+        (WebKit::PageClientImpl::requestDOMPasteAccess):
+        * UIProcess/win/PageClientImpl.h:
+        * WebProcess/WebCoreSupport/WebEditorClient.cpp:
+        (WebKit::WebEditorClient::requestDOMPasteAccess):
+        * WebProcess/WebCoreSupport/WebEditorClient.h:
+        * WebProcess/WebPage/WebPage.cpp:
+        (WebKit::WebPage::requestDOMPasteAccess):
+        * WebProcess/WebPage/WebPage.h:
+
 2019-02-27  Darin Adler  <darin@apple.com>
 
         Fixed makeString(float) to do shortest-form serialization without first converting to double
index 0fd0c59..bfab03a 100644 (file)
@@ -398,6 +398,7 @@ def headers_for_type(type):
         'String': ['<wtf/text/WTFString.h>'],
         'PAL::SessionID': ['<pal/SessionID.h>'],
         'WebCore::AutoplayEventFlags': ['<WebCore/AutoplayEvent.h>'],
+        'WebCore::DOMPasteAccessResponse': ['<WebCore/DOMPasteAccess.h>'],
         'WebCore::DragHandlingMethod': ['<WebCore/DragActions.h>'],
         'WebCore::ExceptionDetails': ['<WebCore/JSDOMExceptionHandling.h>'],
         'WebCore::FileChooserSettings': ['<WebCore/FileChooser.h>'],
index 721ec1e..b3d62dc 100644 (file)
@@ -44,6 +44,7 @@
 #include "WebProcessPool.h"
 #include <WebCore/CairoUtilities.h>
 #include <WebCore/Cursor.h>
+#include <WebCore/DOMPasteAccess.h>
 #include <WebCore/EventNames.h>
 #include <WebCore/GtkUtilities.h>
 #include <WebCore/NotImplemented.h>
@@ -524,9 +525,9 @@ bool PageClientImpl::decidePolicyForInstallMissingMediaPluginsPermissionRequest(
 }
 #endif
 
-void PageClientImpl::requestDOMPasteAccess(const IntRect&, CompletionHandler<void(bool)>&& completionHandler)
+void PageClientImpl::requestDOMPasteAccess(const IntRect&, const String&, CompletionHandler<void(WebCore::DOMPasteAccessResponse)>&& completionHandler)
 {
-    completionHandler(false);
+    completionHandler(WebCore::DOMPasteAccessResponse::DeniedForGesture);
 }
 
 } // namespace WebKit
index b90ac5a..d20dfb7 100644 (file)
 #include <gtk/gtk.h>
 #include <memory>
 
+namespace WebCore {
+enum class DOMPasteAccessPolicy : uint8_t;
+}
+
 namespace WebKit {
 
 class DrawingAreaProxy;
@@ -150,7 +154,7 @@ private:
 
     void didFinishProcessingAllPendingMouseEvents() final { }
 
-    void requestDOMPasteAccess(const WebCore::IntRect&, CompletionHandler<void(bool)>&&) final;
+    void requestDOMPasteAccess(const WebCore::IntRect&, const String&, CompletionHandler<void(WebCore::DOMPasteAccessResponse)>&&) final;
 
 #if ENABLE(VIDEO) && USE(GSTREAMER)
     bool decidePolicyForInstallMissingMediaPluginsPermissionRequest(InstallMissingMediaPluginsPermissionRequest&) override;
index 2c1c6ee..260d469 100644 (file)
@@ -34,6 +34,7 @@
 #include "WebContextMenuProxy.h"
 #include "WebContextMenuProxyWPE.h"
 #include <WebCore/ActivityState.h>
+#include <WebCore/DOMPasteAccess.h>
 #include <WebCore/NotImplemented.h>
 
 namespace WebKit {
@@ -399,9 +400,9 @@ void PageClientImpl::beganExitFullScreen(const WebCore::IntRect& /* initialFrame
 
 #endif // ENABLE(FULLSCREEN_API)
 
-void PageClientImpl::requestDOMPasteAccess(const WebCore::IntRect&, CompletionHandler<void(bool)>&& completionHandler)
+void PageClientImpl::requestDOMPasteAccess(const WebCore::IntRect&, const String&, CompletionHandler<void(WebCore::DOMPasteAccessResponse)>&& completionHandler)
 {
-    completionHandler(false);
+    completionHandler(WebCore::DOMPasteAccessResponse::DeniedForGesture);
 }
 
 } // namespace WebKit
index 5aae0f2..0d8d040 100644 (file)
@@ -34,6 +34,10 @@ namespace WKWPE {
 class View;
 }
 
+namespace WebCore {
+enum class DOMPasteAccessResponse : uint8_t;
+}
+
 namespace WebKit {
 
 class ScrollGestureController;
@@ -146,7 +150,7 @@ private:
     void didFinishProcessingAllPendingMouseEvents() final { }
 
     IPC::Attachment hostFileDescriptor() final;
-    void requestDOMPasteAccess(const WebCore::IntRect&, CompletionHandler<void(bool)>&&) final;
+    void requestDOMPasteAccess(const WebCore::IntRect&, const String&, CompletionHandler<void(WebCore::DOMPasteAccessResponse)>&&) final;
 
     WebCore::UserInterfaceLayoutDirection userInterfaceLayoutDirection() override;
 
index cd7ac05..cdd7d66 100644 (file)
@@ -86,6 +86,7 @@ enum class RouteSharingPolicy : uint8_t;
 enum class ScrollbarStyle : uint8_t;
 enum class TextIndicatorWindowLifetime : uint8_t;
 enum class TextIndicatorWindowDismissalAnimation : uint8_t;
+enum class DOMPasteAccessResponse : uint8_t;
 
 struct DictionaryPopupInfo;
 struct Highlight;
@@ -95,13 +96,11 @@ struct ShareDataWithParsedURL;
 
 template <typename> class RectEdges;
 using FloatBoxExtent = RectEdges<float>;
-}
 
 #if ENABLE(DRAG_SUPPORT)
-namespace WebCore {
 struct DragItem;
-}
 #endif
+}
 
 namespace WebKit {
 
@@ -465,7 +464,7 @@ public:
     virtual void didChangeDragCaretRect(const WebCore::IntRect& previousCaretRect, const WebCore::IntRect& caretRect) = 0;
 #endif
 
-    virtual void requestDOMPasteAccess(const WebCore::IntRect& elementRect, CompletionHandler<void(bool)>&&) = 0;
+    virtual void requestDOMPasteAccess(const WebCore::IntRect& elementRect, const String& originIdentifier, CompletionHandler<void(WebCore::DOMPasteAccessResponse)>&&) = 0;
 
 #if ENABLE(ATTACHMENT_ELEMENT)
     virtual void didInsertAttachment(API::Attachment&, const String& source) { }
index 5fc6658..3caceb2 100644 (file)
 #include "WebsiteDataStore.h"
 #include <WebCore/AdClickAttribution.h>
 #include <WebCore/BitmapImage.h>
+#include <WebCore/DOMPasteAccess.h>
 #include <WebCore/DeprecatedGlobalSettings.h>
 #include <WebCore/DiagnosticLoggingClient.h>
 #include <WebCore/DiagnosticLoggingKeys.h>
@@ -5528,9 +5529,9 @@ void WebPageProxy::setNeedsPlainTextQuirk(bool needsPlainTextQuirk)
     m_needsPlainTextQuirk = needsPlainTextQuirk;
 }
 
-void WebPageProxy::requestDOMPasteAccess(const WebCore::IntRect& elementRect, CompletionHandler<void(bool)>&& completionHandler)
+void WebPageProxy::requestDOMPasteAccess(const WebCore::IntRect& elementRect, const String& originIdentifier, CompletionHandler<void(WebCore::DOMPasteAccessResponse)>&& completionHandler)
 {
-    m_pageClient->requestDOMPasteAccess(elementRect, WTFMove(completionHandler));
+    m_pageClient->requestDOMPasteAccess(elementRect, originIdentifier, WTFMove(completionHandler));
 }
 
 // BackForwardList
index e2f2af5..1ff6044 100644 (file)
@@ -182,6 +182,7 @@ class ValidationBubble;
 enum SelectionDirection : uint8_t;
 
 enum class AutoplayEvent : uint8_t;
+enum class DOMPasteAccessResponse : uint8_t;
 enum class LockBackForwardList : bool;
 enum class HasInsecureContent : bool;
 enum class NotificationDirection : uint8_t;
@@ -1669,7 +1670,7 @@ private:
     void setNeedsHiddenContentEditableQuirk(bool);
     void setNeedsPlainTextQuirk(bool);
 
-    void requestDOMPasteAccess(const WebCore::IntRect&, CompletionHandler<void(bool)>&&);
+    void requestDOMPasteAccess(const WebCore::IntRect&, const String&, CompletionHandler<void(WebCore::DOMPasteAccessResponse)>&&);
 
     // Back/Forward list management
     void backForwardAddItem(BackForwardListItemState&&);
index c10a75c..e864d4a 100644 (file)
@@ -251,7 +251,7 @@ messages -> WebPageProxy {
     SetHasHadSelectionChangesFromUserInteraction(bool hasHadUserSelectionChanges)
     SetNeedsHiddenContentEditableQuirk(bool needsHiddenContentEditableQuirk)
     SetNeedsPlainTextQuirk(bool needsPlainTextQuirk)
-    RequestDOMPasteAccess(WebCore::IntRect elementRect) -> (bool granted) Delayed
+    RequestDOMPasteAccess(WebCore::IntRect elementRect, String originIdentifier) -> (enum:uint8_t WebCore::DOMPasteAccessResponse response) Delayed
 
     # Find messages
     DidCountStringMatches(String string, uint32_t matchCount)
index d0d0384..9fedc68 100644 (file)
@@ -35,6 +35,7 @@ OBJC_CLASS WKContentView;
 OBJC_CLASS WKEditorUndoTarget;
 
 namespace WebCore {
+enum class DOMPasteAccessResponse : uint8_t;
 struct PromisedAttachmentInfo;
 }
 
@@ -224,7 +225,7 @@ private:
     void requestPasswordForQuickLookDocument(const String& fileName, WTF::Function<void(const String&)>&&) override;
 #endif
 
-    void requestDOMPasteAccess(const WebCore::IntRect& elementRect, CompletionHandler<void(bool)>&&) final;
+    void requestDOMPasteAccess(const WebCore::IntRect& elementRect, const String&, CompletionHandler<void(WebCore::DOMPasteAccessResponse)>&&) final;
 
 #if ENABLE(DATA_INTERACTION)
     void didPerformDragOperation(bool handled) override;
index 5e0a690..d954e77 100644 (file)
@@ -54,6 +54,7 @@
 #import "WebEditCommandProxy.h"
 #import "WebProcessProxy.h"
 #import "_WKDownloadInternal.h"
+#import <WebCore/DOMPasteAccess.h>
 #import <WebCore/DictionaryLookup.h>
 #import <WebCore/NotImplemented.h>
 #import <WebCore/PlatformScreen.h>
@@ -837,9 +838,9 @@ void PageClientImpl::requestPasswordForQuickLookDocument(const String& fileName,
 }
 #endif
 
-void PageClientImpl::requestDOMPasteAccess(const WebCore::IntRect& elementRect, CompletionHandler<void(bool)>&& completionHandler)
+void PageClientImpl::requestDOMPasteAccess(const WebCore::IntRect& elementRect, const String& originIdentifier, CompletionHandler<void(WebCore::DOMPasteAccessResponse)>&& completionHandler)
 {
-    [m_contentView _requestDOMPasteAccessWithElementRect:elementRect completionHandler:WTFMove(completionHandler)];
+    [m_contentView _requestDOMPasteAccessWithElementRect:elementRect originIdentifier:originIdentifier completionHandler:WTFMove(completionHandler)];
 }
 
 #if HAVE(PENCILKIT)
index 2cecc2c..773063a 100644 (file)
@@ -71,14 +71,13 @@ class IntSize;
 class SelectionRect;
 struct PromisedAttachmentInfo;
 struct ShareDataWithParsedURL;
+enum class DOMPasteAccessResponse : uint8_t;
 enum class RouteSharingPolicy : uint8_t;
-}
 
 #if ENABLE(DRAG_SUPPORT)
-namespace WebCore {
 struct DragItem;
-}
 #endif
+}
 
 namespace WebKit {
 class InputViewUpdateDeferrer;
@@ -318,7 +317,7 @@ struct WKAutoCorrectionData {
     BOOL _focusRequiresStrongPasswordAssistance;
 
     BOOL _hasSetUpInteractions;
-    CompletionHandler<void(bool)> _domPasteRequestHandler;
+    CompletionHandler<void(WebCore::DOMPasteAccessResponse)> _domPasteRequestHandler;
 
 #if ENABLE(DATA_INTERACTION)
     WebKit::DragDropInteractionState _dragDropInteractionState;
@@ -440,7 +439,7 @@ FOR_EACH_PRIVATE_WKCONTENTVIEW_ACTION(DECLARE_WKCONTENTVIEW_ACTION_FOR_WEB_VIEW)
 - (void)_accessibilityClearSelection;
 - (WKFormInputSession *)_formInputSession;
 
-- (void)_requestDOMPasteAccessWithElementRect:(const WebCore::IntRect&)elementRect completionHandler:(CompletionHandler<void(bool)>&&)completionHandler;
+- (void)_requestDOMPasteAccessWithElementRect:(const WebCore::IntRect&)elementRect originIdentifier:(const String&)originIdentifier completionHandler:(CompletionHandler<void(WebCore::DOMPasteAccessResponse)>&&)completionHandler;
 
 @property (nonatomic, readonly) WebKit::InteractionInformationAtPosition currentPositionInformation;
 - (void)doAfterPositionInformationUpdate:(void (^)(WebKit::InteractionInformationAtPosition))action forRequest:(WebKit::InteractionInformationRequest)request;
index df7f72e..37b3620 100644 (file)
@@ -77,6 +77,7 @@
 #import <CoreText/CTFontDescriptor.h>
 #import <MobileCoreServices/UTCoreTypes.h>
 #import <WebCore/Color.h>
+#import <WebCore/DOMPasteAccess.h>
 #import <WebCore/DataDetection.h>
 #import <WebCore/FloatQuad.h>
 #import <WebCore/FontAttributeChanges.h>
@@ -890,7 +891,7 @@ static inline bool hasFocusedElement(WebKit::FocusedElementInformation focusedEl
 #if ENABLE(POINTER_EVENTS)
     [self _resetPanningPreventionFlags];
 #endif
-    [self _handleDOMPasteRequestWithResult:NO];
+    [self _handleDOMPasteRequestWithResult:WebCore::DOMPasteAccessResponse::DeniedForGesture];
 }
 
 - (void)_removeDefaultGestureRecognizers
@@ -1178,7 +1179,7 @@ static inline bool hasFocusedElement(WebKit::FocusedElementInformation focusedEl
     bool superDidResign = [super resignFirstResponder];
 
     if (superDidResign) {
-        [self _handleDOMPasteRequestWithResult:NO];
+        [self _handleDOMPasteRequestWithResult:WebCore::DOMPasteAccessResponse::DeniedForGesture];
         _page->activityStateDidChange(WebCore::ActivityState::IsFocused);
     }
 
@@ -1214,7 +1215,7 @@ inline static UIKeyModifierFlags gestureRecognizerModifierFlags(UIGestureRecogni
 
     _lastInteractionLocation = lastTouchEvent->locationInDocumentCoordinates;
     if (lastTouchEvent->type == UIWebTouchEventTouchBegin) {
-        [self _handleDOMPasteRequestWithResult:NO];
+        [self _handleDOMPasteRequestWithResult:WebCore::DOMPasteAccessResponse::DeniedForGesture];
         _layerTreeTransactionIdAtLastTouchStart = downcast<WebKit::RemoteLayerTreeDrawingAreaProxy>(*_page->drawingArea()).lastCommittedLayerTreeTransactionID();
     }
 
@@ -2898,7 +2899,7 @@ WEBCORE_COMMAND_FOR_WEBVIEW(pasteAndMatchStyle);
 
 - (void)_willHideMenu:(NSNotification *)notification
 {
-    [self _handleDOMPasteRequestWithResult:NO];
+    [self _handleDOMPasteRequestWithResult:WebCore::DOMPasteAccessResponse::DeniedForGesture];
 }
 
 - (void)_didHideMenu:(NSNotification *)notification
@@ -2926,7 +2927,7 @@ WEBCORE_COMMAND_FOR_WEBVIEW(pasteAndMatchStyle);
 
 - (void)pasteForWebView:(id)sender
 {
-    if (sender == UIMenuController.sharedMenuController && [self _handleDOMPasteRequestWithResult:YES])
+    if (sender == UIMenuController.sharedMenuController && [self _handleDOMPasteRequestWithResult:WebCore::DOMPasteAccessResponse::GrantedForGesture])
         return;
 
     _page->executeEditCommand("paste"_s);
@@ -3058,11 +3059,11 @@ WEBCORE_COMMAND_FOR_WEBVIEW(pasteAndMatchStyle);
     _page->storeSelectionForAccessibility(false);
 }
 
-- (BOOL)_handleDOMPasteRequestWithResult:(BOOL)allowPaste
+- (BOOL)_handleDOMPasteRequestWithResult:(WebCore::DOMPasteAccessResponse)response
 {
     if (auto pasteHandler = WTFMove(_domPasteRequestHandler)) {
         [self hideGlobalMenuController];
-        pasteHandler(allowPaste);
+        pasteHandler(response);
         return YES;
     }
     return NO;
@@ -3071,13 +3072,13 @@ WEBCORE_COMMAND_FOR_WEBVIEW(pasteAndMatchStyle);
 - (void)_willPerformAction:(SEL)action sender:(id)sender
 {
     if (action != @selector(paste:))
-        [self _handleDOMPasteRequestWithResult:NO];
+        [self _handleDOMPasteRequestWithResult:WebCore::DOMPasteAccessResponse::DeniedForGesture];
 }
 
 - (void)_didPerformAction:(SEL)action sender:(id)sender
 {
     if (action == @selector(paste:))
-        [self _handleDOMPasteRequestWithResult:NO];
+        [self _handleDOMPasteRequestWithResult:WebCore::DOMPasteAccessResponse::DeniedForGesture];
 }
 
 // UIWKInteractionViewProtocol
@@ -4295,7 +4296,7 @@ static NSString *contentTypeFromFieldName(WebCore::AutofillFieldName fieldName)
 
 - (void)handleKeyWebEvent:(::WebEvent *)theEvent withCompletionHandler:(void (^)(::WebEvent *theEvent, BOOL wasHandled))completionHandler
 {
-    [self _handleDOMPasteRequestWithResult:NO];
+    [self _handleDOMPasteRequestWithResult:WebCore::DOMPasteAccessResponse::DeniedForGesture];
 
     _keyWebEventHandler = makeBlockPtr(completionHandler);
     _page->handleKeyboardEvent(WebKit::NativeWebKeyboardEvent(theEvent));
@@ -5035,11 +5036,41 @@ static const double minimumFocusedElementAreaForSuppressingSelectionAssistant =
 #endif
 }
 
-- (void)_requestDOMPasteAccessWithElementRect:(const WebCore::IntRect&)elementRect completionHandler:(CompletionHandler<void(bool)>&&)completionHandler
+static BOOL allPasteboardItemOriginsMatchOrigin(UIPasteboard *pasteboard, const String& originIdentifier)
+{
+    if (originIdentifier.isEmpty())
+        return NO;
+
+    auto *indices = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, [pasteboard numberOfItems])];
+    auto *allCustomData = [pasteboard dataForPasteboardType:@(WebCore::PasteboardCustomData::cocoaType()) inItemSet:indices];
+    if (!allCustomData.count)
+        return NO;
+
+    BOOL foundAtLeastOneMatchingIdentifier = NO;
+    for (NSData *data in allCustomData) {
+        if (!data.length)
+            continue;
+
+        auto buffer = WebCore::SharedBuffer::create(data);
+        if (WebCore::PasteboardCustomData::fromSharedBuffer(buffer.get()).origin != originIdentifier)
+            return NO;
+
+        foundAtLeastOneMatchingIdentifier = YES;
+    }
+
+    return foundAtLeastOneMatchingIdentifier;
+}
+
+- (void)_requestDOMPasteAccessWithElementRect:(const WebCore::IntRect&)elementRect originIdentifier:(const String&)originIdentifier completionHandler:(CompletionHandler<void(WebCore::DOMPasteAccessResponse)>&&)completionHandler
 {
     if (auto existingCompletionHandler = std::exchange(_domPasteRequestHandler, WTFMove(completionHandler))) {
         ASSERT_NOT_REACHED();
-        existingCompletionHandler(false);
+        existingCompletionHandler(WebCore::DOMPasteAccessResponse::DeniedForGesture);
+    }
+
+    if (allPasteboardItemOriginsMatchOrigin(UIPasteboard.generalPasteboard, originIdentifier)) {
+        [self _handleDOMPasteRequestWithResult:WebCore::DOMPasteAccessResponse::GrantedForCommand];
+        return;
     }
 
     WebCore::IntRect menuControllerRect = elementRect;
index 42101ab..c93fbcb 100644 (file)
@@ -30,6 +30,7 @@
 #include "CorrectionPanel.h"
 #include "PageClientImplCocoa.h"
 #include "WebFullScreenManagerProxy.h"
+#include <WebCore/DOMPasteAccess.h>
 #include <wtf/CompletionHandler.h>
 #include <wtf/RetainPtr.h>
 
@@ -214,7 +215,7 @@ private:
     void willRecordNavigationSnapshot(WebBackForwardListItem&) override;
     void didRemoveNavigationGestureSnapshot() override;
 
-    void requestDOMPasteAccess(const WebCore::IntRect&, CompletionHandler<void(bool)>&& completion) final { completion(false); }
+    void requestDOMPasteAccess(const WebCore::IntRect&, const String& originIdentifier, CompletionHandler<void(WebCore::DOMPasteAccessResponse)>&& completion) final { completion(WebCore::DOMPasteAccessResponse::DeniedForGesture); }
 
     NSView *activeView() const;
     NSWindow *activeWindow() const;
index ca5023e..264b01c 100644 (file)
@@ -33,6 +33,7 @@
 #include "WebPageProxy.h"
 #include "WebPopupMenuProxyWin.h"
 #include "WebView.h"
+#include <WebCore/DOMPasteAccess.h>
 
 namespace WebKit {
 using namespace WebCore;
@@ -367,9 +368,9 @@ HWND PageClientImpl::viewWidget()
     return m_view.window();
 }
 
-void PageClientImpl::requestDOMPasteAccess(const IntRect&, CompletionHandler<void(bool)>&& completionHandler)
+void PageClientImpl::requestDOMPasteAccess(const IntRect&, const String&, CompletionHandler<void(WebCore::DOMPasteAccessResponse)>&& completionHandler)
 {
-    completionHandler(false);
+    completionHandler(WebCore::DOMPasteAccessResponse::DeniedForGesture);
 }
 
 } // namespace WebKit
index 7396d39..1f9da84 100644 (file)
 #include "WebPageProxy.h"
 #include <WebCore/IntSize.h>
 
+namespace WebCore {
+enum class DOMPasteAccessResponse : uint8_t;
+}
+
 namespace WebKit {
 
 class DrawingAreaProxy;
@@ -144,7 +148,7 @@ private:
 
     void didFinishProcessingAllPendingMouseEvents() final { }
 
-    void requestDOMPasteAccess(const WebCore::IntRect&, CompletionHandler<void(bool)>&&) final;
+    void requestDOMPasteAccess(const WebCore::IntRect&, const String&, CompletionHandler<void(WebCore::DOMPasteAccessResponse)>&&) final;
 
     // Members of PageClientImpl class
     DefaultUndoController m_undoController;
index 9340571..49d5eb5 100644 (file)
@@ -38,6 +38,7 @@
 #include "WebProcess.h"
 #include "WebUndoStep.h"
 #include <WebCore/ArchiveResource.h>
+#include <WebCore/DOMPasteAccess.h>
 #include <WebCore/DocumentFragment.h>
 #include <WebCore/FocusController.h>
 #include <WebCore/Frame.h>
@@ -354,9 +355,9 @@ void WebEditorClient::redo()
     m_page->sendSync(Messages::WebPageProxy::ExecuteUndoRedo(UndoOrRedo::Redo), Messages::WebPageProxy::ExecuteUndoRedo::Reply());
 }
 
-bool WebEditorClient::requestDOMPasteAccess()
+WebCore::DOMPasteAccessResponse WebEditorClient::requestDOMPasteAccess(const String& originIdentifier)
 {
-    return m_page->requestDOMPasteAccess();
+    return m_page->requestDOMPasteAccess(originIdentifier);
 }
 
 #if PLATFORM(WIN)
index b12ba60..30988ed 100644 (file)
 #include <WebCore/EditorClient.h>
 #include <WebCore/TextCheckerClient.h>
 
+namespace WebCore {
+enum class DOMPasteAccessResponse : uint8_t;
+}
+
 namespace WebKit {
 
 class WebPage;
@@ -88,7 +92,7 @@ private:
     void registerRedoStep(WebCore::UndoStep&) final;
     void clearUndoRedoOperations() final;
 
-    bool requestDOMPasteAccess() final;
+    WebCore::DOMPasteAccessResponse requestDOMPasteAccess(const String& originIdentifier) final;
 
     bool canCopyCut(WebCore::Frame*, bool defaultValue) const final;
     bool canPaste(WebCore::Frame*, bool defaultValue) const final;
index 269f6ad..9ace9cc 100644 (file)
 #include <WebCore/Chrome.h>
 #include <WebCore/CommonVM.h>
 #include <WebCore/ContextMenuController.h>
+#include <WebCore/DOMPasteAccess.h>
 #include <WebCore/DataTransfer.h>
 #include <WebCore/DatabaseManager.h>
 #include <WebCore/DeprecatedGlobalSettings.h>
@@ -6440,13 +6441,11 @@ void WebPage::didCompleteShareSheet(bool wasGranted, ShareSheetCallbackID callba
     callback(wasGranted);
 }
 
-bool WebPage::requestDOMPasteAccess()
+WebCore::DOMPasteAccessResponse WebPage::requestDOMPasteAccess(const String& originIdentifier)
 {
-    bool granted = false;
-    if (!sendSyncWithDelayedReply(Messages::WebPageProxy::RequestDOMPasteAccess(rectForElementAtInteractionLocation()), Messages::WebPageProxy::RequestDOMPasteAccess::Reply(granted)))
-        return false;
-
-    return granted;
+    auto response = WebCore::DOMPasteAccessResponse::DeniedForGesture;
+    sendSyncWithDelayedReply(Messages::WebPageProxy::RequestDOMPasteAccess(rectForElementAtInteractionLocation(), originIdentifier), Messages::WebPageProxy::RequestDOMPasteAccess::Reply(response));
+    return response;
 }
 
 void WebPage::simulateDeviceOrientationChange(double alpha, double beta, double gamma)
index 9ee06fb..fc9f9f5 100644 (file)
@@ -171,6 +171,7 @@ class TextCheckingRequest;
 class VisiblePosition;
 
 enum SyntheticClickType : int8_t;
+enum class DOMPasteAccessResponse : uint8_t;
 enum class DragHandlingMethod : uint8_t;
 enum class ShouldTreatAsContinuingLoad : bool;
 enum class TextIndicatorPresentationTransition : uint8_t;
@@ -1144,7 +1145,7 @@ public:
         return sendSync(WTFMove(message), WTFMove(reply), m_pageID, Seconds::infinity(), IPC::SendSyncOption::InformPlatformProcessWillSuspend);
     }
 
-    bool requestDOMPasteAccess();
+    WebCore::DOMPasteAccessResponse requestDOMPasteAccess(const String& originIdentifier);
     WebCore::IntRect rectForElementAtInteractionLocation() const;
 
     const Optional<WebCore::Color>& backgroundColor() const { return m_backgroundColor; }
index 8161892..d897c44 100644 (file)
@@ -1,3 +1,13 @@
+2019-03-02  Wenson Hsieh  <wenson_hsieh@apple.com>
+
+        [iOS] Programmatic paste access should be granted when copying and pasting within the same origin
+        https://bugs.webkit.org/show_bug.cgi?id=195053
+        <rdar://problem/48134710>
+
+        Reviewed by Ryosuke Niwa.
+
+        * WebCoreSupport/WebEditorClient.h:
+
 2019-03-01  Tim Horton  <timothy_horton@apple.com>
 
         Remove unused code in WebKitLegacy
index e4c2fd6..f56cf2c 100644 (file)
@@ -27,6 +27,7 @@
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
+#import <WebCore/DOMPasteAccess.h>
 #import <WebCore/EditorClient.h>
 #import <WebCore/TextCheckerClient.h>
 #import <WebCore/VisibleSelection.h>
@@ -80,7 +81,7 @@ private:
     void getClientPasteboardDataForRange(WebCore::Range*, Vector<String>& pasteboardTypes, Vector<RefPtr<WebCore::SharedBuffer>>& pasteboardData) final;
 
     void setInsertionPasteboard(const String&) final;
-    bool requestDOMPasteAccess() final { return false; }
+    WebCore::DOMPasteAccessResponse requestDOMPasteAccess(const String&) final { return WebCore::DOMPasteAccessResponse::DeniedForGesture; }
 
 #if USE(APPKIT)
     void uppercaseWord() final;
index 26e1eaa..7e827b5 100644 (file)
@@ -1,3 +1,13 @@
+2019-03-02  Wenson Hsieh  <wenson_hsieh@apple.com>
+
+        [iOS] Programmatic paste access should be granted when copying and pasting within the same origin
+        https://bugs.webkit.org/show_bug.cgi?id=195053
+        <rdar://problem/48134710>
+
+        Reviewed by Ryosuke Niwa.
+
+        * WebCoreSupport/WebEditorClient.h:
+
 2019-02-20  Darin Adler  <darin@apple.com>
 
         Finish removing String::format
index b70f7cb..eea2735 100644 (file)
@@ -26,6 +26,7 @@
 #pragma once
 
 #include "WebKit.h"
+#include <WebCore/DOMPasteAccess.h>
 #include <WebCore/EditorClient.h>
 #include <WebCore/TextCheckerClient.h>
 
@@ -114,7 +115,7 @@ private:
     void requestCheckingOfString(WebCore::TextCheckingRequest&, const WebCore::VisibleSelection&) final { }
     bool performTwoStepDrop(WebCore::DocumentFragment&, WebCore::Range&, bool) final { return false; }
 
-    bool requestDOMPasteAccess() final { return false; }
+    WebCore::DOMPasteAccessResponse requestDOMPasteAccess(const String&) final { return WebCore::DOMPasteAccessResponse::DeniedForGesture; }
 
     WebCore::TextCheckerClient* textChecker() final { return this; }