Search code examples
javascriptgoogle-chrome-extensionclipboardnavigator

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);

Solution

  • 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);
        });
    }