tor-browser

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

browser_NavigationManager.js (12942B)


      1 /* This Source Code Form is subject to the terms of the Mozilla Public
      2 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
      3 * You can obtain one at http://mozilla.org/MPL/2.0/. */
      4 
      5 const { NavigableManager } = ChromeUtils.importESModule(
      6  "chrome://remote/content/shared/NavigableManager.sys.mjs"
      7 );
      8 
      9 const FIRST_URL = "https://example.com/document-builder.sjs?html=first";
     10 const SECOND_URL = "https://example.com/document-builder.sjs?html=second";
     11 const THIRD_URL = "https://example.com/document-builder.sjs?html=third";
     12 
     13 const FIRST_COOP_URL =
     14  "https://example.com/document-builder.sjs?headers=Cross-Origin-Opener-Policy:same-origin&html=first_coop";
     15 const SECOND_COOP_URL =
     16  "https://example.net/document-builder.sjs?headers=Cross-Origin-Opener-Policy:same-origin&html=second_coop";
     17 
     18 add_task(async function test_simpleNavigation() {
     19  const events = [];
     20  const onEvent = (name, data) => events.push({ name, data });
     21 
     22  const navigationManager = new NavigationManager();
     23  navigationManager.on("navigation-started", onEvent);
     24  navigationManager.on("navigation-stopped", onEvent);
     25 
     26  const tab = await addTabAndWaitForNavigated(gBrowser, FIRST_URL);
     27  const browser = tab.linkedBrowser;
     28 
     29  const navigableId = NavigableManager.getIdForBrowser(browser);
     30 
     31  navigationManager.startMonitoring();
     32  is(
     33    navigationManager.getNavigationForBrowsingContext(browser.browsingContext),
     34    null,
     35    "No navigation recorded yet"
     36  );
     37  is(events.length, 0, "No event recorded");
     38 
     39  await loadURL(browser, SECOND_URL);
     40  await BrowserTestUtils.waitForCondition(() => events.length === 2);
     41 
     42  const firstNavigation = navigationManager.getNavigationForBrowsingContext(
     43    browser.browsingContext
     44  );
     45  assertNavigation(firstNavigation, SECOND_URL);
     46 
     47  is(events.length, 2, "Two events recorded");
     48  assertNavigationEvents(
     49    events,
     50    SECOND_URL,
     51    firstNavigation.navigationId,
     52    navigableId
     53  );
     54 
     55  await loadURL(browser, THIRD_URL);
     56  await BrowserTestUtils.waitForCondition(() => events.length === 4);
     57 
     58  const secondNavigation = navigationManager.getNavigationForBrowsingContext(
     59    browser.browsingContext
     60  );
     61  assertNavigation(secondNavigation, THIRD_URL);
     62  assertUniqueNavigationIds(firstNavigation, secondNavigation);
     63 
     64  is(events.length, 4, "Two new events recorded");
     65  assertNavigationEvents(
     66    events,
     67    THIRD_URL,
     68    secondNavigation.navigationId,
     69    navigableId
     70  );
     71 
     72  navigationManager.stopMonitoring();
     73 
     74  // Navigate again to the first URL
     75  await loadURL(browser, FIRST_URL);
     76  is(events.length, 4, "No new event recorded");
     77  is(
     78    navigationManager.getNavigationForBrowsingContext(browser.browsingContext),
     79    null,
     80    "No navigation recorded"
     81  );
     82 
     83  navigationManager.off("navigation-started", onEvent);
     84  navigationManager.off("navigation-stopped", onEvent);
     85 });
     86 
     87 add_task(async function test_loadTwoTabsSimultaneously() {
     88  const events = [];
     89  const onEvent = (name, data) => events.push({ name, data });
     90 
     91  const navigationManager = new NavigationManager();
     92  navigationManager.on("navigation-started", onEvent);
     93  navigationManager.on("navigation-stopped", onEvent);
     94 
     95  navigationManager.startMonitoring();
     96 
     97  info("Add two tabs simultaneously");
     98  const tab1 = addTab(gBrowser, FIRST_URL);
     99  const browser1 = tab1.linkedBrowser;
    100  const navigableId1 = NavigableManager.getIdForBrowser(browser1);
    101  const onLoad1 = BrowserTestUtils.browserLoaded(browser1, false, FIRST_URL);
    102 
    103  const tab2 = addTab(gBrowser, SECOND_URL);
    104  const browser2 = tab2.linkedBrowser;
    105  const navigableId2 = NavigableManager.getIdForBrowser(browser2);
    106  const onLoad2 = BrowserTestUtils.browserLoaded(browser2, false, SECOND_URL);
    107 
    108  info("Wait for the tabs to load");
    109  await Promise.all([onLoad1, onLoad2]);
    110  await BrowserTestUtils.waitForCondition(() => events.length === 4);
    111 
    112  is(events.length, 4, "Recorded 4 navigation events");
    113 
    114  info("Check navigation monitored for tab1");
    115  const nav1 = navigationManager.getNavigationForBrowsingContext(
    116    browser1.browsingContext
    117  );
    118  assertNavigation(nav1, FIRST_URL);
    119  assertNavigationEvents(events, FIRST_URL, nav1.navigationId, navigableId1);
    120 
    121  info("Check navigation monitored for tab2");
    122  const nav2 = navigationManager.getNavigationForBrowsingContext(
    123    browser2.browsingContext
    124  );
    125  assertNavigation(nav2, SECOND_URL);
    126  assertNavigationEvents(events, SECOND_URL, nav2.navigationId, navigableId2);
    127  assertUniqueNavigationIds(nav1, nav2);
    128 
    129  info("Reload the two tabs simultaneously");
    130  await Promise.all([
    131    BrowserTestUtils.reloadTab(tab1),
    132    BrowserTestUtils.reloadTab(tab2),
    133  ]);
    134  await BrowserTestUtils.waitForCondition(() => events.length === 8);
    135 
    136  is(events.length, 8, "Recorded 8 navigation events");
    137 
    138  info("Check the second navigation for tab1");
    139  const nav3 = navigationManager.getNavigationForBrowsingContext(
    140    browser1.browsingContext
    141  );
    142  assertNavigation(nav3, FIRST_URL);
    143  assertNavigationEvents(events, FIRST_URL, nav3.navigationId, navigableId1);
    144 
    145  info("Check the second navigation monitored for tab2");
    146  const nav4 = navigationManager.getNavigationForBrowsingContext(
    147    browser2.browsingContext
    148  );
    149  assertNavigation(nav4, SECOND_URL);
    150  assertNavigationEvents(events, SECOND_URL, nav4.navigationId, navigableId2);
    151  assertUniqueNavigationIds(nav1, nav2, nav3, nav4);
    152 
    153  navigationManager.off("navigation-started", onEvent);
    154  navigationManager.off("navigation-stopped", onEvent);
    155  navigationManager.stopMonitoring();
    156 });
    157 
    158 add_task(async function test_loadPageWithIframes() {
    159  const events = [];
    160  const onEvent = (name, data) => events.push({ name, data });
    161 
    162  const navigationManager = new NavigationManager();
    163  navigationManager.on("navigation-started", onEvent);
    164  navigationManager.on("navigation-stopped", onEvent);
    165 
    166  navigationManager.startMonitoring();
    167 
    168  info("Add a tab with iframes");
    169  const testUrl = createTestPageWithFrames();
    170  const tab = addTab(gBrowser, testUrl);
    171  const browser = tab.linkedBrowser;
    172  await BrowserTestUtils.browserLoaded(browser, false, testUrl);
    173  await BrowserTestUtils.waitForCondition(() => events.length === 8);
    174 
    175  is(events.length, 8, "Recorded 8 navigation events");
    176  const contexts = browser.browsingContext.getAllBrowsingContextsInSubtree();
    177 
    178  const navigations = [];
    179  for (const context of contexts) {
    180    const navigation =
    181      navigationManager.getNavigationForBrowsingContext(context);
    182    const navigable = NavigableManager.getIdForBrowsingContext(context);
    183 
    184    const url = context.currentWindowGlobal.documentURI.spec;
    185    assertNavigation(navigation, url);
    186    assertNavigationEvents(events, url, navigation.navigationId, navigable);
    187    navigations.push(navigation);
    188  }
    189  assertUniqueNavigationIds(...navigations);
    190 
    191  await BrowserTestUtils.reloadTab(tab);
    192  await BrowserTestUtils.waitForCondition(() => events.length === 16);
    193 
    194  is(events.length, 16, "Recorded 8 additional navigation events");
    195  const newContexts = browser.browsingContext.getAllBrowsingContextsInSubtree();
    196 
    197  for (const context of newContexts) {
    198    const navigation =
    199      navigationManager.getNavigationForBrowsingContext(context);
    200    const navigable = NavigableManager.getIdForBrowsingContext(context);
    201 
    202    const url = context.currentWindowGlobal.documentURI.spec;
    203    assertNavigation(navigation, url);
    204    assertNavigationEvents(events, url, navigation.navigationId, navigable);
    205    navigations.push(navigation);
    206  }
    207  assertUniqueNavigationIds(...navigations);
    208 
    209  navigationManager.off("navigation-started", onEvent);
    210  navigationManager.off("navigation-stopped", onEvent);
    211  navigationManager.stopMonitoring();
    212 });
    213 
    214 add_task(async function test_loadPageWithCoop() {
    215  const tab = await addTabAndWaitForNavigated(gBrowser, FIRST_COOP_URL);
    216  const browser = tab.linkedBrowser;
    217 
    218  const events = [];
    219  const onEvent = (name, data) => events.push({ name, data });
    220 
    221  const navigationManager = new NavigationManager();
    222  navigationManager.on("navigation-started", onEvent);
    223  navigationManager.on("navigation-stopped", onEvent);
    224 
    225  navigationManager.startMonitoring();
    226 
    227  const navigableId = NavigableManager.getIdForBrowser(browser);
    228  await loadURL(browser, SECOND_COOP_URL);
    229  await BrowserTestUtils.waitForCondition(() => events.length === 2);
    230 
    231  const coopNavigation = navigationManager.getNavigationForBrowsingContext(
    232    browser.browsingContext
    233  );
    234  assertNavigation(coopNavigation, SECOND_COOP_URL);
    235 
    236  is(events.length, 2, "Two events recorded");
    237  assertNavigationEvents(
    238    events,
    239    SECOND_COOP_URL,
    240    coopNavigation.navigationId,
    241    navigableId
    242  );
    243 
    244  navigationManager.off("navigation-started", onEvent);
    245  navigationManager.off("navigation-stopped", onEvent);
    246  navigationManager.stopMonitoring();
    247 });
    248 
    249 add_task(async function test_sameDocumentNavigation() {
    250  const events = [];
    251  const onEvent = (name, data) => events.push({ name, data });
    252 
    253  const navigationManager = new NavigationManager();
    254  navigationManager.on("fragment-navigated", onEvent);
    255  navigationManager.on("navigation-started", onEvent);
    256  navigationManager.on("navigation-stopped", onEvent);
    257  navigationManager.on("same-document-changed", onEvent);
    258 
    259  const url = "https://example.com/document-builder.sjs?html=test";
    260  const tab = await addTabAndWaitForNavigated(gBrowser, url);
    261  const browser = tab.linkedBrowser;
    262 
    263  navigationManager.startMonitoring();
    264  const navigableId = NavigableManager.getIdForBrowser(browser);
    265 
    266  is(events.length, 0, "No event recorded");
    267 
    268  info("Perform a same-document navigation");
    269  let onFragmentNavigated = navigationManager.once("fragment-navigated");
    270  BrowserTestUtils.startLoadingURIString(browser, url + "#hash");
    271  await onFragmentNavigated;
    272 
    273  const hashNavigation = navigationManager.getNavigationForBrowsingContext(
    274    browser.browsingContext
    275  );
    276  is(events.length, 1, "Recorded 1 navigation event");
    277  assertNavigationEvents(
    278    events,
    279    url + "#hash",
    280    hashNavigation.navigationId,
    281    navigableId,
    282    true
    283  );
    284 
    285  // Navigate from `url + "#hash"` to `url`, this will trigger a regular
    286  // navigation and we can use `loadURL` to properly wait for the navigation to
    287  // complete.
    288  info("Perform a regular navigation");
    289  await loadURL(browser, url);
    290  await BrowserTestUtils.waitForCondition(() => events.length === 3);
    291 
    292  const regularNavigation = navigationManager.getNavigationForBrowsingContext(
    293    browser.browsingContext
    294  );
    295  is(events.length, 3, "Recorded 2 additional navigation events");
    296  assertNavigationEvents(
    297    events,
    298    url,
    299    regularNavigation.navigationId,
    300    navigableId
    301  );
    302 
    303  info("Perform another hash navigation");
    304  onFragmentNavigated = navigationManager.once("fragment-navigated");
    305  BrowserTestUtils.startLoadingURIString(browser, url + "#foo");
    306  await onFragmentNavigated;
    307 
    308  const otherHashNavigation = navigationManager.getNavigationForBrowsingContext(
    309    browser.browsingContext
    310  );
    311 
    312  is(events.length, 4, "Recorded 1 additional navigation event");
    313 
    314  info("Perform a same-hash navigation");
    315  onFragmentNavigated = navigationManager.once("fragment-navigated");
    316  BrowserTestUtils.startLoadingURIString(browser, url + "#foo");
    317  await onFragmentNavigated;
    318 
    319  const sameHashNavigation = navigationManager.getNavigationForBrowsingContext(
    320    browser.browsingContext
    321  );
    322 
    323  is(events.length, 5, "Recorded 1 additional navigation event");
    324  assertNavigationEvents(
    325    events,
    326    url + "#foo",
    327    sameHashNavigation.navigationId,
    328    navigableId,
    329    true
    330  );
    331 
    332  assertUniqueNavigationIds([
    333    hashNavigation,
    334    regularNavigation,
    335    otherHashNavigation,
    336    sameHashNavigation,
    337  ]);
    338 
    339  navigationManager.off("fragment-navigated", onEvent);
    340  navigationManager.off("navigation-started", onEvent);
    341  navigationManager.off("navigation-stopped", onEvent);
    342  navigationManager.off("same-document-changed", onEvent);
    343 
    344  navigationManager.stopMonitoring();
    345 });
    346 
    347 add_task(async function test_startNavigationAndCloseTab() {
    348  const events = [];
    349  const onEvent = (name, data) => events.push({ name, data });
    350 
    351  const navigationManager = new NavigationManager();
    352  navigationManager.on("navigation-started", onEvent);
    353  navigationManager.on("navigation-stopped", onEvent);
    354 
    355  const tab = await addTabAndWaitForNavigated(gBrowser, FIRST_URL);
    356  const browser = tab.linkedBrowser;
    357 
    358  navigationManager.startMonitoring();
    359  loadURL(browser, SECOND_URL);
    360  gBrowser.removeTab(tab);
    361 
    362  // On top of the assertions below, the test also validates that there is no
    363  // unhandled promise rejection related to handling the navigation-started event
    364  // for the destroyed browsing context.
    365  is(events.length, 0, "No event was received");
    366  is(
    367    navigationManager.getNavigationForBrowsingContext(browser.browsingContext),
    368    null,
    369    "No navigation was recorded for the destroyed tab"
    370  );
    371  navigationManager.stopMonitoring();
    372 
    373  navigationManager.off("navigation-started", onEvent);
    374  navigationManager.off("navigation-stopped", onEvent);
    375 });