Placing a variable in the clipboard

Placing a variable’s content into the clipboard on chrome or opera is described here

In these browser, it can be done from a background script, and for a user, the textarea is not visible.

To my understanding, this must be done in a content script in Firefox.

I tried the following code in a content script

//request.data is the value I want to place in the clipboard
var ta = document.getElementById(‘filteredClipboard_data’);
if( ta == null){
ta = document.createElement(‘textarea’);
ta.contentEditable = “true”;
//ta.hidden = “true”;
ta.id = ‘filteredClipboard_data’;
document.body.appendChild(ta);
} else {
console.log(“found ta”);

            }
            
        	ta.value = request.data;
            ta.select();
            document.execCommand("Copy");
The copy command works, but my problem is that with ff53 the browser scroll down at the bottom of the page where the textarea is plain visible. This is not the case with nigthly. The code works. Adding ta.hidden = true fails in both version: it's the selected text that is copied and not the the string received from the background script.

Please note: starting the add-on with
web-ext run --firefox=“C:\Program Files\Nightly\firefox.exe”
does not work: it seems that the content script is not reached. I received the error “Error: Could not establish connection. Receiving end does not exist”. I have to pack the add-on and drag it on the extension page to get it work correctly.

François

Using a testarea to put data in the clipboard seems unnecessarily complicated, and the current webside will be able to read the data from the textarea, which can be a privacy issue.

I have written this function to copy to the clipboard:

/**
 * Attempts to write data to the users clipboard.
 * @param  {string|object}  data  Ether a plain string or an object of multiple pairs { [mimeType]: data, } to write.
 * @param  {natural}        time  Maximum runtime of this asynchronous operation after which it will be canceled and rejected.
 * @return {Promise}              Promise that rejects if the timeout or an error occurred. If it resolves the operation should have succeeded.
 */
function writeToClipboard(data, time) { return new Promise(function(resolve, reject) {
	let done = false;
	function onCopy(event) { try {
		if (done) { return; } done = true;
		document.removeEventListener('copy', onCopy);
		const transfer = event.clipboardData;
		transfer.clearData();
		if (typeof data === 'string') {
			transfer.setData('text/plain', data);
		} else {
			Object.keys(data).forEach(mimeType => transfer.setData(mimeType, data[mimeType]));
		}
		event.preventDefault();
		resolve();
	} catch (error) { reject(error); } }
	setTimeout(() => {
		if (done) { return; } done = true;
		document.removeEventListener('copy', onCopy);
		reject(new Error('Timeout after '+ (time || 1000) +'ms'));
	}, time || 1000);
	document.addEventListener('copy', onCopy);
	document.execCommand('copy', false, null);
}); }

It doesn’t need to add any DOM-Elements and won’t tell the page what was copied.

Thanks for the suggestion. Can it be run from the background script ?

What way do you suggest to test my addon ? should I pack it each time I modify something ?
F.

Can it be run from the background script ?

AFAIK not in Firefox. It will only work in visible pages and content scripts.

What way do you suggest to test my addon?

web-ext should work perfectly for that. If it doesn’t, you should open an GitHub issue.

“Open a Github issue” sorry, but could you be more explicit and practical ?
Thanks F.

web-ext run does pretty much the same thing as firefox -no-remote -Profile "path/to/some/temp/folder" and then loading the unpacked extension via about:debugging.

There shouldn’t be any difference (at runtime) between packed and unpacked extensions(*). If there are differences, those are bugs. And if you are reasonably sure that you found a bug, you should report it.

*) One difference I know of are stacktraces. For unpacked extensions they sometimes contain the absolute file system path of the unpacked file, for packed extensions the path to the .xpi file plus ! and the relative path to the file within the .xpi. In both cases, it should be moz-extension://<uuid>/relative/path. But I very much doubt that affects you.

1 Like