tor-browser

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

browser_isInitialDocument.js (11370B)


      1 /* Any copyright is dedicated to the Public Domain.
      2   http://creativecommons.org/publicdomain/zero/1.0/ */
      3 
      4 "use strict";
      5 
      6 // Tag every new WindowGlobalParent with an expando indicating whether or not
      7 // they were an initial document when they were created for the duration of this
      8 // test.
      9 function wasInitialDocumentObserver(subject) {
     10  subject._test_wasInitialDocument = subject.isInitialDocument;
     11 }
     12 Services.obs.addObserver(wasInitialDocumentObserver, "window-global-created");
     13 SimpleTest.registerCleanupFunction(function () {
     14  Services.obs.removeObserver(
     15    wasInitialDocumentObserver,
     16    "window-global-created"
     17  );
     18 });
     19 
     20 add_task(async function new_about_blank_tab() {
     21  await BrowserTestUtils.withNewTab("about:blank", async browser => {
     22    is(
     23      browser.browsingContext.currentWindowGlobal.isInitialDocument,
     24      true,
     25      "After the initial about:blank fires its load event, the field is still true"
     26    );
     27  });
     28 });
     29 
     30 add_task(async function iframe_initial_about_blank() {
     31  await BrowserTestUtils.withNewTab(
     32    // eslint-disable-next-line @microsoft/sdl/no-insecure-url
     33    "http://example.com/document-builder.sjs?html=com",
     34    async browser => {
     35      info("Create an iframe without any explicit location");
     36      await SpecialPowers.spawn(browser, [], async () => {
     37        const iframe = content.document.createElement("iframe");
     38        let loadPromise = new Promise(resolve => {
     39          iframe.addEventListener("load", resolve, { once: true });
     40        });
     41        // Add the iframe to the DOM tree in order to be able to have its browsingContext
     42        content.document.body.appendChild(iframe);
     43        const { browsingContext } = iframe;
     44 
     45        is(
     46          iframe.contentDocument.isInitialDocument,
     47          true,
     48          "The field is true on just-created iframes"
     49        );
     50        let beforeLoadPromise = SpecialPowers.spawnChrome(
     51          [browsingContext],
     52          bc => [
     53            bc.currentWindowGlobal.isInitialDocument,
     54            bc.currentWindowGlobal._test_wasInitialDocument,
     55          ]
     56        );
     57 
     58        await loadPromise;
     59        is(
     60          iframe.contentDocument.isInitialDocument,
     61          true,
     62          "The field remains true when the iframe stays at about:blank"
     63        );
     64        let afterLoadPromise = SpecialPowers.spawnChrome(
     65          [browsingContext],
     66          bc => [
     67            bc.currentWindowGlobal.isInitialDocument,
     68            bc.currentWindowGlobal._test_wasInitialDocument,
     69          ]
     70        );
     71 
     72        // Wait to await the parent process promises, so we can't miss the "load" event.
     73        let [beforeIsInitial, beforeWasInitial] = await beforeLoadPromise;
     74        is(beforeIsInitial, true, "before load is initial in parent");
     75        is(beforeWasInitial, true, "before load was initial in parent");
     76        let [afterIsInitial, afterWasInitial] = await afterLoadPromise;
     77        is(afterIsInitial, true, "after load is initial in parent");
     78        is(afterWasInitial, true, "after load was initial in parent");
     79        iframe.remove();
     80      });
     81 
     82      info("Create an iframe with a cross origin location");
     83      const iframeBC = await SpecialPowers.spawn(browser, [], async () => {
     84        const iframe = content.document.createElement("iframe");
     85        await new Promise(resolve => {
     86          iframe.addEventListener("load", resolve, { once: true });
     87          iframe.src =
     88            // eslint-disable-next-line @microsoft/sdl/no-insecure-url
     89            "http://example.org/document-builder.sjs?html=org-iframe";
     90          content.document.body.appendChild(iframe);
     91        });
     92 
     93        return iframe.browsingContext;
     94      });
     95 
     96      is(
     97        iframeBC.currentWindowGlobal.isInitialDocument,
     98        false,
     99        "The field is true after having loaded the final document"
    100      );
    101    }
    102  );
    103 });
    104 
    105 add_task(async function window_open() {
    106  async function testWindowOpen({ browser, args, isCrossOrigin, willLoad }) {
    107    info(`Open popup with ${JSON.stringify(args)}`);
    108    let url = args[0] || "about:blank";
    109    const onNewTab = BrowserTestUtils.waitForNewTab(gBrowser, url);
    110    await SpecialPowers.spawn(
    111      browser,
    112      [url, args, isCrossOrigin, willLoad],
    113      async (url, args, crossOrigin, willLoad) => {
    114        const win = content.window.open(...args);
    115        is(
    116          win.document.isInitialDocument,
    117          true,
    118          "The field is true right after calling window.open()"
    119        );
    120        let beforeLoadPromise = SpecialPowers.spawnChrome(
    121          [win.browsingContext],
    122          bc => [
    123            bc.currentWindowGlobal.isInitialDocument,
    124            bc.currentWindowGlobal._test_wasInitialDocument,
    125          ]
    126        );
    127 
    128        // In cross origin, it is harder to watch for new document load, and if
    129        // no argument is passed no load will happen.
    130        if (!crossOrigin && willLoad && url != "about:blank") {
    131          await new Promise(r =>
    132            win.addEventListener("load", r, { once: true })
    133          );
    134          is(
    135            win.document.isInitialDocument,
    136            false,
    137            "The field becomes false right after the popup document is loaded"
    138          );
    139        }
    140 
    141        // Perform the await after the load to avoid missing it.
    142        let [beforeIsInitial, beforeWasInitial] = await beforeLoadPromise;
    143        is(beforeIsInitial, true, "before load is initial in parent");
    144        is(beforeWasInitial, true, "before load was initial in parent");
    145      }
    146    );
    147    const newTab = await onNewTab;
    148    const windowGlobal =
    149      newTab.linkedBrowser.browsingContext.currentWindowGlobal;
    150    if (willLoad) {
    151      if (url == "about:blank") {
    152        is(
    153          windowGlobal.isInitialDocument,
    154          true,
    155          "The field is true in the parent process after having loaded about:blank"
    156        );
    157      } else {
    158        is(
    159          windowGlobal.isInitialDocument,
    160          false,
    161          "The field is false in the parent process after having loaded the final document"
    162        );
    163      }
    164    } else {
    165      is(
    166        windowGlobal.isInitialDocument,
    167        true,
    168        "The field remains true in the parent process as nothing will be loaded"
    169      );
    170    }
    171    BrowserTestUtils.removeTab(newTab);
    172  }
    173 
    174  await BrowserTestUtils.withNewTab(
    175    // eslint-disable-next-line @microsoft/sdl/no-insecure-url
    176    "http://example.com/document-builder.sjs?html=com",
    177    async browser => {
    178      info("Use window.open() with cross-origin document");
    179      await testWindowOpen({
    180        browser,
    181        // eslint-disable-next-line @microsoft/sdl/no-insecure-url
    182        args: ["http://example.org/document-builder.sjs?html=org-popup"],
    183        isCrossOrigin: true,
    184        willLoad: true,
    185      });
    186 
    187      info("Use window.open() with same-origin document");
    188      await testWindowOpen({
    189        browser,
    190        // eslint-disable-next-line @microsoft/sdl/no-insecure-url
    191        args: ["http://example.com/document-builder.sjs?html=com-popup"],
    192        isCrossOrigin: false,
    193        willLoad: true,
    194      });
    195 
    196      info("Use window.open() with final about:blank document");
    197      await testWindowOpen({
    198        browser,
    199        args: ["about:blank"],
    200        isCrossOrigin: false,
    201        willLoad: true,
    202      });
    203 
    204      info("Use window.open() with no argument");
    205      await testWindowOpen({
    206        browser,
    207        args: [],
    208        isCrossOrigin: false,
    209        willLoad: false,
    210      });
    211    }
    212  );
    213 });
    214 
    215 add_task(async function document_open() {
    216  await BrowserTestUtils.withNewTab(
    217    // eslint-disable-next-line @microsoft/sdl/no-insecure-url
    218    "http://example.com/document-builder.sjs?html=com",
    219    async browser => {
    220      is(browser.browsingContext.currentWindowGlobal.isInitialDocument, false);
    221      await SpecialPowers.spawn(browser, [], async () => {
    222        const iframe = content.document.createElement("iframe");
    223        // Add the iframe to the DOM tree in order to be able to have its browsingContext
    224        content.document.body.appendChild(iframe);
    225        const { browsingContext } = iframe;
    226 
    227        // Check the state before the call in both parent and content.
    228        is(
    229          iframe.contentDocument.isInitialDocument,
    230          true,
    231          "Is an initial document before calling document.open"
    232        );
    233        let beforeOpenParentPromise = SpecialPowers.spawnChrome(
    234          [browsingContext],
    235          bc => [
    236            bc.currentWindowGlobal.isInitialDocument,
    237            bc.currentWindowGlobal._test_wasInitialDocument,
    238            bc.currentWindowGlobal.innerWindowId,
    239          ]
    240        );
    241 
    242        // Run the `document.open` call with reduced permissions.
    243        iframe.contentWindow.eval(`
    244          document.open();
    245          document.write("new document");
    246          document.close();
    247        `);
    248 
    249        is(
    250          iframe.contentDocument.isInitialDocument,
    251          false,
    252          "Is no longer an initial document after calling document.open"
    253        );
    254        let [afterIsInitial, afterWasInitial, afterID] =
    255          await SpecialPowers.spawnChrome([browsingContext], bc => [
    256            bc.currentWindowGlobal.isInitialDocument,
    257            bc.currentWindowGlobal._test_wasInitialDocument,
    258            bc.currentWindowGlobal.innerWindowId,
    259          ]);
    260        let [beforeIsInitial, beforeWasInitial, beforeID] =
    261          await beforeOpenParentPromise;
    262        is(beforeIsInitial, true, "Should be initial before in the parent");
    263        is(beforeWasInitial, true, "Was initial before in the parent");
    264        is(afterIsInitial, false, "Should not be initial after in the parent");
    265        is(afterWasInitial, true, "Was initial after in the parent");
    266        is(beforeID, afterID, "Should be the same WindowGlobalParent");
    267      });
    268    }
    269  );
    270 });
    271 
    272 add_task(async function windowless_browser() {
    273  info("Create a Windowless browser");
    274  const browser = Services.appShell.createWindowlessBrowser(false);
    275  const { browsingContext } = browser;
    276  is(
    277    browsingContext.currentWindowGlobal.isInitialDocument,
    278    true,
    279    "The field is true for a freshly created WindowlessBrowser"
    280  );
    281  is(
    282    browser.currentURI.spec,
    283    "about:blank",
    284    "The location is immediately set to about:blank"
    285  );
    286 
    287  const principal = Services.scriptSecurityManager.getSystemPrincipal();
    288  browser.docShell.createAboutBlankDocumentViewer(principal, principal);
    289  is(
    290    browsingContext.currentWindowGlobal.isInitialDocument,
    291    false,
    292    "The field becomes false when creating an artificial blank document"
    293  );
    294 
    295  info("Load a final about:blank document in it");
    296  const onLocationChange = new Promise(resolve => {
    297    let wpl = {
    298      QueryInterface: ChromeUtils.generateQI([
    299        "nsIWebProgressListener",
    300        "nsISupportsWeakReference",
    301      ]),
    302      onLocationChange() {
    303        browsingContext.webProgress.removeProgressListener(
    304          wpl,
    305          Ci.nsIWebProgress.NOTIFY_ALL
    306        );
    307        resolve();
    308      },
    309    };
    310    browsingContext.webProgress.addProgressListener(
    311      wpl,
    312      Ci.nsIWebProgress.NOTIFY_ALL
    313    );
    314  });
    315  browser.loadURI(Services.io.newURI("about:blank"), {
    316    triggeringPrincipal: principal,
    317  });
    318  info("Wait for the location change");
    319  await onLocationChange;
    320  is(
    321    browsingContext.currentWindowGlobal.isInitialDocument,
    322    false,
    323    "The field is false after the location change event"
    324  );
    325  browser.close();
    326 });