travisjayday
travisjayday

Reputation: 814

Chrome 76 Copy Content to Clipboard using navigator

I just updated to chrome 76 stable and am trying to use the new clipboard API to copy some text (and later on, images) to the clipboard. However, navigator.clipboard.write(data) does not seem to be working. Here is my code:

setInterval(function() {
    console.log("Wriing to clipbard");  
    navigator.permissions.query({ name: 'clipboard-write' }).then(result => {
        if (result.state === 'granted') {
            var data = new Blob(["Text data"], {type : "text/plain"});
            navigator.clipboard.write(data).then(function() {
              console.log("Copied to clipboard successfully!");
            }, function(error) {
              console.error("unable to write to clipboard. Error:");
              console.log(error);
            });
        } else {
            console.log("clipboard-permissoin not granted: " + result);
        }
    });
}, 3000);

I am running this in a chrome extension content script, with clipboard permissions set, so the permissions indeed get granted. The eror that gets thrown is:

unable to write to clipboard. Error:

TypeError: Failed to execute 'write' on 'Clipboard': Iterator getter is not callable.

Strangely enough, when I use navigator.clipboard.write text(text) instead of navigator.clipboard.write(data), everything works perfectly. The thing is, I want to use write(data) because later on I want to write images to the clipboard as well. Any ideas why it's not working? Thank you.

EDIT: I got the code from the specsheet https://w3c.github.io/clipboard-apis/#dom-clipboard-write

UPDATE: Okay, I got the text-copying to work by using ClipboardItem (see my answer below) but if I do the same with a dataURL encoded image, the whole webpage crashes with an "Aw snap" message. So not sure what's going on there. Here is the code that will crash the site and force a reload:

setInterval(function() {
    console.log("Wriing to clipbard");  
    navigator.permissions.query({ name: 'clipboard-write' }).then(result => {
        if (result.state === 'granted') {
            //var blob = new Blob(['hello'], {type: 'text/plain'});
            var data = new Blob(["iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+P+/HgAFhAJ/wlseKgAAAABJRU5ErkJggg=="], {type : "image/png"});

            var item = new ClipboardItem({'image/png': data});
            navigator.clipboard.write([item]).then(function() {
              console.log("Copied to clipboard successfully!");
            }, function(error) {
              console.error("unable to write to clipboard. Error:");
              console.log(error);
            });
        } else {
            console.log("clipboard-permissoin not granted: " + result);
        }
    });
}, 3000);

Upvotes: 5

Views: 9184

Answers (2)

travisjayday
travisjayday

Reputation: 814

Okay, this is the solution that works for me. You had to wrap the blob in a ClipboardItem object before calling write(). I stumbled across the solution by reading https://bugs.chromium.org/p/chromium/issues/detail?id=150835 and then searching for "ClipboardItem" on the web and then finding this code https://github.com/web-platform-tests/wpt/blob/master/clipboard-apis/async-navigator-clipboard-basics.https.html. There doesn't seem to be a real documentation for this yet, but hey, it works!

setInterval(function() {
    console.log("Wriing to clipbard");  
    navigator.permissions.query({ name: 'clipboard-write' }).then(result => {
        if (result.state === 'granted') {
            var blob = new Blob(['hello'], {type: 'text/plain'});
            var item = new ClipboardItem({'text/plain': blob});
            navigator.clipboard.write([item]).then(function() {
              console.log("Copied to clipboard successfully!");
            }, function(error) {
              console.error("unable to write to clipboard. Error:");
              console.log(error);
            });
        } else {
            console.log("clipboard-permission not granted: " + result);
        }
    });
}, 3000);

UPDATE: Okay, I also got the image copying to work (looking at the same source). url is the url of the image (duh)

async function copyImage(url) {
    console.log("Wriing to clipbard");  

    const response = await fetch(url);
    const blob = await response.blob();

    const item = new ClipboardItem({'image/png': blob});
    await navigator.clipboard.write([item]).then(function() {
      console.log("Copied to clipboard successfully!");
    }, function(error) {
      console.error("unable to write to clipboard. Error:");
      console.log(error);
    });
}

Upvotes: 3

Villapalos
Villapalos

Reputation: 717

A more complete solution:

After having several problems trying to solve this issue, I have a solution that works with Safari, but also with all the others browsers.

We need to try the two scenarios (for Safari and for the rest):

getImage (canvas, width = 200, height = 200) {
  return new Promise((resolve) => {
    const tmpCanvas = document.createElement('canvas')
    tmpCanvas.width = width
    tmpCanvas.height = height
    const ctx = tmpCanvas.getContext('2d')
    ctx.drawImage(canvas, 0, 0, canvas.width, canvas.height, 0, 0, width, height)
    tmpCanvas.toBlob(resolve)
  })
}

copyImg () {
 const canvas = document.getElementsByTagName('canvas')[0]
 if (canvas instanceof HTMLCanvasElement) {
   try {
     // Trying for only Safari browser
     const image = getImage(canvas)
     navigator.clipboard.write([new ClipboardItem({ 'image/png': image })])
     console.log('Copied')
   } catch (safariError: any) {
     try {
       // Trying for rest of browsers
       canvas.toBlob(blob => navigator.clipboard.write([new ClipboardItem({ 'image/png': blob })]))
       console.log('Copied')
     } catch (anotherBrowserError: any) {
       console.log(safariError.message + ' - ' + anotherBrowserError.message)
     }
   }
 }

}

Upvotes: 0

Related Questions