1

The easiest way to do this would be to the navigator.clipboard.write API which is not available to the background pages of Chrome extensions. I have tried to create a blob like so:

    let blobFinal = null; // this will contain our blob object
    const img = document.createElement('img');
    // a train emoji
    img.src = 'data:image/png;base64,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';
    document.body.appendChild(img);
    setTimeout(() => {
      // make <canvas> of the same size
      const canvas = document.createElement('canvas');
      canvas.width = img.clientWidth;
      canvas.height = img.clientHeight;
      const context = canvas.getContext('2d');
      // copy image to it (this method allows to cut image)
      context.drawImage(img, 0, 0);
      // we can context.rotate(), and do many other things on canvas
      // toBlob is async operation, callback is called when done
      canvas.toBlob(function(blob) {
        blobFinal = blob;
        console.log('blob', blob);
        document.body.removeChild(img);
      }, 'image/png');
    }, 1000);

Then I attach this blob to the clipboard during its 'copy' event:

    editor.addEventListener('copy', (evt) => {
      // preserve text data
      evt.clipboardData.setData('text/plain', evt.clipboardData.getData('text/plain'));
      evt.clipboardData.setData('text/html', evt.clipboardData.getData('text/html'));
      // add binary data
      evt.clipboardData.setData('image/png', blobFinal);
      evt.preventDefault();
    });

However, when I paste this data, there are no files in the clipboard:

    editor.addEventListener('paste', (evt) => {
      console.log(evt.clipboardData.files.length); // prints 0
      for (const file of evt.clipboardData.files) {
        console.log('Size of file', file.size);
      }
    });

And, even if there is one file, its property size is zero. I am surprisingly not able to find any question on this topic. So, my question is, how to attach files to clipboard in a Chrome extension?

Gaurang Tandon
  • 6,504
  • 11
  • 47
  • 84
  • Be sure to update the question with a [mre]. – jsejcksn Oct 11 '22 at 14:19
  • Hi @jsejcksn, there is no MVCE applicable to this question, because there is no buggy code. The question is how to attach a file to the clipboard in a Chrome extension. I don't know how to do it. And there is no StackOverflow question that shows how to do it. – Gaurang Tandon Oct 11 '22 at 15:17
  • [^](https://stackoverflow.com/questions/74029445/how-to-attach-files-to-clipboard-in-chrome-extension?noredirect=1#comment130712891_74029445) If your code isn't meeting your expectations, then there's a problem with either the code or your expectations. The code you've shown is not enough for someone to reproduce the behavior you described, so we can't know which one is problematic until you do. – jsejcksn Oct 11 '22 at 15:20
  • Since you can't use navigator.clipboard.write in a service worker, is your idea to use the copy event instead? – Norio Yamamoto Oct 13 '22 at 04:12
  • @NorioYamamoto To clarify my question is both about MV2 and MV3 extensions. We can't use navigator.clipboard.write in the background page of an MV2 extension either. So yes that's my idea, of attaching to the copy event instead. – Gaurang Tandon Oct 13 '22 at 18:22
  • Do you have a way to fire a copy event in service worker? – Norio Yamamoto Oct 14 '22 at 19:04
  • Yes, not in service worker, but in the background pages. For service workers, my hope is that offscreen documents will help down the line. – Gaurang Tandon Oct 15 '22 at 02:59

1 Answers1

1

This sample does the following:

  1. Service worker sets "hoge" to storage.
  2. Service worker opens clipboard.html.
  3. clipboard.html calls clipboard.js.
  4. clipboard.js gets "hoge" from storage and writes to clipboard.
  5. clipboard.js closes clipboard.html.

Note:
The documentation of Clipboard.writeText() has the following description:

The "clipboard-write" permission of the Permissions API is granted automatically to pages when they are in the active tab.

manifest.json

{
  "name": "clipboard",
  "version": "1.0",
  "manifest_version": 3,
  "permissions": [
    "storage"
  ],
  "background": {
    "service_worker": "background.js"
  }
}

background.js

chrome.storage.local.set({ key: "hoge" }, () => {
  chrome.tabs.create({
    url: "clipboard.html"
  });
});

clipboard.html

<!DOCTYPE html>
<html>

<body>
  <script src="clipboard.js"></script>
</body>

</html>

clipboard.js

chrome.storage.local.get("key", (result) => {
  navigator.clipboard.writeText(result["key"]).then(() => {
    chrome.tabs.query({ active: true, currentWindow: true }, (tabs) => {
      chrome.tabs.remove(tabs[0].id);
    });
  });
});
Norio Yamamoto
  • 1,495
  • 2
  • 3
  • 10