tor-browser

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

browser_NavigableManager.js (16746B)


      1 /* Any copyright is dedicated to the Public Domain.
      2 * http://creativecommons.org/publicdomain/zero/1.0/ */
      3 
      4 "use strict";
      5 
      6 const { NavigableManager } = ChromeUtils.importESModule(
      7  "chrome://remote/content/shared/NavigableManager.sys.mjs"
      8 );
      9 
     10 const BUILDER_URL = "https://example.com/document-builder.sjs?html=";
     11 const FRAME_URL = "https://example.com/document-builder.sjs?html=frame";
     12 const FRAME_MARKUP = `
     13  <iframe src="${encodeURI(FRAME_URL)}"></iframe>
     14  <iframe src="${encodeURI(FRAME_URL)}"></iframe>
     15 `;
     16 const TEST_URL = BUILDER_URL + encodeURI(FRAME_MARKUP);
     17 
     18 describe("NavigableManager", function () {
     19  let testData;
     20 
     21  beforeEach(async () => {
     22    NavigableManager.startTracking();
     23 
     24    const initialBrowser = gBrowser.selectedBrowser;
     25    const initialContext = initialBrowser.browsingContext;
     26 
     27    info(`Open a new tab and navigate to ${TEST_URL}`);
     28    const newTab = await addTabAndWaitForNavigated(gBrowser, TEST_URL);
     29 
     30    const newBrowser = newTab.linkedBrowser;
     31    const newContext = newBrowser.browsingContext;
     32    const newFrameContexts = newContext
     33      .getAllBrowsingContextsInSubtree()
     34      .filter(context => context.parent);
     35 
     36    is(newFrameContexts.length, 2, "Top context has 2 child contexts");
     37 
     38    testData = {
     39      initialBrowser,
     40      initialContext,
     41      newBrowser,
     42      newContext,
     43      newFrameContexts,
     44      newTab,
     45    };
     46  });
     47 
     48  afterEach(() => {
     49    NavigableManager.stopTracking();
     50 
     51    gBrowser.removeAllTabsBut(gBrowser.tabs[0]);
     52  });
     53 
     54  it("Get the browser by its Navigable id", async function test_getBrowserById() {
     55    const { initialBrowser, newBrowser } = testData;
     56 
     57    const invalidValues = [undefined, null, 1, "foo", {}, []];
     58    invalidValues.forEach(value =>
     59      is(NavigableManager.getBrowserById(value), null)
     60    );
     61 
     62    const initialBrowserId = NavigableManager.getIdForBrowser(initialBrowser);
     63    const newBrowserId = NavigableManager.getIdForBrowser(newBrowser);
     64 
     65    Assert.stringMatches(
     66      initialBrowserId,
     67      uuidRegex,
     68      "Initial browser is a valid uuid"
     69    );
     70    Assert.stringMatches(
     71      newBrowserId,
     72      uuidRegex,
     73      "New tab's browser is a valid uuid"
     74    );
     75    isnot(initialBrowserId, newBrowserId, "Both browsers have different ids");
     76 
     77    is(NavigableManager.getBrowserById(initialBrowserId), initialBrowser);
     78    is(NavigableManager.getBrowserById(newBrowserId), newBrowser);
     79  });
     80 
     81  it("Get the BrowsingContext by its Navigable id", async function test_getBrowsingContextById() {
     82    const { newContext, newFrameContexts } = testData;
     83 
     84    const invalidValues = [undefined, null, "foo", {}, []];
     85    invalidValues.forEach(value =>
     86      is(NavigableManager.getBrowsingContextById(value), null)
     87    );
     88 
     89    const newContextId = NavigableManager.getIdForBrowsingContext(newContext);
     90    const newFrameContextIds = newFrameContexts.map(context =>
     91      NavigableManager.getIdForBrowsingContext(context)
     92    );
     93 
     94    Assert.stringMatches(
     95      newContextId,
     96      uuidRegex,
     97      "Top context is a valid uuid"
     98    );
     99    Assert.stringMatches(
    100      newFrameContextIds[0],
    101      numberRegex,
    102      "First child context has a valid id"
    103    );
    104    Assert.stringMatches(
    105      newFrameContextIds[1],
    106      numberRegex,
    107      "Second child context has a valid id"
    108    );
    109    isnot(
    110      newContextId,
    111      newFrameContextIds[0],
    112      "Id of top-level context is different from first child context"
    113    );
    114    isnot(
    115      newContextId,
    116      newFrameContextIds[0],
    117      "Id of top-level context is different from second child context"
    118    );
    119    is(
    120      NavigableManager.getBrowsingContextById(newFrameContextIds[0]),
    121      newFrameContexts[0],
    122      "Context of first child can be retrieved by id"
    123    );
    124    is(
    125      NavigableManager.getBrowsingContextById(newFrameContextIds[1]),
    126      newFrameContexts[1],
    127      "Context of second child can be retrieved by id"
    128    );
    129  });
    130 
    131  it("Get the Navigable id for a browser", async function test_getIdForBrowser() {
    132    const { initialBrowser, newBrowser, newContext } = testData;
    133 
    134    const invalidValues = [undefined, null, 1, "foo", {}, []];
    135    invalidValues.forEach(value =>
    136      is(NavigableManager.getBrowserById(value), null)
    137    );
    138 
    139    is(
    140      NavigableManager.getIdForBrowser(newContext),
    141      null,
    142      "Requires a browser instance as argument"
    143    );
    144 
    145    const newBrowserId = NavigableManager.getIdForBrowser(newBrowser);
    146    Assert.stringMatches(
    147      NavigableManager.getIdForBrowser(newBrowser),
    148      uuidRegex,
    149      "Got a valid uuid for the browser"
    150    );
    151    is(
    152      NavigableManager.getIdForBrowser(newBrowser),
    153      newBrowserId,
    154      "For the same browser the identical id is returned"
    155    );
    156    isnot(
    157      NavigableManager.getIdForBrowser(initialBrowser),
    158      newBrowserId,
    159      "For a different browser the id is not the same"
    160    );
    161  });
    162 
    163  it("Get the Navigable id for a BrowsingContext", async function test_getIdForBrowsingContext() {
    164    const { newBrowser, newContext, newFrameContexts } = testData;
    165 
    166    const invalidValues = [undefined, null, 1, "foo", {}, []];
    167    invalidValues.forEach(value =>
    168      is(NavigableManager.getIdForBrowsingContext(value), null)
    169    );
    170 
    171    const newContextId = NavigableManager.getIdForBrowsingContext(newContext);
    172    const newFrameContextIds = newFrameContexts.map(context =>
    173      NavigableManager.getIdForBrowsingContext(context)
    174    );
    175 
    176    Assert.stringMatches(
    177      newContextId,
    178      uuidRegex,
    179      "Got a valid uuid for top-level context"
    180    );
    181    is(
    182      NavigableManager.getIdForBrowsingContext(newContext),
    183      newContextId,
    184      "Id is always the same for a top-level context"
    185    );
    186    is(
    187      NavigableManager.getIdForBrowsingContext(newContext),
    188      NavigableManager.getIdForBrowser(newBrowser),
    189      "Id of a top-level context is equal to the browser id"
    190    );
    191 
    192    Assert.stringMatches(
    193      newFrameContextIds[0],
    194      numberRegex,
    195      "Got a valid id for a child context"
    196    );
    197    is(
    198      NavigableManager.getIdForBrowsingContext(newFrameContexts[0]),
    199      newFrameContextIds[0],
    200      "Id is always the same for a child context"
    201    );
    202  });
    203 
    204  it("Get the Navigable for a BrowsingContext", async function test_getNavigableForBrowsingContext() {
    205    const { newBrowser, newContext, newFrameContexts, newTab } = testData;
    206 
    207    const invalidValues = [undefined, null, 1, "test", {}, [], newBrowser];
    208    invalidValues.forEach(invalidValue =>
    209      Assert.throws(
    210        () => NavigableManager.getNavigableForBrowsingContext(invalidValue),
    211        /Expected browsingContext to be a CanonicalBrowsingContext/
    212      )
    213    );
    214 
    215    is(
    216      NavigableManager.getNavigableForBrowsingContext(newContext),
    217      newBrowser,
    218      "Top-Level context has the content browser as navigable"
    219    );
    220    is(
    221      NavigableManager.getNavigableForBrowsingContext(newFrameContexts[0]),
    222      newFrameContexts[0],
    223      "Child context has itself as navigable"
    224    );
    225 
    226    gBrowser.removeTab(newTab);
    227  });
    228 
    229  it("Get discarded BrowsingContext by id", async function test_getDiscardedBrowsingContextById() {
    230    const { newBrowser, newContext, newFrameContexts, newTab } = testData;
    231 
    232    const newContextId = NavigableManager.getIdForBrowsingContext(newContext);
    233    const newFrameContextIds = newFrameContexts.map(context =>
    234      NavigableManager.getIdForBrowsingContext(context)
    235    );
    236 
    237    is(
    238      NavigableManager.getBrowsingContextById(newContextId),
    239      newContext,
    240      "Top context can be retrieved by its id"
    241    );
    242    is(
    243      NavigableManager.getBrowsingContextById(newFrameContextIds[0]),
    244      newFrameContexts[0],
    245      "Child context can be retrieved by its id"
    246    );
    247 
    248    // Remove all the iframes
    249    await SpecialPowers.spawn(newBrowser, [], async () => {
    250      const frames = content.document.querySelectorAll("iframe");
    251      frames.forEach(frame => frame.remove());
    252    });
    253 
    254    is(
    255      NavigableManager.getBrowsingContextById(newContextId),
    256      newContext,
    257      "Top context can still be retrieved after removing all the frames"
    258    );
    259    is(
    260      NavigableManager.getBrowsingContextById(newFrameContextIds[0]),
    261      null,
    262      "Child context can no longer be retrieved by its id after removing all the frames"
    263    );
    264 
    265    gBrowser.removeTab(newTab);
    266 
    267    is(
    268      NavigableManager.getBrowsingContextById(newContextId),
    269      null,
    270      "Top context can no longer be retrieved by its id after the tab is closed"
    271    );
    272  });
    273 
    274  it("Support unloaded browsers", async function test_unloadedBrowser() {
    275    const { newBrowser, newContext, newTab } = testData;
    276 
    277    const newBrowserId = NavigableManager.getIdForBrowser(newBrowser);
    278    const newContextId = NavigableManager.getIdForBrowsingContext(newContext);
    279 
    280    await gBrowser.discardBrowser(newTab);
    281 
    282    is(
    283      NavigableManager.getIdForBrowser(newBrowser),
    284      newBrowserId,
    285      "Id for the browser is still available after unloading the tab"
    286    );
    287    is(
    288      NavigableManager.getBrowserById(newBrowserId),
    289      newBrowser,
    290      "Unloaded browser can still be retrieved by id"
    291    );
    292 
    293    is(
    294      NavigableManager.getIdForBrowsingContext(newContext),
    295      newContextId,
    296      "Id for the browsing context is still available after unloading the tab"
    297    );
    298    is(
    299      NavigableManager.getBrowsingContextById(newContextId),
    300      null,
    301      "Browsing context can no longer be retrieved after unloading the tab"
    302    );
    303    Assert.throws(
    304      () => NavigableManager.getNavigableForBrowsingContext(newContext),
    305      /Expected browsingContext to be a CanonicalBrowsingContext/
    306    );
    307 
    308    // Loading a new page for new browsing contexts
    309    await loadURL(newBrowser, TEST_URL);
    310 
    311    const newBrowsingContext = newBrowser.browsingContext;
    312 
    313    is(
    314      NavigableManager.getIdForBrowser(newBrowser),
    315      newBrowserId,
    316      "Id for the browser is still the same when navigating after unloading the tab"
    317    );
    318    is(
    319      NavigableManager.getBrowserById(newBrowserId),
    320      newBrowser,
    321      "New browser can be retrieved by its id"
    322    );
    323 
    324    is(
    325      NavigableManager.getIdForBrowsingContext(newBrowsingContext),
    326      newContextId,
    327      "Id for the new top-level context is still the same"
    328    );
    329    is(
    330      NavigableManager.getBrowsingContextById(newContextId),
    331      newBrowsingContext,
    332      "Top-level context can be retrieved again"
    333    );
    334    is(
    335      NavigableManager.getNavigableForBrowsingContext(newBrowsingContext),
    336      newBrowser,
    337      "The navigable can be retrieved again"
    338    );
    339  });
    340 
    341  it("Retrieve id for cross-group opener", async function test_crossGroupOpener() {
    342    const { newContext, newBrowser, newTab } = testData;
    343 
    344    const newContextId = NavigableManager.getIdForBrowsingContext(newContext);
    345 
    346    await SpecialPowers.spawn(newBrowser, [], async () => {
    347      content.open("", "_blank", "");
    348    });
    349 
    350    const browser = gBrowser.selectedBrowser;
    351    const openerContext = browser.browsingContext.crossGroupOpener;
    352 
    353    isnot(
    354      browser.browsingContext.crossGroupOpener,
    355      null,
    356      "Opened popup window has a cross-group opener"
    357    );
    358    is(
    359      NavigableManager.getIdForBrowsingContext(openerContext),
    360      newContextId,
    361      "Id of cross-group opener context is correct"
    362    );
    363 
    364    // Remove the tab which opened the popup window
    365    gBrowser.removeTab(newTab);
    366 
    367    is(
    368      NavigableManager.getIdForBrowsingContext(openerContext),
    369      newContextId,
    370      "Id of cross-group opener context is still correct after closing the opener tab"
    371    );
    372  });
    373 
    374  it("Start and stop tracking of browsing contexts", async function test_startStopTracking() {
    375    async function addUnloadedTab(tabBrowser) {
    376      info(`Open a new tab, navigate, and unload it immediately`);
    377      const newTab = await addTabAndWaitForNavigated(tabBrowser, TEST_URL);
    378 
    379      const newBrowser = newTab.linkedBrowser;
    380      const newContext = newBrowser.browsingContext;
    381 
    382      const newFrameContexts = newContext
    383        .getAllBrowsingContextsInSubtree()
    384        .filter(context => context.parent);
    385 
    386      info(`Unload the newly opened tab`);
    387      await tabBrowser.discardBrowser(newTab);
    388 
    389      return {
    390        newBrowser,
    391        newContext,
    392        newFrameContexts,
    393        newTab,
    394      };
    395    }
    396 
    397    // Calling start tracking multiple times doesn't cause failures.
    398    NavigableManager.startTracking();
    399    NavigableManager.startTracking();
    400 
    401    {
    402      // Stop tracking of new browsing contexts
    403      NavigableManager.stopTracking();
    404 
    405      let { newBrowser, newContext } = await addUnloadedTab(gBrowser);
    406 
    407      Assert.stringMatches(
    408        NavigableManager.getIdForBrowser(newBrowser),
    409        uuidRegex,
    410        "There is always a valid uuid for the browser"
    411      );
    412      is(
    413        NavigableManager.getIdForBrowsingContext(newContext),
    414        null,
    415        "There is no id of a temporarily open top-level context"
    416      );
    417    }
    418 
    419    // Calling stop tracking multiple times doesn't cause failures.
    420    NavigableManager.stopTracking();
    421 
    422    // Re-enable tracking
    423    NavigableManager.startTracking();
    424 
    425    let { newBrowser, newContext } = await addUnloadedTab(gBrowser);
    426 
    427    Assert.stringMatches(
    428      NavigableManager.getIdForBrowser(newBrowser),
    429      uuidRegex,
    430      "Got a valid uuid for the browser"
    431    );
    432    Assert.stringMatches(
    433      NavigableManager.getIdForBrowsingContext(newContext),
    434      uuidRegex,
    435      "Got a valid uuid for the top-level context"
    436    );
    437  });
    438 
    439  it("Get the Navigable id for a chrome browsing context", async function test_getIdForChromeBrowsingContext() {
    440    // Get the parent process browsing context (chrome scope)
    441    const chromeContext = window.browsingContext;
    442 
    443    ok(!chromeContext.isContent, "Chrome context is not a content context");
    444 
    445    const chromeContextId =
    446      NavigableManager.getIdForBrowsingContext(chromeContext);
    447 
    448    Assert.stringMatches(
    449      chromeContextId,
    450      uuidRegex,
    451      "Got a valid uuid for chrome browsing context"
    452    );
    453 
    454    is(
    455      NavigableManager.getIdForBrowsingContext(chromeContext),
    456      chromeContextId,
    457      "Id is always the same for the same chrome browsing context"
    458    );
    459 
    460    const chromeContext2 = BrowsingContext.getFromWindow(
    461      Services.wm.getMostRecentWindow("navigator:browser")
    462    );
    463    is(
    464      NavigableManager.getIdForBrowsingContext(chromeContext2),
    465      chromeContextId,
    466      "Same chrome context returns same id when retrieved differently"
    467    );
    468  });
    469 
    470  it("Get chrome browsing context by its Navigable id", async function test_getChromeBrowsingContextById() {
    471    const chromeContext = BrowsingContext.getFromWindow(window);
    472 
    473    ok(!chromeContext.isContent, "Chrome context is not a content context");
    474 
    475    const chromeContextId =
    476      NavigableManager.getIdForBrowsingContext(chromeContext);
    477 
    478    is(
    479      NavigableManager.getBrowsingContextById(chromeContextId),
    480      chromeContext,
    481      "Chrome browsing context can be retrieved by its id"
    482    );
    483  });
    484 
    485  it("Chrome browsing contexts have different ids than content contexts", async function test_chromeVsContentIds() {
    486    const { newContext } = testData;
    487    const chromeContext = BrowsingContext.getFromWindow(window);
    488 
    489    const chromeContextId =
    490      NavigableManager.getIdForBrowsingContext(chromeContext);
    491    const contentContextId =
    492      NavigableManager.getIdForBrowsingContext(newContext);
    493 
    494    Assert.stringMatches(
    495      chromeContextId,
    496      uuidRegex,
    497      "Chrome context has valid uuid"
    498    );
    499    Assert.stringMatches(
    500      contentContextId,
    501      uuidRegex,
    502      "Content context has valid uuid"
    503    );
    504 
    505    isnot(
    506      chromeContextId,
    507      contentContextId,
    508      "Chrome and content contexts have different ids"
    509    );
    510  });
    511 
    512  it("Chrome browsing context cleanup on discard", async function test_chromeBrowsingContextDiscard() {
    513    // Open a new window to get a chrome browsing context we can close
    514    const newWindow = await BrowserTestUtils.openNewBrowserWindow();
    515    const newChromeContext = BrowsingContext.getFromWindow(newWindow);
    516 
    517    ok(
    518      !newChromeContext.isContent,
    519      "New window's chrome context is not a content context"
    520    );
    521 
    522    const chromeContextId =
    523      NavigableManager.getIdForBrowsingContext(newChromeContext);
    524 
    525    Assert.stringMatches(
    526      chromeContextId,
    527      uuidRegex,
    528      "Got a valid uuid for new window's chrome context"
    529    );
    530 
    531    is(
    532      NavigableManager.getBrowsingContextById(chromeContextId),
    533      newChromeContext,
    534      "Chrome browsing context can be retrieved by id"
    535    );
    536 
    537    // Close the new window
    538    await BrowserTestUtils.closeWindow(newWindow);
    539 
    540    is(
    541      NavigableManager.getBrowsingContextById(chromeContextId),
    542      null,
    543      "Discarded chrome browsing context has no navigable id"
    544    );
    545  });
    546 });