summaryrefslogtreecommitdiffstats
path: root/testing/web-platform/tests/clipboard-apis
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
commit26a029d407be480d791972afb5975cf62c9360a6 (patch)
treef435a8308119effd964b339f76abb83a57c29483 /testing/web-platform/tests/clipboard-apis
parentInitial commit. (diff)
downloadfirefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz
firefox-26a029d407be480d791972afb5975cf62c9360a6.zip
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'testing/web-platform/tests/clipboard-apis')
-rw-r--r--testing/web-platform/tests/clipboard-apis/META.yml3
-rw-r--r--testing/web-platform/tests/clipboard-apis/WEB_FEATURES.yml5
-rw-r--r--testing/web-platform/tests/clipboard-apis/async-custom-formats-write-fail.tentative.https.html123
-rw-r--r--testing/web-platform/tests/clipboard-apis/async-custom-formats-write-read-web-prefix.tentative.https.html39
-rw-r--r--testing/web-platform/tests/clipboard-apis/async-custom-formats-write-read-without-web-prefix.tentative.https.html39
-rw-r--r--testing/web-platform/tests/clipboard-apis/async-custom-formats-write-read.tentative.https.html47
-rw-r--r--testing/web-platform/tests/clipboard-apis/async-html-script-removal.https.html60
-rw-r--r--testing/web-platform/tests/clipboard-apis/async-navigator-clipboard-basics.https.html154
-rw-r--r--testing/web-platform/tests/clipboard-apis/async-navigator-clipboard-read-resource-load.https.html44
-rw-r--r--testing/web-platform/tests/clipboard-apis/async-navigator-clipboard-read-sanitize.https.html48
-rw-r--r--testing/web-platform/tests/clipboard-apis/async-navigator-clipboard-write-multiple.tentative.https.sub.html106
-rw-r--r--testing/web-platform/tests/clipboard-apis/async-promise-write-blobs-read-blobs.https.html46
-rw-r--r--testing/web-platform/tests/clipboard-apis/async-svg-script-removal.https.html61
-rw-r--r--testing/web-platform/tests/clipboard-apis/async-unsanitized-html-formats-write-read.tentative.https.html74
-rw-r--r--testing/web-platform/tests/clipboard-apis/async-unsanitized-plaintext-formats-write-read.tentative.https.html52
-rw-r--r--testing/web-platform/tests/clipboard-apis/async-unsanitized-standard-html-read-fail.tentative.https.html46
-rw-r--r--testing/web-platform/tests/clipboard-apis/async-write-blobs-read-blobs.https.html48
-rw-r--r--testing/web-platform/tests/clipboard-apis/async-write-html-read-html.https.html62
-rw-r--r--testing/web-platform/tests/clipboard-apis/async-write-image-read-image.https.html85
-rw-r--r--testing/web-platform/tests/clipboard-apis/async-write-svg-read-svg.https.html60
-rw-r--r--testing/web-platform/tests/clipboard-apis/clipboard-events-synthetic.html32
-rw-r--r--testing/web-platform/tests/clipboard-apis/clipboard-file-manual.html87
-rw-r--r--testing/web-platform/tests/clipboard-apis/clipboard-item.https.html116
-rw-r--r--testing/web-platform/tests/clipboard-apis/dataTransfer-clearData.html59
-rw-r--r--testing/web-platform/tests/clipboard-apis/detached-iframe/WEB_FEATURES.yml3
-rw-r--r--testing/web-platform/tests/clipboard-apis/detached-iframe/clipboard-on-detached-iframe.https.html26
-rw-r--r--testing/web-platform/tests/clipboard-apis/detached-iframe/read-on-detaching-iframe.https.html34
-rw-r--r--testing/web-platform/tests/clipboard-apis/detached-iframe/write-on-detaching-iframe.https.html34
-rw-r--r--testing/web-platform/tests/clipboard-apis/detached-iframe/write-read-on-detached-iframe.https.html44
-rw-r--r--testing/web-platform/tests/clipboard-apis/detached-iframe/writeText-readText-on-detached-iframe.https.html40
-rw-r--r--testing/web-platform/tests/clipboard-apis/events/copy-event.html33
-rw-r--r--testing/web-platform/tests/clipboard-apis/events/cut-event-manual.html19
-rw-r--r--testing/web-platform/tests/clipboard-apis/events/paste-event-manual.html21
-rw-r--r--testing/web-platform/tests/clipboard-apis/feature-policy/clipboard-read/clipboard-read-disabled-by-feature-policy.tentative.https.sub.html40
-rw-r--r--testing/web-platform/tests/clipboard-apis/feature-policy/clipboard-read/clipboard-read-disabled-by-feature-policy.tentative.https.sub.html.headers1
-rw-r--r--testing/web-platform/tests/clipboard-apis/feature-policy/clipboard-read/clipboard-read-enabled-by-feature-policy-attribute-cross-origin-tentative.https.sub.html31
-rw-r--r--testing/web-platform/tests/clipboard-apis/feature-policy/clipboard-read/clipboard-read-enabled-by-feature-policy-attribute-tentative.https.sub.html25
-rw-r--r--testing/web-platform/tests/clipboard-apis/feature-policy/clipboard-read/clipboard-read-enabled-by-feature-policy-cross-origin-tentative.https.sub.html30
-rw-r--r--testing/web-platform/tests/clipboard-apis/feature-policy/clipboard-read/clipboard-read-enabled-by-feature-policy-cross-origin.tentative.https.sub.html.headers1
-rw-r--r--testing/web-platform/tests/clipboard-apis/feature-policy/clipboard-read/clipboard-read-enabled-by-feature-policy.tentative.https.sub.html32
-rw-r--r--testing/web-platform/tests/clipboard-apis/feature-policy/clipboard-read/clipboard-read-enabled-by-feature-policy.tentative.https.sub.html.headers1
-rw-r--r--testing/web-platform/tests/clipboard-apis/feature-policy/clipboard-read/clipboard-read-enabled-on-self-origin-by-feature-policy.tentative.https.sub.html45
-rw-r--r--testing/web-platform/tests/clipboard-apis/feature-policy/clipboard-read/clipboard-read-enabled-on-self-origin-by-feature-policy.tentative.https.sub.html.headers1
-rw-r--r--testing/web-platform/tests/clipboard-apis/feature-policy/clipboard-write/clipboard-write-disabled-by-feature-policy.tentative.https.sub.html40
-rw-r--r--testing/web-platform/tests/clipboard-apis/feature-policy/clipboard-write/clipboard-write-disabled-by-feature-policy.tentative.https.sub.html.headers1
-rw-r--r--testing/web-platform/tests/clipboard-apis/feature-policy/clipboard-write/clipboard-write-enabled-by-feature-policy-attribute-cross-origin-tentative.https.sub.html31
-rw-r--r--testing/web-platform/tests/clipboard-apis/feature-policy/clipboard-write/clipboard-write-enabled-by-feature-policy-attribute-tentative.https.sub.html25
-rw-r--r--testing/web-platform/tests/clipboard-apis/feature-policy/clipboard-write/clipboard-write-enabled-by-feature-policy-cross-origin-tentative.https.sub.html30
-rw-r--r--testing/web-platform/tests/clipboard-apis/feature-policy/clipboard-write/clipboard-write-enabled-by-feature-policy-cross-origin.tentative.https.sub.html.headers1
-rw-r--r--testing/web-platform/tests/clipboard-apis/feature-policy/clipboard-write/clipboard-write-enabled-by-feature-policy.tentative.https.sub.html32
-rw-r--r--testing/web-platform/tests/clipboard-apis/feature-policy/clipboard-write/clipboard-write-enabled-by-feature-policy.tentative.https.sub.html.headers1
-rw-r--r--testing/web-platform/tests/clipboard-apis/feature-policy/clipboard-write/clipboard-write-enabled-on-self-origin-by-feature-policy.tentative.https.sub.html41
-rw-r--r--testing/web-platform/tests/clipboard-apis/feature-policy/clipboard-write/clipboard-write-enabled-on-self-origin-by-feature-policy.tentative.https.sub.html.headers1
-rw-r--r--testing/web-platform/tests/clipboard-apis/idlharness.https.window.js17
-rw-r--r--testing/web-platform/tests/clipboard-apis/permissions/WEB_FEATURES.yml3
-rw-r--r--testing/web-platform/tests/clipboard-apis/permissions/readText-denied.https.html20
-rw-r--r--testing/web-platform/tests/clipboard-apis/permissions/readText-granted.https.html19
-rw-r--r--testing/web-platform/tests/clipboard-apis/permissions/writeText-denied.https.html20
-rw-r--r--testing/web-platform/tests/clipboard-apis/permissions/writeText-granted.https.html19
-rw-r--r--testing/web-platform/tests/clipboard-apis/resources/copied-file.txt1
-rw-r--r--testing/web-platform/tests/clipboard-apis/resources/greenbox.pngbin0 -> 95 bytes
-rw-r--r--testing/web-platform/tests/clipboard-apis/resources/page.html26
-rw-r--r--testing/web-platform/tests/clipboard-apis/resources/user-activation.js25
-rw-r--r--testing/web-platform/tests/clipboard-apis/text-write-read/WEB_FEATURES.yml3
-rw-r--r--testing/web-platform/tests/clipboard-apis/text-write-read/async-write-read.https.html41
-rw-r--r--testing/web-platform/tests/clipboard-apis/text-write-read/async-write-readText.https.html33
-rw-r--r--testing/web-platform/tests/clipboard-apis/text-write-read/async-writeText-read.https.html37
-rw-r--r--testing/web-platform/tests/clipboard-apis/text-write-read/async-writeText-readText.https.html28
68 files changed, 2452 insertions, 0 deletions
diff --git a/testing/web-platform/tests/clipboard-apis/META.yml b/testing/web-platform/tests/clipboard-apis/META.yml
new file mode 100644
index 0000000000..ecbac54806
--- /dev/null
+++ b/testing/web-platform/tests/clipboard-apis/META.yml
@@ -0,0 +1,3 @@
+spec: https://w3c.github.io/clipboard-apis/
+suggested_reviewers:
+ - garykac
diff --git a/testing/web-platform/tests/clipboard-apis/WEB_FEATURES.yml b/testing/web-platform/tests/clipboard-apis/WEB_FEATURES.yml
new file mode 100644
index 0000000000..3e5025581f
--- /dev/null
+++ b/testing/web-platform/tests/clipboard-apis/WEB_FEATURES.yml
@@ -0,0 +1,5 @@
+features:
+- name: async-clipboard
+ files:
+ - async-*
+ - clipboard-item.https.html
diff --git a/testing/web-platform/tests/clipboard-apis/async-custom-formats-write-fail.tentative.https.html b/testing/web-platform/tests/clipboard-apis/async-custom-formats-write-fail.tentative.https.html
new file mode 100644
index 0000000000..8b1b42ec78
--- /dev/null
+++ b/testing/web-platform/tests/clipboard-apis/async-custom-formats-write-fail.tentative.https.html
@@ -0,0 +1,123 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>Async Clipboard web custom format write validation tests</title>
+<link rel="help" href="https://w3c.github.io/clipboard-apis/#async-clipboard-api">
+<body>Body needed for test_driver.click()</body>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="resources/user-activation.js"></script>
+<script>
+'use strict';
+
+promise_test(async t => {
+ await test_driver.set_permission({name: 'clipboard-write'}, 'granted');
+
+ const customFormatArray = [];
+ const customFormatMap = {};
+ for (let i = 0; i <= 100; i++) {
+ customFormatArray.push("web text/CustomFormat" + i);
+ const blobInput = new Blob(['input data'], {type: customFormatArray[i]});
+ customFormatMap[customFormatArray[i]] = blobInput;
+ }
+ const clipboardItemInput = new ClipboardItem(customFormatMap);
+ await waitForUserActivation();
+ await promise_rejects_dom(t, 'NotAllowedError',
+ navigator.clipboard.write([clipboardItemInput]));
+}, 'navigator.clipboard.write() fails for more than 100 custom formats');
+
+promise_test(async t => {
+ await test_driver.set_permission({name: 'clipboard-write'}, 'granted');
+
+ const format1 = 'application/x-custom-format-clipboard-test-format-1';
+ const format2 = 'application/x-custom-format-clipboard-test-format-2';
+ const blobInput1 = new Blob(['input data 1'], {type: format1});
+ const blobInput2 = new Blob(['input data 2'], {type: format2});
+ const clipboardItemInput = new ClipboardItem(
+ {[format1]: blobInput1, [format2]: blobInput2});
+ await waitForUserActivation();
+ await promise_rejects_dom(t, 'NotAllowedError',
+ navigator.clipboard.write([clipboardItemInput]));
+}, 'navigator.clipboard.write() fails for custom formats without web prefix');
+
+promise_test(async t => {
+ await test_driver.set_permission({name: 'clipboard-write'}, 'granted');
+
+ const format1 = 'web ';
+ const format2 = 'web a';
+ const blobInput1 = new Blob(['input data 1'], {type: format1});
+ const blobInput2 = new Blob(['input data 2'], {type: format2});
+ const clipboardItemInput = new ClipboardItem(
+ {[format1]: blobInput1, [format2]: blobInput2});
+ await waitForUserActivation();
+ await promise_rejects_dom(t, 'NotAllowedError',
+ navigator.clipboard.write([clipboardItemInput]));
+}, 'navigator.clipboard.write() fails for custom formats with web prefix, but invalid MIME types');
+
+promise_test(async t => {
+ await test_driver.set_permission({name: 'clipboard-write'}, 'granted');
+
+ const format1 = 'web text/plain';
+ const format2 = 'text/custom';
+ const blobInput1 = new Blob(['input data 1'], {type: format2});
+ const clipboardItemInput = new ClipboardItem(
+ {[format1]: blobInput1});
+ await waitForUserActivation();
+ await promise_rejects_dom(t, 'NotAllowedError',
+ navigator.clipboard.write([clipboardItemInput]));
+}, 'navigator.clipboard.write() fails for custom format with web prefix, but different Blob type');
+
+promise_test(async t => {
+ await test_driver.set_permission({name: 'clipboard-write'}, 'granted');
+
+ const format1 = 'web Text/plain';
+ const format2 = 'text/plain';
+ const blobInput1 = new Blob(['input data 1'], {type: format2});
+ const clipboardItemInput = new ClipboardItem(
+ {[format1]: blobInput1});
+ await waitForUserActivation();
+ await promise_rejects_dom(t, 'NotAllowedError',
+ navigator.clipboard.write([clipboardItemInput]));
+}, 'navigator.clipboard.write() fails for custom format with different case than the Blob type');
+
+promise_test(async t => {
+ await test_driver.set_permission({name: 'clipboard-write'}, 'granted');
+
+ const format1 = 'web text/plain';
+ const format2 = 'Text/plain';
+ const blobInput1 = new Blob(['input data 1'], {type: format1});
+ const clipboardItemInput = new ClipboardItem(
+ {[format2]: blobInput1});
+ await waitForUserActivation();
+ await promise_rejects_dom(t, 'NotAllowedError',
+ navigator.clipboard.write([clipboardItemInput]));
+}, 'navigator.clipboard.write() fails for invalid mime type that is different than the Blob type');
+
+promise_test(async t => {
+ await test_driver.set_permission({name: 'clipboard-write'}, 'granted');
+
+ const format1 = 'web Text/plain';
+ const format2 = 'web text/plain';
+ const blobInput1 = new Blob(['input data 1'], {type: format2});
+ const clipboardItemInput = new ClipboardItem(
+ {[format1]: blobInput1});
+ await waitForUserActivation();
+ await promise_rejects_dom(t, 'NotAllowedError',
+ navigator.clipboard.write([clipboardItemInput]));
+}, 'navigator.clipboard.write() fails for invalid mime type with web prefix and the Blob type');
+
+promise_test(async t => {
+ await test_driver.set_permission({name: 'clipboard-write'}, 'granted');
+
+ const format1 = 'Text/plain';
+ const format2 = 'text/plain';
+ const blobInput1 = new Blob(['input data 1'], {type: format2});
+ const clipboardItemInput = new ClipboardItem(
+ {[format1]: blobInput1});
+ await waitForUserActivation();
+ await promise_rejects_dom(t, 'NotAllowedError',
+ navigator.clipboard.write([clipboardItemInput]));
+}, 'navigator.clipboard.write() fails for custom format and Blob type with different case');
+
+</script>
diff --git a/testing/web-platform/tests/clipboard-apis/async-custom-formats-write-read-web-prefix.tentative.https.html b/testing/web-platform/tests/clipboard-apis/async-custom-formats-write-read-web-prefix.tentative.https.html
new file mode 100644
index 0000000000..9a6e5da6ff
--- /dev/null
+++ b/testing/web-platform/tests/clipboard-apis/async-custom-formats-write-read-web-prefix.tentative.https.html
@@ -0,0 +1,39 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>Async Clipboard web custom format read/write test.</title>
+<link rel="help" href="https://w3c.github.io/clipboard-apis/#async-clipboard-api">
+<body>Body needed for test_driver.click()</body>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="resources/user-activation.js"></script>
+<script>
+'use strict';
+
+promise_test(async t => {
+ await test_driver.set_permission({name: 'clipboard-read'}, 'granted');
+ await test_driver.set_permission({name: 'clipboard-write'}, 'granted');
+
+ const format1 = 'web text/plain';
+ const format2 = 'web text/plain';
+ const blobInput1 = new Blob(['input data 1'], {type: format2});
+ const clipboardItemInput = new ClipboardItem(
+ {[format1]: blobInput1});
+ await waitForUserActivation();
+ await navigator.clipboard.write([clipboardItemInput]);
+ await waitForUserActivation();
+ const clipboardItems = await navigator.clipboard.read();
+ assert_equals(clipboardItems.length, 1);
+ const clipboardItem = clipboardItems[0];
+ assert_true(clipboardItem instanceof ClipboardItem);
+ // This test can't verify clipboardItem.types, because its size and values
+ // are both platform-dependent.
+
+ const blobOutput1 = await clipboardItem.getType(format1);
+ assert_equals(blobOutput1.type, format1);
+ const data1 = await (new Response(blobOutput1)).text();
+ assert_equals(data1, 'input data 1');
+}, 'navigator.clipboard.write() for custom format and Blob type with web prefix');
+
+</script>
diff --git a/testing/web-platform/tests/clipboard-apis/async-custom-formats-write-read-without-web-prefix.tentative.https.html b/testing/web-platform/tests/clipboard-apis/async-custom-formats-write-read-without-web-prefix.tentative.https.html
new file mode 100644
index 0000000000..8b9d4de0a1
--- /dev/null
+++ b/testing/web-platform/tests/clipboard-apis/async-custom-formats-write-read-without-web-prefix.tentative.https.html
@@ -0,0 +1,39 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>Async Clipboard web custom format write using Blob without web prefix test.</title>
+<link rel="help" href="https://w3c.github.io/clipboard-apis/#async-clipboard-api">
+<body>Body needed for test_driver.click()</body>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="resources/user-activation.js"></script>
+<script>
+'use strict';
+
+promise_test(async t => {
+ await test_driver.set_permission({name: 'clipboard-read'}, 'granted');
+ await test_driver.set_permission({name: 'clipboard-write'}, 'granted');
+
+ const format1 = 'web text/plain';
+ const format2 = 'text/plain';
+ const blobInput1 = new Blob(['input data 1'], {type: format2});
+ const clipboardItemInput = new ClipboardItem(
+ {[format1]: blobInput1});
+ await waitForUserActivation();
+ await navigator.clipboard.write([clipboardItemInput]);
+ await waitForUserActivation();
+ const clipboardItems = await navigator.clipboard.read();
+ assert_equals(clipboardItems.length, 1);
+ const clipboardItem = clipboardItems[0];
+ assert_true(clipboardItem instanceof ClipboardItem);
+ // This test can't verify clipboardItem.types, because its size and values
+ // are both platform-dependent.
+
+ const blobOutput1 = await clipboardItem.getType(format1);
+ assert_equals(blobOutput1.type, format1);
+ const data1 = await (new Response(blobOutput1)).text();
+ assert_equals(data1, 'input data 1');
+}, 'navigator.clipboard.write() for custom format with web prefix, but Blob type without web prefix');
+
+</script>
diff --git a/testing/web-platform/tests/clipboard-apis/async-custom-formats-write-read.tentative.https.html b/testing/web-platform/tests/clipboard-apis/async-custom-formats-write-read.tentative.https.html
new file mode 100644
index 0000000000..a005f8c98f
--- /dev/null
+++ b/testing/web-platform/tests/clipboard-apis/async-custom-formats-write-read.tentative.https.html
@@ -0,0 +1,47 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>Async Clipboard custom write -> Async Clipboard custom read test</title>
+<link rel="help" href="https://w3c.github.io/clipboard-apis/#async-clipboard-api">
+<body>Body needed for test_driver.click()</body>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="resources/user-activation.js"></script>
+<script>
+'use strict';
+
+promise_test(async t => {
+ await test_driver.set_permission({name: 'clipboard-read'}, 'granted');
+ await test_driver.set_permission({name: 'clipboard-write'}, 'granted');
+ const format1 = 'web application/x-custom-format-clipboard-test-format-1';
+ const format2 = 'web application/x-custom-format-clipboard-test-format-2';
+ const blobInput1 = new Blob(['input data 1'], {type: format1});
+ const blobInput2 = new Blob(['input data 2'], {type: format2});
+ const clipboardItemInput = new ClipboardItem(
+ {[format1]: blobInput1, [format2]: blobInput2});
+ await waitForUserActivation();
+ await navigator.clipboard.write([clipboardItemInput]);
+
+ // Items should be readable on a custom format clipboard after custom format
+ // write.
+ await waitForUserActivation();
+ const clipboardItems = await navigator.clipboard.read();
+ assert_equals(clipboardItems.length, 1);
+ const clipboardItem = clipboardItems[0];
+ assert_true(clipboardItem instanceof ClipboardItem);
+ // This test can't verify clipboardItem.types, because its size and values
+ // are both platform-dependent.
+
+ const blobOutput1 = await clipboardItem.getType(format1);
+ assert_equals(blobOutput1.type, format1);
+ const data1 = await (new Response(blobOutput1)).text();
+ assert_equals(data1, 'input data 1');
+
+ const blobOutput2 = await clipboardItem.getType(format2);
+ assert_equals(blobOutput2.type, format2);
+ const data2 = await (new Response(blobOutput2)).text();
+ assert_equals(data2, 'input data 2');
+}, 'Verify write and read clipboard given 2 platform-neutral custom format inputs');
+
+</script>
diff --git a/testing/web-platform/tests/clipboard-apis/async-html-script-removal.https.html b/testing/web-platform/tests/clipboard-apis/async-html-script-removal.https.html
new file mode 100644
index 0000000000..44c11add85
--- /dev/null
+++ b/testing/web-platform/tests/clipboard-apis/async-html-script-removal.https.html
@@ -0,0 +1,60 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>
+ Async Clipboard write ([text/html ClipboardItem]) -> readHtml (and remove scripts) tests
+</title>
+<link rel="help" href="https://w3c.github.io/clipboard-apis/#async-clipboard-api">
+<body>Body needed for test_driver.click()</body>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="resources/user-activation.js"></script>
+<script>
+'use strict';
+// This function removes extra spaces between tags in html. For example, the
+// following html: "<p> Hello </p> <body> World </body>" would turn into this
+// html: "<p> Hello </p> <body> World </body>"
+// We remove the extra spaces because in html they are considered equivalent,
+// but when we are comparing for equality the spaces make a difference.
+function reformatHtml(html) {
+ const parser = new DOMParser();
+ const htmlString =
+ parser.parseFromString(html, 'text/html').documentElement.innerHTML;
+ const reformattedString = htmlString.replace(/\>\s*\</g, '> <');
+ return reformattedString;
+}
+
+// The string must be concatenated in this way because the html parser
+// will recognize a script tag even in quotes as a real script tag. By
+// splitting it up in this way we avoid that error.
+const html_with_script =
+ '<title>Title of the document</title> <script>const a = 5;</scr'
+ + 'ipt> <p>Hello World</p>';
+const html_without_script =
+ '<title>Title of the document</title> <p>Hello World</p>';
+promise_test(async t => {
+ await test_driver.set_permission({name: 'clipboard-read'}, 'granted');
+ await test_driver.set_permission({name: 'clipboard-write'}, 'granted');
+ const blobInput = new Blob([html_with_script], {type: 'text/html'});
+ const clipboardItem = new ClipboardItem({'text/html': blobInput});
+ await waitForUserActivation();
+ await navigator.clipboard.write([clipboardItem]);
+ await waitForUserActivation();
+ const clipboardItems = await navigator.clipboard.read();
+
+ const html = clipboardItems[0];
+ assert_equals(html.types.length, 1);
+ assert_equals(html.types[0], 'text/html');
+
+ const blobOutput = await html.getType('text/html');
+ assert_equals(blobOutput.type, 'text/html');
+
+ const blobText = await (new Response(blobOutput)).text();
+
+ const outputHtml = reformatHtml(blobText);
+ const inputHtml = reformatHtml(html_without_script);
+ assert_equals(outputHtml, inputHtml);
+}, 'Verify write and read clipboard with scripts removed given text/html: '
+ + html_with_script);
+</script>
diff --git a/testing/web-platform/tests/clipboard-apis/async-navigator-clipboard-basics.https.html b/testing/web-platform/tests/clipboard-apis/async-navigator-clipboard-basics.https.html
new file mode 100644
index 0000000000..4a11d5ac66
--- /dev/null
+++ b/testing/web-platform/tests/clipboard-apis/async-navigator-clipboard-basics.https.html
@@ -0,0 +1,154 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>Async Clipboard input type validation tests</title>
+<link rel="help" href="https://w3c.github.io/clipboard-apis/#async-clipboard-api">
+<body>Body needed for test_driver.click()</body>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="resources/user-activation.js"></script>
+<script>
+
+// Permissions are required in order to invoke navigator.clipboard functions in
+// an automated test.
+async function getPermissions() {
+ await test_driver.set_permission({name: 'clipboard-read'}, 'granted');
+ await test_driver.set_permission({name: 'clipboard-write'}, 'granted');
+ await waitForUserActivation();
+}
+
+test(() => {
+ assert_not_equals(navigator.clipboard, undefined);
+ assert_true(navigator.clipboard instanceof Clipboard);
+ assert_equals(navigator.clipboard, navigator.clipboard);
+}, 'navigator.clipboard exists');
+
+promise_test(async t => {
+ await getPermissions();
+ const text_plain = "This text was copied using `Clipboard.prototype.write`.";
+ const html_text = "<p style='color: red; font-style: oblique;'>Test</p>";
+ await promise_rejects_dom(t, "NotAllowedError", navigator.clipboard.write([
+ new ClipboardItem({
+ "text/plain": text_plain,
+ "text/html" : html_text
+ }),
+ ]));
+ }, 'navigator.clipboard.write(Promise<DOMString>) fails');
+
+promise_test(async () => {
+ await getPermissions();
+ const blob = new Blob(['hello'], {type: 'text/plain'});
+ const item = new ClipboardItem({'text/plain': blob});
+
+ await navigator.clipboard.write([item]);
+}, 'navigator.clipboard.write([text/plain ClipboardItem]) succeeds');
+
+promise_test(async t => {
+ await getPermissions();
+ const blob1 = new Blob(['hello'], {type: 'text/plain'});
+ const blob2 = new Blob(['world'], {type: 'text/plain'});
+
+ const item1 = new ClipboardItem({'text/plain': blob1});
+ const item2 = new ClipboardItem({'text/plain': blob2});
+
+ await promise_rejects_dom(t, "NotAllowedError",
+ navigator.clipboard.write([item1, item2]));
+}, 'navigator.clipboard.write([>1 ClipboardItems]) fails (not implemented)');
+
+promise_test(async t => {
+ await getPermissions();
+ await promise_rejects_js(t, TypeError, navigator.clipboard.write());
+}, 'navigator.clipboard.write() fails (expect [ClipboardItem])');
+
+promise_test(async t => {
+ await getPermissions();
+ await promise_rejects_js(t, TypeError, navigator.clipboard.write(null));
+}, 'navigator.clipboard.write(null) fails (expect [ClipboardItem])');
+
+promise_test(async t => {
+ await getPermissions();
+ await promise_rejects_js(t, TypeError,
+ navigator.clipboard.write('Bad string'));
+}, 'navigator.clipboard.write(DOMString) fails (expect [ClipboardItem])');
+
+promise_test(async t => {
+ await getPermissions();
+ const blob = new Blob(['hello'], {type: 'text/plain'});
+ await promise_rejects_js(t, TypeError, navigator.clipboard.write(blob));
+}, 'navigator.clipboard.write(Blob) fails (expect [ClipboardItem])');
+
+promise_test(async () => {
+ await getPermissions();
+ await navigator.clipboard.writeText('New clipboard text');
+}, 'navigator.clipboard.writeText(DOMString) succeeds');
+
+promise_test(async t => {
+ await getPermissions();
+ await promise_rejects_js(t, TypeError,
+ navigator.clipboard.writeText());
+}, 'navigator.clipboard.writeText() fails (expect DOMString)');
+
+promise_test(async () => {
+ await getPermissions();
+ const item = new ClipboardItem({'text/plain': 'test'});
+ await navigator.clipboard.write([item]);
+}, 'navigator.clipboard.write({string : DOMString}) succeeds');
+
+promise_test(async () => {
+ await getPermissions();
+ const fetched = await fetch('/clipboard-apis/resources/greenbox.png');
+ const image = await fetched.blob();
+ const item = new ClipboardItem({'image/png': image});
+
+ await navigator.clipboard.write([item]);
+}, 'navigator.clipboard.write({string : image/png Blob}) succeeds');
+
+promise_test(async() => {
+ await getPermissions();
+ const fetched = await fetch('/clipboard-apis/resources/greenbox.png');
+ const image = await fetched.blob();
+ const item = new ClipboardItem({
+ 'text/plain': new Blob(['first'], {type: 'text/plain'}),
+ 'image/png': image});
+
+ await navigator.clipboard.write([item]);
+}, 'navigator.clipboard.write([text + png] succeeds');
+
+promise_test(async t => {
+ await getPermissions();
+ const item = new ClipboardItem({'image/png': 'not an image'});
+ await promise_rejects_js(t, TypeError, navigator.clipboard.write([item]));
+}, 'navigator.clipboard.write(image/png DOMString) fails');
+
+promise_test(async () => {
+ await getPermissions();
+ const result = await navigator.clipboard.read();
+ assert_true(result instanceof Object);
+ assert_true(result[0] instanceof ClipboardItem);
+}, 'navigator.clipboard.read() succeeds');
+
+promise_test(async () => {
+ await getPermissions();
+ const result = await navigator.clipboard.readText();
+ assert_equals(typeof result, 'string');
+}, 'navigator.clipboard.readText() succeeds');
+
+promise_test(async () => {
+ await getPermissions();
+ const promise_blob = Promise.resolve(new Blob(['hello'], {type: 'text/plain'}));
+ const item = new ClipboardItem({'text/plain': promise_blob});
+
+ await navigator.clipboard.write([item]);
+}, 'navigator.clipboard.write(Promise<Blob>) succeeds');
+
+promise_test(async () => {
+ await getPermissions();
+ const promise_text_blob = Promise.resolve(new Blob(['hello'], {type: 'text/plain'}));
+ const promise_html_blob = Promise.resolve(new Blob(["<p style='color: red; font-style: oblique;'>Test</p>"], {type: 'text/html'}));
+ const item = new ClipboardItem({'text/plain': promise_text_blob, 'text/html': promise_html_blob});
+
+ await navigator.clipboard.write([item]);
+}, 'navigator.clipboard.write(Promise<Blob>s) succeeds');
+
+</script>
diff --git a/testing/web-platform/tests/clipboard-apis/async-navigator-clipboard-read-resource-load.https.html b/testing/web-platform/tests/clipboard-apis/async-navigator-clipboard-read-resource-load.https.html
new file mode 100644
index 0000000000..d1e3019e7f
--- /dev/null
+++ b/testing/web-platform/tests/clipboard-apis/async-navigator-clipboard-read-resource-load.https.html
@@ -0,0 +1,44 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>Async Clipboard.read() should not trigger resource loading</title>
+<link rel="help" href="https://w3c.github.io/clipboard-apis/#dom-clipboard-read">
+<link rel="help" href="https://bugs.chromium.org/p/chromium/issues/detail?id=1315563">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="resources/user-activation.js"></script>
+
+<body>Body needed for test_driver.click()
+<p><button id="button">Put payload in the clipboard</button></p>
+<div id="output"></div>
+
+<script>
+button.onclick = () => document.execCommand('copy');
+document.oncopy = ev => {
+ ev.preventDefault();
+ ev.clipboardData.setData(
+ 'text/html',
+ '<img src="https://example.com/oops">');
+};
+
+promise_test(async test => {
+ let loadObserved = false;
+ const observer = new PerformanceObserver(() => loadObserved = true);
+ observer.observe({type: 'resource'});
+ await test_driver.set_permission({name: 'clipboard-read'}, 'granted');
+ await test_driver.click(button);
+
+ await waitForUserActivation();
+ const items = await navigator.clipboard.read();
+ const htmlBlob = await items[0].getType("text/html");
+ const html = await htmlBlob.text();
+
+ assert_equals(html, '<img src="https://example.com/oops">');
+
+ // Allow resource loading to start asynchronously
+ await new Promise(resolve => test.step_timeout(resolve, 100));
+ assert_false(loadObserved, 'Should not observe resource loading');
+});
+</script>
+</body>
diff --git a/testing/web-platform/tests/clipboard-apis/async-navigator-clipboard-read-sanitize.https.html b/testing/web-platform/tests/clipboard-apis/async-navigator-clipboard-read-sanitize.https.html
new file mode 100644
index 0000000000..cc18367534
--- /dev/null
+++ b/testing/web-platform/tests/clipboard-apis/async-navigator-clipboard-read-sanitize.https.html
@@ -0,0 +1,48 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>Async Clipboard.read() should sanitize text/html</title>
+<link rel="help" href="https://w3c.github.io/clipboard-apis/#dom-clipboard-read">
+<link rel="help" href="https://bugs.chromium.org/p/chromium/issues/detail?id=1315563">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="resources/user-activation.js"></script>
+
+<body>Body needed for test_driver.click()
+<p><button id="button">Put payload in the clipboard</button></p>
+<div id="output"></div>
+
+<script>
+let testFailed = false;
+function fail() {
+ testFailed = true;
+}
+
+button.onclick = () => document.execCommand('copy');
+document.oncopy = ev => {
+ ev.preventDefault();
+ ev.clipboardData.setData(
+ 'text/html',
+ `<form><math><mtext></form><form><mglyph><xmp></math><img src=invalid onerror=fail()></xmp>`);
+};
+
+promise_test(async test => {
+ await test_driver.set_permission({name: 'clipboard-read'}, 'granted');
+ await test_driver.click(button);
+
+ await waitForUserActivation();
+ const items = await navigator.clipboard.read();
+ const htmlBlob = await items[0].getType("text/html");
+ const html = await htmlBlob.text();
+
+ // This inserts an image with `onerror` handler if `html` is not properly sanitized
+ output.innerHTML = html;
+
+ // Allow the 'error' event to be dispatched asynchronously
+ await new Promise(resolve => test.step_timeout(resolve, 100));
+
+ assert_false(testFailed);
+});
+</script>
+</body>
diff --git a/testing/web-platform/tests/clipboard-apis/async-navigator-clipboard-write-multiple.tentative.https.sub.html b/testing/web-platform/tests/clipboard-apis/async-navigator-clipboard-write-multiple.tentative.https.sub.html
new file mode 100644
index 0000000000..73cdd2f049
--- /dev/null
+++ b/testing/web-platform/tests/clipboard-apis/async-navigator-clipboard-write-multiple.tentative.https.sub.html
@@ -0,0 +1,106 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>Async Clipboard write should cancel the prior pending request</title>
+<link rel="help" href="https://github.com/w3c/clipboard-apis/issues/161">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="resources/user-activation.js"></script>
+
+<iframe width="50" height="50" id="same" src="resources/page.html"></iframe><br>
+<iframe width="50" height="50" id="cross" src="https://{{hosts[alt][]}}:{{ports[https][1]}}/clipboard-apis/resources/page.html"></iframe><br>
+<input value="Test">
+<script>
+"use strict";
+
+// Permissions are required in order to invoke navigator.clipboard functions in
+// an automated test.
+async function getPermissions() {
+ await test_driver.set_permission({name: "clipboard-read"}, "granted");
+ await test_driver.set_permission({name: "clipboard-write"}, "granted");
+ await waitForUserActivation();
+}
+
+function waitForMessage(msg) {
+ return new Promise((resolve) => {
+ window.addEventListener("message", function handler(e) {
+ if (e.data == msg) {
+ window.removeEventListener("message", handler);
+ resolve();
+ }
+ });
+ });
+}
+
+function generateRandomString() {
+ return "random number: " + Math.random();
+}
+
+function testCancelPendingWrite(funcToMakeNewRequest, msg) {
+ promise_test(async t => {
+ await getPermissions();
+
+ // Create a pending write request which should be rejected after a new
+ // request is made.
+ let resolvePendingPromise;
+ let promise = navigator.clipboard.write([
+ new ClipboardItem({
+ "text/plain": new Promise((resolve) => { resolvePendingPromise = resolve; }),
+ }),
+ ]).catch(e => { return e; });
+
+ // Make a new request that should cancel the prior pending request.
+ let str = generateRandomString();
+ await funcToMakeNewRequest(str);
+
+ // Pending request should be rejected with NotAllowedError.
+ let error = await promise;
+ assert_not_equals(error, undefined);
+ assert_equals(error.name, "NotAllowedError");
+
+ // Resolve pending promise.
+ resolvePendingPromise(generateRandomString());
+ // Check clipboard data.
+ assert_equals(await navigator.clipboard.readText(), str);
+ }, msg);
+}
+
+testCancelPendingWrite(async (str) => {
+ // A new write request should cancel the prior pending request.
+ await navigator.clipboard.write([
+ new ClipboardItem({
+ "text/plain": str,
+ }),
+ ]).catch(() => {
+ assert_true(false, "should not fail");
+ });
+}, "clipboard.write() should cancel the prior pending one (same document)");
+
+testCancelPendingWrite(async (str) => {
+ // A new write should cancel the prior pending request.
+ const iframe = document.getElementById("same");
+ iframe.contentWindow.postMessage(["write", str], "*");
+ await waitForMessage("done");
+}, "clipboard.write() should cancel the prior pending one (same-origin iframe)");
+
+testCancelPendingWrite(async (str) => {
+ // A new write should cancel the prior pending request.
+ const iframe = document.getElementById("cross");
+ iframe.contentWindow.postMessage(["write", str], "*");
+ await waitForMessage("done");
+}, "clipboard.write() should cancel the prior pending one (cross-origin iframe)");
+
+testCancelPendingWrite(async (str) => {
+ const input = document.querySelector("input");
+ input.value = str;
+ input.focus();
+ input.select();
+
+ // A new copy action should cancel the prior pending request.
+ const modifier_key = navigator.platform.includes("Mac") ? "\uE03D" : "\uE009";
+ await new test_driver.Actions().keyDown(modifier_key).keyDown("c").send();
+}, "copy action should cancel the prior pending clipboard.write() request");
+
+</script>
diff --git a/testing/web-platform/tests/clipboard-apis/async-promise-write-blobs-read-blobs.https.html b/testing/web-platform/tests/clipboard-apis/async-promise-write-blobs-read-blobs.https.html
new file mode 100644
index 0000000000..12184c92e0
--- /dev/null
+++ b/testing/web-platform/tests/clipboard-apis/async-promise-write-blobs-read-blobs.https.html
@@ -0,0 +1,46 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>
+ Async Clipboard write blobs -> read blobs with promise tests
+</title>
+<link rel="help" href="https://w3c.github.io/clipboard-apis/#async-clipboard-api">
+<body>Body needed for test_driver.click()</body>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="resources/user-activation.js"></script>
+
+<script>
+async function loadBlob(fileName) {
+ const fetched = await fetch(fileName);
+ return await fetched.blob();
+}
+
+promise_test(async t => {
+ const promise1 = new Promise((resolve, reject) => {
+ resolve(loadBlob('resources/greenbox.png'));
+ });
+ await test_driver.set_permission({name: 'clipboard-read'}, 'granted');
+ await test_driver.set_permission({name: 'clipboard-write'}, 'granted');
+
+ const blobText = new Blob(['test text'], {type: 'text/plain'});
+
+ const clipboardItemInput = new ClipboardItem(
+ {'text/plain' : blobText, 'image/png' : promise1});
+ await waitForUserActivation();
+ await navigator.clipboard.write([clipboardItemInput]);
+ await waitForUserActivation();
+ const clipboardItems = await navigator.clipboard.read();
+
+ assert_equals(clipboardItems.length, 1);
+ const clipboardItem = clipboardItems[0];
+ assert_true(clipboardItem instanceof ClipboardItem);
+
+ assert_equals(clipboardItem.types.length, 2);
+ const blobTextOutput = await clipboardItem.getType('text/plain');
+ const blobImageOutput = await clipboardItem.getType('image/png');
+ assert_equals(blobTextOutput.type, 'text/plain');
+ assert_equals(blobImageOutput.type, 'image/png');
+}, 'Verify write and read clipboard (multiple types) with promise to Blobs');
+</script>
diff --git a/testing/web-platform/tests/clipboard-apis/async-svg-script-removal.https.html b/testing/web-platform/tests/clipboard-apis/async-svg-script-removal.https.html
new file mode 100644
index 0000000000..292d100b2f
--- /dev/null
+++ b/testing/web-platform/tests/clipboard-apis/async-svg-script-removal.https.html
@@ -0,0 +1,61 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>
+ Async Clipboard write ([image/svg+xml ClipboardItem]) -> readSvg (and remove scripts) tests
+</title>
+<link rel="help" href="https://w3c.github.io/clipboard-apis/#async-clipboard-api">
+<body>Body needed for test_driver.click()</body>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="resources/user-activation.js"></script>
+<script>
+'use strict';
+// This function removes extra spaces between tags in svg. For example, the
+// following svg: "<svg> <g> </g> </svg>" would turn into this
+// svg: "<svg> <g> </g> </svg>"
+// We remove the extra spaces because in svg they are considered equivalent,
+// but when we are comparing for equality the spaces make a difference.
+function reformatSvg(svg) {
+ const parser = new DOMParser();
+ const svgString =
+ parser.parseFromString(svg, 'text/html').documentElement.innerHTML;
+ const reformattedString = svgString.replace(/\>\s*\</g, '> <');
+ return reformattedString;
+}
+
+// The string must be concatenated in this way because the html parser
+// will recognize a script tag even in quotes as a real script tag. By
+// splitting it up in this way we avoid that error.
+const svg_with_script =
+ `<svg> <script>const a = 5;</scr' + 'ipt>
+ <a href="javascript:alert(2)"> test </a> </svg>`;
+const svg_without_script =
+ `<svg> </svg>`;
+promise_test(async t => {
+ await test_driver.set_permission({name: 'clipboard-read'}, 'granted');
+ await test_driver.set_permission({name: 'clipboard-write'}, 'granted');
+ const blobInput = new Blob([svg_with_script], {type: 'image/svg+xml'});
+ const clipboardItem = new ClipboardItem({'image/svg+xml': blobInput});
+ await waitForUserActivation();
+ await navigator.clipboard.write([clipboardItem]);
+ await waitForUserActivation();
+ const clipboardItems =
+ await navigator.clipboard.read({type: 'image/svg+xml'});
+
+ const svg = clipboardItems[0];
+ assert_equals(svg.types.length, 1);
+ assert_equals(svg.types[0], 'image/svg+xml');
+
+ const blobOutput = await svg.getType('image/svg+xml');
+ assert_equals(blobOutput.type, 'image/svg+xml');
+
+ const blobText = await (new Response(blobOutput)).text();
+
+ const outputSvg = reformatSvg(blobText);
+ const inputSvg = reformatSvg(svg_without_script);
+ assert_equals(outputSvg, inputSvg);
+}, 'Verify write and read clipboard with scripts removed given image/svg+xml: '
+ + svg_with_script);
+</script>
diff --git a/testing/web-platform/tests/clipboard-apis/async-unsanitized-html-formats-write-read.tentative.https.html b/testing/web-platform/tests/clipboard-apis/async-unsanitized-html-formats-write-read.tentative.https.html
new file mode 100644
index 0000000000..46e335c804
--- /dev/null
+++ b/testing/web-platform/tests/clipboard-apis/async-unsanitized-html-formats-write-read.tentative.https.html
@@ -0,0 +1,74 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>Async Clipboard unsanitized HTML write -> Async Clipboard unsanitized HTML read test</title>
+<link rel="help" href="https://w3c.github.io/clipboard-apis/#async-clipboard-api">
+<body>Body needed for test_driver.click()</body>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="resources/user-activation.js"></script>
+<script>
+'use strict';
+
+// This function removes extra spaces between tags in html. For example, the
+// following html: "<p> Hello </p> <body> World </body>" would turn into this
+// html: "<p> Hello </p> <body> World </body>"
+// We remove the extra spaces because in html they are considered equivalent,
+// but when we are comparing for equality the spaces make a difference.
+function reformatHtml(html) {
+ const parser = new DOMParser();
+ const htmlString =
+ parser.parseFromString(html, 'text/html').documentElement.innerHTML;
+ const reformattedString = htmlString.replace(/\>\s*\</g, '> <');
+ return reformattedString;
+}
+
+// Writes a payload with custom content and checks to ensure the correct data
+// was written successfully.
+promise_test(async t => {
+ await test_driver.set_permission({name: 'clipboard-read'}, 'granted');
+ await test_driver.set_permission({name: 'clipboard-write'}, 'granted');
+
+ // Create and write unsanitized version of standard HTML and custom formats.
+ const format1 = 'text/html';
+ const format2 = 'web text/html';
+ const textInput = '<style>p {color:blue}</style><p>Hello World</p>';
+ const blobInput1 = new Blob([textInput], {type: format1});
+ const blobInput2 = new Blob([textInput], {type: format2});
+ const clipboardItemInput = new ClipboardItem(
+ {[format1]: blobInput1, [format2]: blobInput2});
+ await waitForUserActivation();
+ await navigator.clipboard.write([clipboardItemInput]);
+
+ // Read unsanitized version of HTML format.
+ await waitForUserActivation();
+ const clipboardItems = await navigator.clipboard.read();
+
+ assert_equals(clipboardItems.length, 1);
+ const clipboardItem = clipboardItems[0];
+ assert_true(clipboardItem instanceof ClipboardItem);
+
+ const blobOutput1 = await clipboardItem.getType(format1);
+ assert_equals(blobOutput1.type, format1);
+ const data1 = await (new Response(blobOutput1)).text();
+ const outputHtml = reformatHtml(data1);
+ const expectedHtml = '<p style="color: blue; font-size: medium; font-style: normal; ' +
+ 'font-variant-ligatures: normal; font-variant-caps: normal; ' +
+ 'font-weight: 400; letter-spacing: normal; orphans: 2; ' +
+ 'text-align: start; text-indent: 0px; text-transform: none; '+
+ 'widows: 2; word-spacing: 0px; ' +
+ '-webkit-text-stroke-width: 0px; ' +
+ 'white-space: normal; ' +
+ 'text-decoration-thickness: initial; ' +
+ 'text-decoration-style: initial; text-decoration-color: initial;">' +
+ 'Hello World</p>';
+ const inputHtml = reformatHtml(expectedHtml);
+ assert_equals(outputHtml, inputHtml);
+
+ const blobOutput2 = await clipboardItem.getType(format2);
+ assert_equals(blobOutput2.type, format2);
+ const data2 = await (new Response(blobOutput2)).text();
+ assert_equals(data2, textInput);
+}, 'Verify write and read unsanitized content to the clipboard given text/html format as input');
+</script>
diff --git a/testing/web-platform/tests/clipboard-apis/async-unsanitized-plaintext-formats-write-read.tentative.https.html b/testing/web-platform/tests/clipboard-apis/async-unsanitized-plaintext-formats-write-read.tentative.https.html
new file mode 100644
index 0000000000..1c5638ca0a
--- /dev/null
+++ b/testing/web-platform/tests/clipboard-apis/async-unsanitized-plaintext-formats-write-read.tentative.https.html
@@ -0,0 +1,52 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>Async Clipboard unsanitized write -> Async Clipboard unsanitized read test</title>
+<link rel="help" href="https://w3c.github.io/clipboard-apis/#async-clipboard-api">
+<body>Body needed for test_driver.click()</body>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="resources/user-activation.js"></script>
+<script>
+'use strict';
+
+// Writes a payload with custom content and checks to ensure the correct data
+// was written successfully.
+promise_test(async t => {
+ await test_driver.set_permission({name: 'clipboard-read'}, 'granted');
+ await test_driver.set_permission({name: 'clipboard-write'}, 'granted');
+
+ const dataToWrite = 'Test text.';
+ const format1 = 'web text/plain';
+ const format2 = 'text/plain';
+
+ const blobInput1 = new Blob([dataToWrite], {type: format1});
+ const blobInput2 = new Blob([dataToWrite], {type: format2});
+ // Blob types are automatically converted to lower-case.
+ assert_equals(blobInput1.type, format1.toLowerCase());
+ assert_equals(blobInput2.type, format2.toLowerCase());
+ const clipboardItemInput = new ClipboardItem(
+ {[format1]: blobInput1, [format2]: blobInput2});
+ await waitForUserActivation();
+ await navigator.clipboard.write([clipboardItemInput]);
+
+ // Items should be readable on a system clipboard after custom format write.
+ await waitForUserActivation();
+ const clipboardItems = await navigator.clipboard.read();
+ assert_equals(clipboardItems.length, 1);
+ const clipboardItem = clipboardItems[0];
+ assert_true(clipboardItem instanceof ClipboardItem);
+
+ const blobOutput1 = await clipboardItem.getType(format1);
+ assert_equals(blobOutput1.type, format1);
+ const data1 = await (new Response(blobOutput1)).text();
+ assert_equals(data1, dataToWrite);
+
+ // These examples use native text formats, so these formats should be
+ // accessible as text.
+ await waitForUserActivation();
+ const textOutput = await navigator.clipboard.readText();
+ assert_equals(textOutput, dataToWrite);
+}, 'Verify write and read unsanitized content to the clipboard given standard and custom formats as input');
+</script>
diff --git a/testing/web-platform/tests/clipboard-apis/async-unsanitized-standard-html-read-fail.tentative.https.html b/testing/web-platform/tests/clipboard-apis/async-unsanitized-standard-html-read-fail.tentative.https.html
new file mode 100644
index 0000000000..e7ddbb0cdf
--- /dev/null
+++ b/testing/web-platform/tests/clipboard-apis/async-unsanitized-standard-html-read-fail.tentative.https.html
@@ -0,0 +1,46 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>Async Clipboard unsanitized HTML read validation tests</title>
+<link rel="help" href="https://w3c.github.io/clipboard-apis/#async-clipboard-api">
+<body>Body needed for test_driver.click()</body>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="resources/user-activation.js"></script>
+<script>
+'use strict';
+
+promise_test(async t => {
+ await test_driver.set_permission({name: 'clipboard-read'}, 'granted');
+
+ await waitForUserActivation();
+ await promise_rejects_dom(t, 'NotAllowedError',
+ navigator.clipboard.read({unsanitized: ['text/html', 'text/plain']}));
+}, 'navigator.clipboard.read() fails for multiple unsanitized formats requested.');
+
+promise_test(async t => {
+ await test_driver.set_permission({name: 'clipboard-read'}, 'granted');
+
+ await waitForUserActivation();
+ await promise_rejects_dom(t, 'NotAllowedError',
+ navigator.clipboard.read({unsanitized: ['text/plain']}));
+}, 'navigator.clipboard.read() fails for unsanitized text/plain requested.');
+
+promise_test(async t => {
+ await test_driver.set_permission({name: 'clipboard-read'}, 'granted');
+
+ await waitForUserActivation();
+ await promise_rejects_dom(t, 'NotAllowedError',
+ navigator.clipboard.read({unsanitized: ['image/png']}));
+}, 'navigator.clipboard.read() fails for unsanitized image/png requested.');
+
+promise_test(async t => {
+ await test_driver.set_permission({name: 'clipboard-read'}, 'granted');
+
+ await waitForUserActivation();
+ await promise_rejects_dom(t, 'NotAllowedError',
+ navigator.clipboard.read({unsanitized: ['image/svg+xml']}));
+}, 'navigator.clipboard.read() fails for unsanitized image/svg+xml requested.');
+
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/clipboard-apis/async-write-blobs-read-blobs.https.html b/testing/web-platform/tests/clipboard-apis/async-write-blobs-read-blobs.https.html
new file mode 100644
index 0000000000..8bec558b2b
--- /dev/null
+++ b/testing/web-platform/tests/clipboard-apis/async-write-blobs-read-blobs.https.html
@@ -0,0 +1,48 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>
+ Async Clipboard write blobs -> read blobs tests
+</title>
+<link rel="help" href="https://w3c.github.io/clipboard-apis/#async-clipboard-api">
+<body>Body needed for test_driver.click()</body>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="resources/user-activation.js"></script>
+
+<script>
+async function loadBlob(fileName) {
+ const fetched = await fetch(fileName);
+ return await fetched.blob();
+}
+
+promise_test(async t => {
+ await test_driver.set_permission({name: 'clipboard-read'}, 'granted');
+ await test_driver.set_permission({name: 'clipboard-write'}, 'granted');
+
+ const blobText = new Blob(['test text'], {type: 'text/plain'});
+ const blobImage = await loadBlob('resources/greenbox.png');
+
+ assert_equals(blobText.type, 'text/plain');
+ assert_equals(blobImage.type, 'image/png');
+
+ const clipboardItemInput = new ClipboardItem(
+ {'text/plain' : blobText, 'image/png' : blobImage});
+
+ await waitForUserActivation();
+ await navigator.clipboard.write([clipboardItemInput]);
+ await waitForUserActivation();
+ const clipboardItems = await navigator.clipboard.read();
+
+ assert_equals(clipboardItems.length, 1);
+ const clipboardItem = clipboardItems[0];
+ assert_true(clipboardItem instanceof ClipboardItem);
+
+ assert_equals(clipboardItem.types.length, 2);
+ const blobTextOutput = await clipboardItem.getType('text/plain');
+ const blobImageOutput = await clipboardItem.getType('image/png');
+ assert_equals(blobTextOutput.type, 'text/plain');
+ assert_equals(blobImageOutput.type, 'image/png');
+}, 'Verify write and read clipboard (multiple types)');
+</script>
diff --git a/testing/web-platform/tests/clipboard-apis/async-write-html-read-html.https.html b/testing/web-platform/tests/clipboard-apis/async-write-html-read-html.https.html
new file mode 100644
index 0000000000..ec1817c027
--- /dev/null
+++ b/testing/web-platform/tests/clipboard-apis/async-write-html-read-html.https.html
@@ -0,0 +1,62 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>
+ Async Clipboard write ([text/html ClipboardItem]) -> readHtml tests
+</title>
+<link rel="help" href="https://w3c.github.io/clipboard-apis/#async-clipboard-api">
+<body>Body needed for test_driver.click()</body>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="resources/user-activation.js"></script>
+<script>
+'use strict';
+// This function removes extra spaces between tags in html. For example, the
+// following html: "<p> Hello </p> <body> World </body>" would turn into this
+// html: "<p> Hello </p> <body> World </body>"
+// We remove the extra spaces because in html they are considered equivalent,
+// but when we are comparing for equality the spaces make a difference.
+function reformatHtml(html) {
+ const parser = new DOMParser();
+ const htmlString =
+ parser.parseFromString(html, 'text/html').documentElement.innerHTML;
+ const reformattedString = htmlString.replace(/\>\s*\</g, '> <');
+ return reformattedString;
+}
+
+async function readWriteTest(textInput) {
+ await test_driver.set_permission({name: 'clipboard-read'}, 'granted');
+ await test_driver.set_permission({name: 'clipboard-write'}, 'granted');
+ const blobInput = new Blob([textInput], {type: 'text/html'});
+ const clipboardItem = new ClipboardItem({'text/html': blobInput});
+ await waitForUserActivation();
+ await navigator.clipboard.write([clipboardItem]);
+ await waitForUserActivation();
+ const clipboardItems = await navigator.clipboard.read({type: 'text/html'});
+
+ const html = clipboardItems[0];
+ assert_equals(html.types.length, 1);
+ assert_equals(html.types[0], 'text/html');
+
+ const blobOutput = await html.getType('text/html');
+ assert_equals(blobOutput.type, 'text/html');
+
+ const blobText = await (new Response(blobOutput)).text();
+
+ const outputHtml = reformatHtml(blobText);
+ const inputHtml = reformatHtml(textInput);
+ assert_equals(outputHtml, inputHtml);
+}
+const testCases = [`<!doctype html> <html> <head> <title>Title of the
+ document</title> </head> <body> <p>Hello World</p>
+ </body> </html>`,
+ '<title>Title of the document</title> <p>Hello World</p>'];
+
+promise_test(async t => {
+ for (const testCase of testCases) {
+ await readWriteTest(testCase);
+ }
+}, 'Verify read and write of some text/html content');
+
+</script>
diff --git a/testing/web-platform/tests/clipboard-apis/async-write-image-read-image.https.html b/testing/web-platform/tests/clipboard-apis/async-write-image-read-image.https.html
new file mode 100644
index 0000000000..e10b69d824
--- /dev/null
+++ b/testing/web-platform/tests/clipboard-apis/async-write-image-read-image.https.html
@@ -0,0 +1,85 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>
+ Async Clipboard write [image/png ClipboardItem] ->
+ read [image/png ClipboardItem] tests
+</title>
+<link rel="help" href="https://w3c.github.io/clipboard-apis/#async-clipboard-api">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="resources/user-activation.js"></script>
+<body>Body needed for test_driver.click()
+<p>
+ <p>The bottom image should display the same image as the top image.</p>
+ <p>Original Image:</p>
+ <image id="image-to-copy" width="20" height="20"
+ src="resources/greenbox.png"></image>
+ <p>Image after copy/paste:</p>
+ <image id="image-on-clipboard"></image>
+ <canvas id="canvas" width="20" height="20"></canvas>
+</p>
+
+<script>
+// Must compare a bitmap as opposed to simply blob data, because an encoded
+// image may have different contents depending on encoder.
+async function getBitmapString(blob) {
+ const imageBitmap = await createImageBitmap(blob);
+ const canvas = document.getElementById('canvas');
+ const ctx = canvas.getContext('2d');
+
+ ctx.drawImage(imageBitmap, 0,0);
+
+ let imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
+ return imageData.data.toString();
+};
+
+async function loadBlob(fileName) {
+ const fetched = await fetch(fileName);
+ return await fetched.blob();
+}
+
+promise_test(async t => {
+ await test_driver.set_permission({name: 'clipboard-read'}, 'granted');
+ await test_driver.set_permission({name: 'clipboard-write'}, 'granted');
+
+ const blobInput = await loadBlob('resources/greenbox.png');
+
+ assert_equals(blobInput.type, 'image/png');
+ const clipboardItemInput = new ClipboardItem({'image/png' : blobInput});
+ await waitForUserActivation();
+ await navigator.clipboard.write([clipboardItemInput]);
+ await waitForUserActivation();
+ const clipboardItems = await navigator.clipboard.read();
+
+ assert_equals(clipboardItems.length, 1);
+ const clipboardItem = clipboardItems[0];
+ assert_true(clipboardItem instanceof ClipboardItem);
+ assert_equals(clipboardItem.types.length, 1);
+ const blobOutput = await clipboardItem.getType('image/png');
+ assert_equals(blobOutput.type, 'image/png');
+
+ document.getElementById('image-on-clipboard').src =
+ window.URL.createObjectURL(blobOutput);
+
+ const comparableInput = await getBitmapString(blobInput);
+ const comparableOutput = await getBitmapString(blobOutput);
+
+ assert_equals(comparableOutput, comparableInput);
+}, 'Verify write and read clipboard [image/png Blob]');
+
+promise_test(async t => {
+ await test_driver.set_permission({name: 'clipboard-read'}, 'granted');
+ await test_driver.set_permission({name: 'clipboard-write'}, 'granted');
+
+ const invalidPngBlob = new Blob(['this text is not a valid png image'],
+ {type: 'image/png'});
+ const clipboardItemInput = new ClipboardItem({'image/png' : invalidPngBlob});
+ await waitForUserActivation();
+ await promise_rejects_dom(t, 'DataError',
+ navigator.clipboard.write([clipboardItemInput]));
+}, 'Verify write error on malformed data [image/png ClipboardItem]');
+</script>
+</body>
diff --git a/testing/web-platform/tests/clipboard-apis/async-write-svg-read-svg.https.html b/testing/web-platform/tests/clipboard-apis/async-write-svg-read-svg.https.html
new file mode 100644
index 0000000000..42f6c547b2
--- /dev/null
+++ b/testing/web-platform/tests/clipboard-apis/async-write-svg-read-svg.https.html
@@ -0,0 +1,60 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>
+ Async Clipboard write ([image/svg+xml ClipboardItem]) -> read and write svg tests
+</title>
+<link rel="help" href="https://w3c.github.io/clipboard-apis/#async-clipboard-api">
+<body>Body needed for test_driver.click()</body>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="resources/user-activation.js"></script>
+<script>
+'use strict';
+// This function removes extra spaces between tags in svg. For example, the
+// following html: "<svg> <g> </g> </svg>" would turn into this
+// html: "<svg> <g> </g> </svg>"
+// We remove the extra spaces because in svg they are considered equivalent,
+// but when we are comparing for equality the spaces make a difference.
+function reformatSvg(svg) {
+ const parser = new DOMParser();
+ const svgString =
+ parser.parseFromString(svg, 'text/html').documentElement.innerHTML;
+ const reformattedString = svgString.replace(/\>\s*\</g, '> <');
+ return reformattedString;
+}
+
+async function readWriteTest(textInput) {
+ await test_driver.set_permission({name: 'clipboard-read'}, 'granted');
+ await test_driver.set_permission({name: 'clipboard-write'}, 'granted');
+ const blobInput = new Blob([textInput], {type: 'image/svg+xml'});
+ const clipboardItem = new ClipboardItem({'image/svg+xml': blobInput});
+ await waitForUserActivation();
+ await navigator.clipboard.write([clipboardItem]);
+ await waitForUserActivation();
+ const clipboardItems =
+ await navigator.clipboard.read({type: 'image/svg+xml'});
+
+ const svg = clipboardItems[0];
+ assert_equals(svg.types.length, 1);
+ assert_equals(svg.types[0], 'image/svg+xml');
+
+ const blobOutput = await svg.getType('image/svg+xml');
+ assert_equals(blobOutput.type, 'image/svg+xml');
+
+ const blobText = await (new Response(blobOutput)).text();
+ const outputSvg = reformatSvg(blobText);
+ const inputSvg = reformatSvg(textInput);
+ assert_equals(outputSvg, inputSvg);
+}
+const testCases = ['<svg></svg>',
+ '<svg> <circle cx="50" cy="50" r="40" /> </svg>'];
+
+promise_test(async t => {
+ for (const testCase of testCases) {
+ await readWriteTest(testCase);
+ }
+}, 'Verify read and write of some image/svg+xml content');
+
+</script>
diff --git a/testing/web-platform/tests/clipboard-apis/clipboard-events-synthetic.html b/testing/web-platform/tests/clipboard-apis/clipboard-events-synthetic.html
new file mode 100644
index 0000000000..8786829752
--- /dev/null
+++ b/testing/web-platform/tests/clipboard-apis/clipboard-events-synthetic.html
@@ -0,0 +1,32 @@
+<!doctype html>
+<title>synthetic clipboard events should not be composed</title>
+<link rel="help" href="https://w3c.github.io/clipboard-apis/#clipboard-event-copy">
+<link rel="help" href="https://w3c.github.io/clipboard-apis/#clipboard-event-cut">
+<link rel="help" href="https://w3c.github.io/clipboard-apis/#clipboard-event-paste">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id=log></div>
+<script>
+const EVENTS = [ 'copy', 'cut', 'paste' ];
+
+function testEvent(eventName, init, composed_flag_expectation, testName) {
+ async_test(test => {
+ document.addEventListener(eventName, test.step_func_done(e => {
+ assert_false(e.isTrusted, `synthetic ${eventName} event is untrusted`);
+ assert_equals(e.composed, composed_flag_expectation,
+ `composed flag should be ${composed_flag_expectation}`);
+ }));
+ const event = new ClipboardEvent(eventName, init);
+ document.dispatchEvent(event);
+ }, testName);
+}
+
+EVENTS.forEach(name => {
+ testEvent(name, { bubbles: true, cancellable: true }, false,
+ `Unspecified synthetic ${name} event should not be composed.`);
+ testEvent(name, { bubbles: true, cancelable: true, composed: true }, true,
+ `Synthetic ${name} event can be explicitly composed.`);
+ testEvent(name, { bubbles: true, cancelable: true, composed: false }, false,
+ `Synthetic ${name} event can be explicitly uncomposed.`);
+});
+</script>
diff --git a/testing/web-platform/tests/clipboard-apis/clipboard-file-manual.html b/testing/web-platform/tests/clipboard-apis/clipboard-file-manual.html
new file mode 100644
index 0000000000..e934f2fd0d
--- /dev/null
+++ b/testing/web-platform/tests/clipboard-apis/clipboard-file-manual.html
@@ -0,0 +1,87 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>Clipboard: DataTransfer File manual test</title>
+<link rel="help" href="https://w3c.github.io/clipboard-apis/#to-fire-a-clipboard-event">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<style>
+#pastewrapper {
+ display: block;
+ width: 400px;
+ height: 200px;
+ position: relative;
+ padding: 50px 0 0 100px;
+}
+#pastezone {
+ display: block;
+ border: 1px solid black;
+ width: 200px;
+ height: 100px;
+}
+</style>
+<p>
+ Please download <a download href="resources/copied-file.txt">this file</a>,
+ and copy and paste it into the box below.
+</p>
+
+<div id="pastewrapper">
+ <div id="pastezone">
+ Paste Here
+ </div>
+</div>
+
+<script>
+'use strict';
+
+const pasteWrapper = document.querySelector('#pastewrapper');
+const pasteZone = document.querySelector('#pastezone');
+
+const pastePromise = new Promise((resolve, reject) => {
+ pasteZone.onpaste = event => {
+ event.preventDefault();
+
+ // Copy the information out of the DataTransfer instance before it is
+ // neutered when the event handler exits.
+ const dataTransfer = event.clipboardData;
+ const items = Array.from(dataTransfer.items).map(item => {
+ return {kind: item.kind, type: item.type, file: item.getAsFile() };
+ });
+ resolve({ types: dataTransfer.types, files: dataTransfer.files, items });
+ };
+});
+
+promise_test(async () => {
+ const dataTransfer = await pastePromise;
+ assert_true(dataTransfer.types.includes('Files'));
+}, 'DataTransfer.types in paste file');
+
+promise_test(async () => {
+ const dataTransfer = await pastePromise;
+ assert_equals(
+ dataTransfer.files.length, 1,
+ 'DataTransfer.files should have one element');
+ const file = dataTransfer.files[0];
+ assert_true(
+ file instanceof File,
+ 'DataTransfer.files[0] should be a File instance');
+ assert_equals(file.name, 'copied-file.txt');
+ assert_equals(file.type, 'text/plain');
+ assert_equals(file.size, 21);
+ assert_equals(await file.text(), 'copied-file-contents\n');
+}, 'DataTransfer.files in paste');
+
+promise_test(async () => {
+ const dataTransfer = await pastePromise;
+ const items = dataTransfer.items.filter(i => i.kind === 'file');
+ assert_equals(items.length, 1,
+ 'DataTransfer.items[kind="file"] should have 1 element');
+ const item = items[0];
+ assert_true(
+ item.file instanceof File,
+ 'DataTransfer.items[0] should be a File instance');
+ assert_equals(item.file.name, 'copied-file.txt');
+ assert_equals(item.file.type, 'text/plain');
+ assert_equals(item.file.size, 21);
+ assert_equals(await item.file.text(), 'copied-file-contents\n');
+}, 'DataTransfer.items in paste');
+</script>
diff --git a/testing/web-platform/tests/clipboard-apis/clipboard-item.https.html b/testing/web-platform/tests/clipboard-apis/clipboard-item.https.html
new file mode 100644
index 0000000000..7e148703a2
--- /dev/null
+++ b/testing/web-platform/tests/clipboard-apis/clipboard-item.https.html
@@ -0,0 +1,116 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>ClipboardItem tests</title>
+<link rel="help" href="https://w3c.github.io/clipboard-apis/#async-clipboard-api">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+
+const blob = new Blob(['hello'], {type: 'text/plain'});
+const blob2 = new Blob(['this should work'], {type: 'not a/real type'});
+
+test(() => {
+ new ClipboardItem({'text/plain': blob});
+ new ClipboardItem({'text/plain': blob, 'not a/real type': blob2});
+}, "ClipboardItem({string, Blob}) succeeds with different types");
+
+test(() => {
+ new ClipboardItem({'text/plain': blob}, {});
+}, "ClipboardItem() succeeds with empty options");
+
+test(() => {
+ assert_throws_js(TypeError, () => {new ClipboardItem({});});
+}, "ClipboardItem({}) fails with empty dictionary input");
+
+test(() => {
+ assert_throws_js(TypeError, () => {new ClipboardItem(blob);});
+}, "ClipboardItem(Blob) fails");
+
+test(() => {
+ assert_throws_js(TypeError, () => {new ClipboardItem(null);});
+}, "ClipboardItem() fails with null input");
+
+test(() => {
+ assert_throws_js(TypeError, () => {new ClipboardItem();});
+}, "ClipboardItem() fails with no input");
+
+test(() => {
+ const item = new ClipboardItem({'text/plain': blob});
+ const types = item.types;
+ assert_equals(types.length, 1);
+ assert_equals(types[0], 'text/plain');
+ const item2 =
+ new ClipboardItem({'text/plain': blob, 'not a/real type': blob2});
+ const types2 = item2.types;
+ assert_equals(types2.length, 2);
+ assert_equals(types2[0], 'text/plain');
+ assert_equals(types2[1], 'not a/real type');
+}, "types() returns correct values");
+
+promise_test(async () => {
+ const item =
+ new ClipboardItem({'text/plain': blob, 'not a/real type': blob2});
+
+ const blobOutput = await item.getType('text/plain');
+ assert_true(blobOutput.type.includes('text/plain'));
+ const text = await (new Response(blobOutput)).text();
+
+ assert_equals('hello', text);
+}, "getType(DOMString valid type) succeeds with correct output");
+
+promise_test(async () => {
+ const item =
+ new ClipboardItem({'text/plain': blob, 'not a/real type': blob2});
+
+ const blobOutput = await item.getType('not a/real type');
+ assert_true(blobOutput.type.includes('not a/real type'));
+ const text = await (new Response(blobOutput)).text();
+
+ assert_equals('this should work', text);
+}, "getType(DOMString invalid type) succeeds with correct output");
+
+promise_test(async t => {
+ const item =
+ new ClipboardItem({'text/plain': blob, 'not a/real type': blob2});
+ promise_rejects_dom(t, "NotFoundError", item.getType('type not in item'));
+ promise_rejects_dom(t, "NotFoundError", item.getType('text/plain:subtype'));
+}, "getType(DOMString type) rejects correctly when querying for missing type");
+
+promise_test(async () => {
+ const item =
+ new ClipboardItem({'text/plain': 'abc', 'not a/real type': 'xxx'});
+ const blob = await item.getType('text/plain');
+ assert_equals(blob.type, 'text/plain');
+
+ const text = await (new Response(blob)).text();
+ assert_equals(text, 'abc');
+}, "getType(DOMString valid type) converts DOMString to Blob");
+
+promise_test(async () => {
+ const item =
+ new ClipboardItem({'text/plain': 'abc', 'not a/real type': 'xxx'});
+ const blob = await item.getType('not a/real type');
+ assert_equals(blob.type, 'not a/real type');
+
+ const text = await (new Response(blob)).text();
+ assert_equals(text, 'xxx');
+}, "getType(DOMString invalid type) converts DOMString to Blob");
+
+promise_test(async () => {
+ assert_true(ClipboardItem.supports('text/plain'));
+ assert_true(ClipboardItem.supports('text/html'));
+ assert_true(ClipboardItem.supports('image/png'));
+ assert_true(ClipboardItem.supports('image/svg+xml'));
+ assert_false(ClipboardItem.supports('web '));
+ assert_false(ClipboardItem.supports('web')); // without space.
+ assert_false(ClipboardItem.supports('web foo'));
+ assert_false(ClipboardItem.supports('foo/bar'));
+ assert_true(ClipboardItem.supports('web foo/bar'));
+ assert_true(ClipboardItem.supports('web text/html'));
+ assert_false(ClipboardItem.supports('weB text/html'));
+ assert_false(ClipboardItem.supports(' web text/html'));
+ assert_false(ClipboardItem.supports('not a/real type'));
+ assert_false(ClipboardItem.supports(''));
+ assert_false(ClipboardItem.supports(' '));
+}, "supports(DOMString) returns true for types that are supported, false otherwise");
+</script>
diff --git a/testing/web-platform/tests/clipboard-apis/dataTransfer-clearData.html b/testing/web-platform/tests/clipboard-apis/dataTransfer-clearData.html
new file mode 100644
index 0000000000..6db7fa44e2
--- /dev/null
+++ b/testing/web-platform/tests/clipboard-apis/dataTransfer-clearData.html
@@ -0,0 +1,59 @@
+<!doctype html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<p id="description"></p>
+<div id="console"></div>
+<script>
+const dataTransfer = new DataTransfer();
+const filename = "1.png";
+const filename2 = "2.png";
+
+test(() => {
+ const file = new File(["🕺💃"], filename, {
+ type: "image/png"
+ });
+
+ const file2 = new File(["🕺💃"], filename2, {
+ type: "image/png"
+ });
+
+ dataTransfer.items.add(file);
+ dataTransfer.items.add(file2);
+ assert_equals(dataTransfer.items.length, 2);
+ assert_equals(dataTransfer.types.length, 1);
+
+ dataTransfer.setData("text/plain", "hi");
+ assert_equals(dataTransfer.items.length, 3);
+ assert_equals(dataTransfer.types.length, 2);
+
+ dataTransfer.items.add("hi 2", "text/html");
+ dataTransfer.items.add("new octet item", "application/octet-stream");
+
+ assert_equals(dataTransfer.items.length, 5);
+ assert_equals(dataTransfer.types.length, 4);
+
+ dataTransfer.clearData("text/html");
+ assert_equals(dataTransfer.items.length, 4);
+ assert_equals(dataTransfer.types.length, 3);
+
+ dataTransfer.clearData();
+ assert_equals(dataTransfer.items.length, 2);
+ assert_equals(dataTransfer.files.length, 2);
+ assert_equals(dataTransfer.types.length, 1);
+ assert_equals(dataTransfer.types[0], "Files");
+ assert_equals(dataTransfer.files[0].name, filename);
+ assert_equals(dataTransfer.files[1].name, filename2);
+
+ dataTransfer.items.add("hi", "text/plain");
+ assert_equals(dataTransfer.items.length, 3);
+ assert_equals(dataTransfer.files.length, 2);
+ assert_equals(dataTransfer.types.length, 2);
+
+ dataTransfer.items.clear();
+ assert_equals(dataTransfer.items.length, 0);
+ assert_equals(dataTransfer.files.length, 0);
+ assert_equals(dataTransfer.types.length, 0);
+}, 'Clear data from dataTransfer');
+</script>
+</body>
+</html> \ No newline at end of file
diff --git a/testing/web-platform/tests/clipboard-apis/detached-iframe/WEB_FEATURES.yml b/testing/web-platform/tests/clipboard-apis/detached-iframe/WEB_FEATURES.yml
new file mode 100644
index 0000000000..92abfee358
--- /dev/null
+++ b/testing/web-platform/tests/clipboard-apis/detached-iframe/WEB_FEATURES.yml
@@ -0,0 +1,3 @@
+features:
+- name: async-clipboard
+ files: "**"
diff --git a/testing/web-platform/tests/clipboard-apis/detached-iframe/clipboard-on-detached-iframe.https.html b/testing/web-platform/tests/clipboard-apis/detached-iframe/clipboard-on-detached-iframe.https.html
new file mode 100644
index 0000000000..5eb58e3213
--- /dev/null
+++ b/testing/web-platform/tests/clipboard-apis/detached-iframe/clipboard-on-detached-iframe.https.html
@@ -0,0 +1,26 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>Clipboard API on detached iframe</title>
+<link rel='help' href='https://w3c.github.io/clipboard-apis/#async-clipboard-api'>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<iframe id="iframe"></iframe>
+<script>
+'use strict';
+
+promise_test(async () => {
+ const iframe = document.getElementById('iframe');
+ const iframeNavigator = iframe.contentWindow.navigator;
+ assert_not_equals(navigator.clipboard, null,
+ "parent frame's clipboard should exist with iframe attached");
+ assert_not_equals(iframeNavigator.clipboard, null,
+ "attached child iframe's clipboard should exist");
+
+ iframe.parentNode.removeChild(iframe);
+ assert_not_equals(navigator.clipboard, null,
+ "parent frame's clipboard should exist with iframe detached");
+ assert_not_equals(iframeNavigator.clipboard, null,
+ "detached child iframe's clipboard should still exist");
+}, 'Verify navigator.clipboard behavior in detached frames');
+</script>
diff --git a/testing/web-platform/tests/clipboard-apis/detached-iframe/read-on-detaching-iframe.https.html b/testing/web-platform/tests/clipboard-apis/detached-iframe/read-on-detaching-iframe.https.html
new file mode 100644
index 0000000000..8e8e015aa0
--- /dev/null
+++ b/testing/web-platform/tests/clipboard-apis/detached-iframe/read-on-detaching-iframe.https.html
@@ -0,0 +1,34 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>navigator.clipboard read on detaching iframe</title>
+<link rel='help' href='https://w3c.github.io/clipboard-apis/#async-clipboard-api'>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<iframe id="iframe"></iframe>
+<script>
+'use strict';
+
+promise_test(async t => {
+ // This tests proper behavior on a detaching iframe. text/plain is chosen for
+ // simplicity, and the test should fail the same way no matter what the input
+ // type is.
+ await test_driver.set_permission({name: 'clipboard-read'}, 'granted');
+ await test_driver.set_permission({name: 'clipboard-write'}, 'granted');
+
+ const iframe = document.getElementById('iframe');
+ const iframeClipboard = iframe.contentWindow.navigator.clipboard;
+ const blobInput = new Blob(['test string'], {type: 'text/plain'});
+ const clipboardItemInput = new ClipboardItem({'text/plain': blobInput});
+ // Clipboard API must only be available in focused documents.
+ // reference: https://www.w3.org/TR/clipboard-apis/#privacy-async
+ iframe.focus();
+
+ // An iframe detaching while writing to the clipboard should fail, but not
+ // crash. The lack of await here means that the iframe will detach while the
+ // write operation is running.
+ iframeClipboard.read([clipboardItemInput]);
+ iframe.parentNode.removeChild(iframe);
+}, 'Verify read fails on detaching iframe');
+</script>
diff --git a/testing/web-platform/tests/clipboard-apis/detached-iframe/write-on-detaching-iframe.https.html b/testing/web-platform/tests/clipboard-apis/detached-iframe/write-on-detaching-iframe.https.html
new file mode 100644
index 0000000000..c6913d9e9f
--- /dev/null
+++ b/testing/web-platform/tests/clipboard-apis/detached-iframe/write-on-detaching-iframe.https.html
@@ -0,0 +1,34 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>navigator.clipboard write on detaching iframe</title>
+<link rel='help' href='https://w3c.github.io/clipboard-apis/#async-clipboard-api'>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<iframe id="iframe"></iframe>
+<script>
+'use strict';
+
+promise_test(async t => {
+ // This tests proper behavior on a detaching iframe. text/plain is chosen for
+ // simplicity, and the test should fail the same way no matter what the input
+ // type is.
+ await test_driver.set_permission({name: 'clipboard-read'}, 'granted');
+ await test_driver.set_permission({name: 'clipboard-write'}, 'granted');
+
+ const iframe = document.getElementById('iframe');
+ const iframeClipboard = iframe.contentWindow.navigator.clipboard;
+ const blobInput = new Blob(['test string'], {type: 'text/plain'});
+ const clipboardItemInput = new ClipboardItem({'text/plain': blobInput});
+ // Clipboard API must only be available in focused documents.
+ // reference: https://www.w3.org/TR/clipboard-apis/#privacy-async
+ iframe.focus();
+
+ // An iframe detaching while writing to the clipboard should fail, but not
+ // crash. The lack of await here means that the iframe will detach while the
+ // write operation is running.
+ iframeClipboard.write([clipboardItemInput]);
+ iframe.parentNode.removeChild(iframe);
+}, 'Verify write fails on detaching iframe');
+</script>
diff --git a/testing/web-platform/tests/clipboard-apis/detached-iframe/write-read-on-detached-iframe.https.html b/testing/web-platform/tests/clipboard-apis/detached-iframe/write-read-on-detached-iframe.https.html
new file mode 100644
index 0000000000..b21e6b20bc
--- /dev/null
+++ b/testing/web-platform/tests/clipboard-apis/detached-iframe/write-read-on-detached-iframe.https.html
@@ -0,0 +1,44 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>navigator.clipboard read and write on detached iframe</title>
+<link rel='help' href='https://w3c.github.io/clipboard-apis/#async-clipboard-api'>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="../resources/user-activation.js"></script>
+<iframe id="iframe"></iframe>
+<script>
+'use strict';
+
+promise_test(async t => {
+ // This tests proper behavior on a detaching iframe. text/plain is chosen for
+ // simplicity, and the test should fail the same way no matter what the input
+ // type is.
+ await test_driver.set_permission({name: 'clipboard-read'}, 'granted');
+ await test_driver.set_permission({name: 'clipboard-write'}, 'granted');
+
+ const iframe = document.getElementById('iframe');
+ const iframeClipboard = iframe.contentWindow.navigator.clipboard;
+ const blobInput = new Blob(['test string'], {type: 'text/plain'});
+ const clipboardItemInput = new ClipboardItem({'text/plain': blobInput});
+ await waitForUserActivation();
+ // Clipboard API must only be available in focused documents.
+ // reference: https://www.w3.org/TR/clipboard-apis/#privacy-async
+ iframe.focus();
+
+ // Writing and reading should succeed on same-origin iframes.
+ await iframeClipboard.write([clipboardItemInput]);
+ const readResultAttached = await iframeClipboard.read();
+ assert_not_equals(readResultAttached, undefined);
+ assert_equals(readResultAttached.length, 1,
+ 'attached iframes should be able to read and write normally');
+
+ iframe.parentNode.removeChild(iframe);
+ // Writing onto a detached iframe's clipboard should fail, but not crash.
+ await iframeClipboard.write([clipboardItemInput]);
+ const readResultDetached = await iframeClipboard.read();
+ assert_equals(readResultDetached, undefined,
+ 'reading from detached iframes should output undefined');
+}, 'Verify read and write fail on detached iframe');
+</script>
diff --git a/testing/web-platform/tests/clipboard-apis/detached-iframe/writeText-readText-on-detached-iframe.https.html b/testing/web-platform/tests/clipboard-apis/detached-iframe/writeText-readText-on-detached-iframe.https.html
new file mode 100644
index 0000000000..24fa586fc7
--- /dev/null
+++ b/testing/web-platform/tests/clipboard-apis/detached-iframe/writeText-readText-on-detached-iframe.https.html
@@ -0,0 +1,40 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>navigator.clipboard readText and writeText on detached iframe</title>
+<link rel='help' href='https://w3c.github.io/clipboard-apis/#async-clipboard-api'>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="../resources/user-activation.js"></script>
+<iframe id="iframe"></iframe>
+<script>
+'use strict';
+
+promise_test(async t => {
+ await test_driver.set_permission({name: 'clipboard-read'}, 'granted');
+ await test_driver.set_permission({name: 'clipboard-write'}, 'granted');
+
+ const iframe = document.getElementById('iframe');
+ await waitForUserActivation();
+ // Clipboard API must only be available in focused documents.
+ // reference: https://www.w3.org/TR/clipboard-apis/#privacy-async
+ iframe.focus();
+ const iframeClipboard = iframe.contentWindow.navigator.clipboard;
+
+ // Writing and reading should succeed on same-origin iframes.
+ const attachedWriteText = 'attached write text'
+ await iframeClipboard.writeText(attachedWriteText);
+ const attachedWriteResult = await iframeClipboard.readText();
+ assert_equals(attachedWriteResult, attachedWriteText,
+ 'attached iframes should be able to readText and writeText normally');
+
+ iframe.parentNode.removeChild(iframe);
+ // Writing onto a detached iframe's clipboard should fail, but not crash.
+ const detachedWriteText = 'detached write text';
+ await iframeClipboard.writeText(detachedWriteText);
+ const readResultDetached = await iframeClipboard.readText();
+ assert_equals(readResultDetached, undefined,
+ 'reading from detached iframes should output undefined');
+}, 'Verify readText and writeText fails on detached iframe');
+</script>
diff --git a/testing/web-platform/tests/clipboard-apis/events/copy-event.html b/testing/web-platform/tests/clipboard-apis/events/copy-event.html
new file mode 100644
index 0000000000..c8c0593a98
--- /dev/null
+++ b/testing/web-platform/tests/clipboard-apis/events/copy-event.html
@@ -0,0 +1,33 @@
+<!doctype html>
+<title>The copy event</title>
+<link rel="help" href="https://w3c.github.io/clipboard-apis/#clipboard-event-copy">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-actions.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<div id=log></div>
+<button id="copy">Trigger copy</button>
+<input id="copyTarget" value="this text should be copied">
+<script>
+async_test(t => {
+ let button = document.getElementById("copy");
+
+ button.addEventListener("click", function(e) {
+ let input = document.getElementById("copyTarget");
+ input.focus();
+ input.select();
+ document.execCommand("copy");
+ });
+
+ document.oncopy = t.step_func_done(event => {
+ // Nothing can be asserted about the event target until
+ // https://github.com/w3c/clipboard-apis/issues/70 is resolved.
+ // assert_equals(event.target, document.body, 'event.target');
+ assert_true(event.isTrusted, 'event.isTrusted');
+ assert_true(event.composed, 'event.composed');
+ });
+
+ test_driver.click(button);
+});
+</script>
diff --git a/testing/web-platform/tests/clipboard-apis/events/cut-event-manual.html b/testing/web-platform/tests/clipboard-apis/events/cut-event-manual.html
new file mode 100644
index 0000000000..72c11ec3b9
--- /dev/null
+++ b/testing/web-platform/tests/clipboard-apis/events/cut-event-manual.html
@@ -0,0 +1,19 @@
+<!doctype html>
+<title>The cut event</title>
+<link rel="help" href="https://w3c.github.io/clipboard-apis/#clipboard-event-cut">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id=log></div>
+<input value="Select and cut any part of this text to continue" size="100">
+<script>
+setup({explicit_timeout: true});
+async_test(t => {
+ document.oncut = t.step_func_done(event => {
+ // Nothing can be asserted about the event target until
+ // https://github.com/w3c/clipboard-apis/issues/70 is resolved.
+ // assert_equals(event.target, document.body, 'event.target');
+ assert_true(event.isTrusted, 'event.isTrusted');
+ assert_true(event.composed, 'event.composed');
+ });
+});
+</script>
diff --git a/testing/web-platform/tests/clipboard-apis/events/paste-event-manual.html b/testing/web-platform/tests/clipboard-apis/events/paste-event-manual.html
new file mode 100644
index 0000000000..608a0d6f23
--- /dev/null
+++ b/testing/web-platform/tests/clipboard-apis/events/paste-event-manual.html
@@ -0,0 +1,21 @@
+<!doctype html>
+<title>The paste event</title>
+<link rel="help" href="https://w3c.github.io/clipboard-apis/#clipboard-event-paste">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id=log></div>
+<input placeholder="Paste any text here to continue" size="100">
+<p>Some pre-selected text to copy for convenience</p>
+<script>
+setup({explicit_timeout: true});
+async_test(t => {
+ getSelection().selectAllChildren(document.querySelector('p'));
+ document.onpaste = t.step_func_done(event => {
+ // Nothing can be asserted about the event target until
+ // https://github.com/w3c/clipboard-apis/issues/70 is resolved.
+ // assert_equals(event.target, document.body, 'event.target');
+ assert_true(event.isTrusted, 'event.isTrusted');
+ assert_true(event.composed, 'event.composed');
+ });
+});
+</script>
diff --git a/testing/web-platform/tests/clipboard-apis/feature-policy/clipboard-read/clipboard-read-disabled-by-feature-policy.tentative.https.sub.html b/testing/web-platform/tests/clipboard-apis/feature-policy/clipboard-read/clipboard-read-disabled-by-feature-policy.tentative.https.sub.html
new file mode 100644
index 0000000000..7af2b8944e
--- /dev/null
+++ b/testing/web-platform/tests/clipboard-apis/feature-policy/clipboard-read/clipboard-read-disabled-by-feature-policy.tentative.https.sub.html
@@ -0,0 +1,40 @@
+<!doctype html>
+<body>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="/feature-policy/resources/featurepolicy.js"></script>
+<script>
+'use strict';
+
+const same_origin_src =
+ '/feature-policy/resources/feature-policy-clipboard-read.html';
+const cross_origin_src =
+ 'https://{{domains[www]}}:{{ports[https][0]}}' + same_origin_src;
+
+promise_test(async t => {
+ await test_driver.set_permission({ name: 'clipboard-read' }, 'granted');
+ return promise_rejects_dom(t, 'NotAllowedError',
+ navigator.clipboard.readText('test text'));
+}, 'Feature-Policy header clipboard-read "none" disallows the top-level document.');
+
+async_test(t => {
+ test_feature_availability(
+ 'navigator.clipboard.readText()',
+ t,
+ same_origin_src,
+ expect_feature_unavailable_default
+ );
+}, 'Feature-Policy header clipboard-read "none" disallows same-origin iframes.');
+
+async_test(t => {
+ test_feature_availability(
+ 'navigator.clipboard.readText()',
+ t,
+ cross_origin_src,
+ expect_feature_unavailable_default
+ );
+}, 'Feature-Policy header clipboard-read "none" disallows cross-origin iframes.');
+</script>
+</body>
diff --git a/testing/web-platform/tests/clipboard-apis/feature-policy/clipboard-read/clipboard-read-disabled-by-feature-policy.tentative.https.sub.html.headers b/testing/web-platform/tests/clipboard-apis/feature-policy/clipboard-read/clipboard-read-disabled-by-feature-policy.tentative.https.sub.html.headers
new file mode 100644
index 0000000000..ee9a2b6fb6
--- /dev/null
+++ b/testing/web-platform/tests/clipboard-apis/feature-policy/clipboard-read/clipboard-read-disabled-by-feature-policy.tentative.https.sub.html.headers
@@ -0,0 +1 @@
+Feature-Policy: clipboard-read 'none'
diff --git a/testing/web-platform/tests/clipboard-apis/feature-policy/clipboard-read/clipboard-read-enabled-by-feature-policy-attribute-cross-origin-tentative.https.sub.html b/testing/web-platform/tests/clipboard-apis/feature-policy/clipboard-read/clipboard-read-enabled-by-feature-policy-attribute-cross-origin-tentative.https.sub.html
new file mode 100644
index 0000000000..367d033d0f
--- /dev/null
+++ b/testing/web-platform/tests/clipboard-apis/feature-policy/clipboard-read/clipboard-read-enabled-by-feature-policy-attribute-cross-origin-tentative.https.sub.html
@@ -0,0 +1,31 @@
+<!doctype html>
+<body>Body needed for test_driver.click()</body>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="/feature-policy/resources/featurepolicy.js"></script>
+<script src="../../resources/user-activation.js"></script>
+<script>
+'use strict';
+
+const same_origin_src =
+ '/feature-policy/resources/feature-policy-clipboard-read.html';
+const cross_origin_src =
+ 'https://{{domains[www]}}:{{ports[https][0]}}' + same_origin_src;
+
+// TODO(https://github.com/whatwg/html/issues/5493, https://crbug.com/1074482):
+// In Chrome and Firefox, Cross-origin focus requires user gesture. In Chrome
+// only, cross-origin focus is asynchronous. Implement WPT support for
+// cross-origin focus.
+promise_test(async t => {
+ await waitForUserActivation();
+ test_feature_availability(
+ 'navigator.clipboard.readText()',
+ t,
+ cross_origin_src,
+ expect_feature_available_default,
+ 'clipboard-read'
+ );
+}, 'Feature policy "clipboard-read" can be enabled in cross-origin iframe using allow="clipboard-read" attribute');
+</script>
diff --git a/testing/web-platform/tests/clipboard-apis/feature-policy/clipboard-read/clipboard-read-enabled-by-feature-policy-attribute-tentative.https.sub.html b/testing/web-platform/tests/clipboard-apis/feature-policy/clipboard-read/clipboard-read-enabled-by-feature-policy-attribute-tentative.https.sub.html
new file mode 100644
index 0000000000..e812854b4c
--- /dev/null
+++ b/testing/web-platform/tests/clipboard-apis/feature-policy/clipboard-read/clipboard-read-enabled-by-feature-policy-attribute-tentative.https.sub.html
@@ -0,0 +1,25 @@
+<!doctype html>
+<body>Body needed for test_driver.click()</body>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="/feature-policy/resources/featurepolicy.js"></script>
+<script src="../../resources/user-activation.js"></script>
+<script>
+'use strict';
+
+const same_origin_src =
+ '/feature-policy/resources/feature-policy-clipboard-read.html';
+
+promise_test(async t => {
+ await waitForUserActivation();
+ test_feature_availability(
+ 'navigator.clipboard.readText()',
+ t,
+ same_origin_src,
+ expect_feature_available_default,
+ 'clipboard-read'
+ );
+}, 'Feature policy "clipboard-read" can be enabled in same-origin iframe using allow="clipboard-read" attribute');
+</script>
diff --git a/testing/web-platform/tests/clipboard-apis/feature-policy/clipboard-read/clipboard-read-enabled-by-feature-policy-cross-origin-tentative.https.sub.html b/testing/web-platform/tests/clipboard-apis/feature-policy/clipboard-read/clipboard-read-enabled-by-feature-policy-cross-origin-tentative.https.sub.html
new file mode 100644
index 0000000000..c371ea3b41
--- /dev/null
+++ b/testing/web-platform/tests/clipboard-apis/feature-policy/clipboard-read/clipboard-read-enabled-by-feature-policy-cross-origin-tentative.https.sub.html
@@ -0,0 +1,30 @@
+<!doctype html>
+<body>Body needed for test_driver.click()</body>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="/feature-policy/resources/featurepolicy.js"></script>
+<script src="../../resources/user-activation.js"></script>
+<script>
+'use strict';
+
+const same_origin_src =
+ '/feature-policy/resources/feature-policy-clipboard-read.html';
+const cross_origin_src =
+ 'https://{{domains[www]}}:{{ports[https][0]}}' + same_origin_src;
+
+// TODO(https://github.com/whatwg/html/issues/5493, https://crbug.com/1074482):
+// In Chrome and Firefox, Cross-origin focus requires user gesture. In Chrome
+// only, cross-origin focus is asynchronous. Implement WPT support for
+// cross-origin focus.
+promise_test(async t => {
+ await waitForUserActivation();
+ test_feature_availability(
+ 'navigator.clipboard.readText()',
+ t,
+ cross_origin_src,
+ expect_feature_available_default
+ );
+}, 'Feature-Policy header clipboard-read "*" allows cross-origin iframes.');
+</script>
diff --git a/testing/web-platform/tests/clipboard-apis/feature-policy/clipboard-read/clipboard-read-enabled-by-feature-policy-cross-origin.tentative.https.sub.html.headers b/testing/web-platform/tests/clipboard-apis/feature-policy/clipboard-read/clipboard-read-enabled-by-feature-policy-cross-origin.tentative.https.sub.html.headers
new file mode 100644
index 0000000000..a147e2a64f
--- /dev/null
+++ b/testing/web-platform/tests/clipboard-apis/feature-policy/clipboard-read/clipboard-read-enabled-by-feature-policy-cross-origin.tentative.https.sub.html.headers
@@ -0,0 +1 @@
+Feature-Policy: clipboard-read *
diff --git a/testing/web-platform/tests/clipboard-apis/feature-policy/clipboard-read/clipboard-read-enabled-by-feature-policy.tentative.https.sub.html b/testing/web-platform/tests/clipboard-apis/feature-policy/clipboard-read/clipboard-read-enabled-by-feature-policy.tentative.https.sub.html
new file mode 100644
index 0000000000..552183cc67
--- /dev/null
+++ b/testing/web-platform/tests/clipboard-apis/feature-policy/clipboard-read/clipboard-read-enabled-by-feature-policy.tentative.https.sub.html
@@ -0,0 +1,32 @@
+<!doctype html>
+<body>Body needed for test_driver.click()</body>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="/feature-policy/resources/featurepolicy.js"></script>
+<script src="../../resources/user-activation.js"></script>
+<script>
+'use strict';
+
+const same_origin_src =
+ '/feature-policy/resources/feature-policy-clipboard-read.html';
+const cross_origin_src =
+ 'https://{{domains[www]}}:{{ports[https][0]}}' + same_origin_src;
+
+promise_test(async t => {
+ await test_driver.set_permission({ name: 'clipboard-read' }, 'granted');
+ await waitForUserActivation();
+ await navigator.clipboard.readText('test text');
+}, 'Feature-Policy header clipboard-read "*" allows the top-level document.');
+
+promise_test(async t => {
+ await waitForUserActivation();
+ test_feature_availability(
+ 'navigator.clipboard.readText()',
+ t,
+ same_origin_src,
+ expect_feature_available_default
+ );
+}, 'Feature-Policy header clipboard-read "*" allows same-origin iframes.');
+</script>
diff --git a/testing/web-platform/tests/clipboard-apis/feature-policy/clipboard-read/clipboard-read-enabled-by-feature-policy.tentative.https.sub.html.headers b/testing/web-platform/tests/clipboard-apis/feature-policy/clipboard-read/clipboard-read-enabled-by-feature-policy.tentative.https.sub.html.headers
new file mode 100644
index 0000000000..a147e2a64f
--- /dev/null
+++ b/testing/web-platform/tests/clipboard-apis/feature-policy/clipboard-read/clipboard-read-enabled-by-feature-policy.tentative.https.sub.html.headers
@@ -0,0 +1 @@
+Feature-Policy: clipboard-read *
diff --git a/testing/web-platform/tests/clipboard-apis/feature-policy/clipboard-read/clipboard-read-enabled-on-self-origin-by-feature-policy.tentative.https.sub.html b/testing/web-platform/tests/clipboard-apis/feature-policy/clipboard-read/clipboard-read-enabled-on-self-origin-by-feature-policy.tentative.https.sub.html
new file mode 100644
index 0000000000..17dc3628a7
--- /dev/null
+++ b/testing/web-platform/tests/clipboard-apis/feature-policy/clipboard-read/clipboard-read-enabled-on-self-origin-by-feature-policy.tentative.https.sub.html
@@ -0,0 +1,45 @@
+<!doctype html>
+<body>Body needed for test_driver.click()</body>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="/feature-policy/resources/featurepolicy.js"></script>
+<script src="../../resources/user-activation.js"></script>
+<script>
+'use strict';
+
+const same_origin_src =
+ '/feature-policy/resources/feature-policy-clipboard-read.html';
+const cross_origin_src =
+ 'https://{{domains[www]}}:{{ports[https][0]}}' + same_origin_src;
+
+promise_test(async t => {
+ await test_driver.set_permission({ name: 'clipboard-read' }, 'granted');
+ await waitForUserActivation();
+ await navigator.clipboard.readText('test text');
+}, 'Feature-Policy header clipboard-read "self" allows the top-level document.');
+
+promise_test(async t => {
+ await waitForUserActivation();
+ test_feature_availability(
+ 'navigator.clipboard.readText()',
+ t,
+ same_origin_src,
+ expect_feature_available_default
+ );
+}, 'Feature-Policy header clipboard-read "self" allows same-origin iframes.');
+
+// TODO(https://github.com/whatwg/html/issues/5493, https://crbug.com/1074482):
+// In Chrome and Firefox, Cross-origin focus requires user gesture. In Chrome
+// only, cross-origin focus is asynchronous. Implement WPT support for
+// cross-origin focus.
+promise_test(async t => {
+ test_feature_availability(
+ 'navigator.clipboard.readText()',
+ t,
+ cross_origin_src,
+ expect_feature_unavailable_default
+ );
+}, 'Feature-Policy header clipboard-read "self" disallows cross-origin iframes.');
+</script>
diff --git a/testing/web-platform/tests/clipboard-apis/feature-policy/clipboard-read/clipboard-read-enabled-on-self-origin-by-feature-policy.tentative.https.sub.html.headers b/testing/web-platform/tests/clipboard-apis/feature-policy/clipboard-read/clipboard-read-enabled-on-self-origin-by-feature-policy.tentative.https.sub.html.headers
new file mode 100644
index 0000000000..752d7faff4
--- /dev/null
+++ b/testing/web-platform/tests/clipboard-apis/feature-policy/clipboard-read/clipboard-read-enabled-on-self-origin-by-feature-policy.tentative.https.sub.html.headers
@@ -0,0 +1 @@
+Feature-Policy: clipboard-read 'self'
diff --git a/testing/web-platform/tests/clipboard-apis/feature-policy/clipboard-write/clipboard-write-disabled-by-feature-policy.tentative.https.sub.html b/testing/web-platform/tests/clipboard-apis/feature-policy/clipboard-write/clipboard-write-disabled-by-feature-policy.tentative.https.sub.html
new file mode 100644
index 0000000000..5d19d8dd6f
--- /dev/null
+++ b/testing/web-platform/tests/clipboard-apis/feature-policy/clipboard-write/clipboard-write-disabled-by-feature-policy.tentative.https.sub.html
@@ -0,0 +1,40 @@
+<!doctype html>
+<body>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="/feature-policy/resources/featurepolicy.js"></script>
+<script>
+'use strict';
+
+const same_origin_src =
+ '/feature-policy/resources/feature-policy-clipboard-write.html';
+const cross_origin_src =
+ 'https://{{domains[www]}}:{{ports[https][0]}}' + same_origin_src;
+
+promise_test(async t => {
+ await test_driver.set_permission({ name: 'clipboard-write' }, 'granted');
+ return promise_rejects_dom(t, 'NotAllowedError',
+ navigator.clipboard.writeText('test text'));
+}, 'Feature-Policy header clipboard-write "none" disallows the top-level document.');
+
+async_test(t => {
+ test_feature_availability(
+ 'navigator.clipboard.writeText("test text")',
+ t,
+ same_origin_src,
+ expect_feature_unavailable_default
+ );
+}, 'Feature-Policy header clipboard-write "none" disallows same-origin iframes.');
+
+async_test(t => {
+ test_feature_availability(
+ 'navigator.clipboard.writeText("test text")',
+ t,
+ cross_origin_src,
+ expect_feature_unavailable_default
+ );
+}, 'Feature-Policy header clipboard-write "none" disallows cross-origin iframes.');
+</script>
+</body>
diff --git a/testing/web-platform/tests/clipboard-apis/feature-policy/clipboard-write/clipboard-write-disabled-by-feature-policy.tentative.https.sub.html.headers b/testing/web-platform/tests/clipboard-apis/feature-policy/clipboard-write/clipboard-write-disabled-by-feature-policy.tentative.https.sub.html.headers
new file mode 100644
index 0000000000..f35f5b6a09
--- /dev/null
+++ b/testing/web-platform/tests/clipboard-apis/feature-policy/clipboard-write/clipboard-write-disabled-by-feature-policy.tentative.https.sub.html.headers
@@ -0,0 +1 @@
+Feature-Policy: clipboard-write 'none'
diff --git a/testing/web-platform/tests/clipboard-apis/feature-policy/clipboard-write/clipboard-write-enabled-by-feature-policy-attribute-cross-origin-tentative.https.sub.html b/testing/web-platform/tests/clipboard-apis/feature-policy/clipboard-write/clipboard-write-enabled-by-feature-policy-attribute-cross-origin-tentative.https.sub.html
new file mode 100644
index 0000000000..e669c8fec4
--- /dev/null
+++ b/testing/web-platform/tests/clipboard-apis/feature-policy/clipboard-write/clipboard-write-enabled-by-feature-policy-attribute-cross-origin-tentative.https.sub.html
@@ -0,0 +1,31 @@
+<!doctype html>
+<body>Body needed for test_driver.click()</body>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="/feature-policy/resources/featurepolicy.js"></script>
+<script src="../../resources/user-activation.js"></script>
+<script>
+'use strict';
+
+const same_origin_src =
+ '/feature-policy/resources/feature-policy-clipboard-write.html';
+const cross_origin_src =
+ 'https://{{domains[www]}}:{{ports[https][0]}}' + same_origin_src;
+
+// TODO(https://github.com/whatwg/html/issues/5493, https://crbug.com/1074482):
+// In Chrome and Firefox, Cross-origin focus requires user gesture. In Chrome
+// only, cross-origin focus is asynchronous. Implement WPT support for
+// cross-origin focus.
+promise_test(async t => {
+ await waitForUserActivation();
+ test_feature_availability(
+ 'navigator.clipboard.writeText("test text")',
+ t,
+ cross_origin_src,
+ expect_feature_available_default,
+ 'clipboard-write'
+ );
+}, 'Feature policy "clipboard-write" can be enabled in cross-origin iframe using allow="clipboard-write" attribute');
+</script>
diff --git a/testing/web-platform/tests/clipboard-apis/feature-policy/clipboard-write/clipboard-write-enabled-by-feature-policy-attribute-tentative.https.sub.html b/testing/web-platform/tests/clipboard-apis/feature-policy/clipboard-write/clipboard-write-enabled-by-feature-policy-attribute-tentative.https.sub.html
new file mode 100644
index 0000000000..b57dfe3dd2
--- /dev/null
+++ b/testing/web-platform/tests/clipboard-apis/feature-policy/clipboard-write/clipboard-write-enabled-by-feature-policy-attribute-tentative.https.sub.html
@@ -0,0 +1,25 @@
+<!doctype html>
+<body>Body needed for test_driver.click()</body>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="/feature-policy/resources/featurepolicy.js"></script>
+<script src="../../resources/user-activation.js"></script>
+<script>
+'use strict';
+
+const same_origin_src =
+ '/feature-policy/resources/feature-policy-clipboard-write.html';
+
+promise_test(async t => {
+ await waitForUserActivation();
+ test_feature_availability(
+ 'navigator.clipboard.writeText("test text")',
+ t,
+ same_origin_src,
+ expect_feature_available_default,
+ 'clipboard-write'
+ );
+}, 'Feature policy "clipboard-write" can be enabled in same-origin iframe using allow="clipboard-write" attribute');
+</script>
diff --git a/testing/web-platform/tests/clipboard-apis/feature-policy/clipboard-write/clipboard-write-enabled-by-feature-policy-cross-origin-tentative.https.sub.html b/testing/web-platform/tests/clipboard-apis/feature-policy/clipboard-write/clipboard-write-enabled-by-feature-policy-cross-origin-tentative.https.sub.html
new file mode 100644
index 0000000000..6e7029cc78
--- /dev/null
+++ b/testing/web-platform/tests/clipboard-apis/feature-policy/clipboard-write/clipboard-write-enabled-by-feature-policy-cross-origin-tentative.https.sub.html
@@ -0,0 +1,30 @@
+<!doctype html>
+<body>Body needed for test_driver.click()</body>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="/feature-policy/resources/featurepolicy.js"></script>
+<script src="../../resources/user-activation.js"></script>
+<script>
+'use strict';
+
+const same_origin_src =
+ '/feature-policy/resources/feature-policy-clipboard-write.html';
+const cross_origin_src =
+ 'https://{{domains[www]}}:{{ports[https][0]}}' + same_origin_src;
+
+// TODO(https://github.com/whatwg/html/issues/5493, https://crbug.com/1074482):
+// In Chrome and Firefox, Cross-origin focus requires user gesture. In Chrome
+// only, cross-origin focus is asynchronous. Implement WPT support for
+// cross-origin focus.
+promise_test(async t => {
+ await waitForUserActivation();
+ test_feature_availability(
+ 'navigator.clipboard.writeText("test text")',
+ t,
+ cross_origin_src,
+ expect_feature_available_default
+ );
+}, 'Feature-Policy header clipboard-write "*" allows cross-origin iframes.');
+</script>
diff --git a/testing/web-platform/tests/clipboard-apis/feature-policy/clipboard-write/clipboard-write-enabled-by-feature-policy-cross-origin.tentative.https.sub.html.headers b/testing/web-platform/tests/clipboard-apis/feature-policy/clipboard-write/clipboard-write-enabled-by-feature-policy-cross-origin.tentative.https.sub.html.headers
new file mode 100644
index 0000000000..81b10d8e33
--- /dev/null
+++ b/testing/web-platform/tests/clipboard-apis/feature-policy/clipboard-write/clipboard-write-enabled-by-feature-policy-cross-origin.tentative.https.sub.html.headers
@@ -0,0 +1 @@
+Feature-Policy: clipboard-write *
diff --git a/testing/web-platform/tests/clipboard-apis/feature-policy/clipboard-write/clipboard-write-enabled-by-feature-policy.tentative.https.sub.html b/testing/web-platform/tests/clipboard-apis/feature-policy/clipboard-write/clipboard-write-enabled-by-feature-policy.tentative.https.sub.html
new file mode 100644
index 0000000000..ca97994c61
--- /dev/null
+++ b/testing/web-platform/tests/clipboard-apis/feature-policy/clipboard-write/clipboard-write-enabled-by-feature-policy.tentative.https.sub.html
@@ -0,0 +1,32 @@
+<!doctype html>
+<body>Body needed for test_driver.click()</body>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="/feature-policy/resources/featurepolicy.js"></script>
+<script src="../../resources/user-activation.js"></script>
+<script>
+'use strict';
+
+const same_origin_src =
+ '/feature-policy/resources/feature-policy-clipboard-write.html';
+const cross_origin_src =
+ 'https://{{domains[www]}}:{{ports[https][0]}}' + same_origin_src;
+
+promise_test(async t => {
+ await test_driver.set_permission({ name: 'clipboard-write' }, 'granted');
+ await waitForUserActivation();
+ await navigator.clipboard.writeText('test text');
+}, 'Feature-Policy header clipboard-write "*" allows the top-level document.');
+
+promise_test(async t => {
+ await waitForUserActivation();
+ test_feature_availability(
+ 'navigator.clipboard.writeText("test text")',
+ t,
+ same_origin_src,
+ expect_feature_available_default
+ );
+}, 'Feature-Policy header clipboard-write "*" allows same-origin iframes.');
+</script>
diff --git a/testing/web-platform/tests/clipboard-apis/feature-policy/clipboard-write/clipboard-write-enabled-by-feature-policy.tentative.https.sub.html.headers b/testing/web-platform/tests/clipboard-apis/feature-policy/clipboard-write/clipboard-write-enabled-by-feature-policy.tentative.https.sub.html.headers
new file mode 100644
index 0000000000..81b10d8e33
--- /dev/null
+++ b/testing/web-platform/tests/clipboard-apis/feature-policy/clipboard-write/clipboard-write-enabled-by-feature-policy.tentative.https.sub.html.headers
@@ -0,0 +1 @@
+Feature-Policy: clipboard-write *
diff --git a/testing/web-platform/tests/clipboard-apis/feature-policy/clipboard-write/clipboard-write-enabled-on-self-origin-by-feature-policy.tentative.https.sub.html b/testing/web-platform/tests/clipboard-apis/feature-policy/clipboard-write/clipboard-write-enabled-on-self-origin-by-feature-policy.tentative.https.sub.html
new file mode 100644
index 0000000000..5615a68ac5
--- /dev/null
+++ b/testing/web-platform/tests/clipboard-apis/feature-policy/clipboard-write/clipboard-write-enabled-on-self-origin-by-feature-policy.tentative.https.sub.html
@@ -0,0 +1,41 @@
+<!doctype html>
+<body>Body needed for test_driver.click()</body>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="/feature-policy/resources/featurepolicy.js"></script>
+<script src="../../resources/user-activation.js"></script>
+<script>
+'use strict';
+
+const same_origin_src =
+ '/feature-policy/resources/feature-policy-clipboard-write.html';
+const cross_origin_src =
+ 'https://{{domains[www]}}:{{ports[https][0]}}' + same_origin_src;
+
+promise_test(async t => {
+ await test_driver.set_permission({ name: 'clipboard-write' }, 'granted');
+ await waitForUserActivation();
+ await navigator.clipboard.writeText('test text');
+}, 'Feature-Policy header clipboard-write "self" allows the top-level document.');
+
+promise_test(async t => {
+ await waitForUserActivation();
+ test_feature_availability(
+ 'navigator.clipboard.writeText("test text")',
+ t,
+ same_origin_src,
+ expect_feature_available_default
+ );
+}, 'Feature-Policy header clipboard-write "self" allows same-origin iframes.');
+
+promise_test(async t => {
+ test_feature_availability(
+ 'navigator.clipboard.writeText("test text")',
+ t,
+ cross_origin_src,
+ expect_feature_unavailable_default
+ );
+}, 'Feature-Policy header clipboard-write "self" disallows cross-origin iframes.');
+</script>
diff --git a/testing/web-platform/tests/clipboard-apis/feature-policy/clipboard-write/clipboard-write-enabled-on-self-origin-by-feature-policy.tentative.https.sub.html.headers b/testing/web-platform/tests/clipboard-apis/feature-policy/clipboard-write/clipboard-write-enabled-on-self-origin-by-feature-policy.tentative.https.sub.html.headers
new file mode 100644
index 0000000000..e226f41c6a
--- /dev/null
+++ b/testing/web-platform/tests/clipboard-apis/feature-policy/clipboard-write/clipboard-write-enabled-on-self-origin-by-feature-policy.tentative.https.sub.html.headers
@@ -0,0 +1 @@
+Feature-Policy: clipboard-write 'self'
diff --git a/testing/web-platform/tests/clipboard-apis/idlharness.https.window.js b/testing/web-platform/tests/clipboard-apis/idlharness.https.window.js
new file mode 100644
index 0000000000..c22ee24544
--- /dev/null
+++ b/testing/web-platform/tests/clipboard-apis/idlharness.https.window.js
@@ -0,0 +1,17 @@
+// META: timeout=long
+// META: script=/resources/WebIDLParser.js
+// META: script=/resources/idlharness.js
+
+'use strict';
+
+idl_test(
+ ['clipboard-apis'],
+ ['dom', 'html', 'permissions'],
+ idl_array => {
+ idl_array.add_objects({
+ Navigator: ['navigator'],
+ Clipboard: ['navigator.clipboard'],
+ ClipboardEvent: ['new ClipboardEvent("x")'],
+ });
+ }
+);
diff --git a/testing/web-platform/tests/clipboard-apis/permissions/WEB_FEATURES.yml b/testing/web-platform/tests/clipboard-apis/permissions/WEB_FEATURES.yml
new file mode 100644
index 0000000000..92abfee358
--- /dev/null
+++ b/testing/web-platform/tests/clipboard-apis/permissions/WEB_FEATURES.yml
@@ -0,0 +1,3 @@
+features:
+- name: async-clipboard
+ files: "**"
diff --git a/testing/web-platform/tests/clipboard-apis/permissions/readText-denied.https.html b/testing/web-platform/tests/clipboard-apis/permissions/readText-denied.https.html
new file mode 100644
index 0000000000..010f4ba21b
--- /dev/null
+++ b/testing/web-platform/tests/clipboard-apis/permissions/readText-denied.https.html
@@ -0,0 +1,20 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>navigator.clipboard.readText() fails when permission denied</title>
+<link rel="help" href="https://w3c.github.io/clipboard-apis/#async-clipboard-api">
+<body>Body needed for test_driver.click()</body>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="../resources/user-activation.js"></script>
+<script>
+'use strict';
+
+promise_test(async t => {
+ await test_driver.set_permission({name: 'clipboard-read'}, 'denied');
+ await waitForUserActivation();
+ await promise_rejects_dom(t,
+ 'NotAllowedError', navigator.clipboard.readText());
+}, 'navigator.clipboard.readText() fails when permission denied');
+</script>
diff --git a/testing/web-platform/tests/clipboard-apis/permissions/readText-granted.https.html b/testing/web-platform/tests/clipboard-apis/permissions/readText-granted.https.html
new file mode 100644
index 0000000000..e912bd64a8
--- /dev/null
+++ b/testing/web-platform/tests/clipboard-apis/permissions/readText-granted.https.html
@@ -0,0 +1,19 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>navigator.clipboard.readText() succeeds when permission granted</title>
+<link rel="help" href="https://w3c.github.io/clipboard-apis/#async-clipboard-api">
+<body>Body needed for test_driver.click()</body>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="../resources/user-activation.js"></script>
+<script>
+'use strict';
+
+promise_test(async () => {
+ await test_driver.set_permission({name: 'clipboard-read'}, 'granted');
+ await waitForUserActivation();
+ await navigator.clipboard.readText();
+}, 'navigator.clipboard.readText() succeeds when permission granted');
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/clipboard-apis/permissions/writeText-denied.https.html b/testing/web-platform/tests/clipboard-apis/permissions/writeText-denied.https.html
new file mode 100644
index 0000000000..5fbcab4117
--- /dev/null
+++ b/testing/web-platform/tests/clipboard-apis/permissions/writeText-denied.https.html
@@ -0,0 +1,20 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>navigator.clipboard.writeText() fails when permission denied</title>
+<link rel="help" href="https://w3c.github.io/clipboard-apis/#async-clipboard-api">
+<body>Body needed for test_driver.click()</body>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="../resources/user-activation.js"></script>
+<script>
+'use strict';
+
+promise_test(async t => {
+ await test_driver.set_permission({name: 'clipboard-write'}, 'denied');
+ await waitForUserActivation();
+ await promise_rejects_dom(t, 'NotAllowedError',
+ navigator.clipboard.writeText('xyz'));
+}, 'navigator.clipboard.writeText() fails when permission denied');
+</script>
diff --git a/testing/web-platform/tests/clipboard-apis/permissions/writeText-granted.https.html b/testing/web-platform/tests/clipboard-apis/permissions/writeText-granted.https.html
new file mode 100644
index 0000000000..ff347b7add
--- /dev/null
+++ b/testing/web-platform/tests/clipboard-apis/permissions/writeText-granted.https.html
@@ -0,0 +1,19 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>navigator.clipboard.writeText() succeeds when permission granted</title>
+<link rel="help" href="https://w3c.github.io/clipboard-apis/#async-clipboard-api">
+<body>Body needed for test_driver.click()</body>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="../resources/user-activation.js"></script>
+<script>
+'use strict';
+
+promise_test(async () => {
+ await test_driver.set_permission({name: 'clipboard-write'}, 'granted');
+ await waitForUserActivation();
+ await navigator.clipboard.writeText('xyz');
+}, 'navigator.clipboard.writeText() succeeds when permission granted');
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/clipboard-apis/resources/copied-file.txt b/testing/web-platform/tests/clipboard-apis/resources/copied-file.txt
new file mode 100644
index 0000000000..56a2838b7d
--- /dev/null
+++ b/testing/web-platform/tests/clipboard-apis/resources/copied-file.txt
@@ -0,0 +1 @@
+copied-file-contents
diff --git a/testing/web-platform/tests/clipboard-apis/resources/greenbox.png b/testing/web-platform/tests/clipboard-apis/resources/greenbox.png
new file mode 100644
index 0000000000..6e555e3b19
--- /dev/null
+++ b/testing/web-platform/tests/clipboard-apis/resources/greenbox.png
Binary files differ
diff --git a/testing/web-platform/tests/clipboard-apis/resources/page.html b/testing/web-platform/tests/clipboard-apis/resources/page.html
new file mode 100644
index 0000000000..35bde8e501
--- /dev/null
+++ b/testing/web-platform/tests/clipboard-apis/resources/page.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-action.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="user-activation.js"></script>
+
+<div style="width: 10px; height: 10px"></div>
+<script>
+window.addEventListener("message", async (e) => {
+ if (e.data && e.data[0] == "write") {
+ test_driver.set_test_context(window.parent);
+ await test_driver.set_permission({name: 'clipboard-read'}, 'granted');
+ await test_driver.set_permission({name: 'clipboard-write'}, 'granted');
+ await waitForUserActivation();
+ await navigator.clipboard.write([
+ new ClipboardItem({
+ "text/plain": e.data[1],
+ }),
+ ]).catch(() => {
+ assert_true(false, `should not fail`);
+ });
+ window.parent.postMessage("done", "*");
+ }
+});
+</script>
+</html>
diff --git a/testing/web-platform/tests/clipboard-apis/resources/user-activation.js b/testing/web-platform/tests/clipboard-apis/resources/user-activation.js
new file mode 100644
index 0000000000..ed294bb9cb
--- /dev/null
+++ b/testing/web-platform/tests/clipboard-apis/resources/user-activation.js
@@ -0,0 +1,25 @@
+'use strict';
+
+// In order to use this function, please import testdriver.js and
+// testdriver-vendor.js, and include a <body> element.
+async function waitForUserActivation() {
+ if (window.opener) {
+ throw new Error(
+ "waitForUserActivation() only works in the top-level frame");
+ }
+ const loadedPromise = new Promise(resolve => {
+ if(document.readyState == 'complete') {
+ resolve();
+ return;
+ }
+ window.addEventListener('load', resolve, {once: true});
+ });
+ await loadedPromise;
+
+ const clickedPromise = new Promise(resolve => {
+ document.body.addEventListener('click', resolve, {once: true});
+ });
+
+ test_driver.click(document.body);
+ await clickedPromise;
+}
diff --git a/testing/web-platform/tests/clipboard-apis/text-write-read/WEB_FEATURES.yml b/testing/web-platform/tests/clipboard-apis/text-write-read/WEB_FEATURES.yml
new file mode 100644
index 0000000000..92abfee358
--- /dev/null
+++ b/testing/web-platform/tests/clipboard-apis/text-write-read/WEB_FEATURES.yml
@@ -0,0 +1,3 @@
+features:
+- name: async-clipboard
+ files: "**"
diff --git a/testing/web-platform/tests/clipboard-apis/text-write-read/async-write-read.https.html b/testing/web-platform/tests/clipboard-apis/text-write-read/async-write-read.https.html
new file mode 100644
index 0000000000..c46e5d4317
--- /dev/null
+++ b/testing/web-platform/tests/clipboard-apis/text-write-read/async-write-read.https.html
@@ -0,0 +1,41 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>
+ Async Clipboard write ([text/plain ClipboardItem]) ->
+ read ([text/plain ClipboardItem]) tests
+</title>
+<link rel="help" href="https://w3c.github.io/clipboard-apis/#async-clipboard-api">
+<body>Body needed for test_driver.click()</body>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="../resources/user-activation.js"></script>
+<script>
+async function readWriteTest(textInput) {
+ promise_test(async t => {
+ await test_driver.set_permission({name: 'clipboard-read'}, 'granted');
+ await test_driver.set_permission({name: 'clipboard-write'}, 'granted');
+
+ const blobInput = new Blob([textInput], {type: 'text/plain'});
+ const clipboardItemInput = new ClipboardItem({'text/plain': blobInput});
+
+ await waitForUserActivation();
+ await navigator.clipboard.write([clipboardItemInput]);
+ await waitForUserActivation();
+ const clipboardItems = await navigator.clipboard.read();
+ assert_equals(clipboardItems.length, 1);
+ const clipboardItemOutput = clipboardItems[0];
+ assert_true(clipboardItemOutput instanceof ClipboardItem);
+ assert_equals(clipboardItemOutput.types.length, 1);
+ const blobOutput = await clipboardItemOutput.getType('text/plain');
+ assert_equals(blobOutput.type, 'text/plain');
+
+ const textOutput = await (new Response(blobOutput)).text();
+ assert_equals(textOutput, textInput);
+ }, 'Verify write and read clipboard given text: ' + textInput);
+}
+
+readWriteTest('Clipboard write ([text/plain ClipboardItem]) -> read ([text/plain ClipboardItem]) test');
+readWriteTest('non-Latin1 text encoding test データ');
+</script>
diff --git a/testing/web-platform/tests/clipboard-apis/text-write-read/async-write-readText.https.html b/testing/web-platform/tests/clipboard-apis/text-write-read/async-write-readText.https.html
new file mode 100644
index 0000000000..66969b1777
--- /dev/null
+++ b/testing/web-platform/tests/clipboard-apis/text-write-read/async-write-readText.https.html
@@ -0,0 +1,33 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>
+ Async Clipboard write ([text/plain ClipboardItem]) -> readText tests
+</title>
+<link rel="help" href="https://w3c.github.io/clipboard-apis/#async-clipboard-api">
+<body>Body needed for test_driver.click()</body>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="../resources/user-activation.js"></script>
+<script>
+async function readWriteTest(textInput) {
+ promise_test(async t => {
+ await test_driver.set_permission({name: 'clipboard-read'}, 'granted');
+ await test_driver.set_permission({name: 'clipboard-write'}, 'granted');
+
+ const blobInput = new Blob([textInput], {type: 'text/plain'});
+ const clipboardItem = new ClipboardItem({'text/plain': blobInput});
+
+ await waitForUserActivation();
+ await navigator.clipboard.write([clipboardItem]);
+ await waitForUserActivation();
+ const textOutput = await navigator.clipboard.readText();
+
+ assert_equals(textOutput, textInput);
+ }, 'Verify write and read clipboard given text: ' + textInput);
+}
+
+readWriteTest('Clipboard write ([text/plain ClipboardItem) -> read text test');
+readWriteTest('non-Latin1 text encoding test データ');
+</script>
diff --git a/testing/web-platform/tests/clipboard-apis/text-write-read/async-writeText-read.https.html b/testing/web-platform/tests/clipboard-apis/text-write-read/async-writeText-read.https.html
new file mode 100644
index 0000000000..ddf563269a
--- /dev/null
+++ b/testing/web-platform/tests/clipboard-apis/text-write-read/async-writeText-read.https.html
@@ -0,0 +1,37 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>
+ Async Clipboard writeText -> read ([text/plain ClipboardItem]) tests
+</title>
+<link rel="help" href="https://w3c.github.io/clipboard-apis/#async-clipboard-api">
+<body>Body needed for test_driver.click()</body>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="../resources/user-activation.js"></script>
+<script>
+async function readWriteTest(textInput) {
+ promise_test(async t => {
+ await test_driver.set_permission({name: 'clipboard-read'}, 'granted');
+ await test_driver.set_permission({name: 'clipboard-write'}, 'granted');
+
+ await waitForUserActivation();
+ await navigator.clipboard.writeText(textInput);
+ await waitForUserActivation();
+ const clipboardItems = await navigator.clipboard.read();
+ assert_equals(clipboardItems.length, 1);
+ const clipboardItem = clipboardItems[0];
+ assert_true(clipboardItem instanceof ClipboardItem);
+ assert_equals(clipboardItem.types.length, 1);
+ const blobOutput = await clipboardItem.getType('text/plain');
+ assert_equals(blobOutput.type, 'text/plain');
+
+ const textOutput = await (new Response(blobOutput)).text();
+ assert_equals(textOutput, textInput);
+ }, 'Verify write and read clipboard given text: ' + textInput);
+}
+
+readWriteTest('Clipboard write text -> read ([text/plain ClipboardItem]) test');
+readWriteTest('non-Latin1 text encoding test データ');
+</script>
diff --git a/testing/web-platform/tests/clipboard-apis/text-write-read/async-writeText-readText.https.html b/testing/web-platform/tests/clipboard-apis/text-write-read/async-writeText-readText.https.html
new file mode 100644
index 0000000000..0defdf7a70
--- /dev/null
+++ b/testing/web-platform/tests/clipboard-apis/text-write-read/async-writeText-readText.https.html
@@ -0,0 +1,28 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>Async Clipboard writeText -> readText tests</title>
+<link rel="help" href="https://w3c.github.io/clipboard-apis/#async-clipboard-api">
+<body>Body needed for test_driver.click()</body>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+<script src="../resources/user-activation.js"></script>
+<script>
+async function readWriteTest(textInput) {
+ promise_test(async t => {
+ await test_driver.set_permission({name: 'clipboard-read'}, 'granted');
+ await test_driver.set_permission({name: 'clipboard-write'}, 'granted');
+
+ await waitForUserActivation();
+ await navigator.clipboard.writeText(textInput);
+ await waitForUserActivation();
+ const textOutput = await navigator.clipboard.readText();
+
+ assert_equals(textOutput, textInput);
+ }, 'Verify write and read clipboard given text: ' + textInput);
+}
+
+readWriteTest('Clipboard write text -> read text test');
+readWriteTest('non-Latin1 text encoding test データ');
+</script>