tor-browser

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

browser_ext_getViews.js (12272B)


      1 /* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
      2 /* vim: set sts=2 sw=2 et tw=80: */
      3 "use strict";
      4 
      5 function genericChecker() {
      6  let kind = window.location.search.slice(1) || "background";
      7  window.kind = kind;
      8 
      9  let bcGroupId = SpecialPowers.wrap(window).browsingContext.group.id;
     10 
     11  browser.test.onMessage.addListener((msg, ...args) => {
     12    if (msg == kind + "-check-views") {
     13      let counts = {
     14        background: 0,
     15        tab: 0,
     16        popup: 0,
     17        kind: 0,
     18        sidebar: 0,
     19      };
     20      if (kind !== "background") {
     21        counts.kind = browser.extension.getViews({ type: kind }).length;
     22      }
     23      let views = browser.extension.getViews();
     24      let background;
     25      for (let i = 0; i < views.length; i++) {
     26        let view = views[i];
     27        browser.test.assertTrue(view.kind in counts, "view type is valid");
     28        counts[view.kind]++;
     29        if (view.kind == "background") {
     30          browser.test.assertTrue(
     31            view === browser.extension.getBackgroundPage(),
     32            "background page is correct"
     33          );
     34          background = view;
     35        }
     36 
     37        browser.test.assertEq(
     38          bcGroupId,
     39          SpecialPowers.wrap(view).browsingContext.group.id,
     40          "browsing context group is correct"
     41        );
     42      }
     43      if (background) {
     44        browser.runtime.getBackgroundPage().then(view => {
     45          browser.test.assertEq(
     46            background,
     47            view,
     48            "runtime.getBackgroundPage() is correct"
     49          );
     50          browser.test.sendMessage("counts", counts);
     51        });
     52      } else {
     53        browser.test.sendMessage("counts", counts);
     54      }
     55    } else if (msg == kind + "-getViews-with-filter") {
     56      let filter = args[0];
     57      let count = browser.extension.getViews(filter).length;
     58      browser.test.sendMessage("getViews-count", count);
     59    } else if (msg == kind + "-open-tab") {
     60      let url = browser.runtime.getURL("page.html?tab");
     61      browser.tabs
     62        .create({ windowId: args[0], url })
     63        .then(tab => browser.test.sendMessage("opened-tab", tab.id));
     64    } else if (msg == kind + "-close-tab") {
     65      browser.tabs.query(
     66        {
     67          windowId: args[0],
     68        },
     69        tabs => {
     70          let tab = tabs.find(tab => tab.url.includes("page.html?tab"));
     71          browser.tabs.remove(tab.id, () => {
     72            browser.test.sendMessage("closed");
     73          });
     74        }
     75      );
     76    }
     77  });
     78  browser.test.sendMessage(kind + "-ready");
     79 }
     80 
     81 add_task(async function () {
     82  let win1 = await BrowserTestUtils.openNewBrowserWindow();
     83  let win2 = await BrowserTestUtils.openNewBrowserWindow();
     84 
     85  let extension = ExtensionTestUtils.loadExtension({
     86    useAddonManager: "temporary", // To automatically show sidebar on load.
     87    manifest: {
     88      permissions: ["tabs"],
     89 
     90      browser_action: {
     91        default_popup: "page.html?popup",
     92        default_area: "navbar",
     93      },
     94 
     95      sidebar_action: {
     96        default_panel: "page.html?sidebar",
     97      },
     98    },
     99 
    100    files: {
    101      "page.html": `
    102      <!DOCTYPE html>
    103      <html>
    104      <head><meta charset="utf-8"></head>
    105      <body>
    106      <script src="page.js"></script>
    107      </body></html>
    108      `,
    109 
    110      "page.js": genericChecker,
    111    },
    112 
    113    background: genericChecker,
    114  });
    115 
    116  await Promise.all([
    117    extension.startup(),
    118    extension.awaitMessage("background-ready"),
    119  ]);
    120 
    121  await extension.awaitMessage("sidebar-ready");
    122  await extension.awaitMessage("sidebar-ready");
    123  await extension.awaitMessage("sidebar-ready");
    124 
    125  info("started");
    126 
    127  const {
    128    Management: {
    129      global: { windowTracker },
    130    },
    131  } = ChromeUtils.importESModule("resource://gre/modules/Extension.sys.mjs");
    132 
    133  let winId1 = windowTracker.getId(win1);
    134  let winId2 = windowTracker.getId(win2);
    135 
    136  async function openTab(winId) {
    137    extension.sendMessage("background-open-tab", winId);
    138    await extension.awaitMessage("tab-ready");
    139    return extension.awaitMessage("opened-tab");
    140  }
    141 
    142  async function checkViews(kind, tabCount, popupCount, kindCount) {
    143    extension.sendMessage(kind + "-check-views");
    144    let counts = await extension.awaitMessage("counts");
    145    if (kind === "sidebar") {
    146      // We have 3 sidebars thaat will answer.
    147      await extension.awaitMessage("counts");
    148      await extension.awaitMessage("counts");
    149    }
    150    is(counts.background, 1, "background count correct");
    151    is(counts.tab, tabCount, "tab count correct");
    152    is(counts.popup, popupCount, "popup count correct");
    153    is(counts.kind, kindCount, "count for type correct");
    154    is(counts.sidebar, 3, "sidebar count is constant");
    155  }
    156 
    157  async function checkViewsWithFilter(filter, expectedCount) {
    158    extension.sendMessage("background-getViews-with-filter", filter);
    159    let count = await extension.awaitMessage("getViews-count");
    160    is(count, expectedCount, `count for ${JSON.stringify(filter)} correct`);
    161  }
    162 
    163  await checkViews("background", 0, 0, 0);
    164  await checkViews("sidebar", 0, 0, 3);
    165  await checkViewsWithFilter({ windowId: -1 }, 1);
    166  await checkViewsWithFilter({ windowId: 0 }, 0);
    167  await checkViewsWithFilter({ tabId: -1 }, 4);
    168  await checkViewsWithFilter({ tabId: 0 }, 0);
    169 
    170  let tabId1 = await openTab(winId1);
    171 
    172  await checkViews("background", 1, 0, 0);
    173  await checkViews("sidebar", 1, 0, 3);
    174  await checkViews("tab", 1, 0, 1);
    175  await checkViewsWithFilter({ windowId: winId1 }, 2);
    176  await checkViewsWithFilter({ tabId: tabId1 }, 1);
    177 
    178  let tabId2 = await openTab(winId2);
    179 
    180  await checkViews("background", 2, 0, 0);
    181  await checkViews("sidebar", 2, 0, 3);
    182  await checkViewsWithFilter({ windowId: winId2 }, 2);
    183  await checkViewsWithFilter({ tabId: tabId2 }, 1);
    184 
    185  async function triggerPopup(win, callback) {
    186    // Window needs focus to open popups.
    187    await focusWindow(win);
    188    await clickBrowserAction(extension, win);
    189    let browser = await awaitExtensionPanel(extension, win);
    190 
    191    await extension.awaitMessage("popup-ready");
    192 
    193    await callback();
    194 
    195    let { unloadPromise } = await promiseBrowserContentUnloaded(browser);
    196    closeBrowserAction(extension, win);
    197    await unloadPromise;
    198  }
    199 
    200  await triggerPopup(win1, async function () {
    201    await checkViews("background", 2, 1, 0);
    202    await checkViews("sidebar", 2, 1, 3);
    203    await checkViews("popup", 2, 1, 1);
    204    await checkViewsWithFilter({ windowId: winId1 }, 3);
    205    await checkViewsWithFilter({ type: "popup", tabId: -1 }, 1);
    206  });
    207 
    208  await triggerPopup(win2, async function () {
    209    await checkViews("background", 2, 1, 0);
    210    await checkViews("sidebar", 2, 1, 3);
    211    await checkViews("popup", 2, 1, 1);
    212    await checkViewsWithFilter({ windowId: winId2 }, 3);
    213    await checkViewsWithFilter({ type: "popup", tabId: -1 }, 1);
    214  });
    215 
    216  info("checking counts after popups");
    217 
    218  await checkViews("background", 2, 0, 0);
    219  await checkViews("sidebar", 2, 0, 3);
    220  await checkViewsWithFilter({ windowId: winId1 }, 2);
    221  await checkViewsWithFilter({ tabId: -1 }, 4);
    222 
    223  info("closing one tab");
    224 
    225  let { unloadPromise } = await promiseBrowserContentUnloaded(
    226    win1.gBrowser.selectedBrowser
    227  );
    228  extension.sendMessage("background-close-tab", winId1);
    229  await extension.awaitMessage("closed");
    230  await unloadPromise;
    231 
    232  info("one tab closed, one remains");
    233 
    234  await checkViews("background", 1, 0, 0);
    235  await checkViews("sidebar", 1, 0, 3);
    236 
    237  info("opening win1 popup");
    238 
    239  await triggerPopup(win1, async function () {
    240    await checkViews("background", 1, 1, 0);
    241    await checkViews("sidebar", 1, 1, 3);
    242    await checkViews("tab", 1, 1, 1);
    243    await checkViews("popup", 1, 1, 1);
    244  });
    245 
    246  info("opening win2 popup");
    247 
    248  await triggerPopup(win2, async function () {
    249    await checkViews("background", 1, 1, 0);
    250    await checkViews("sidebar", 1, 1, 3);
    251    await checkViews("tab", 1, 1, 1);
    252    await checkViews("popup", 1, 1, 1);
    253  });
    254 
    255  await checkViews("sidebar", 1, 0, 3);
    256 
    257  await extension.unload();
    258 
    259  await BrowserTestUtils.closeWindow(win1);
    260  await BrowserTestUtils.closeWindow(win2);
    261 });
    262 
    263 add_task(async function test_getViews_excludes_blocked_parsing_documents() {
    264  const extension = ExtensionTestUtils.loadExtension({
    265    manifest: {
    266      browser_action: {
    267        default_popup: "popup.html",
    268        default_area: "navbar",
    269      },
    270    },
    271    files: {
    272      "popup.html": `<!DOCTYPE html>
    273        <script src="popup.js">
    274        </script>
    275        <h1>ExtensionPopup</h1>
    276      `,
    277      "popup.js": function () {
    278        browser.test.sendMessage(
    279          "browserActionPopup:loaded",
    280          window.location.href
    281        );
    282      },
    283    },
    284    background() {
    285      browser.test.onMessage.addListener(msg => {
    286        browser.test.assertEq("getViews", msg, "Got the expected test message");
    287        const views = browser.extension
    288          .getViews()
    289          .map(win => win.location?.href);
    290 
    291        browser.test.sendMessage("getViews:done", views);
    292      });
    293      browser.test.sendMessage("bgpage:loaded", window.location.href);
    294    },
    295  });
    296 
    297  await extension.startup();
    298  const bgpageURL = await extension.awaitMessage("bgpage:loaded");
    299  extension.sendMessage("getViews");
    300  Assert.deepEqual(
    301    await extension.awaitMessage("getViews:done"),
    302    [bgpageURL],
    303    "Expect only the background page to be initially listed in getViews"
    304  );
    305 
    306  const {
    307    Management: {
    308      global: { browserActionFor },
    309    },
    310  } = ChromeUtils.importESModule("resource://gre/modules/Extension.sys.mjs");
    311 
    312  let ext = WebExtensionPolicy.getByID(extension.id)?.extension;
    313  let browserAction = browserActionFor(ext);
    314 
    315  // Ensure the mouse is not initially hovering the browserAction widget.
    316  EventUtils.synthesizeMouseAtCenter(
    317    window.gURLBar,
    318    { type: "mouseover" },
    319    window
    320  );
    321 
    322  let widget = await TestUtils.waitForCondition(
    323    () => getBrowserActionWidget(extension).forWindow(window),
    324    "Wait for browserAction widget"
    325  );
    326 
    327  await TestUtils.waitForCondition(
    328    () => !browserAction.pendingPopup,
    329    "Wait for no pending preloaded popup"
    330  );
    331 
    332  await TestUtils.waitForCondition(async () => {
    333    // Trigger preload browserAction popup (by directly dispatching a MouseEvent
    334    // to prevent intermittent failures that where often triggered in macos
    335    // PGO builds when this was using EventUtils.synthesizeMouseAtCenter).
    336    let mouseOverEvent = new MouseEvent("mouseover");
    337    widget.node
    338      .querySelector(".unified-extensions-item-action-button")
    339      .dispatchEvent(mouseOverEvent);
    340 
    341    await TestUtils.waitForCondition(
    342      () => browserAction.pendingPopup?.browser,
    343      "Wait for pending preloaded popup browser"
    344    );
    345 
    346    return SpecialPowers.spawn(
    347      browserAction.pendingPopup.browser,
    348      [],
    349      async () => {
    350        const policy = this.content.WebExtensionPolicy.getByHostname(
    351          this.content.location.hostname
    352        );
    353        return policy?.weakExtension
    354          ?.get()
    355          ?.blockedParsingDocuments.has(this.content.document);
    356      }
    357    ).catch(err => {
    358      // Tolerate errors triggered by SpecialPowers.spawn
    359      // being aborted before we got a result back.
    360      if (err.name === "AbortError") {
    361        return false;
    362      }
    363      throw err;
    364    });
    365  }, "Wait for preload browserAction document to be blocked on parsing");
    366 
    367  extension.sendMessage("getViews");
    368  Assert.deepEqual(
    369    await extension.awaitMessage("getViews:done"),
    370    [bgpageURL],
    371    "Expect preloaded browserAction popup to not be listed in getViews"
    372  );
    373 
    374  // Test browserAction popup is listed in getViews once document parser is unblocked.
    375  EventUtils.synthesizeMouseAtCenter(
    376    widget.node,
    377    { type: "mousedown", button: 0 },
    378    window
    379  );
    380  EventUtils.synthesizeMouseAtCenter(
    381    widget.node,
    382    { type: "mouseup", button: 0 },
    383    window
    384  );
    385 
    386  const popupURL = await extension.awaitMessage("browserActionPopup:loaded");
    387 
    388  extension.sendMessage("getViews");
    389  Assert.deepEqual(
    390    (await extension.awaitMessage("getViews:done")).sort(),
    391    [bgpageURL, popupURL].sort(),
    392    "Expect loaded browserAction popup to be listed in getViews"
    393  );
    394 
    395  // Ensure the mouse is not hovering the browserAction widget anymore when exiting the test case.
    396  EventUtils.synthesizeMouseAtCenter(
    397    window.gURLBar,
    398    { type: "mouseover", button: 0 },
    399    window
    400  );
    401 
    402  await extension.unload();
    403 });