forked from mirrors/gecko-dev
		
	This test is based on the test written by ochameau for https://phabricator.services.mozilla.com/D118740, but with modifications to specifically test `isInitialDocument`. Differential Revision: https://phabricator.services.mozilla.com/D119817
		
			
				
	
	
		
			317 lines
		
	
	
	
		
			10 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			317 lines
		
	
	
	
		
			10 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
/* Any copyright is dedicated to the Public Domain.
 | 
						|
   http://creativecommons.org/publicdomain/zero/1.0/ */
 | 
						|
 | 
						|
"use strict";
 | 
						|
 | 
						|
// Tag every new WindowGlobalParent with an expando indicating whether or not
 | 
						|
// they were an initial document when they were created for the duration of this
 | 
						|
// test.
 | 
						|
function wasInitialDocumentObserver(subject) {
 | 
						|
  subject._test_wasInitialDocument = subject.isInitialDocument;
 | 
						|
}
 | 
						|
Services.obs.addObserver(wasInitialDocumentObserver, "window-global-created");
 | 
						|
SimpleTest.registerCleanupFunction(function() {
 | 
						|
  Services.obs.removeObserver(
 | 
						|
    wasInitialDocumentObserver,
 | 
						|
    "window-global-created"
 | 
						|
  );
 | 
						|
});
 | 
						|
 | 
						|
add_task(async function new_about_blank_tab() {
 | 
						|
  await BrowserTestUtils.withNewTab("about:blank", async browser => {
 | 
						|
    is(
 | 
						|
      browser.browsingContext.currentWindowGlobal.isInitialDocument,
 | 
						|
      false,
 | 
						|
      "After loading an actual, final about:blank in the tab, the field is false"
 | 
						|
    );
 | 
						|
  });
 | 
						|
});
 | 
						|
 | 
						|
add_task(async function iframe_initial_about_blank() {
 | 
						|
  await BrowserTestUtils.withNewTab(
 | 
						|
    "http://example.com/document-builder.sjs?html=com",
 | 
						|
    async browser => {
 | 
						|
      info("Create an iframe without any explicit location");
 | 
						|
      await SpecialPowers.spawn(browser, [], async () => {
 | 
						|
        const iframe = content.document.createElement("iframe");
 | 
						|
        // Add the iframe to the DOM tree in order to be able to have its browsingContext
 | 
						|
        content.document.body.appendChild(iframe);
 | 
						|
        const { browsingContext } = iframe;
 | 
						|
 | 
						|
        is(
 | 
						|
          iframe.contentDocument.isInitialDocument,
 | 
						|
          true,
 | 
						|
          "The field is true on just-created iframes"
 | 
						|
        );
 | 
						|
        let beforeLoadPromise = SpecialPowers.spawnChrome(
 | 
						|
          [browsingContext],
 | 
						|
          bc => [
 | 
						|
            bc.currentWindowGlobal.isInitialDocument,
 | 
						|
            bc.currentWindowGlobal._test_wasInitialDocument,
 | 
						|
          ]
 | 
						|
        );
 | 
						|
 | 
						|
        await new Promise(resolve => {
 | 
						|
          iframe.addEventListener("load", resolve, { once: true });
 | 
						|
        });
 | 
						|
        is(
 | 
						|
          iframe.contentDocument.isInitialDocument,
 | 
						|
          false,
 | 
						|
          "The field is false after having loaded the final about:blank document"
 | 
						|
        );
 | 
						|
        let afterLoadPromise = SpecialPowers.spawnChrome(
 | 
						|
          [browsingContext],
 | 
						|
          bc => [
 | 
						|
            bc.currentWindowGlobal.isInitialDocument,
 | 
						|
            bc.currentWindowGlobal._test_wasInitialDocument,
 | 
						|
          ]
 | 
						|
        );
 | 
						|
 | 
						|
        // Wait to await the parent process promises, so we can't miss the "load" event.
 | 
						|
        let [beforeIsInitial, beforeWasInitial] = await beforeLoadPromise;
 | 
						|
        is(beforeIsInitial, true, "before load is initial in parent");
 | 
						|
        is(beforeWasInitial, true, "before load was initial in parent");
 | 
						|
        let [afterIsInitial, afterWasInitial] = await afterLoadPromise;
 | 
						|
        is(afterIsInitial, false, "after load is not initial in parent");
 | 
						|
        is(afterWasInitial, true, "after load was initial in parent");
 | 
						|
        iframe.remove();
 | 
						|
      });
 | 
						|
 | 
						|
      info("Create an iframe with a cross origin location");
 | 
						|
      const iframeBC = await SpecialPowers.spawn(browser, [], async () => {
 | 
						|
        const iframe = content.document.createElement("iframe");
 | 
						|
        await new Promise(resolve => {
 | 
						|
          iframe.addEventListener("load", resolve, { once: true });
 | 
						|
          iframe.src =
 | 
						|
            "http://example.org/document-builder.sjs?html=org-iframe";
 | 
						|
          content.document.body.appendChild(iframe);
 | 
						|
        });
 | 
						|
 | 
						|
        return iframe.browsingContext;
 | 
						|
      });
 | 
						|
 | 
						|
      is(
 | 
						|
        iframeBC.currentWindowGlobal.isInitialDocument,
 | 
						|
        false,
 | 
						|
        "The field is true after having loaded the final document"
 | 
						|
      );
 | 
						|
    }
 | 
						|
  );
 | 
						|
});
 | 
						|
 | 
						|
add_task(async function window_open() {
 | 
						|
  async function testWindowOpen({ browser, args, isCrossOrigin, willLoad }) {
 | 
						|
    info(`Open popup with ${JSON.stringify(args)}`);
 | 
						|
    const onNewTab = BrowserTestUtils.waitForNewTab(
 | 
						|
      gBrowser,
 | 
						|
      args[0] || "about:blank"
 | 
						|
    );
 | 
						|
    await SpecialPowers.spawn(
 | 
						|
      browser,
 | 
						|
      [args, isCrossOrigin, willLoad],
 | 
						|
      async (args, crossOrigin, willLoad) => {
 | 
						|
        const win = content.window.open(...args);
 | 
						|
        is(
 | 
						|
          win.document.isInitialDocument,
 | 
						|
          true,
 | 
						|
          "The field is true right after calling window.open()"
 | 
						|
        );
 | 
						|
        let beforeLoadPromise = SpecialPowers.spawnChrome(
 | 
						|
          [win.browsingContext],
 | 
						|
          bc => [
 | 
						|
            bc.currentWindowGlobal.isInitialDocument,
 | 
						|
            bc.currentWindowGlobal._test_wasInitialDocument,
 | 
						|
          ]
 | 
						|
        );
 | 
						|
 | 
						|
        // In cross origin, it is harder to watch for new document load, and if
 | 
						|
        // no argument is passed no load will happen.
 | 
						|
        if (!crossOrigin && willLoad) {
 | 
						|
          await new Promise(r =>
 | 
						|
            win.addEventListener("load", r, { once: true })
 | 
						|
          );
 | 
						|
          is(
 | 
						|
            win.document.isInitialDocument,
 | 
						|
            false,
 | 
						|
            "The field becomes false right after the popup document is loaded"
 | 
						|
          );
 | 
						|
        }
 | 
						|
 | 
						|
        // Perform the await after the load to avoid missing it.
 | 
						|
        let [beforeIsInitial, beforeWasInitial] = await beforeLoadPromise;
 | 
						|
        is(beforeIsInitial, true, "before load is initial in parent");
 | 
						|
        is(beforeWasInitial, true, "before load was initial in parent");
 | 
						|
      }
 | 
						|
    );
 | 
						|
    const newTab = await onNewTab;
 | 
						|
    const windowGlobal =
 | 
						|
      newTab.linkedBrowser.browsingContext.currentWindowGlobal;
 | 
						|
    if (willLoad) {
 | 
						|
      is(
 | 
						|
        windowGlobal.isInitialDocument,
 | 
						|
        false,
 | 
						|
        "The field is false in the parent process after having loaded the final document"
 | 
						|
      );
 | 
						|
    } else {
 | 
						|
      is(
 | 
						|
        windowGlobal.isInitialDocument,
 | 
						|
        true,
 | 
						|
        "The field remains true in the parent process as nothing will be loaded"
 | 
						|
      );
 | 
						|
    }
 | 
						|
    BrowserTestUtils.removeTab(newTab);
 | 
						|
  }
 | 
						|
 | 
						|
  await BrowserTestUtils.withNewTab(
 | 
						|
    "http://example.com/document-builder.sjs?html=com",
 | 
						|
    async browser => {
 | 
						|
      info("Use window.open() with cross-origin document");
 | 
						|
      await testWindowOpen({
 | 
						|
        browser,
 | 
						|
        args: ["http://example.org/document-builder.sjs?html=org-popup"],
 | 
						|
        isCrossOrigin: true,
 | 
						|
        willLoad: true,
 | 
						|
      });
 | 
						|
 | 
						|
      info("Use window.open() with same-origin document");
 | 
						|
      await testWindowOpen({
 | 
						|
        browser,
 | 
						|
        args: ["http://example.com/document-builder.sjs?html=com-popup"],
 | 
						|
        isCrossOrigin: false,
 | 
						|
        willLoad: true,
 | 
						|
      });
 | 
						|
 | 
						|
      info("Use window.open() with final about:blank document");
 | 
						|
      await testWindowOpen({
 | 
						|
        browser,
 | 
						|
        args: ["about:blank"],
 | 
						|
        isCrossOrigin: false,
 | 
						|
        willLoad: true,
 | 
						|
      });
 | 
						|
 | 
						|
      info("Use window.open() with no argument");
 | 
						|
      await testWindowOpen({
 | 
						|
        browser,
 | 
						|
        args: [],
 | 
						|
        isCrossOrigin: false,
 | 
						|
        willLoad: false,
 | 
						|
      });
 | 
						|
    }
 | 
						|
  );
 | 
						|
});
 | 
						|
 | 
						|
add_task(async function document_open() {
 | 
						|
  await BrowserTestUtils.withNewTab(
 | 
						|
    "http://example.com/document-builder.sjs?html=com",
 | 
						|
    async browser => {
 | 
						|
      is(browser.browsingContext.currentWindowGlobal.isInitialDocument, false);
 | 
						|
      await SpecialPowers.spawn(browser, [], async () => {
 | 
						|
        const iframe = content.document.createElement("iframe");
 | 
						|
        // Add the iframe to the DOM tree in order to be able to have its browsingContext
 | 
						|
        content.document.body.appendChild(iframe);
 | 
						|
        const { browsingContext } = iframe;
 | 
						|
 | 
						|
        // Check the state before the call in both parent and content.
 | 
						|
        is(
 | 
						|
          iframe.contentDocument.isInitialDocument,
 | 
						|
          true,
 | 
						|
          "Is an initial document before calling document.open"
 | 
						|
        );
 | 
						|
        let beforeOpenParentPromise = SpecialPowers.spawnChrome(
 | 
						|
          [browsingContext],
 | 
						|
          bc => [
 | 
						|
            bc.currentWindowGlobal.isInitialDocument,
 | 
						|
            bc.currentWindowGlobal._test_wasInitialDocument,
 | 
						|
            bc.currentWindowGlobal.innerWindowId,
 | 
						|
          ]
 | 
						|
        );
 | 
						|
 | 
						|
        // Run the `document.open` call with reduced permissions.
 | 
						|
        iframe.contentWindow.eval(`
 | 
						|
          document.open();
 | 
						|
          document.write("new document");
 | 
						|
          document.close();
 | 
						|
        `);
 | 
						|
 | 
						|
        is(
 | 
						|
          iframe.contentDocument.isInitialDocument,
 | 
						|
          false,
 | 
						|
          "Is no longer an initial document after calling document.open"
 | 
						|
        );
 | 
						|
        let [
 | 
						|
          afterIsInitial,
 | 
						|
          afterWasInitial,
 | 
						|
          afterID,
 | 
						|
        ] = await SpecialPowers.spawnChrome([browsingContext], bc => [
 | 
						|
          bc.currentWindowGlobal.isInitialDocument,
 | 
						|
          bc.currentWindowGlobal._test_wasInitialDocument,
 | 
						|
          bc.currentWindowGlobal.innerWindowId,
 | 
						|
        ]);
 | 
						|
        let [
 | 
						|
          beforeIsInitial,
 | 
						|
          beforeWasInitial,
 | 
						|
          beforeID,
 | 
						|
        ] = await beforeOpenParentPromise;
 | 
						|
        is(beforeIsInitial, true, "Should be initial before in the parent");
 | 
						|
        is(beforeWasInitial, true, "Was initial before in the parent");
 | 
						|
        is(afterIsInitial, false, "Should not be initial after in the parent");
 | 
						|
        is(afterWasInitial, true, "Was initial after in the parent");
 | 
						|
        is(beforeID, afterID, "Should be the same WindowGlobalParent");
 | 
						|
      });
 | 
						|
    }
 | 
						|
  );
 | 
						|
});
 | 
						|
 | 
						|
add_task(async function windowless_browser() {
 | 
						|
  info("Create a Windowless browser");
 | 
						|
  const browser = Services.appShell.createWindowlessBrowser(false);
 | 
						|
  const { browsingContext } = browser;
 | 
						|
  is(
 | 
						|
    browsingContext.currentWindowGlobal.isInitialDocument,
 | 
						|
    true,
 | 
						|
    "The field is true for a freshly created WindowlessBrowser"
 | 
						|
  );
 | 
						|
  is(
 | 
						|
    browser.currentURI.spec,
 | 
						|
    "about:blank",
 | 
						|
    "The location is immediately set to about:blank"
 | 
						|
  );
 | 
						|
 | 
						|
  const principal = Services.scriptSecurityManager.getSystemPrincipal();
 | 
						|
  browser.docShell.createAboutBlankContentViewer(principal, principal);
 | 
						|
  is(
 | 
						|
    browsingContext.currentWindowGlobal.isInitialDocument,
 | 
						|
    false,
 | 
						|
    "The field becomes false when creating an artificial blank document"
 | 
						|
  );
 | 
						|
 | 
						|
  info("Load a final about:blank document in it");
 | 
						|
  const onLocationChange = new Promise(resolve => {
 | 
						|
    let wpl = {
 | 
						|
      QueryInterface: ChromeUtils.generateQI([
 | 
						|
        "nsIWebProgressListener",
 | 
						|
        "nsISupportsWeakReference",
 | 
						|
      ]),
 | 
						|
      onLocationChange() {
 | 
						|
        browsingContext.webProgress.removeProgressListener(
 | 
						|
          wpl,
 | 
						|
          Ci.nsIWebProgress.NOTIFY_ALL
 | 
						|
        );
 | 
						|
        resolve();
 | 
						|
      },
 | 
						|
    };
 | 
						|
    browsingContext.webProgress.addProgressListener(
 | 
						|
      wpl,
 | 
						|
      Ci.nsIWebProgress.NOTIFY_ALL
 | 
						|
    );
 | 
						|
  });
 | 
						|
  browser.loadURI("about:blank", { triggeringPrincipal: principal });
 | 
						|
  info("Wait for the location change");
 | 
						|
  await onLocationChange;
 | 
						|
  is(
 | 
						|
    browsingContext.currentWindowGlobal.isInitialDocument,
 | 
						|
    false,
 | 
						|
    "The field is false after the location change event"
 | 
						|
  );
 | 
						|
  browser.close();
 | 
						|
});
 |