Diff
Modified: trunk/LayoutTests/ChangeLog (250972 => 250973)
--- trunk/LayoutTests/ChangeLog 2019-10-10 15:56:17 UTC (rev 250972)
+++ trunk/LayoutTests/ChangeLog 2019-10-10 16:17:18 UTC (rev 250973)
@@ -1,3 +1,57 @@
+2019-10-10 Wenson Hsieh <[email protected]>
+
+ Support programmatic paste requests on macOS
+ https://bugs.webkit.org/show_bug.cgi?id=202773
+ <rdar://problem/48957166>
+
+ Reviewed by Tim Horton.
+
+ Refactors existing layout tests for programmatic paste requests on iOS, such that they now run in both iOS and
+ macOS. See below for more details.
+
+ * TestExpectations:
+ * editing/pasteboard/dom-paste/dom-paste-confirmation-expected.txt: Renamed from LayoutTests/editing/pasteboard/ios/dom-paste-confirmation-expected.txt.
+ * editing/pasteboard/dom-paste/dom-paste-confirmation.html: Renamed from LayoutTests/editing/pasteboard/ios/dom-paste-confirmation.html.
+ * editing/pasteboard/dom-paste/dom-paste-consecutive-confirmations-expected.txt: Added.
+ * editing/pasteboard/dom-paste/dom-paste-consecutive-confirmations.html: Renamed from LayoutTests/editing/pasteboard/ios/dom-paste-consecutive-confirmations.html.
+ * editing/pasteboard/dom-paste/dom-paste-rejection-expected.txt: Added.
+ * editing/pasteboard/dom-paste/dom-paste-rejection.html: Renamed from LayoutTests/editing/pasteboard/ios/dom-paste-rejection.html.
+ * editing/pasteboard/dom-paste/dom-paste-requires-user-gesture-expected.txt: Renamed from LayoutTests/editing/pasteboard/ios/dom-paste-requires-user-gesture-expected.txt.
+ * editing/pasteboard/dom-paste/dom-paste-requires-user-gesture.html: Renamed from LayoutTests/editing/pasteboard/ios/dom-paste-requires-user-gesture.html.
+ * editing/pasteboard/dom-paste/dom-paste-same-origin-expected.txt: Renamed from LayoutTests/editing/pasteboard/ios/dom-paste-same-origin-expected.txt.
+ * editing/pasteboard/dom-paste/dom-paste-same-origin.html: Renamed from LayoutTests/editing/pasteboard/ios/dom-paste-same-origin.html.
+ * editing/pasteboard/dom-paste/resources/dom-paste-helper.js: Added.
+
+ Re-word some of these layout tests' descriptions to reference "clicks or taps", instead of just "taps", and also
+ replace mentions of "callout bars" with platform-agnostic "menus".
+
+ (return.new.Promise.):
+ (async._waitForOrTriggerPasteMenu):
+ (async.triggerPasteMenuAfterActivatingLocation):
+ (async.waitForPasteMenu):
+
+ Refactor these testing helpers to support both iOS and macOS:
+
+ (1) Replace code that finds callout bar menu items and synthesizes taps on iOS, with code that instead chooses a
+ menu item with the given title (in this case, "Paste"). This is supported on both macOS and iOS, where we invoke
+ the NSMenuItem's action and dismiss the menu item, and find and tap the callout bar menu item, respectively.
+
+ (2) Implement UIScriptController::activateAtPoint, which is used as a cross-platform way of activating an
+ element at the given point. On iOS, this taps the given location, and on macOS, this moves the mouse to that
+ location and then simulates a click (mouse down and mouse up). In a subsequent patch, we should additionally use
+ this in the implementation of UIHelper.activateAt().
+
+ * editing/pasteboard/ios/dom-paste-consecutive-confirmations-expected.txt: Removed.
+ * editing/pasteboard/ios/dom-paste-rejection-expected.txt: Removed.
+ * editing/pasteboard/ios/resources/dom-paste-helper.js: Removed.
+ * platform/ios-wk2/TestExpectations:
+ * platform/ios/TestExpectations:
+ * platform/mac-wk2/TestExpectations:
+ * platform/win/TestExpectations:
+ * platform/wincairo/TestExpectations:
+
+ Skip editing/pasteboard/dom-paste everywhere for now, except for macOS and iOS WebKit2.
+
2019-10-10 Chris Lord <[email protected]>
Flaky Test: imported/w3c/web-platform-tests/offscreen-canvas/drawing-images-to-the-canvas/2d.drawImage.floatsource.html
Modified: trunk/LayoutTests/TestExpectations (250972 => 250973)
--- trunk/LayoutTests/TestExpectations 2019-10-10 15:56:17 UTC (rev 250972)
+++ trunk/LayoutTests/TestExpectations 2019-10-10 16:17:18 UTC (rev 250973)
@@ -56,7 +56,7 @@
system-preview [ Skip ]
editing/images [ Skip ]
pointerevents/ios [ Skip ]
-editing/pasteboard/ios [ Skip ]
+editing/pasteboard/dom-paste [ Skip ]
editing/pasteboard/mac [ Skip ]
fast/media/ios [ Skip ]
Copied: trunk/LayoutTests/editing/pasteboard/dom-paste/dom-paste-confirmation-expected.txt (from rev 250972, trunk/LayoutTests/editing/pasteboard/ios/dom-paste-confirmation-expected.txt) (0 => 250973)
--- trunk/LayoutTests/editing/pasteboard/dom-paste/dom-paste-confirmation-expected.txt (rev 0)
+++ trunk/LayoutTests/editing/pasteboard/dom-paste/dom-paste-confirmation-expected.txt 2019-10-10 16:17:18 UTC (rev 250973)
@@ -0,0 +1,18 @@
+Click here to copy
+Click here to copy
+
+Verifies that a menu is shown when the page programmatically triggers paste, and that selecting Paste in the menu allows the paste to happen. To manually test, click or tap the text on the bottom, click or tap the editable area above, and then select 'Paste' in the resulting 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
+
Copied: trunk/LayoutTests/editing/pasteboard/dom-paste/dom-paste-confirmation.html (from rev 250972, trunk/LayoutTests/editing/pasteboard/ios/dom-paste-confirmation.html) (0 => 250973)
--- trunk/LayoutTests/editing/pasteboard/dom-paste/dom-paste-confirmation.html (rev 0)
+++ trunk/LayoutTests/editing/pasteboard/dom-paste/dom-paste-confirmation.html 2019-10-10 16:17:18 UTC (rev 250973)
@@ -0,0 +1,69 @@
+<!DOCTYPE html> <!-- webkit-test-runner [ domPasteAllowed=false useFlexibleViewport=true ignoreSynchronousMessagingTimeouts=true ] -->
+<html>
+<meta name="viewport" content="width=device-width, initial-scale=1">
+<head>
+<script src=""
+<script src=""
+<script src=""
+<style>
+body {
+ margin: 0;
+}
+
+#copy {
+ width: 100%;
+ height: 50px;
+ border: 1px dashed black;
+}
+
+#editor {
+ width: 100%;
+ height: 100px;
+ border: 1px dashed silver;
+ text-align: center;
+}
+</style>
+</head>
+<body>
+<div id="editor" contenteditable></div>
+<iframe id="copy" src="" 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 editor = document.getElementById("editor");
+
+description("Verifies that a menu is shown when the page programmatically triggers paste, and that selecting Paste in the menu allows the paste to happen. To manually test, click or tap the text on the bottom, click or tap the editable area above, and then select 'Paste' in the resulting menu. The text 'Click here to copy' should be pasted <strong><em>twice</em></strong> in the editable area.");
+
+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();
+});
+
+addEventListener("load", async () => {
+ if (!window.testRunner)
+ return;
+
+ await UIHelper.activateAt(160, 125);
+ await triggerPasteMenuAfterActivatingLocation(160, 50);
+ finishJSTest();
+});
+</script>
+</body>
+</html>
Added: trunk/LayoutTests/editing/pasteboard/dom-paste/dom-paste-consecutive-confirmations-expected.txt (0 => 250973)
--- trunk/LayoutTests/editing/pasteboard/dom-paste/dom-paste-consecutive-confirmations-expected.txt (rev 0)
+++ trunk/LayoutTests/editing/pasteboard/dom-paste/dom-paste-consecutive-confirmations-expected.txt 2019-10-10 16:17:18 UTC (rev 250973)
@@ -0,0 +1,13 @@
+
+Verifies that no menu is shown when the page programmatically triggers paste on a timer after user interaction. To test manually, click or tap the text on the bottom to copy, and then click or tap the editable area above to trigger two programmatic pastes with the menu. 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
+
Copied: trunk/LayoutTests/editing/pasteboard/dom-paste/dom-paste-consecutive-confirmations.html (from rev 250972, trunk/LayoutTests/editing/pasteboard/ios/dom-paste-consecutive-confirmations.html) (0 => 250973)
--- trunk/LayoutTests/editing/pasteboard/dom-paste/dom-paste-consecutive-confirmations.html (rev 0)
+++ trunk/LayoutTests/editing/pasteboard/dom-paste/dom-paste-consecutive-confirmations.html 2019-10-10 16:17:18 UTC (rev 250973)
@@ -0,0 +1,92 @@
+<!DOCTYPE html> <!-- webkit-test-runner [ domPasteAllowed=false useFlexibleViewport=true ignoreSynchronousMessagingTimeouts=true ] -->
+<html>
+<meta name="viewport" content="width=device-width, initial-scale=1">
+<head>
+<script src=""
+<script src=""
+<script src=""
+<style>
+body {
+ margin: 0;
+}
+
+#copy {
+ width: 100%;
+ height: 50px;
+ border: 1px dashed black;
+}
+
+#editor {
+ width: 100%;
+ height: 100px;
+ border: 1px dashed silver;
+ text-align: center;
+}
+</style>
+</head>
+<body>
+<div id="editor" contenteditable></div>
+<iframe id="copy" src="" 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 editor = document.getElementById("editor");
+
+description("Verifies that no menu is shown when the page programmatically triggers paste on a timer after user interaction. To test manually, click or tap the text on the bottom to copy, and then click or tap the editable area above to trigger two programmatic pastes with the menu. 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 === 5)
+ uiController.uiScriptComplete();
+ }
+
+ uiController.didHideMenuCallback = incrementProgress;
+ uiController.didShowMenuCallback = tapPasteMenuAction;
+
+ function tapPasteMenuAction() {
+ uiController.chooseMenuAction("Paste", incrementProgress);
+ }
+
+ uiController.activateAtPoint(160, 50, incrementProgress);
+ })()`, resolve);
+ });
+}
+
+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);
+});
+
+addEventListener("load", async () => {
+ if (!window.testRunner || !window.internals)
+ return;
+
+ await UIHelper.activateAt(160, 125);
+ await waitForAndTapPasteMenuTwice();
+ finishJSTest();
+});
+</script>
+</body>
+</html>
Added: trunk/LayoutTests/editing/pasteboard/dom-paste/dom-paste-rejection-expected.txt (0 => 250973)
--- trunk/LayoutTests/editing/pasteboard/dom-paste/dom-paste-rejection-expected.txt (rev 0)
+++ trunk/LayoutTests/editing/pasteboard/dom-paste/dom-paste-rejection-expected.txt 2019-10-10 16:17:18 UTC (rev 250973)
@@ -0,0 +1,14 @@
+
+Verifies that a menu is shown when the page programmatically triggers paste, and that dismissing the menu prevents the paste from happening. To manually test, click or tap the text on the bottom, click or tap the editable area above, and then dismiss the resulting menu by interacting elsewhere. The text 'Click here to copy' should not be pasted, and the menu 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
+
Copied: trunk/LayoutTests/editing/pasteboard/dom-paste/dom-paste-rejection.html (from rev 250972, trunk/LayoutTests/editing/pasteboard/ios/dom-paste-rejection.html) (0 => 250973)
--- trunk/LayoutTests/editing/pasteboard/dom-paste/dom-paste-rejection.html (rev 0)
+++ trunk/LayoutTests/editing/pasteboard/dom-paste/dom-paste-rejection.html 2019-10-10 16:17:18 UTC (rev 250973)
@@ -0,0 +1,68 @@
+<!DOCTYPE html> <!-- webkit-test-runner [ domPasteAllowed=false useFlexibleViewport=true ignoreSynchronousMessagingTimeouts=true ] -->
+<html>
+<meta name="viewport" content="width=device-width, initial-scale=1">
+<head>
+<script src=""
+<script src=""
+<script src=""
+<style>
+body {
+ margin: 0;
+}
+
+#copy {
+ width: 100%;
+ height: 50px;
+ border: 1px dashed black;
+}
+
+#editor {
+ width: 100%;
+ height: 100px;
+ border: 1px dashed silver;
+ text-align: center;
+}
+</style>
+</head>
+<body>
+<div id="editor" contenteditable></div>
+<iframe id="copy" src="" 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 editor = document.getElementById("editor");
+
+description("Verifies that a menu is shown when the page programmatically triggers paste, and that dismissing the menu prevents the paste from happening. To manually test, click or tap the text on the bottom, click or tap the editable area above, and then dismiss the resulting menu by interacting elsewhere. The text 'Click here to copy' should <strong>not</strong> be pasted, and the menu should disappear.");
+
+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();
+});
+
+addEventListener("load", async () => {
+ if (!window.testRunner)
+ return;
+
+ await UIHelper.activateAt(160, 125);
+ await triggerPasteMenuAfterActivatingLocation(160, 50, false);
+ finishJSTest();
+});
+</script>
+</body>
+</html>
Copied: trunk/LayoutTests/editing/pasteboard/dom-paste/dom-paste-requires-user-gesture-expected.txt (from rev 250972, trunk/LayoutTests/editing/pasteboard/ios/dom-paste-requires-user-gesture-expected.txt) (0 => 250973)
--- trunk/LayoutTests/editing/pasteboard/dom-paste/dom-paste-requires-user-gesture-expected.txt (rev 0)
+++ trunk/LayoutTests/editing/pasteboard/dom-paste/dom-paste-requires-user-gesture-expected.txt 2019-10-10 16:17:18 UTC (rev 250973)
@@ -0,0 +1,13 @@
+Click here to copy
+
+Verifies that no menu 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
+
Copied: trunk/LayoutTests/editing/pasteboard/dom-paste/dom-paste-requires-user-gesture.html (from rev 250972, trunk/LayoutTests/editing/pasteboard/ios/dom-paste-requires-user-gesture.html) (0 => 250973)
--- trunk/LayoutTests/editing/pasteboard/dom-paste/dom-paste-requires-user-gesture.html (rev 0)
+++ trunk/LayoutTests/editing/pasteboard/dom-paste/dom-paste-requires-user-gesture.html 2019-10-10 16:17:18 UTC (rev 250973)
@@ -0,0 +1,71 @@
+<!DOCTYPE html> <!-- webkit-test-runner [ domPasteAllowed=false useFlexibleViewport=true ignoreSynchronousMessagingTimeouts=true ] -->
+<html>
+<meta name="viewport" content="width=device-width, initial-scale=1">
+<head>
+<script src=""
+<script src=""
+<script src=""
+<style>
+body {
+ margin: 0;
+}
+
+#copy {
+ width: 100%;
+ height: 50px;
+ border: 1px dashed black;
+}
+
+#editor {
+ width: 100%;
+ height: 100px;
+ border: 1px dashed silver;
+ text-align: center;
+}
+</style>
+</head>
+<body>
+<div id="editor" contenteditable></div>
+<iframe id="copy" src="" 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 editor = document.getElementById("editor");
+
+description("Verifies that no menu is shown when the page programmatically triggers paste outside the scope of user interaction. This test requires WebKitTestRunner.");
+
+function checkDone() {
+ if (!window.doneCount)
+ doneCount = 0;
+
+ if (++doneCount == 2)
+ finishJSTest();
+}
+
+editor.addEventListener("click", event => {
+ editor.focus();
+ shouldBe("document.execCommand('Paste')", "true");
+ requestAnimationFrame(() => {
+ shouldBe("document.execCommand('Paste')", "false")
+ shouldBeEqualToString("editor.textContent", "Click here to copy");
+ checkDone();
+ });
+});
+
+addEventListener("load", async () => {
+ await UIHelper.activateAt(160, 125);
+ await triggerPasteMenuAfterActivatingLocation(160, 50);
+ checkDone();
+});
+</script>
+</body>
+</html>
Copied: trunk/LayoutTests/editing/pasteboard/dom-paste/dom-paste-same-origin-expected.txt (from rev 250972, trunk/LayoutTests/editing/pasteboard/ios/dom-paste-same-origin-expected.txt) (0 => 250973)
--- trunk/LayoutTests/editing/pasteboard/dom-paste/dom-paste-same-origin-expected.txt (rev 0)
+++ trunk/LayoutTests/editing/pasteboard/dom-paste/dom-paste-same-origin-expected.txt 2019-10-10 16:17:18 UTC (rev 250973)
@@ -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, click or tap the text on the bottom to programmatically copy, and then click or 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
+
Copied: trunk/LayoutTests/editing/pasteboard/dom-paste/dom-paste-same-origin.html (from rev 250972, trunk/LayoutTests/editing/pasteboard/ios/dom-paste-same-origin.html) (0 => 250973)
--- trunk/LayoutTests/editing/pasteboard/dom-paste/dom-paste-same-origin.html (rev 0)
+++ trunk/LayoutTests/editing/pasteboard/dom-paste/dom-paste-same-origin.html 2019-10-10 16:17:18 UTC (rev 250973)
@@ -0,0 +1,71 @@
+<!DOCTYPE html> <!-- webkit-test-runner [ domPasteAllowed=false useFlexibleViewport=true ignoreSynchronousMessagingTimeouts=true ] -->
+<html>
+<meta name="viewport" content="width=device-width, initial-scale=1">
+<head>
+<script src=""
+<script src=""
+<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, click or tap the text on the bottom to programmatically copy, and then click or 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>
Added: trunk/LayoutTests/editing/pasteboard/dom-paste/resources/dom-paste-helper.js (0 => 250973)
--- trunk/LayoutTests/editing/pasteboard/dom-paste/resources/dom-paste-helper.js (rev 0)
+++ trunk/LayoutTests/editing/pasteboard/dom-paste/resources/dom-paste-helper.js 2019-10-10 16:17:18 UTC (rev 250973)
@@ -0,0 +1,40 @@
+
+async function _waitForOrTriggerPasteMenu(x, y, proceedWithPaste, shouldActivate) {
+ return new Promise(resolve => {
+ testRunner.runUIScript(`
+ (() => {
+ doneCount = 0;
+ function checkDone() {
+ if (++doneCount === (${shouldActivate} ? 3 : 2))
+ uiController.uiScriptComplete();
+ }
+
+ uiController.didHideMenuCallback = checkDone;
+
+ function resolveDOMPasteRequest() {
+ if (${proceedWithPaste})
+ uiController.chooseMenuAction("Paste", checkDone);
+ else {
+ uiController.dismissMenu();
+ checkDone();
+ }
+ }
+
+ if (uiController.isShowingMenu)
+ resolveDOMPasteRequest();
+ else
+ uiController.didShowMenuCallback = resolveDOMPasteRequest;
+
+ if (${shouldActivate})
+ uiController.activateAtPoint(${x}, ${y}, checkDone);
+ })()`, resolve);
+ });
+}
+
+async function triggerPasteMenuAfterActivatingLocation(x, y, proceedWithPaste = true) {
+ return _waitForOrTriggerPasteMenu(x, y, proceedWithPaste, true);
+}
+
+async function waitForPasteMenu(proceedWithPaste = true) {
+ return _waitForOrTriggerPasteMenu(null, null, proceedWithPaste, false);
+}
Modified: trunk/LayoutTests/platform/ios/TestExpectations (250972 => 250973)
--- trunk/LayoutTests/platform/ios/TestExpectations 2019-10-10 15:56:17 UTC (rev 250972)
+++ trunk/LayoutTests/platform/ios/TestExpectations 2019-10-10 16:17:18 UTC (rev 250973)
@@ -3366,8 +3366,6 @@
# <rdar://problem/52962272> fast/scrolling/ios/body-overflow-hidden.html is an Image failure
fast/scrolling/ios/body-overflow-hidden.html [ Pass ImageOnlyFailure ]
-webkit.org/b/201898 editing/pasteboard/ios/dom-paste-same-origin.html [ Failure ]
-
webkit.org/b/201899 editing/pasteboard/paste-does-not-fire-promises-while-sanitizing-web-content.html [ Failure ]
webkit.org/b/201900 webrtc/datachannel/mdns-ice-candidates.html [ Failure ]
Modified: trunk/LayoutTests/platform/ios-wk2/TestExpectations (250972 => 250973)
--- trunk/LayoutTests/platform/ios-wk2/TestExpectations 2019-10-10 15:56:17 UTC (rev 250972)
+++ trunk/LayoutTests/platform/ios-wk2/TestExpectations 2019-10-10 16:17:18 UTC (rev 250973)
@@ -21,7 +21,7 @@
editing/caret/ios [ Pass ]
editing/find [ Pass ]
editing/input/ios [ Pass ]
-editing/pasteboard/ios [ Pass ]
+editing/pasteboard/dom-paste [ Pass ]
editing/undo-manager [ Pass ]
accessibility/set-selected-text-range-after-newline.html [ Pass ]
@@ -1363,7 +1363,6 @@
# problem with blur handling
mathml/focus-event-handling.html [ Failure ]
-webkit.org/b/201898 editing/pasteboard/ios/dom-paste-same-origin.html [ Failure ]
# <rdar://problem/51756254>REGRESSION (r244582-r244596) Layout tests fast/scrolling/ios/touch-scroll-visibility-hidden.html fast/scrolling/ios/touch-scroll-pointer-events-none.html are failing
fast/scrolling/ios/touch-scroll-pointer-events-none.html [ Failure ]
fast/scrolling/ios/touch-scroll-visibility-hidden.html [ Failure ]
Modified: trunk/LayoutTests/platform/mac-wk2/TestExpectations (250972 => 250973)
--- trunk/LayoutTests/platform/mac-wk2/TestExpectations 2019-10-10 15:56:17 UTC (rev 250972)
+++ trunk/LayoutTests/platform/mac-wk2/TestExpectations 2019-10-10 16:17:18 UTC (rev 250973)
@@ -10,6 +10,7 @@
compositing/layer-creation/clipping-scope [ Pass ]
editing/find [ Pass ]
editing/undo-manager [ Pass ]
+editing/pasteboard/dom-paste [ Pass ]
fast/forms/select/mac-wk2 [ Pass ]
fast/visual-viewport/tiled-drawing [ Pass ]
fast/web-share [ Pass ]
Modified: trunk/LayoutTests/platform/win/TestExpectations (250972 => 250973)
--- trunk/LayoutTests/platform/win/TestExpectations 2019-10-10 15:56:17 UTC (rev 250972)
+++ trunk/LayoutTests/platform/win/TestExpectations 2019-10-10 16:17:18 UTC (rev 250973)
@@ -1173,7 +1173,7 @@
###### Pasteboard
###### These tests are very flaky.
editing/pasteboard/ [ Pass Failure ]
-editing/pasteboard/ios [ Skip ]
+editing/pasteboard/dom-paste/ [ 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
Modified: trunk/LayoutTests/platform/wincairo/TestExpectations (250972 => 250973)
--- trunk/LayoutTests/platform/wincairo/TestExpectations 2019-10-10 15:56:17 UTC (rev 250972)
+++ trunk/LayoutTests/platform/wincairo/TestExpectations 2019-10-10 16:17:18 UTC (rev 250973)
@@ -1306,7 +1306,7 @@
editing/pasteboard/ [ Pass Failure ImageOnlyFailure ]
editing/pasteboard/copy-paste-across-shadow-boundaries-with-style-2.html [ Skip ]
editing/pasteboard/drag-and-drop-color-input-events.html [ Skip ]
-editing/pasteboard/ios/ [ Skip ]
+editing/pasteboard/dom-paste/ [ Skip ]
editing/pasteboard/paste-image-as-blob-url.html [ Skip ]
# TODO eventSender.contextClick() needs to return a JS array of the context menu items.
webkit.org/b/62597 editing/pasteboard/copy-standalone-image-crash.html [ Skip ]
Modified: trunk/Source/WebCore/ChangeLog (250972 => 250973)
--- trunk/Source/WebCore/ChangeLog 2019-10-10 15:56:17 UTC (rev 250972)
+++ trunk/Source/WebCore/ChangeLog 2019-10-10 16:17:18 UTC (rev 250973)
@@ -1,3 +1,46 @@
+2019-10-10 Wenson Hsieh <[email protected]>
+
+ Support programmatic paste requests on macOS
+ https://bugs.webkit.org/show_bug.cgi?id=202773
+ <rdar://problem/48957166>
+
+ Reviewed by Tim Horton.
+
+ Adds support for programmatic paste requests on macOS. See below for more details.
+
+ Tests: editing/pasteboard/dom-paste/dom-paste-confirmation.html
+ editing/pasteboard/dom-paste/dom-paste-consecutive-confirmations.html
+ editing/pasteboard/dom-paste/dom-paste-rejection.html
+ editing/pasteboard/dom-paste/dom-paste-requires-user-gesture.html
+ editing/pasteboard/dom-paste/dom-paste-same-origin.html
+
+ * editing/EditorCommand.cpp:
+ (WebCore::defaultValueForSupportedPaste):
+ (WebCore::supportedPaste):
+ (WebCore::allowPasteFromDOM):
+ (WebCore::enabledPaste):
+
+ Fixes an existing bug uncovered by the layout test editing/execCommand/clipboard-access.html, which tests the
+ results of `document.queryCommandEnabled("copy")` and `document.queryCommandEnabled("paste")`. The problem here
+ is that document.queryCommandEnabled("paste") returns true if DOM paste access requests are enabled, regardless
+ of whether or not there is an active user gesture. This is inconsistent with the behavior of "copy" and "cut",
+ which return false in the case where there is no user gesture (and the clipboard access policy is also equal to
+ ClipboardAccessPolicy::RequiresUserGesture -- refer to `allowCopyCutFromDOM`).
+
+ When pasting, we only DOM paste access requests to be triggered only in the case where there is a user gesture.
+ This means that enabledPaste should additionally be gated on a user gesture check. For consistency with the
+ implementation of `enabledCopy`, we introduce a `allowPasteFromDOM` helper that is similar to
+ `allowCopyCutFromDOM`, and additionally check this constraint when the paste command's source is the DOM (as
+ opposed to a menu or key binding).
+
+ This adjustment also adds a missing canDHTMLPaste() check prior to consulting canPaste(). This ensures that when
+ evaluating document.queryCommandEnabled("Paste"), we'll dispatch a "beforepaste" event, similar to how
+ evaluating document.queryCommandEnabled("Copy") dispatches a "beforecopy" event.
+
+ * platform/LocalizedStrings.h:
+
+ Mark a function as WEBCORE_EXPORT.
+
2019-10-10 Eric Carlson <[email protected]>
[GTK][WPE] Lots of media related tests crashing or flaky after r250918 - [ Mac WK2 ] Layout Test fast/mediastream/MediaStreamTrack-getSettings.html is a flaky failure
Modified: trunk/Source/WebCore/editing/EditorCommand.cpp (250972 => 250973)
--- trunk/Source/WebCore/editing/EditorCommand.cpp 2019-10-10 15:56:17 UTC (rev 250972)
+++ trunk/Source/WebCore/editing/EditorCommand.cpp 2019-10-10 16:17:18 UTC (rev 250973)
@@ -1235,13 +1235,21 @@
return client ? client->canCopyCut(frame, defaultValue) : defaultValue;
}
+static bool defaultValueForSupportedPaste(Frame& frame)
+{
+ auto& settings = frame.settings();
+ if (settings._javascript_CanAccessClipboard() && settings.DOMPasteAllowed())
+ return true;
+
+ return settings.domPasteAccessRequestsEnabled();
+}
+
static bool supportedPaste(Frame* frame)
{
if (!frame)
return false;
- auto& settings = frame->settings();
- bool defaultValue = (settings._javascript_CanAccessClipboard() && settings.DOMPasteAllowed()) || settings.domPasteAccessRequestsEnabled();
+ bool defaultValue = defaultValueForSupportedPaste(*frame);
EditorClient* client = frame->editor().client();
return client ? client->canPaste(frame, defaultValue) : defaultValue;
@@ -1370,11 +1378,28 @@
return selection.isCaretOrRange() && selection.isContentRichlyEditable() && selection.rootEditableElement();
}
-static bool enabledPaste(Frame& frame, Event*, EditorCommandSource)
+static bool allowPasteFromDOM(Frame& frame)
{
- return frame.editor().canPaste();
+ auto& settings = frame.settings();
+ if (settings._javascript_CanAccessClipboard() && settings.DOMPasteAllowed())
+ return true;
+
+ return settings.domPasteAccessRequestsEnabled() && UserGestureIndicator::processingUserGesture();
}
+static bool enabledPaste(Frame& frame, Event*, EditorCommandSource source)
+{
+ switch (source) {
+ case CommandFromMenuOrKeyBinding:
+ return frame.editor().canDHTMLPaste() || frame.editor().canPaste();
+ case CommandFromDOM:
+ case CommandFromDOMWithUserInterface:
+ return allowPasteFromDOM(frame) && (frame.editor().canDHTMLPaste() || frame.editor().canPaste());
+ }
+ ASSERT_NOT_REACHED();
+ return false;
+}
+
static bool enabledRangeInEditableText(Frame& frame, Event*, EditorCommandSource)
{
return frame.selection().isRange() && frame.selection().selection().isContentEditable();
Modified: trunk/Source/WebCore/platform/LocalizedStrings.h (250972 => 250973)
--- trunk/Source/WebCore/platform/LocalizedStrings.h 2019-10-10 15:56:17 UTC (rev 250972)
+++ trunk/Source/WebCore/platform/LocalizedStrings.h 2019-10-10 16:17:18 UTC (rev 250973)
@@ -68,7 +68,7 @@
String contextMenuItemTagStop();
String contextMenuItemTagReload();
String contextMenuItemTagCut();
- String contextMenuItemTagPaste();
+ WEBCORE_EXPORT String contextMenuItemTagPaste();
#if PLATFORM(GTK)
String contextMenuItemTagDelete();
String contextMenuItemTagInputMethods();
Modified: trunk/Source/WebKit/ChangeLog (250972 => 250973)
--- trunk/Source/WebKit/ChangeLog 2019-10-10 15:56:17 UTC (rev 250972)
+++ trunk/Source/WebKit/ChangeLog 2019-10-10 16:17:18 UTC (rev 250973)
@@ -1,3 +1,64 @@
+2019-10-10 Wenson Hsieh <[email protected]>
+
+ Support programmatic paste requests on macOS
+ https://bugs.webkit.org/show_bug.cgi?id=202773
+ <rdar://problem/48957166>
+
+ Reviewed by Tim Horton.
+
+ Adds support for programmatic paste requests on macOS, as well as some testing SPI in WKWebView to allow
+ WebKitTestRunner to grab the NSMenu used for the DOM paste request. This patch adopts the same strategy taken to
+ allow programmatic paste on iOS, by allowing programmatic pastes coming from the page to show platform UI which
+ the user must then interact with in order to proceed with the paste. See below for more details.
+
+ * Shared/WebPreferencesDefaultValues.h:
+
+ Make this available on both iOS and macOS (iOS family is omitted for now, since callout bar UI is not generally
+ present on non-iOS iOS-family platforms such as Apple Watch).
+
+ * UIProcess/API/Cocoa/WKWebView.mm:
+ (-[WKWebView _web_grantDOMPasteAccess]):
+
+ This selector is called when the user taps the Paste option in the presented NSMenu.
+
+ (-[WKWebView _activeMenu]):
+
+ Returns the currently active NSMenu. Only for testing purposes.
+
+ * UIProcess/API/Cocoa/WKWebViewInternal.h:
+ * UIProcess/API/Cocoa/WKWebViewPrivate.h:
+ * UIProcess/API/mac/WKView.mm:
+ (-[WKView _web_grantDOMPasteAccess]):
+
+ Same exercise as above, only for WKView instead of WKWebView.
+
+ * UIProcess/Cocoa/WebViewImpl.h:
+ (WebKit::WebViewImpl::domPasteMenu const):
+ * UIProcess/Cocoa/WebViewImpl.mm:
+ (-[WKDOMPasteMenuDelegate initWithWebViewImpl:]):
+ (-[WKDOMPasteMenuDelegate menuDidClose:]):
+ (-[WKDOMPasteMenuDelegate numberOfItemsInMenu:]):
+ (-[WKDOMPasteMenuDelegate confinementRectForMenu:onScreen:]):
+
+ Adds a new object, whose purpose is to be a delegate for the NSMenu that is presented when requesting DOM paste
+ access. This object is used instead of WKWebView, since API clients may end up making the WKWebView the delegate
+ for a different menu, in which case some implementations (either theirs or ours) of NSMenuDelegate methods would
+ not be called. Avoiding this would require the client to be aware that WKWebView conforms to NSMenuDelegate,
+ which is only declared privately.
+
+ (WebKit::WebViewImpl::handleProcessSwapOrExit):
+
+ On process swap or exit, automatically bail out of any pending DOM paste request by denying it.
+
+ (WebKit::WebViewImpl::requestDOMPasteAccess):
+ (WebKit::WebViewImpl::handleDOMPasteRequestWithResult):
+
+ Handle the DOM paste request by showing an NSMenu near the mouse cursor with a single option to paste.
+
+ * UIProcess/mac/PageClientImplMac.h:
+ * UIProcess/mac/PageClientImplMac.mm:
+ (WebKit::PageClientImpl::requestDOMPasteAccess):
+
2019-10-10 youenn fablet <[email protected]>
Remove unified plan runtime flag
Modified: trunk/Source/WebKit/Shared/WebPreferencesDefaultValues.h (250972 => 250973)
--- trunk/Source/WebKit/Shared/WebPreferencesDefaultValues.h 2019-10-10 15:56:17 UTC (rev 250972)
+++ trunk/Source/WebKit/Shared/WebPreferencesDefaultValues.h 2019-10-10 16:17:18 UTC (rev 250973)
@@ -256,7 +256,7 @@
#define DEFAULT_CUSTOM_PASTEBOARD_DATA_ENABLED false
#endif
-#if PLATFORM(IOS)
+#if PLATFORM(IOS) || PLATFORM(MAC)
#define DEFAULT_DOM_PASTE_ACCESS_REQUESTS_ENABLED true
#else
#define DEFAULT_DOM_PASTE_ACCESS_REQUESTS_ENABLED false
Modified: trunk/Source/WebKit/UIProcess/API/Cocoa/WKWebView.mm (250972 => 250973)
--- trunk/Source/WebKit/UIProcess/API/Cocoa/WKWebView.mm 2019-10-10 15:56:17 UTC (rev 250972)
+++ trunk/Source/WebKit/UIProcess/API/Cocoa/WKWebView.mm 2019-10-10 16:17:18 UTC (rev 250973)
@@ -3640,6 +3640,11 @@
_impl->setFrameSize(NSSizeToCGSize(size));
}
+- (void)_web_grantDOMPasteAccess
+{
+ _impl->handleDOMPasteRequestWithResult(WebCore::DOMPasteAccessResponse::GrantedForGesture);
+}
+
ALLOW_DEPRECATED_IMPLEMENTATIONS_BEGIN
- (void)renewGState
ALLOW_DEPRECATED_IMPLEMENTATIONS_END
@@ -7314,6 +7319,13 @@
_impl->doAfterProcessingAllPendingMouseEvents(action);
}
+- (NSMenu *)_activeMenu
+{
+ // FIXME: Only the DOM paste access menu is supported for now. In the future, it could be
+ // extended to recognize the regular context menu as well.
+ return _impl->domPasteMenu();
+}
+
#endif // PLATFORM(MAC)
- (void)_requestActiveNowPlayingSessionInfo:(void(^)(BOOL, BOOL, NSString*, double, double, NSInteger))callback
Modified: trunk/Source/WebKit/UIProcess/API/Cocoa/WKWebViewInternal.h (250972 => 250973)
--- trunk/Source/WebKit/UIProcess/API/Cocoa/WKWebViewInternal.h 2019-10-10 15:56:17 UTC (rev 250972)
+++ trunk/Source/WebKit/UIProcess/API/Cocoa/WKWebViewInternal.h 2019-10-10 16:17:18 UTC (rev 250973)
@@ -189,6 +189,10 @@
- (WKPageRef)_pageForTesting;
- (WebKit::WebPageProxy*)_page;
+#if PLATFORM(MAC)
+- (void)_web_grantDOMPasteAccess;
+#endif
+
@end
WKWebView* fromWebPageProxy(WebKit::WebPageProxy&);
Modified: trunk/Source/WebKit/UIProcess/API/Cocoa/WKWebViewPrivate.h (250972 => 250973)
--- trunk/Source/WebKit/UIProcess/API/Cocoa/WKWebViewPrivate.h 2019-10-10 15:56:17 UTC (rev 250972)
+++ trunk/Source/WebKit/UIProcess/API/Cocoa/WKWebViewPrivate.h 2019-10-10 16:17:18 UTC (rev 250973)
@@ -543,6 +543,7 @@
- (void)_insertText:(id)string replacementRange:(NSRange)replacementRange WK_API_AVAILABLE(macos(10.12.3));
- (NSRect)_candidateRect WK_API_AVAILABLE(macos(10.13));
@property (nonatomic, readwrite, setter=_setUseSystemAppearance:) BOOL _useSystemAppearance WK_API_AVAILABLE(macos(10.14));
+@property (nonatomic, readonly) NSMenu *_activeMenu WK_API_AVAILABLE(macos(WK_MAC_TBA));
- (void)_setHeaderBannerHeight:(int)height WK_API_AVAILABLE(macos(10.12.3));
- (void)_setFooterBannerHeight:(int)height WK_API_AVAILABLE(macos(10.12.3));
Modified: trunk/Source/WebKit/UIProcess/API/mac/WKView.mm (250972 => 250973)
--- trunk/Source/WebKit/UIProcess/API/mac/WKView.mm 2019-10-10 15:56:17 UTC (rev 250972)
+++ trunk/Source/WebKit/UIProcess/API/mac/WKView.mm 2019-10-10 16:17:18 UTC (rev 250973)
@@ -891,6 +891,11 @@
return _data->_impl->namesOfPromisedFilesDroppedAtDestination(dropDestination);
}
+- (void)_web_grantDOMPasteAccess
+{
+ _data->_impl->handleDOMPasteRequestWithResult(WebCore::DOMPasteAccessResponse::GrantedForGesture);
+}
+
- (void)maybeInstallIconLoadingClient
{
ALLOW_DEPRECATED_DECLARATIONS_BEGIN
Modified: trunk/Source/WebKit/UIProcess/Cocoa/WebViewImpl.h (250972 => 250973)
--- trunk/Source/WebKit/UIProcess/Cocoa/WebViewImpl.h 2019-10-10 15:56:17 UTC (rev 250972)
+++ trunk/Source/WebKit/UIProcess/Cocoa/WebViewImpl.h 2019-10-10 16:17:18 UTC (rev 250973)
@@ -31,6 +31,7 @@
#include "ShareableBitmap.h"
#include "WKLayoutMode.h"
#include "_WKOverlayScrollbarStyle.h"
+#include <WebCore/DOMPasteAccess.h>
#include <WebCore/FocusDirection.h>
#include <WebCore/ScrollTypes.h>
#include <WebCore/TextIndicatorWindow.h>
@@ -38,6 +39,7 @@
#include <WebKit/WKDragDestinationAction.h>
#include <pal/spi/cocoa/AVKitSPI.h>
#include <wtf/BlockPtr.h>
+#include <wtf/CompletionHandler.h>
#include <wtf/RetainPtr.h>
#include <wtf/WeakObjCPtr.h>
#include <wtf/WeakPtr.h>
@@ -47,10 +49,12 @@
OBJC_CLASS NSAccessibilityRemoteUIElement;
OBJC_CLASS NSImmediateActionGestureRecognizer;
+OBJC_CLASS NSMenu;
OBJC_CLASS NSTextInputContext;
OBJC_CLASS NSView;
OBJC_CLASS WKAccessibilitySettingsObserver;
OBJC_CLASS WKBrowsingContextController;
+OBJC_CLASS WKDOMPasteMenuDelegate;
OBJC_CLASS WKEditorUndoTarget;
OBJC_CLASS WKFullScreenWindowController;
OBJC_CLASS WKImmediateActionController;
@@ -114,6 +118,8 @@
- (void)_web_didPerformDragOperation:(BOOL)handled;
#endif
+- (void)_web_grantDOMPasteAccess;
+
@optional
- (void)_web_didAddMediaControlsManager:(id)controlsManager;
- (void)_web_didRemoveMediaControlsManager;
@@ -600,6 +606,10 @@
void takeFocus(WebCore::FocusDirection);
void clearPromisedDragImage();
+ void requestDOMPasteAccess(const WebCore::IntRect&, const String& originIdentifier, CompletionHandler<void(WebCore::DOMPasteAccessResponse)>&&);
+ void handleDOMPasteRequestWithResult(WebCore::DOMPasteAccessResponse);
+ NSMenu *domPasteMenu() const { return m_domPasteMenu.get(); }
+
private:
#if HAVE(TOUCH_BAR)
void setUpTextTouchBar(NSTouchBar *);
@@ -794,6 +804,9 @@
NSInteger m_initialNumberOfValidItemsForDrop { 0 };
#endif
+ RetainPtr<NSMenu> m_domPasteMenu;
+ RetainPtr<WKDOMPasteMenuDelegate> m_domPasteMenuDelegate;
+ CompletionHandler<void(WebCore::DOMPasteAccessResponse)> m_domPasteRequestHandler;
};
} // namespace WebKit
Modified: trunk/Source/WebKit/UIProcess/Cocoa/WebViewImpl.mm (250972 => 250973)
--- trunk/Source/WebKit/UIProcess/Cocoa/WebViewImpl.mm 2019-10-10 15:56:17 UTC (rev 250972)
+++ trunk/Source/WebKit/UIProcess/Cocoa/WebViewImpl.mm 2019-10-10 16:17:18 UTC (rev 250973)
@@ -92,6 +92,7 @@
#import <WebCore/LegacyNSPasteboardTypes.h>
#import <WebCore/LoaderNSURLExtras.h>
#import <WebCore/LocalizedStrings.h>
+#import <WebCore/Pasteboard.h>
#import <WebCore/PlatformEventFactoryMac.h>
#import <WebCore/PromisedAttachmentInfo.h>
#import <WebCore/TextAlternativeWithRange.h>
@@ -879,6 +880,45 @@
@end
+@interface WKDOMPasteMenuDelegate : NSObject<NSMenuDelegate>
+- (instancetype)initWithWebViewImpl:(WebKit::WebViewImpl&)impl;
+@end
+
+@implementation WKDOMPasteMenuDelegate {
+ WeakPtr<WebKit::WebViewImpl> _impl;
+}
+
+- (instancetype)initWithWebViewImpl:(WebKit::WebViewImpl&)impl
+{
+ if (!(self = [super init]))
+ return nil;
+
+ _impl = makeWeakPtr(impl);
+ return self;
+}
+
+- (void)menuDidClose:(NSMenu *)menu
+{
+ dispatch_async(dispatch_get_main_queue(), [impl = _impl] {
+ if (impl)
+ impl->handleDOMPasteRequestWithResult(WebCore::DOMPasteAccessResponse::DeniedForGesture);
+ });
+}
+
+- (NSInteger)numberOfItemsInMenu:(NSMenu *)menu
+{
+ return 1;
+}
+
+- (NSRect)confinementRectForMenu:(NSMenu *)menu onScreen:(NSScreen *)screen
+{
+ auto confinementRect = WebCore::enclosingIntRect(NSRect { NSEvent.mouseLocation, menu.size });
+ confinementRect.move(0, -confinementRect.height());
+ return confinementRect;
+}
+
+@end
+
namespace WebKit {
NSTouchBar *WebViewImpl::makeTouchBar()
@@ -1417,6 +1457,8 @@
updateRemoteAccessibilityRegistration(false);
flushPendingMouseEventCallbacks();
+
+ handleDOMPasteRequestWithResult(WebCore::DOMPasteAccessResponse::DeniedForGesture);
}
void WebViewImpl::processWillSwap()
@@ -4263,6 +4305,39 @@
return [NSArray arrayWithObject:[path lastPathComponent]];
}
+void WebViewImpl::requestDOMPasteAccess(const WebCore::IntRect&, const String& originIdentifier, CompletionHandler<void(WebCore::DOMPasteAccessResponse)>&& completion)
+{
+ ASSERT(!m_domPasteRequestHandler);
+ handleDOMPasteRequestWithResult(WebCore::DOMPasteAccessResponse::DeniedForGesture);
+
+ NSData *data = "" dataForType:@(WebCore::PasteboardCustomData::cocoaType())];
+ auto buffer = WebCore::SharedBuffer::create(data);
+ if (WebCore::PasteboardCustomData::fromSharedBuffer(buffer.get()).origin == originIdentifier) {
+ completion(WebCore::DOMPasteAccessResponse::GrantedForGesture);
+ return;
+ }
+
+ m_domPasteMenuDelegate = adoptNS([[WKDOMPasteMenuDelegate alloc] initWithWebViewImpl:*this]);
+ m_domPasteRequestHandler = WTFMove(completion);
+ m_domPasteMenu = adoptNS([[NSMenu alloc] initWithTitle:WebCore::contextMenuItemTagPaste()]);
+
+ [m_domPasteMenu setDelegate:m_domPasteMenuDelegate.get()];
+ [m_domPasteMenu setAllowsContextMenuPlugIns:NO];
+ [m_domPasteMenu insertItemWithTitle:WebCore::contextMenuItemTagPaste() action:@selector(_web_grantDOMPasteAccess) keyEquivalent:emptyString() atIndex:0];
+ [NSMenu popUpContextMenu:m_domPasteMenu.get() withEvent:m_lastMouseDownEvent.get() forView:m_view.getAutoreleased()];
+}
+
+void WebViewImpl::handleDOMPasteRequestWithResult(WebCore::DOMPasteAccessResponse response)
+{
+ if (auto handler = std::exchange(m_domPasteRequestHandler, { }))
+ handler(response);
+ [m_domPasteMenu removeAllItems];
+ [m_domPasteMenu update];
+ [m_domPasteMenu cancelTracking];
+ m_domPasteMenu = nil;
+ m_domPasteMenuDelegate = nil;
+}
+
static RetainPtr<CGImageRef> takeWindowSnapshot(CGSWindowID windowID, bool captureAtNominalResolution)
{
CGSWindowCaptureOptions options = kCGSCaptureIgnoreGlobalClipShape;
Modified: trunk/Source/WebKit/UIProcess/mac/PageClientImplMac.h (250972 => 250973)
--- trunk/Source/WebKit/UIProcess/mac/PageClientImplMac.h 2019-10-10 15:56:17 UTC (rev 250972)
+++ trunk/Source/WebKit/UIProcess/mac/PageClientImplMac.h 2019-10-10 16:17:18 UTC (rev 250973)
@@ -213,7 +213,7 @@
void willRecordNavigationSnapshot(WebBackForwardListItem&) override;
void didRemoveNavigationGestureSnapshot() override;
- void requestDOMPasteAccess(const WebCore::IntRect&, const String& originIdentifier, CompletionHandler<void(WebCore::DOMPasteAccessResponse)>&& completion) final { completion(WebCore::DOMPasteAccessResponse::DeniedForGesture); }
+ void requestDOMPasteAccess(const WebCore::IntRect&, const String&, CompletionHandler<void(WebCore::DOMPasteAccessResponse)>&&) final;
NSView *activeView() const;
NSWindow *activeWindow() const;
Modified: trunk/Source/WebKit/UIProcess/mac/PageClientImplMac.mm (250972 => 250973)
--- trunk/Source/WebKit/UIProcess/mac/PageClientImplMac.mm 2019-10-10 15:56:17 UTC (rev 250972)
+++ trunk/Source/WebKit/UIProcess/mac/PageClientImplMac.mm 2019-10-10 16:17:18 UTC (rev 250973)
@@ -955,6 +955,11 @@
m_impl->takeFocus(direction);
}
+void PageClientImpl::requestDOMPasteAccess(const WebCore::IntRect& elementRect, const String& originIdentifier, CompletionHandler<void(WebCore::DOMPasteAccessResponse)>&& completion)
+{
+ m_impl->requestDOMPasteAccess(elementRect, originIdentifier, WTFMove(completion));
+}
+
} // namespace WebKit
#endif // PLATFORM(MAC)
Modified: trunk/Tools/ChangeLog (250972 => 250973)
--- trunk/Tools/ChangeLog 2019-10-10 15:56:17 UTC (rev 250972)
+++ trunk/Tools/ChangeLog 2019-10-10 16:17:18 UTC (rev 250973)
@@ -1,3 +1,85 @@
+2019-10-10 Wenson Hsieh <[email protected]>
+
+ Support programmatic paste requests on macOS
+ https://bugs.webkit.org/show_bug.cgi?id=202773
+ <rdar://problem/48957166>
+
+ Reviewed by Tim Horton.
+
+ Adds new testing support to enable us to test programmatic paste requests on macOS.
+
+ * TestRunnerShared/UIScriptContext/Bindings/UIScriptController.idl:
+
+ Add a few new UIScriptController methods:
+ - activateAtPoint(x, y, callback): used to activate content underneath at (x, y), in root view coordinates
+ (WKWebView on macOS, and WKContentView on iOS). On macOS, this moves the mouse to the given location and
+ clicks.
+ - chooseMenuAction(action, callback): used to select a menu item with the given title.
+ - dismissMenu(): dismisses the platform menu.
+
+ Note that dismissMenu and chooseMenuAction currently only work for the DOM paste menu, but could be extended in
+ the future to handle the system context menu.
+
+ * TestRunnerShared/UIScriptContext/UIScriptController.cpp:
+ (WTR::UIScriptController::dismissMenu):
+ (WTR::UIScriptController::chooseMenuAction):
+ * TestRunnerShared/UIScriptContext/UIScriptController.h:
+ (WTR::UIScriptController::activateAtPoint):
+ * WebKitTestRunner/cocoa/TestControllerCocoa.mm:
+ (WTR::TestController::cocoaResetStateToConsistentValues):
+ * WebKitTestRunner/cocoa/TestRunnerWKWebView.h:
+ * WebKitTestRunner/cocoa/TestRunnerWKWebView.mm:
+ (-[TestRunnerWKWebView initWithFrame:configuration:]):
+ (-[TestRunnerWKWebView _didShowMenu]):
+ (-[TestRunnerWKWebView _didHideMenu]):
+
+ Make these present across both macOS and iOS. On macOS, we listen for NSMenuDidBeginTrackingNotification and
+ NSMenuDidEndTrackingNotification to know when a menu has been shown or dismissed.
+
+ (-[TestRunnerWKWebView dismissActiveMenu]):
+ (-[TestRunnerWKWebView resetInteractionCallbacks]):
+
+ Make these available on both iOS and macOS. The only interaction callbacks on macOS are currently
+ didShowMenuCallback and didHideMenuCallback.
+
+ (-[TestRunnerWKWebView _willHideMenu]):
+ * WebKitTestRunner/cocoa/UIScriptControllerCocoa.h:
+ * WebKitTestRunner/cocoa/UIScriptControllerCocoa.mm:
+ (WTR::UIScriptControllerCocoa::setDidShowMenuCallback):
+ (WTR::UIScriptControllerCocoa::setDidHideMenuCallback):
+ (WTR::UIScriptControllerCocoa::dismissMenu):
+ (WTR::UIScriptControllerCocoa::isShowingMenu const):
+
+ Move these implementations into UIScriptControllerCocoa, from UIScriptControllerIOS.
+
+ * WebKitTestRunner/ios/TestControllerIOS.mm:
+ (WTR::TestController::platformResetStateToConsistentValues):
+
+ Instead of clearing all interaction callbacks in TestControllerIOS, do it in TestControllerCocoa where it
+ affects both macOS and iOS.
+
+ * WebKitTestRunner/ios/UIScriptControllerIOS.h:
+ * WebKitTestRunner/ios/UIScriptControllerIOS.mm:
+ (WTR::UIScriptControllerIOS::activateAtPoint):
+ (WTR::UIScriptControllerIOS::singleTapAtPointWithModifiers):
+ (WTR::UIScriptControllerIOS::chooseMenuAction):
+ (WTR::UIScriptControllerIOS::rectForMenuAction const):
+ (WTR::UIScriptControllerIOS::setDidShowMenuCallback): Deleted.
+ (WTR::UIScriptControllerIOS::setDidHideMenuCallback): Deleted.
+ (WTR::UIScriptControllerIOS::isShowingMenu const): Deleted.
+
+ Abstract rectForMenuAction and singleTapAtPointWithModifiers out into private helper methods, such that they can
+ be used from within other script controller methods.
+
+ * WebKitTestRunner/mac/UIScriptControllerMac.h:
+ * WebKitTestRunner/mac/UIScriptControllerMac.mm:
+
+ Implement the new script controller hooks on macOS.
+
+ (WTR::UIScriptControllerMac::clearAllCallbacks):
+ (WTR::UIScriptControllerMac::chooseMenuAction):
+ (WTR::UIScriptControllerMac::activateAtPoint):
+
2019-10-10 Jonathan Bedard <[email protected]>
results.webkit.org: Increase default limit for test results (Follow-up fix)
Modified: trunk/Tools/TestRunnerShared/UIScriptContext/Bindings/UIScriptController.idl (250972 => 250973)
--- trunk/Tools/TestRunnerShared/UIScriptContext/Bindings/UIScriptController.idl 2019-10-10 15:56:17 UTC (rev 250972)
+++ trunk/Tools/TestRunnerShared/UIScriptContext/Bindings/UIScriptController.idl 2019-10-10 16:17:18 UTC (rev 250973)
@@ -63,6 +63,8 @@
void doubleTapAtPoint(long x, long y, float delay, object callback);
void dragFromPointToPoint(long startX, long startY, long endX, long endY, double durationSeconds, object callback);
+ void activateAtPoint(long x, long y, object callback);
+
void longPressAtPoint(long x, long y, object callback);
void stylusDownAtPoint(long x, long y, float azimuthAngle, float altitudeAngle, float pressure, object callback);
@@ -238,6 +240,8 @@
readonly attribute boolean isShowingMenu;
readonly attribute object menuRect;
object rectForMenuAction(DOMString action);
+ void chooseMenuAction(DOMString action, object callback);
+ void dismissMenu();
readonly attribute boolean isShowingPopover;
attribute object willPresentPopoverCallback;
Modified: trunk/Tools/TestRunnerShared/UIScriptContext/UIScriptController.cpp (250972 => 250973)
--- trunk/Tools/TestRunnerShared/UIScriptContext/UIScriptController.cpp 2019-10-10 15:56:17 UTC (rev 250972)
+++ trunk/Tools/TestRunnerShared/UIScriptContext/UIScriptController.cpp 2019-10-10 16:17:18 UTC (rev 250973)
@@ -231,4 +231,12 @@
clearAllCallbacks();
}
+void UIScriptController::dismissMenu()
+{
}
+
+void UIScriptController::chooseMenuAction(JSStringRef, JSValueRef)
+{
+}
+
+}
Modified: trunk/Tools/TestRunnerShared/UIScriptContext/UIScriptController.h (250972 => 250973)
--- trunk/Tools/TestRunnerShared/UIScriptContext/UIScriptController.h 2019-10-10 15:56:17 UTC (rev 250972)
+++ trunk/Tools/TestRunnerShared/UIScriptContext/UIScriptController.h 2019-10-10 16:17:18 UTC (rev 250973)
@@ -105,6 +105,9 @@
virtual void becomeFirstResponder() { notImplemented(); }
virtual void resignFirstResponder() { notImplemented(); }
+ virtual void chooseMenuAction(JSStringRef, JSValueRef);
+ virtual void dismissMenu();
+
virtual void firstResponderSuppressionForWebView(bool) { notImplemented(); }
virtual void makeWindowContentViewFirstResponder() { notImplemented(); }
virtual bool isWindowContentViewFirstResponder() const { notImplemented(); return false; }
@@ -142,6 +145,8 @@
virtual void dragFromPointToPoint(long startX, long startY, long endX, long endY, double durationSeconds, JSValueRef callback) { notImplemented(); }
virtual void longPressAtPoint(long x, long y, JSValueRef callback) { notImplemented(); }
+ virtual void activateAtPoint(long x, long y, JSValueRef callback) { notImplemented(); }
+
// Keyboard
virtual void enterText(JSStringRef) { notImplemented(); }
Modified: trunk/Tools/WebKitTestRunner/cocoa/TestControllerCocoa.mm (250972 => 250973)
--- trunk/Tools/WebKitTestRunner/cocoa/TestControllerCocoa.mm 2019-10-10 15:56:17 UTC (rev 250972)
+++ trunk/Tools/WebKitTestRunner/cocoa/TestControllerCocoa.mm 2019-10-10 16:17:18 UTC (rev 250973)
@@ -262,6 +262,8 @@
// Toggle on before the test, and toggle off after the test.
if (options.shouldShowSpellCheckingDots)
[platformView toggleContinuousSpellChecking:nil];
+
+ [platformView resetInteractionCallbacks];
}
[globalWebsiteDataStoreDelegateClient setAllowRaisingQuota: true];
Modified: trunk/Tools/WebKitTestRunner/cocoa/TestRunnerWKWebView.h (250972 => 250973)
--- trunk/Tools/WebKitTestRunner/cocoa/TestRunnerWKWebView.h 2019-10-10 15:56:17 UTC (rev 250972)
+++ trunk/Tools/WebKitTestRunner/cocoa/TestRunnerWKWebView.h 2019-10-10 16:17:18 UTC (rev 250973)
@@ -41,8 +41,6 @@
@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 (^willPresentPopoverCallback)(void);
@property (nonatomic, copy) void (^didDismissPopoverCallback)(void);
@property (nonatomic, copy) void (^didEndScrollingCallback)(void);
@@ -54,7 +52,6 @@
- (void)resetCustomMenuAction;
- (void)installCustomMenuAction:(NSString *)name dismissesAutomatically:(BOOL)dismissesAutomatically callback:(dispatch_block_t)callback;
-- (void)resetInteractionCallbacks;
- (void)zoomToScale:(double)scale animated:(BOOL)animated completionHandler:(void (^)(void))completionHandler;
- (void)accessibilityRetrieveSpeakSelectionContentWithCompletionHandler:(void (^)(void))completionHandler;
- (void)_didEndRotation;
@@ -62,7 +59,6 @@
@property (nonatomic, assign) UIEdgeInsets overrideSafeAreaInsets;
@property (nonatomic, readonly, getter=isShowingKeyboard) BOOL showingKeyboard;
-@property (nonatomic, readonly, getter=isShowingMenu) BOOL showingMenu;
@property (nonatomic, readonly, getter=isDismissingMenu) BOOL dismissingMenu;
@property (nonatomic, readonly, getter=isShowingPopover) BOOL showingPopover;
@property (nonatomic, assign) BOOL usesSafariLikeRotation;
@@ -70,7 +66,13 @@
#endif
+@property (nonatomic, readonly, getter=isShowingMenu) BOOL showingMenu;
+@property (nonatomic, copy) void (^didShowMenuCallback)(void);
+@property (nonatomic, copy) void (^didHideMenuCallback)(void);
@property (nonatomic, retain, setter=_setStableStateOverride:) NSNumber *_stableStateOverride;
@property (nonatomic, setter=_setScrollingUpdatesDisabledForTesting:) BOOL _scrollingUpdatesDisabledForTesting;
+- (void)dismissActiveMenu;
+- (void)resetInteractionCallbacks;
+
@end
Modified: trunk/Tools/WebKitTestRunner/cocoa/TestRunnerWKWebView.mm (250972 => 250973)
--- trunk/Tools/WebKitTestRunner/cocoa/TestRunnerWKWebView.mm 2019-10-10 15:56:17 UTC (rev 250972)
+++ trunk/Tools/WebKitTestRunner/cocoa/TestRunnerWKWebView.mm 2019-10-10 16:17:18 UTC (rev 250973)
@@ -84,11 +84,14 @@
}
#endif
-#if PLATFORM(IOS_FAMILY)
- (instancetype)initWithFrame:(CGRect)frame configuration:(WKWebViewConfiguration *)configuration
{
if (self = [super initWithFrame:frame configuration:configuration]) {
NSNotificationCenter* center = [NSNotificationCenter defaultCenter];
+#if PLATFORM(MAC)
+ [center addObserver:self selector:@selector(_didShowMenu) name:NSMenuDidBeginTrackingNotification object:nil];
+ [center addObserver:self selector:@selector(_didHideMenu) name:NSMenuDidEndTrackingNotification object:nil];
+#else
[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];
@@ -97,6 +100,7 @@
[center addObserver:self selector:@selector(_willPresentPopover) name:@"UIPopoverControllerWillPresentPopoverNotification" object:nil];
[center addObserver:self selector:@selector(_didDismissPopover) name:@"UIPopoverControllerDidDismissPopoverNotification" object:nil];
self.UIDelegate = self;
+#endif
}
return self;
}
@@ -113,6 +117,69 @@
[super dealloc];
}
+- (void)_didShowMenu
+{
+ if (self.showingMenu)
+ return;
+
+ self.showingMenu = YES;
+ if (self.didShowMenuCallback)
+ self.didShowMenuCallback();
+}
+
+- (void)_didHideMenu
+{
+#if PLATFORM(IOS_FAMILY)
+ self.dismissingMenu = NO;
+#endif
+
+ if (!self.showingMenu)
+ return;
+
+ self.showingMenu = NO;
+ if (self.didHideMenuCallback)
+ self.didHideMenuCallback();
+}
+
+- (void)dismissActiveMenu
+{
+#if PLATFORM(IOS_FAMILY)
+ [self resignFirstResponder];
+#else
+ auto menu = retainPtr(self._activeMenu);
+ [menu removeAllItems];
+ [menu update];
+ [menu cancelTracking];
+#endif
+}
+
+- (void)resetInteractionCallbacks
+{
+ self.didShowMenuCallback = nil;
+ self.didHideMenuCallback = nil;
+#if PLATFORM(IOS_FAMILY)
+ self.didStartFormControlInteractionCallback = nil;
+ self.didEndFormControlInteractionCallback = nil;
+ self.didShowForcePressPreviewCallback = nil;
+ self.didDismissForcePressPreviewCallback = nil;
+ self.willBeginZoomingCallback = nil;
+ self.didEndZoomingCallback = nil;
+ self.didShowKeyboardCallback = nil;
+ self.didHideKeyboardCallback = nil;
+ self.willPresentPopoverCallback = nil;
+ self.didDismissPopoverCallback = nil;
+ self.didEndScrollingCallback = nil;
+ self.rotationDidEndCallback = nil;
+#endif // PLATFORM(IOS_FAMILY)
+}
+
+#if PLATFORM(IOS_FAMILY)
+
+- (void)_willHideMenu
+{
+ self.dismissingMenu = YES;
+}
+
- (void)didStartFormControlInteraction
{
_isInteractingWithFormControl = YES;
@@ -228,24 +295,6 @@
return canPerformActionByDefault;
}
-- (void)resetInteractionCallbacks
-{
- self.didStartFormControlInteractionCallback = nil;
- self.didEndFormControlInteractionCallback = nil;
- self.didShowForcePressPreviewCallback = nil;
- self.didDismissForcePressPreviewCallback = nil;
- self.willBeginZoomingCallback = nil;
- self.didEndZoomingCallback = nil;
- self.didShowKeyboardCallback = nil;
- self.didHideKeyboardCallback = nil;
- self.didShowMenuCallback = nil;
- self.didHideMenuCallback = nil;
- self.willPresentPopoverCallback = nil;
- self.didDismissPopoverCallback = nil;
- self.didEndScrollingCallback = nil;
- self.rotationDidEndCallback = nil;
-}
-
- (void)zoomToScale:(double)scale animated:(BOOL)animated completionHandler:(void (^)(void))completionHandler
{
ASSERT(!self.zoomToScaleCompletionHandler);
@@ -281,33 +330,6 @@
self.didHideKeyboardCallback();
}
-- (void)_didShowMenu
-{
- if (self.showingMenu)
- return;
-
- self.showingMenu = YES;
- if (self.didShowMenuCallback)
- self.didShowMenuCallback();
-}
-
-- (void)_willHideMenu
-{
- self.dismissingMenu = YES;
-}
-
-- (void)_didHideMenu
-{
- self.dismissingMenu = NO;
-
- if (!self.showingMenu)
- return;
-
- self.showingMenu = NO;
- if (self.didHideMenuCallback)
- self.didHideMenuCallback();
-}
-
- (void)_willPresentPopover
{
if (self.showingPopover)
Modified: trunk/Tools/WebKitTestRunner/cocoa/UIScriptControllerCocoa.h (250972 => 250973)
--- trunk/Tools/WebKitTestRunner/cocoa/UIScriptControllerCocoa.h 2019-10-10 15:56:17 UTC (rev 250972)
+++ trunk/Tools/WebKitTestRunner/cocoa/UIScriptControllerCocoa.h 2019-10-10 16:17:18 UTC (rev 250973)
@@ -49,6 +49,11 @@
JSRetainPtr<JSStringRef> firstRedoLabel() const override;
NSUndoManager *platformUndoManager() const override;
+ void setDidShowMenuCallback(JSValueRef) override;
+ void setDidHideMenuCallback(JSValueRef) override;
+ void dismissMenu() override;
+ bool isShowingMenu() const override;
+
protected:
explicit UIScriptControllerCocoa(UIScriptContext&);
TestRunnerWKWebView *webView() const;
Modified: trunk/Tools/WebKitTestRunner/cocoa/UIScriptControllerCocoa.mm (250972 => 250973)
--- trunk/Tools/WebKitTestRunner/cocoa/UIScriptControllerCocoa.mm 2019-10-10 15:56:17 UTC (rev 250972)
+++ trunk/Tools/WebKitTestRunner/cocoa/UIScriptControllerCocoa.mm 2019-10-10 16:17:18 UTC (rev 250973)
@@ -166,4 +166,34 @@
return platformContentView().undoManager;
}
+void UIScriptControllerCocoa::setDidShowMenuCallback(JSValueRef callback)
+{
+ UIScriptController::setDidShowMenuCallback(callback);
+ webView().didShowMenuCallback = ^{
+ if (!m_context)
+ return;
+ m_context->fireCallback(CallbackTypeDidShowMenu);
+ };
+}
+
+void UIScriptControllerCocoa::setDidHideMenuCallback(JSValueRef callback)
+{
+ UIScriptController::setDidHideMenuCallback(callback);
+ webView().didHideMenuCallback = ^{
+ if (!m_context)
+ return;
+ m_context->fireCallback(CallbackTypeDidHideMenu);
+ };
+}
+
+void UIScriptControllerCocoa::dismissMenu()
+{
+ [webView() dismissActiveMenu];
+}
+
+bool UIScriptControllerCocoa::isShowingMenu() const
+{
+ return webView().showingMenu;
+}
+
} // namespace WTR
Modified: trunk/Tools/WebKitTestRunner/ios/TestControllerIOS.mm (250972 => 250973)
--- trunk/Tools/WebKitTestRunner/ios/TestControllerIOS.mm 2019-10-10 15:56:17 UTC (rev 250972)
+++ trunk/Tools/WebKitTestRunner/ios/TestControllerIOS.mm 2019-10-10 16:17:18 UTC (rev 250973)
@@ -167,7 +167,6 @@
webView.overrideSafeAreaInsets = UIEdgeInsetsZero;
[webView _clearOverrideLayoutParameters];
[webView _clearInterfaceOrientationOverride];
- [webView resetInteractionCallbacks];
[webView resetCustomMenuAction];
[webView setAllowedMenuActions:nil];
Modified: trunk/Tools/WebKitTestRunner/ios/UIScriptControllerIOS.h (250972 => 250973)
--- trunk/Tools/WebKitTestRunner/ios/UIScriptControllerIOS.h 2019-10-10 15:56:17 UTC (rev 250972)
+++ trunk/Tools/WebKitTestRunner/ios/UIScriptControllerIOS.h 2019-10-10 16:17:18 UTC (rev 250973)
@@ -26,9 +26,15 @@
#pragma once
#import "UIScriptControllerCocoa.h"
+#import <wtf/BlockPtr.h>
#if PLATFORM(IOS_FAMILY)
+namespace WebCore {
+class FloatPoint;
+class FloatRect;
+}
+
namespace WTR {
class UIScriptControllerIOS : public UIScriptControllerCocoa {
@@ -67,6 +73,8 @@
void typeCharacterUsingHardwareKeyboard(JSStringRef character, JSValueRef) override;
void keyDown(JSStringRef character, JSValueRef modifierArray) override;
+ void activateAtPoint(long x, long y, JSValueRef callback) override;
+
void rawKeyDown(JSStringRef) override;
void rawKeyUp(JSStringRef) override;
@@ -112,7 +120,7 @@
JSObjectRef rectForMenuAction(JSStringRef) const override;
JSObjectRef menuRect() const override;
bool isDismissingMenu() const override;
- bool isShowingMenu() const override;
+ void chooseMenuAction(JSStringRef, JSValueRef) override;
void setSafeAreaInsets(double top, double right, double bottom, double left) override;
void beginBackSwipe(JSValueRef) override;
void completeBackSwipe(JSValueRef) override;
@@ -135,8 +143,6 @@
void setDidEndZoomingCallback(JSValueRef) override;
void setDidShowKeyboardCallback(JSValueRef) override;
void setDidHideKeyboardCallback(JSValueRef) override;
- void setDidShowMenuCallback(JSValueRef) override;
- void setDidHideMenuCallback(JSValueRef) override;
void setWillPresentPopoverCallback(JSValueRef) override;
void setDidDismissPopoverCallback(JSValueRef) override;
void setDidEndScrollingCallback(JSValueRef) override;
@@ -144,6 +150,8 @@
private:
void waitForSingleTapToReset() const;
+ WebCore::FloatRect rectForMenuAction(CFStringRef) const;
+ void singleTapAtPointWithModifiers(WebCore::FloatPoint location, Vector<String>&& modifierFlags, BlockPtr<void()>&&);
};
}
Modified: trunk/Tools/WebKitTestRunner/ios/UIScriptControllerIOS.mm (250972 => 250973)
--- trunk/Tools/WebKitTestRunner/ios/UIScriptControllerIOS.mm 2019-10-10 15:56:17 UTC (rev 250972)
+++ trunk/Tools/WebKitTestRunner/ios/UIScriptControllerIOS.mm 2019-10-10 16:17:18 UTC (rev 250973)
@@ -39,6 +39,7 @@
#import <_javascript_Core/_javascript_Core.h>
#import <_javascript_Core/OpaqueJSString.h>
#import <UIKit/UIKit.h>
+#import <WebCore/FloatPoint.h>
#import <WebCore/FloatRect.h>
#import <WebKit/WKWebViewPrivate.h>
#import <WebKit/WebKit.h>
@@ -264,6 +265,11 @@
singleTapAtPointWithModifiers(x, y, nullptr, callback);
}
+void UIScriptControllerIOS::activateAtPoint(long x, long y, JSValueRef callback)
+{
+ singleTapAtPoint(x, y, callback);
+}
+
void UIScriptControllerIOS::waitForSingleTapToReset() const
{
bool doneWaitingForSingleTapToReset = false;
@@ -288,14 +294,21 @@
void UIScriptControllerIOS::singleTapAtPointWithModifiers(long x, long y, JSValueRef modifierArray, JSValueRef callback)
{
unsigned callbackID = m_context->prepareForAsyncTask(callback, CallbackTypeNonPersistent);
+ singleTapAtPointWithModifiers(WebCore::FloatPoint(x, y), parseModifierArray(m_context->jsContext(), modifierArray), makeBlockPtr([this, protectedThis = makeRefPtr(*this), callbackID] {
+ if (!m_context)
+ return;
+ m_context->asyncTaskComplete(callbackID);
+ }));
+}
+void UIScriptControllerIOS::singleTapAtPointWithModifiers(WebCore::FloatPoint location, Vector<String>&& modifierFlags, BlockPtr<void()>&& block)
+{
waitForSingleTapToReset();
- auto modifierFlags = parseModifierArray(m_context->jsContext(), modifierArray);
for (auto& modifierFlag : modifierFlags)
[[HIDEventGenerator sharedHIDEventGenerator] keyDown:modifierFlag];
- [[HIDEventGenerator sharedHIDEventGenerator] tap:globalToContentCoordinates(webView(), x, y) completionBlock:^{
+ [[HIDEventGenerator sharedHIDEventGenerator] tap:globalToContentCoordinates(webView(), location.x(), location.y()) completionBlock:[this, protectedThis = makeRefPtr(*this), modifierFlags = WTFMove(modifierFlags), block = WTFMove(block)] () mutable {
if (!m_context)
return;
for (size_t i = modifierFlags.size(); i; ) {
@@ -302,11 +315,7 @@
--i;
[[HIDEventGenerator sharedHIDEventGenerator] keyUp:modifierFlags[i]];
}
- [[HIDEventGenerator sharedHIDEventGenerator] sendMarkerHIDEventWithCompletionBlock:^{
- if (!m_context)
- return;
- m_context->asyncTaskComplete(callbackID);
- }];
+ [[HIDEventGenerator sharedHIDEventGenerator] sendMarkerHIDEventWithCompletionBlock:block.get()];
}];
}
@@ -924,24 +933,18 @@
};
}
-void UIScriptControllerIOS::setDidShowMenuCallback(JSValueRef callback)
+void UIScriptControllerIOS::chooseMenuAction(JSStringRef jsAction, JSValueRef callback)
{
- UIScriptController::setDidShowMenuCallback(callback);
- webView().didShowMenuCallback = ^{
- if (!m_context)
- return;
- m_context->fireCallback(CallbackTypeDidShowMenu);
- };
-}
+ auto action = "" jsAction));
+ auto rect = rectForMenuAction(action.get());
+ if (rect.isEmpty())
+ return;
-void UIScriptControllerIOS::setDidHideMenuCallback(JSValueRef callback)
-{
- UIScriptController::setDidHideMenuCallback(callback);
- webView().didHideMenuCallback = ^{
- if (!m_context)
- return;
- m_context->fireCallback(CallbackTypeDidHideMenu);
- };
+ unsigned callbackID = m_context->prepareForAsyncTask(callback, CallbackTypeNonPersistent);
+ singleTapAtPointWithModifiers(rect.center(), { }, makeBlockPtr([this, protectedThis = makeRef(*this), callbackID] {
+ if (m_context)
+ m_context->asyncTaskComplete(callbackID);
+ }));
}
bool UIScriptControllerIOS::isShowingPopover() const
@@ -972,12 +975,20 @@
JSObjectRef UIScriptControllerIOS::rectForMenuAction(JSStringRef jsAction) const
{
auto action = "" jsAction));
+ auto rect = rectForMenuAction(action.get());
+ if (rect.isEmpty())
+ return nullptr;
+ return m_context->objectFromRect(rect);
+}
+
+WebCore::FloatRect UIScriptControllerIOS::rectForMenuAction(CFStringRef action) const
+{
UIWindow *windowForButton = nil;
UIButton *buttonForAction = nil;
UIView *calloutBar = UICalloutBar.activeCalloutBar;
if (!calloutBar.window)
- return nullptr;
+ return { };
for (UIButton *button in findAllViewsInHierarchyOfType(calloutBar, UIButton.class)) {
NSString *buttonTitle = [button titleForState:UIControlStateNormal];
@@ -984,7 +995,7 @@
if (!buttonTitle.length)
continue;
- if (![buttonTitle isEqualToString:(__bridge NSString *)action.get()])
+ if (![buttonTitle isEqualToString:(__bridge NSString *)action])
continue;
buttonForAction = button;
@@ -993,10 +1004,10 @@
}
if (!buttonForAction)
- return nullptr;
+ return { };
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));
+ return WebCore::FloatRect(rectInRootViewCoordinates.origin.x, rectInRootViewCoordinates.origin.y, rectInRootViewCoordinates.size.width, rectInRootViewCoordinates.size.height);
}
JSObjectRef UIScriptControllerIOS::menuRect() const
@@ -1014,11 +1025,6 @@
return webView().dismissingMenu;
}
-bool UIScriptControllerIOS::isShowingMenu() const
-{
- return webView().showingMenu;
-}
-
void UIScriptControllerIOS::setDidEndScrollingCallback(JSValueRef callback)
{
UIScriptController::setDidEndScrollingCallback(callback);
Modified: trunk/Tools/WebKitTestRunner/mac/UIScriptControllerMac.h (250972 => 250973)
--- trunk/Tools/WebKitTestRunner/mac/UIScriptControllerMac.h 2019-10-10 15:56:17 UTC (rev 250972)
+++ trunk/Tools/WebKitTestRunner/mac/UIScriptControllerMac.h 2019-10-10 16:17:18 UTC (rev 250973)
@@ -51,6 +51,11 @@
bool isWindowContentViewFirstResponder() const override;
void toggleCapsLock(JSValueRef) override;
NSView *platformContentView() const override;
+ void clearAllCallbacks() override;
+
+ void chooseMenuAction(JSStringRef, JSValueRef) override;
+
+ void activateAtPoint(long x, long y, JSValueRef callback) override;
};
} // namespace WTR
Modified: trunk/Tools/WebKitTestRunner/mac/UIScriptControllerMac.mm (250972 => 250973)
--- trunk/Tools/WebKitTestRunner/mac/UIScriptControllerMac.mm 2019-10-10 15:56:17 UTC (rev 250972)
+++ trunk/Tools/WebKitTestRunner/mac/UIScriptControllerMac.mm 2019-10-10 16:17:18 UTC (rev 250973)
@@ -26,6 +26,7 @@
#import "config.h"
#import "UIScriptControllerMac.h"
+#import "EventSenderProxy.h"
#import "EventSerializerMac.h"
#import "PlatformWebView.h"
#import "SharedEventStreamsMac.h"
@@ -118,6 +119,36 @@
}];
}
+void UIScriptControllerMac::clearAllCallbacks()
+{
+ [webView() resetInteractionCallbacks];
+}
+
+void UIScriptControllerMac::chooseMenuAction(JSStringRef jsAction, JSValueRef callback)
+{
+ unsigned callbackID = m_context->prepareForAsyncTask(callback, CallbackTypeNonPersistent);
+
+ auto action = "" jsAction));
+ __block NSUInteger matchIndex = NSNotFound;
+ auto activeMenu = retainPtr(webView()._activeMenu);
+ [[activeMenu itemArray] enumerateObjectsUsingBlock:^(NSMenuItem *item, NSUInteger index, BOOL *stop) {
+ if ([item.title isEqualToString:(__bridge NSString *)action.get()])
+ matchIndex = index;
+ }];
+
+ if (matchIndex != NSNotFound) {
+ [activeMenu performActionForItemAtIndex:matchIndex];
+ [activeMenu removeAllItems];
+ [activeMenu update];
+ [activeMenu cancelTracking];
+ }
+
+ dispatch_async(dispatch_get_main_queue(), ^{
+ if (m_context)
+ m_context->asyncTaskComplete(callbackID);
+ });
+}
+
void UIScriptControllerMac::beginBackSwipe(JSValueRef callback)
{
playBackEvents(webView(), m_context, beginSwipeBackEventStream(), callback);
@@ -174,4 +205,24 @@
return webView();
}
+void UIScriptControllerMac::activateAtPoint(long x, long y, JSValueRef callback)
+{
+ auto* eventSender = TestController::singleton().eventSenderProxy();
+ if (!eventSender) {
+ ASSERT_NOT_REACHED();
+ return;
+ }
+
+ unsigned callbackID = m_context->prepareForAsyncTask(callback, CallbackTypeNonPersistent);
+
+ eventSender->mouseMoveTo(x, y);
+ eventSender->mouseDown(0, 0);
+ eventSender->mouseUp(0, 0);
+
+ dispatch_async(dispatch_get_main_queue(), ^{
+ if (m_context)
+ m_context->asyncTaskComplete(callbackID);
+ });
+}
+
} // namespace WTR