From b8ebea7f172334f5a5d9ac87860aec5f55becd26 Mon Sep 17 00:00:00 2001 From: Anupam Snigdha Date: Mon, 6 Jun 2022 11:01:40 -0700 Subject: [PATCH] [Clipboard API] Clipboard Web Custom Formats implementation. This patch addresses the changes proposed by the EditingWG[1] and agreed upon by all browser vendors. We are removing the `unsanitized` option, and instead, adding custom format support for MIME types that have "web " prefix in them. Added few wpt tests to test these changes. Below is a summary of the changes in this CL: 1. Removed `unsanitized` option from read/write methods. 2. If the custom format doesn't have a "web " prefix, then clipboard read/write fails. 3. Transient user activation is applicable to all supported formats - text/html, text/plain, image/png and web custom formats. 4. There are two "buckets" of clipboard formats. One for the well-known formats and the other for the web custom format. If the author doesn't specify the web format explicitly, then they don't get access to it. This means, we won't write web custom formats for well-known types implicitly if authors have not indicated that during the write call via a "web " prefix (e.g. "web text/html"). Same applies for reading web custom formats for well-known types- if there aren't any formats in the web custom format map, then we won't return any web custom formats i.e. text/html won't be automatically converted into "web text/html". Spec: https://github.com/w3c/clipboard-apis/pull/175 Explainer: https://github.com/w3c/editing/blob/gh-pages/docs/clipboard-pickling/explainer.md i2p: https://groups.google.com/a/chromium.org/g/blink-dev/c/Lo7WBM_v_LY/m/LncCKkXeAwAJ i2s: https://groups.google.com/a/chromium.org/g/blink-dev/c/k2rgX-4Cigc/m/P0RijrpzBAAJ?utm_medium=email&utm_source=footer&pli=1 1. Github issue: https://github.com/w3c/clipboard-apis/issues/165 Bug: 106449 Change-Id: I86aae6a662089efeede2a01ac87cb698e9646df5 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3650952 Commit-Queue: Anupam Snigdha Reviewed-by: Alexander Timin Reviewed-by: Daniel Cheng Reviewed-by: Austin Sullivan Cr-Commit-Position: refs/heads/main@{#1011078} --- ...om-formats-write-read.tentative.https.html | 50 +++++++++++++------ .../async-html-script-removal.https.html | 6 ++- ...or-clipboard-read-resource-load.https.html | 4 ++ ...vigator-clipboard-read-sanitize.https.html | 4 ++ ...-promise-write-blobs-read-blobs.https.html | 5 +- .../async-svg-script-removal.https.html | 4 ++ ...ml-formats-write-read.tentative.https.html | 24 +++++---- ...xt-formats-write-read.tentative.https.html | 25 ++++++---- .../async-write-blobs-read-blobs.https.html | 4 ++ .../async-write-html-read-html.https.html | 4 ++ .../async-write-image-read-image.https.html | 6 +++ .../async-write-svg-read-svg.https.html | 4 ++ .../write-read-on-detached-iframe.https.html | 2 + ...ext-readText-on-detached-iframe.https.html | 2 + ...bute-cross-origin-tentative.https.sub.html | 7 +-- ...-policy-attribute-tentative.https.sub.html | 7 +-- ...licy-cross-origin-tentative.https.sub.html | 7 +-- ...by-feature-policy.tentative.https.sub.html | 8 +-- ...by-feature-policy.tentative.https.sub.html | 14 ++++-- ...bute-cross-origin-tentative.https.sub.html | 7 +-- ...-policy-attribute-tentative.https.sub.html | 7 +-- ...licy-cross-origin-tentative.https.sub.html | 7 +-- ...by-feature-policy.tentative.https.sub.html | 8 +-- ...by-feature-policy.tentative.https.sub.html | 10 ++-- .../permissions/readText-denied.https.html | 3 ++ .../permissions/readText-granted.https.html | 3 ++ .../permissions/writeText-denied.https.html | 3 ++ .../permissions/writeText-granted.https.html | 3 ++ .../async-write-read.https.html | 4 ++ .../async-write-readText.https.html | 4 ++ .../async-writeText-read.https.html | 4 ++ .../async-writeText-readText.https.html | 4 ++ 32 files changed, 186 insertions(+), 68 deletions(-) diff --git a/clipboard-apis/async-custom-formats-write-read.tentative.https.html b/clipboard-apis/async-custom-formats-write-read.tentative.https.html index b6c368f75bc2f2..77991ec98aa098 100644 --- a/clipboard-apis/async-custom-formats-write-read.tentative.https.html +++ b/clipboard-apis/async-custom-formats-write-read.tentative.https.html @@ -14,26 +14,19 @@ promise_test(async t => { await test_driver.set_permission({name: 'clipboard-read'}, 'granted'); 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 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}, - {unsanitized: [format1, format2]}); + {[format1]: blobInput1, [format2]: blobInput2}); await waitForUserActivation(); await navigator.clipboard.write([clipboardItemInput]); - // Items may not be readable on the sanitized clipboard after custom format - // write. - await promise_rejects_dom(t, 'DataError', - navigator.clipboard.read()); - // Items should be readable on a custom format clipboard after custom format // write. await waitForUserActivation(); - const clipboardItems = await navigator.clipboard.read( - {unsanitized: [format1, format2]}); + const clipboardItems = await navigator.clipboard.read(); assert_equals(clipboardItems.length, 1); const clipboardItem = clipboardItems[0]; assert_true(clipboardItem instanceof ClipboardItem); @@ -58,15 +51,44 @@ const customFormatArray = []; const customFormatMap = {}; for (let i = 0; i <= 100; i++) { - customFormatArray.push("CustomFormat" + 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, - {unsanitized: customFormatArray}); + 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-read'}, 'granted'); + 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-read'}, 'granted'); + 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'); + diff --git a/clipboard-apis/async-html-script-removal.https.html b/clipboard-apis/async-html-script-removal.https.html index 90dc44c9db20e3..44c11add855137 100644 --- a/clipboard-apis/async-html-script-removal.https.html +++ b/clipboard-apis/async-html-script-removal.https.html @@ -4,10 +4,12 @@ Async Clipboard write ([text/html ClipboardItem]) -> readHtml (and remove scripts) tests +Body needed for test_driver.click() + + +Body needed for test_driver.click()

@@ -27,6 +29,7 @@ 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(); @@ -38,3 +41,4 @@ assert_false(loadObserved, 'Should not observe resource loading'); }); + diff --git a/clipboard-apis/async-navigator-clipboard-read-sanitize.https.html b/clipboard-apis/async-navigator-clipboard-read-sanitize.https.html index 9e0ab2ee740f85..cc1836753478b7 100644 --- a/clipboard-apis/async-navigator-clipboard-read-sanitize.https.html +++ b/clipboard-apis/async-navigator-clipboard-read-sanitize.https.html @@ -7,7 +7,9 @@ + +Body needed for test_driver.click()

@@ -29,6 +31,7 @@ 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(); @@ -42,3 +45,4 @@ assert_false(testFailed); }); + diff --git a/clipboard-apis/async-promise-write-blobs-read-blobs.https.html b/clipboard-apis/async-promise-write-blobs-read-blobs.https.html index e4b93c7c5ffb7c..12184c92e0777f 100644 --- a/clipboard-apis/async-promise-write-blobs-read-blobs.https.html +++ b/clipboard-apis/async-promise-write-blobs-read-blobs.https.html @@ -4,10 +4,12 @@ Async Clipboard write blobs -> read blobs with promise tests +Body needed for test_driver.click() + + diff --git a/clipboard-apis/async-unsanitized-plaintext-formats-write-read.tentative.https.html b/clipboard-apis/async-unsanitized-plaintext-formats-write-read.tentative.https.html index f44ed22618e2fd..1c5638ca0a5faf 100644 --- a/clipboard-apis/async-unsanitized-plaintext-formats-write-read.tentative.https.html +++ b/clipboard-apis/async-unsanitized-plaintext-formats-write-read.tentative.https.html @@ -18,32 +18,35 @@ await test_driver.set_permission({name: 'clipboard-write'}, 'granted'); const dataToWrite = 'Test text.'; - const format = 'text/plain'; + const format1 = 'web text/plain'; + const format2 = 'text/plain'; - const blobInput = new Blob([dataToWrite], {type: format}); + const blobInput1 = new Blob([dataToWrite], {type: format1}); + const blobInput2 = new Blob([dataToWrite], {type: format2}); // Blob types are automatically converted to lower-case. - assert_equals(blobInput.type, format.toLowerCase()); + assert_equals(blobInput1.type, format1.toLowerCase()); + assert_equals(blobInput2.type, format2.toLowerCase()); const clipboardItemInput = new ClipboardItem( - {[format]: blobInput}, {unsanitized: [format]}); + {[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( - {unsanitized: [format]}); + const clipboardItems = await navigator.clipboard.read(); assert_equals(clipboardItems.length, 1); const clipboardItem = clipboardItems[0]; assert_true(clipboardItem instanceof ClipboardItem); - const blobOutput = await clipboardItem.getType(format); - assert_equals(blobOutput.type, format); - const data = await (new Response(blobOutput)).text(); - assert_equals(data, dataToWrite); + 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 format as input'); +}, 'Verify write and read unsanitized content to the clipboard given standard and custom formats as input'); diff --git a/clipboard-apis/async-write-blobs-read-blobs.https.html b/clipboard-apis/async-write-blobs-read-blobs.https.html index 50d23a9c3625d5..8bec558b2b2de8 100644 --- a/clipboard-apis/async-write-blobs-read-blobs.https.html +++ b/clipboard-apis/async-write-blobs-read-blobs.https.html @@ -4,10 +4,12 @@ Async Clipboard write blobs -> read blobs tests +Body needed for test_driver.click() + + + +Body needed for test_driver.click()

The bottom image should display the same image as the top image.

Original Image:

@@ -47,7 +49,9 @@ 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); @@ -73,7 +77,9 @@ 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]'); + diff --git a/clipboard-apis/async-write-svg-read-svg.https.html b/clipboard-apis/async-write-svg-read-svg.https.html index 19c91597c111ee..42f6c547b29799 100644 --- a/clipboard-apis/async-write-svg-read-svg.https.html +++ b/clipboard-apis/async-write-svg-read-svg.https.html @@ -4,10 +4,12 @@ Async Clipboard write ([image/svg+xml ClipboardItem]) -> read and write svg tests +Body needed for test_driver.click() + + + + - diff --git a/clipboard-apis/feature-policy/clipboard-read/clipboard-read-enabled-by-feature-policy-attribute-tentative.https.sub.html b/clipboard-apis/feature-policy/clipboard-read/clipboard-read-enabled-by-feature-policy-attribute-tentative.https.sub.html index 15d26842616372..e812854b4c7505 100644 --- a/clipboard-apis/feature-policy/clipboard-read/clipboard-read-enabled-by-feature-policy-attribute-tentative.https.sub.html +++ b/clipboard-apis/feature-policy/clipboard-read/clipboard-read-enabled-by-feature-policy-attribute-tentative.https.sub.html @@ -1,17 +1,19 @@ - +Body needed for test_driver.click() + - diff --git a/clipboard-apis/feature-policy/clipboard-read/clipboard-read-enabled-by-feature-policy-cross-origin-tentative.https.sub.html b/clipboard-apis/feature-policy/clipboard-read/clipboard-read-enabled-by-feature-policy-cross-origin-tentative.https.sub.html index 1b6f4929a19dea..c371ea3b41cb2e 100644 --- a/clipboard-apis/feature-policy/clipboard-read/clipboard-read-enabled-by-feature-policy-cross-origin-tentative.https.sub.html +++ b/clipboard-apis/feature-policy/clipboard-read/clipboard-read-enabled-by-feature-policy-cross-origin-tentative.https.sub.html @@ -1,10 +1,11 @@ - +Body needed for test_driver.click() + - diff --git a/clipboard-apis/feature-policy/clipboard-read/clipboard-read-enabled-by-feature-policy.tentative.https.sub.html b/clipboard-apis/feature-policy/clipboard-read/clipboard-read-enabled-by-feature-policy.tentative.https.sub.html index f8c5bcb129f932..552183cc67d461 100644 --- a/clipboard-apis/feature-policy/clipboard-read/clipboard-read-enabled-by-feature-policy.tentative.https.sub.html +++ b/clipboard-apis/feature-policy/clipboard-read/clipboard-read-enabled-by-feature-policy.tentative.https.sub.html @@ -1,10 +1,11 @@ - +Body needed for test_driver.click() + - diff --git a/clipboard-apis/feature-policy/clipboard-read/clipboard-read-enabled-on-self-origin-by-feature-policy.tentative.https.sub.html b/clipboard-apis/feature-policy/clipboard-read/clipboard-read-enabled-on-self-origin-by-feature-policy.tentative.https.sub.html index 47aa6511ec599b..17dc3628a77220 100644 --- a/clipboard-apis/feature-policy/clipboard-read/clipboard-read-enabled-on-self-origin-by-feature-policy.tentative.https.sub.html +++ b/clipboard-apis/feature-policy/clipboard-read/clipboard-read-enabled-on-self-origin-by-feature-policy.tentative.https.sub.html @@ -1,10 +1,11 @@ - +Body needed for test_driver.click() + - diff --git a/clipboard-apis/feature-policy/clipboard-write/clipboard-write-enabled-by-feature-policy-attribute-cross-origin-tentative.https.sub.html b/clipboard-apis/feature-policy/clipboard-write/clipboard-write-enabled-by-feature-policy-attribute-cross-origin-tentative.https.sub.html index c931bbbb89f32f..e669c8fec4910d 100644 --- a/clipboard-apis/feature-policy/clipboard-write/clipboard-write-enabled-by-feature-policy-attribute-cross-origin-tentative.https.sub.html +++ b/clipboard-apis/feature-policy/clipboard-write/clipboard-write-enabled-by-feature-policy-attribute-cross-origin-tentative.https.sub.html @@ -1,10 +1,11 @@ - +Body needed for test_driver.click() + - diff --git a/clipboard-apis/feature-policy/clipboard-write/clipboard-write-enabled-by-feature-policy-attribute-tentative.https.sub.html b/clipboard-apis/feature-policy/clipboard-write/clipboard-write-enabled-by-feature-policy-attribute-tentative.https.sub.html index a2858c638e6c05..b57dfe3dd2888d 100644 --- a/clipboard-apis/feature-policy/clipboard-write/clipboard-write-enabled-by-feature-policy-attribute-tentative.https.sub.html +++ b/clipboard-apis/feature-policy/clipboard-write/clipboard-write-enabled-by-feature-policy-attribute-tentative.https.sub.html @@ -1,17 +1,19 @@ - +Body needed for test_driver.click() + - diff --git a/clipboard-apis/feature-policy/clipboard-write/clipboard-write-enabled-by-feature-policy-cross-origin-tentative.https.sub.html b/clipboard-apis/feature-policy/clipboard-write/clipboard-write-enabled-by-feature-policy-cross-origin-tentative.https.sub.html index 0f3164d9b02a13..6e7029cc789a63 100644 --- a/clipboard-apis/feature-policy/clipboard-write/clipboard-write-enabled-by-feature-policy-cross-origin-tentative.https.sub.html +++ b/clipboard-apis/feature-policy/clipboard-write/clipboard-write-enabled-by-feature-policy-cross-origin-tentative.https.sub.html @@ -1,10 +1,11 @@ - +Body needed for test_driver.click() + - diff --git a/clipboard-apis/feature-policy/clipboard-write/clipboard-write-enabled-by-feature-policy.tentative.https.sub.html b/clipboard-apis/feature-policy/clipboard-write/clipboard-write-enabled-by-feature-policy.tentative.https.sub.html index 1c6fc49a056605..ca97994c617d0e 100644 --- a/clipboard-apis/feature-policy/clipboard-write/clipboard-write-enabled-by-feature-policy.tentative.https.sub.html +++ b/clipboard-apis/feature-policy/clipboard-write/clipboard-write-enabled-by-feature-policy.tentative.https.sub.html @@ -1,10 +1,11 @@ - +Body needed for test_driver.click() + - diff --git a/clipboard-apis/feature-policy/clipboard-write/clipboard-write-enabled-on-self-origin-by-feature-policy.tentative.https.sub.html b/clipboard-apis/feature-policy/clipboard-write/clipboard-write-enabled-on-self-origin-by-feature-policy.tentative.https.sub.html index 51db5a427d2b41..5615a68ac55af8 100644 --- a/clipboard-apis/feature-policy/clipboard-write/clipboard-write-enabled-on-self-origin-by-feature-policy.tentative.https.sub.html +++ b/clipboard-apis/feature-policy/clipboard-write/clipboard-write-enabled-on-self-origin-by-feature-policy.tentative.https.sub.html @@ -1,10 +1,11 @@ - +Body needed for test_driver.click() + - diff --git a/clipboard-apis/permissions/readText-denied.https.html b/clipboard-apis/permissions/readText-denied.https.html index 935f520e7de2dd..010f4ba21b9533 100644 --- a/clipboard-apis/permissions/readText-denied.https.html +++ b/clipboard-apis/permissions/readText-denied.https.html @@ -2,15 +2,18 @@ navigator.clipboard.readText() fails when permission denied +Body needed for test_driver.click() + + \ No newline at end of file diff --git a/clipboard-apis/permissions/writeText-denied.https.html b/clipboard-apis/permissions/writeText-denied.https.html index 4d0530f0bc2217..5fbcab411748e2 100644 --- a/clipboard-apis/permissions/writeText-denied.https.html +++ b/clipboard-apis/permissions/writeText-denied.https.html @@ -2,15 +2,18 @@ navigator.clipboard.writeText() fails when permission denied +Body needed for test_driver.click() + + \ No newline at end of file diff --git a/clipboard-apis/text-write-read/async-write-read.https.html b/clipboard-apis/text-write-read/async-write-read.https.html index a00c5b27a4d65f..c46e5d43171314 100644 --- a/clipboard-apis/text-write-read/async-write-read.https.html +++ b/clipboard-apis/text-write-read/async-write-read.https.html @@ -5,10 +5,12 @@ read ([text/plain ClipboardItem]) tests +Body needed for test_driver.click() + + + +