/* Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/ */ "use strict"; const Cc = SpecialPowers.Cc; const Ci = SpecialPowers.Ci; const Cr = SpecialPowers.Cr; const clipboard = SpecialPowers.Services.clipboard; const clipboardTypes = [ clipboard.kGlobalClipboard, clipboard.kSelectionClipboard, clipboard.kFindClipboard, clipboard.kSelectionCache, ]; function emptyClipboardData(aType) { // XXX gtk doesn't support emptying clipboard data which is stored from // other application (bug 1853884). As a workaround, we set dummy data // to the clipboard first to ensure the subsequent emptyClipboard call // works. if (navigator.platform.includes("Linux")) { writeStringToClipboard("foo", "text/plain", aType); } clipboard.emptyClipboard(aType); } function cleanupAllClipboard() { clipboardTypes.forEach(function (type) { if (clipboard.isClipboardTypeSupported(type)) { info(`cleanup clipboard ${type}`); emptyClipboardData(type); } }); } function generateRandomString() { return "random number: " + Math.random(); } function generateNewTransferable(aFlavor, aStr) { let trans = Cc["@mozilla.org/widget/transferable;1"].createInstance( Ci.nsITransferable ); trans.init(null); trans.addDataFlavor(aFlavor); let supportsStr = Cc["@mozilla.org/supports-string;1"].createInstance( Ci.nsISupportsString ); supportsStr.data = aStr; trans.setTransferData(aFlavor, supportsStr); return trans; } function addStringToTransferable(aFlavor, aStr, aTrans) { aTrans.addDataFlavor(aFlavor); let supportsStr = Cc["@mozilla.org/supports-string;1"].createInstance( Ci.nsISupportsString ); supportsStr.data = aStr; aTrans.setTransferData(aFlavor, supportsStr); } function updateStringToTransferable(aFlavor, aStr, aTrans) { let supportsStr = Cc["@mozilla.org/supports-string;1"].createInstance( Ci.nsISupportsString ); supportsStr.data = aStr; aTrans.setTransferData(aFlavor, supportsStr); } function writeStringToClipboard( aStr, aFlavor, aClipboardType, aClipboardOwner = null, aAsync = false ) { let trans = Cc["@mozilla.org/widget/transferable;1"].createInstance( Ci.nsITransferable ); trans.init(null); trans.addDataFlavor(aFlavor); let supportsStr = Cc["@mozilla.org/supports-string;1"].createInstance( Ci.nsISupportsString ); supportsStr.data = aStr; trans.setTransferData(aFlavor, supportsStr); if (aAsync) { let request = clipboard.asyncSetData(aClipboardType); request.setData(trans, aClipboardOwner); return; } clipboard.setData(trans, aClipboardOwner, aClipboardType); // XXX gtk doesn't support get empty text data from clipboard, bug 1852983. if (aStr == "" && navigator.platform.includes("Linux")) { todo_is( getClipboardData(aFlavor, aClipboardType), "", `Should get empty string on clipboard type ${aClipboardType}` ); } else { is( getClipboardData(aFlavor, aClipboardType), // On Windows, widget adds extra data into HTML clipboard. aFlavor == "text/html" && navigator.platform.includes("Win") ? `\n${aStr}\n\n` : aStr, "ensure clipboard data is set" ); } } function writeRandomStringToClipboard( aFlavor, aClipboardType, aClipboardOwner = null, aAsync = false ) { let randomString = generateRandomString(); writeStringToClipboard( randomString, aFlavor, aClipboardType, aClipboardOwner, aAsync ); return randomString; } function getClipboardData(aFlavor, aClipboardType) { var trans = Cc["@mozilla.org/widget/transferable;1"].createInstance( Ci.nsITransferable ); trans.init(null); trans.addDataFlavor(aFlavor); clipboard.getData( trans, aClipboardType, SpecialPowers.wrap(window).browsingContext.currentWindowContext ); try { var data = SpecialPowers.createBlankObject(); trans.getTransferData(aFlavor, data); return data.value.QueryInterface(SpecialPowers.Ci.nsISupportsString).data; } catch (ex) { // If the clipboard is empty getTransferData will throw. return null; } } function getClipboardDataSnapshotSync(aClipboardType) { return clipboard.getDataSnapshotSync( ["text/plain", "text/html", "image/png"], aClipboardType ); } function asyncGetClipboardData( aClipboardType, aFormats = ["text/plain", "text/html", "image/png"] ) { return new Promise((resolve, reject) => { try { clipboard.asyncGetData( aFormats, aClipboardType, null, SpecialPowers.Services.scriptSecurityManager.getSystemPrincipal(), { QueryInterface: SpecialPowers.ChromeUtils.generateQI([ "nsIAsyncClipboardGetCallback", ]), // nsIAsyncClipboardGetCallback onSuccess: SpecialPowers.wrapCallback(function ( aAsyncGetClipboardData ) { resolve(aAsyncGetClipboardData); }), onError: SpecialPowers.wrapCallback(function (aResult) { reject(aResult); }), } ); } catch (e) { ok(false, `asyncGetData should not throw`); reject(e); } }); } function asyncClipboardRequestGetData(aRequest, aFlavor, aThrows = false) { return new Promise((resolve, reject) => { var trans = Cc["@mozilla.org/widget/transferable;1"].createInstance( Ci.nsITransferable ); trans.init(null); trans.addDataFlavor(aFlavor); try { aRequest.getData(trans, aResult => { if (aResult != Cr.NS_OK) { reject(aResult); return; } try { var data = SpecialPowers.createBlankObject(); trans.getTransferData(aFlavor, data); resolve(data.value.QueryInterface(Ci.nsISupportsString).data); } catch (ex) { // XXX: should widget set empty string to transferable when there no // data in system clipboard? resolve(""); } }); ok( !aThrows, `nsIAsyncGetClipboardData.getData should ${ aThrows ? "throw" : "success" }` ); } catch (e) { ok( aThrows, `nsIAsyncGetClipboardData.getData should ${ aThrows ? "throw" : "success" }` ); reject(e); } }); }