tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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