Summary: | Javascript Clipboard API write() does not work after await | ||
---|---|---|---|
Product: | WebKit | Reporter: | Felipe Ruiz <fruiz> |
Component: | UI Events | Assignee: | Nobody <webkit-unassigned> |
Status: | NEW --- | ||
Severity: | Major | CC: | ap, bfulgham, christian, eoconnor, jer.noble, mjs, rniwa, tomac, webkit-bug-importer, wenson_hsieh |
Priority: | P2 | Keywords: | InRadar |
Version: | Safari 14 | ||
Hardware: | All | ||
OS: | All | ||
See Also: | https://bugs.webkit.org/show_bug.cgi?id=225559 |
Description
Felipe Ruiz
2021-02-22 01:59:50 PST
(In reply to Felipe Ruiz from comment #0) > I'm using javascript Clipboard API to copy an image to the clipboard. It > works in Chrome and Edge but not in Safari in spite of official > documentation of Safari says that it's supported. The issue is not so much async Clipboard API and the difference in the way user activation is tracked between Blink and WebKit. > In this example (not my real code), write() throws an error: > > document.getElementById("copy").addEventListener("click", async function() { > const response = await > fetch('https://upload.wikimedia.org/wikipedia/commons/4/47/ > PNG_transparency_demonstration_1.png'); > const blob = await response.blob(); > > navigator.clipboard.write([new ClipboardItem({ "image/png": blob })]) > .then(function () { console.log('copied'); }) > .catch(function (error) { console.log(error); }); > }); You need to initiate the write inside click event handler as in: document.getElementById("copy-html").addEventListener("click", event => { navigator.clipboard.write([ new ClipboardItem({ "text/png": async () => { const response = await fetch('https://upload.wikimedia.org/wikipedia/commons/4/47/PNG_transparency_demonstration_1.png'); return await response.blob(); } }), ]); }); (In reply to Ryosuke Niwa from comment #1) > (In reply to Felipe Ruiz from comment #0) > > I'm using javascript Clipboard API to copy an image to the clipboard. It > > works in Chrome and Edge but not in Safari in spite of official > > documentation of Safari says that it's supported. > > The issue is not so much async Clipboard API and the difference in the way > user activation is tracked between Blink and WebKit. > > > In this example (not my real code), write() throws an error: > > > > document.getElementById("copy").addEventListener("click", async function() { > > const response = await > > fetch('https://upload.wikimedia.org/wikipedia/commons/4/47/ > > PNG_transparency_demonstration_1.png'); > > const blob = await response.blob(); > > > > navigator.clipboard.write([new ClipboardItem({ "image/png": blob })]) > > .then(function () { console.log('copied'); }) > > .catch(function (error) { console.log(error); }); > > }); > > You need to initiate the write inside click event handler as in: > > document.getElementById("copy-html").addEventListener("click", event => { > navigator.clipboard.write([ > new ClipboardItem({ > "text/png": async () => { > const response = await > fetch('https://upload.wikimedia.org/wikipedia/commons/4/47/ > PNG_transparency_demonstration_1.png'); > return await response.blob(); > } > }), > ]); > }); Hi Ryosuke, first of all, thanks for your reply. Unfortunately, I have tested your solution and I get the same error message. > > You need to initiate the write inside click event handler as in: > > > > document.getElementById("copy-html").addEventListener("click", event => { > > navigator.clipboard.write([ > > new ClipboardItem({ > > "text/png": async () => { > > const response = await > > fetch('https://upload.wikimedia.org/wikipedia/commons/4/47/ > > PNG_transparency_demonstration_1.png'); > > return await response.blob(); > > } > > }), > > ]); > > }); > > > Hi Ryosuke, > > first of all, thanks for your reply. Unfortunately, I have tested your > solution and I get the same error message. Hi Felipe, I adapted the above code as a test case here: https://whsieh.github.io/examples/dice, and it seems to work in Safari on macOS with a couple of adjustments: - Changing the "text/png" above to "image/png". - Making the corresponding value for the "image/png" type a Promise instead of an async function. Please make sure that you're also testing in a secure context (https), where the clipboard API is available. Thanks! Wenson The different implementations unfortunately at the moment force developers to write code like this: ```js copyButton.addEventListener('click', async () => { try { // Safari treats user activation differently: // https://bugs.webkit.org/show_bug.cgi?id=222262. navigator.clipboard.write([ new ClipboardItem({ 'text/plain': new Promise(async (resolve) => { const svg = svgOutput.innerHTML; resolve(new Blob([svg], { type: 'text/plain' })); }), }), ]); } catch { // Chromium const svg = svgOutput.innerHTML; const blob = new Blob([svg], { type: 'text/plain' }); navigator.clipboard.write([ new ClipboardItem({ [blob.type]: blob, }), ]); } }); ``` Chromium is working on adding support for delayed generation of clipboard items (see https://crbug.com/1014310). At the same time it would be great if WebKit didn't expire the user activation. *** Bug 244812 has been marked as a duplicate of this bug. *** |