browser_navigator_clipboard_read_ext.js (7988B)
1 /* -*- Mode: JavaScript; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 "use strict"; 8 9 /* import-globals-from head.js */ 10 11 const kContentFileUrl = kBaseUrlForContent + "simple_page_ext.html"; 12 13 const sharedScript = function () { 14 this.clipboardRead = function () { 15 return navigator.clipboard 16 .read() 17 .then(items => { 18 browser.test.assertEq(1, items.length, "Should only 1 ClipboardItem"); 19 20 const item = items[0]; 21 browser.test.assertEq( 22 1, 23 item.types.length, 24 "ClipboardItem should contain only one type" 25 ); 26 browser.test.assertEq( 27 "text/plain", 28 item.types[0], 29 "Type should be text/plain" 30 ); 31 32 return item.getType("text/plain"); 33 }) 34 .then(blob => { 35 return blob.text(); 36 }) 37 .then(text => { 38 browser.test.sendMessage("result", text); 39 }) 40 .catch(error => { 41 browser.test.sendMessage("result", [error.name, error.message]); 42 }); 43 }; 44 }; 45 46 const contentScript = function () { 47 document.querySelector("button").addEventListener("click", function (e) { 48 clipboardRead(); 49 }); 50 browser.test.sendMessage("ready"); 51 }; 52 53 const backgroundScript = function () { 54 clipboardRead(); 55 }; 56 57 add_setup(async function () { 58 await SpecialPowers.pushPrefEnv({ 59 set: [["test.events.async.enabled", true]], 60 }); 61 }); 62 63 // There’s another test that checks calling read() without permission in 64 // toolkit/components/extensions/test/mochitest/test_ext_async_clipboard.html. 65 describe("test extension without clipboardRead permission", () => { 66 it("test content script", async function () { 67 const text = await promiseWritingRandomTextToClipboard(); 68 const extensionData = { 69 manifest: { 70 content_scripts: [ 71 { 72 js: ["sharedScript.js", "contentscript.js"], 73 matches: ["https://example.com/*"], 74 }, 75 ], 76 }, 77 files: { 78 "sharedScript.js": sharedScript, 79 "contentscript.js": contentScript, 80 }, 81 }; 82 const extension = ExtensionTestUtils.loadExtension(extensionData); 83 await extension.startup(); 84 await BrowserTestUtils.withNewTab(kContentFileUrl, async browser => { 85 await extension.awaitMessage("ready"); 86 87 // click on the content and wait for pasted button shown. 88 await Promise.all([ 89 promisePasteButtonIsShown(), 90 promiseClickContentElement(browser, "btn"), 91 ]); 92 93 // click on the paste button. 94 let resultPromise = extension.awaitMessage("result"); 95 await Promise.all([ 96 promisePasteButtonIsHidden(), 97 promiseClickPasteButton(), 98 ]); 99 is(await resultPromise, text, "check read() result"); 100 101 // click on the content and wait for pasted button shown. 102 await Promise.all([ 103 promisePasteButtonIsShown(), 104 promiseClickContentElement(browser, "btn"), 105 ]); 106 107 // dismiss the paste button. 108 resultPromise = extension.awaitMessage("result"); 109 await Promise.all([ 110 promisePasteButtonIsHidden(), 111 promiseDismissPasteButton(), 112 ]); 113 const [name, message] = await resultPromise; 114 is(name, "NotAllowedError", "check read() error name"); 115 is( 116 message, 117 "Clipboard read operation is not allowed.", 118 "check read() error message" 119 ); 120 }); 121 await extension.unload(); 122 }); 123 124 describe("test background script", () => { 125 // paste button should not be shown during the test. 126 const popupShownListener = function (e) { 127 const pastePopup = document.getElementById(kPasteMenuPopupId); 128 if (e.target != pastePopup) { 129 return; 130 } 131 ok( 132 false, 133 "Paste popup should not be shown for extension with permission" 134 ); 135 promiseDismissPasteButton(); 136 }; 137 beforeEach(() => { 138 document.addEventListener("popupshown", popupShownListener); 139 }); 140 afterEach(() => { 141 document.removeEventListener("popupshown", popupShownListener); 142 }); 143 144 it("test without user activation", async function () { 145 const text = await promiseWritingRandomTextToClipboard(); 146 const extensionData = { 147 background: [sharedScript, backgroundScript], 148 }; 149 const extension = ExtensionTestUtils.loadExtension(extensionData); 150 await extension.startup(); 151 const [name, message] = await extension.awaitMessage("result"); 152 is(name, "NotAllowedError", "check read() error name"); 153 is( 154 message, 155 "Clipboard read request was blocked due to lack of user activation.", 156 "check read() error message" 157 ); 158 await extension.unload(); 159 }); 160 161 it("test with user activation", async function () { 162 const backgroundScriptWithUserActivation = function () { 163 browser.test.withHandlingUserInput(() => { 164 clipboardRead(); 165 }); 166 }; 167 const text = await promiseWritingRandomTextToClipboard(); 168 const extensionData = { 169 background: [sharedScript, backgroundScriptWithUserActivation], 170 }; 171 const extension = ExtensionTestUtils.loadExtension(extensionData); 172 await extension.startup(); 173 const [name, message] = await extension.awaitMessage("result"); 174 is(name, "NotAllowedError", "check read() error name"); 175 is( 176 message, 177 "Clipboard read operation is not allowed.", 178 "check read() error message" 179 ); 180 await extension.unload(); 181 }); 182 }); 183 }); 184 185 // There’s another test that checks calling read() with permission in 186 // toolkit/components/extensions/test/mochitest/test_ext_async_clipboard.html. 187 describe("test extension with clipboardRead permission", () => { 188 // paste button should not be shown during the test. 189 const popupShownListener = function (e) { 190 const pastePopup = document.getElementById(kPasteMenuPopupId); 191 if (e.target != pastePopup) { 192 return; 193 } 194 ok(false, "Paste popup should not be shown for extension with permission"); 195 promiseDismissPasteButton(); 196 }; 197 beforeEach(() => { 198 document.addEventListener("popupshown", popupShownListener); 199 }); 200 afterEach(() => { 201 document.removeEventListener("popupshown", popupShownListener); 202 }); 203 204 it("test content script", async function () { 205 const text = await promiseWritingRandomTextToClipboard(); 206 const extensionData = { 207 manifest: { 208 content_scripts: [ 209 { 210 js: ["sharedScript.js", "contentscript.js"], 211 matches: ["https://example.com/*"], 212 }, 213 ], 214 permissions: ["clipboardRead"], 215 }, 216 files: { 217 "sharedScript.js": sharedScript, 218 "contentscript.js": contentScript, 219 }, 220 }; 221 const extension = ExtensionTestUtils.loadExtension(extensionData); 222 await extension.startup(); 223 await BrowserTestUtils.withNewTab(kContentFileUrl, async browser => { 224 await extension.awaitMessage("ready"); 225 226 // extension with clipboardRead permission should not show paste button. 227 const resultPromise = extension.awaitMessage("result"); 228 await promiseClickContentElement(browser, "btn"); 229 is(await resultPromise, text, "check read() result"); 230 }); 231 await extension.unload(); 232 }); 233 234 it("test background script", async function () { 235 const text = await promiseWritingRandomTextToClipboard(); 236 const extensionData = { 237 background: [sharedScript, backgroundScript], 238 manifest: { 239 permissions: ["clipboardRead"], 240 }, 241 }; 242 const extension = ExtensionTestUtils.loadExtension(extensionData); 243 await extension.startup(); 244 is(await extension.awaitMessage("result"), text, "check read() result"); 245 await extension.unload(); 246 }); 247 });