Bug 1331087 - Disable the largeAllocation header by default outside of Win32 builds. r=ehsan, a=lizzard
authorMichael Layzell <michael@thelayzells.com>
Fri, 27 Jan 2017 15:35:26 -0500
changeset 378162 c422e37f16671ba9d28ecd501927271cb6905ad5
parent 378161 c3e53ebb65e74b5fd0579d9a3d3889c9be98900d
child 378163 a943b51dbee90aeddb0804c59f40c550652b0ff6
push id1419
push userjlund@mozilla.com
push dateMon, 10 Apr 2017 20:44:07 +0000
treeherdermozilla-release@5e6801b73ef6 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersehsan, lizzard
bugs1331087
milestone53.0a2
Bug 1331087 - Disable the largeAllocation header by default outside of Win32 builds. r=ehsan, a=lizzard MozReview-Commit-ID: 4jUFLv4fMZa
dom/base/nsContentUtils.cpp
dom/base/nsGlobalWindow.cpp
dom/base/nsPIDOMWindow.h
dom/tests/browser/browser.ini
dom/tests/browser/browser_largeAllocation.js
dom/tests/browser/browser_largeAllocation_non_win32.js
dom/tests/browser/browser_largeAllocation_win32.js
dom/tests/browser/helper_largeAllocation.js
--- a/dom/base/nsContentUtils.cpp
+++ b/dom/base/nsContentUtils.cpp
@@ -9726,16 +9726,47 @@ nsContentUtils::AttemptLargeAllocationLo
   NS_ENSURE_TRUE(tabChild, false);
 
   if (tabChild->TakeAwaitingLargeAlloc())  {
     NS_WARNING("In a Large-Allocation TabChild, ignoring Large-Allocation header!");
     outer->SetLargeAllocStatus(LargeAllocStatus::SUCCESS);
     return false;
   }
 
+  // On Win32 systems, we want to behave differently, so set the isWin32 bool to
+  // be true iff we are on win32.
+#if defined(XP_WIN) && defined(_X86_)
+  const bool isWin32 = true;
+#else
+  const bool isWin32 = false;
+#endif
+
+  static bool sLargeAllocForceEnable = false;
+  static bool sCachedLargeAllocForceEnable = false;
+  if (!sCachedLargeAllocForceEnable) {
+    sCachedLargeAllocForceEnable = true;
+    mozilla::Preferences::AddBoolVarCache(&sLargeAllocForceEnable,
+                                          "dom.largeAllocation.forceEnable");
+  }
+
+  // We want to enable the large allocation header on 32-bit windows machines,
+  // and disable it on other machines, while still printing diagnostic messages.
+  // dom.largeAllocation.forceEnable can allow you to enable the process
+  // switching behavior of the Large-Allocation header on non 32-bit windows
+  // machines.
+  bool largeAllocEnabled = isWin32 || sLargeAllocForceEnable;
+  if (!largeAllocEnabled) {
+    NS_WARNING("dom.largeAllocation.forceEnable not set - "
+               "ignoring otherwise successful Large-Allocation header.");
+    // On platforms which aren't WIN32, we don't activate the largeAllocation
+    // header, instead we simply emit diagnostics into the console.
+    outer->SetLargeAllocStatus(LargeAllocStatus::NON_WIN32);
+    return false;
+  }
+
   // At this point the fress process load should succeed! We just need to get
   // ourselves a nsIWebBrowserChrome3 to ask to perform the reload. We should
   // have one, as we have already confirmed that we are running in a content
   // process.
   nsCOMPtr<nsIDocShellTreeOwner> treeOwner;
   docShell->GetTreeOwner(getter_AddRefs(treeOwner));
   NS_ENSURE_TRUE(treeOwner, false);
 
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -13941,16 +13941,22 @@ nsGlobalWindow::ReportLargeAllocStatus()
 
   switch (mLargeAllocStatus) {
     case LargeAllocStatus::SUCCESS:
       // Override the error flags such that the success message isn't reported
       // as a warning.
       errorFlags = nsIScriptError::infoFlag;
       message = "LargeAllocationSuccess";
       break;
+    case LargeAllocStatus::NON_WIN32:
+      // XXXmystor: We want to have a seperate error message from the success
+      // message in this situation.
+      errorFlags = nsIScriptError::infoFlag;
+      message = "LargeAllocationSuccess";
+      break;
     case LargeAllocStatus::NON_GET:
       message = "LargeAllocationNonGetRequest";
       break;
     case LargeAllocStatus::NON_E10S:
       message = "LargeAllocationNonE10S";
       break;
     case LargeAllocStatus::NOT_ONLY_TOPLEVEL_IN_TABGROUP:
       message = "LargeAllocationNotOnlyToplevelInTabGroup";
--- a/dom/base/nsPIDOMWindow.h
+++ b/dom/base/nsPIDOMWindow.h
@@ -102,17 +102,18 @@ enum class LargeAllocStatus : uint8_t
   NONE,
   SUCCESS,
 
   // These are the ERROR states. If a window is in one of these states, then the
   // next document loaded in that window should have an error message reported
   // to it.
   NON_GET,
   NON_E10S,
-  NOT_ONLY_TOPLEVEL_IN_TABGROUP
+  NOT_ONLY_TOPLEVEL_IN_TABGROUP,
+  NON_WIN32
 };
 } // namespace dom
 } // namespace mozilla
 
 // nsPIDOMWindowInner and nsPIDOMWindowOuter are identical in all respects
 // except for the type name. They *must* remain identical so that we can
 // reinterpret_cast between them.
 template<class T>
--- a/dom/tests/browser/browser.ini
+++ b/dom/tests/browser/browser.ini
@@ -7,16 +7,17 @@ support-files =
   test_bug1004814.html
   worker_bug1004814.js
   geo_leak_test.html
   dummy.html
   test_largeAllocation.html
   test_largeAllocation.html^headers^
   test_largeAllocation2.html
   test_largeAllocation2.html^headers^
+  helper_largeAllocation.js
   !/dom/tests/mochitest/geolocation/network_geolocation.sjs
 
 [browser_allocateGigabyte.js]
 disabled = Does not reliably pass on 32-bit systems - bug 1314098
 skip-if = !e10s
 [browser_autofocus_background.js]
 [browser_autofocus_preference.js]
 [browser_beforeunload_between_chrome_content.js]
@@ -30,18 +31,20 @@ skip-if = !e10s
 [browser_ConsoleAPI_originAttributes.js]
 [browser_ConsoleAPITests.js]
 skip-if = e10s
 [browser_ConsoleStorageAPITests.js]
 [browser_ConsoleStoragePBTest_perwindowpb.js]
 [browser_focus_steal_from_chrome.js]
 [browser_focus_steal_from_chrome_during_mousedown.js]
 [browser_frame_elements.js]
-[browser_largeAllocation.js]
-skip-if = !e10s # Large-Allocation requires e10s
+[browser_largeAllocation_win32.js]
+skip-if = !e10s || os != "win" || processor != "x86" # Large-Allocation requires e10s
+[browser_largeAllocation_non_win32.js]
+skip-if = !e10s || (os == "win" && processor == "x86") # Large-Allocation requires e10s
 [browser_localStorage_privatestorageevent.js]
 [browser_test__content.js]
 [browser_test_new_window_from_content.js]
 tags = openwindow
 skip-if = toolkit == 'android'  || (os == "linux" && debug) # see bug 1261495 for Linux debug time outs
 support-files =
   test_new_window_from_content_child.html
 [browser_test_toolbars_visibility.js]
deleted file mode 100644
--- a/dom/tests/browser/browser_largeAllocation.js
+++ /dev/null
@@ -1,389 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-const TEST_URI = "http://example.com/browser/dom/tests/browser/test_largeAllocation.html";
-const TEST_URI_2 = "http://example.com/browser/dom/tests/browser/test_largeAllocation2.html";
-
-function expectProcessCreated() {
-  let os = Cc["@mozilla.org/observer-service;1"].getService(Ci.nsIObserverService);
-  let kill; // A kill function which will disable the promise.
-  let promise = new Promise((resolve, reject) => {
-    let topic = "ipc:content-created";
-    function observer() {
-      os.removeObserver(observer, topic);
-      ok(true, "Expect process created");
-      resolve();
-    }
-    os.addObserver(observer, topic, /* weak = */ false);
-    kill = () => {
-      os.removeObserver(observer, topic);
-      ok(true, "Expect process created killed");
-      reject();
-    };
-  });
-  promise.kill = kill;
-  return promise;
-}
-
-function expectNoProcess() {
-  let os = Cc["@mozilla.org/observer-service;1"].getService(Ci.nsIObserverService);
-  let topic = "ipc:content-created";
-  function observer() {
-    ok(false, "A process was created!");
-    os.removeObserver(observer, topic);
-  }
-  os.addObserver(observer, topic, /* weak = */ false);
-
-  return () => os.removeObserver(observer, topic);
-}
-
-function getPID(aBrowser) {
-  return ContentTask.spawn(aBrowser, null, () => {
-    const appinfo = Components.classes["@mozilla.org/xre/app-info;1"]
-            .getService(Components.interfaces.nsIXULRuntime);
-    return appinfo.processID;
-  });
-}
-
-function getInLAProc(aBrowser) {
-  return ContentTask.spawn(aBrowser, null, () => {
-    try {
-      return docShell.inLargeAllocProcess;
-    } catch (e) {
-      // This must be a non-remote browser, which means it is not fresh
-      return false;
-    }
-  });
-}
-
-add_task(function*() {
-  // I'm terrible and put this set of tests into a single file, so I need a longer timeout
-  requestLongerTimeout(2);
-
-  yield SpecialPowers.pushPrefEnv({
-    set: [
-      ["dom.largeAllocationHeader.enabled", true],
-      // Increase processCount.webLargeAllocation to avoid any races where
-      // processes aren't being cleaned up quickly enough.
-      ["dom.ipc.processCount.webLargeAllocation", 20]
-    ]
-  });
-
-  // A toplevel tab should be able to navigate cross process!
-  yield BrowserTestUtils.withNewTab("about:blank", function*(aBrowser) {
-    info("Starting test 0");
-    let pid1 = yield getPID(aBrowser);
-    is(false, yield getInLAProc(aBrowser));
-
-    let epc = expectProcessCreated();
-    yield ContentTask.spawn(aBrowser, TEST_URI, TEST_URI => {
-      content.document.location = TEST_URI;
-    });
-
-    // Wait for the new process to be created
-    yield epc;
-
-    let pid2 = yield getPID(aBrowser);
-
-    isnot(pid1, pid2, "The pids should be different between the initial load and the new load");
-    is(true, yield getInLAProc(aBrowser));
-  });
-
-  // When a Large-Allocation document is loaded in an iframe, the header should
-  // be ignored, and the tab should stay in the current process.
-  yield BrowserTestUtils.withNewTab("about:blank", function*(aBrowser) {
-    info("Starting test 1");
-    let pid1 = yield getPID(aBrowser);
-    is(false, yield getInLAProc(aBrowser));
-
-    // Fail the test if we create a process
-    let stopExpectNoProcess = expectNoProcess();
-
-    yield ContentTask.spawn(aBrowser, TEST_URI, TEST_URI => {
-      content.document.body.innerHTML = `<iframe src='${TEST_URI}'></iframe>`;
-
-      return new Promise(resolve => {
-        content.document.body.querySelector('iframe').onload = () => {
-          ok(true, "Iframe finished loading");
-          resolve();
-        };
-      });
-    });
-
-    let pid2 = yield getPID(aBrowser);
-
-    is(pid1, pid2, "The PID should not have changed");
-    is(false, yield getInLAProc(aBrowser));
-
-    stopExpectNoProcess();
-  });
-
-  // If you have an opener cross process navigation shouldn't work
-  yield BrowserTestUtils.withNewTab("http://example.com", function*(aBrowser) {
-    info("Starting test 2");
-    let pid1 = yield getPID(aBrowser);
-    is(false, yield getInLAProc(aBrowser));
-
-    // Fail the test if we create a process
-    let stopExpectNoProcess = expectNoProcess();
-
-    let loaded = ContentTask.spawn(aBrowser, TEST_URI, TEST_URI => {
-      content.document.body.innerHTML = '<button>CLICK ME</button>';
-
-      return new Promise(resolve => {
-        content.document.querySelector('button').onclick = e => {
-          let w = content.window.open(TEST_URI, '_blank');
-          w.onload = () => {
-            ok(true, "Window finished loading");
-            w.close();
-            resolve();
-          };
-        };
-      });
-    });
-
-    yield BrowserTestUtils.synthesizeMouseAtCenter("button", {}, aBrowser);
-
-    yield loaded;
-
-    let pid2 = yield getPID(aBrowser);
-
-    is(pid1, pid2, "The PID should not have changed");
-    is(false, yield getInLAProc(aBrowser));
-
-    stopExpectNoProcess();
-  });
-
-  // Load Large-Allocation twice with about:blank load in between
-  yield BrowserTestUtils.withNewTab("about:blank", function*(aBrowser) {
-    info("Starting test 3");
-    let pid1 = yield getPID(aBrowser);
-    is(false, yield getInLAProc(aBrowser));
-
-    let epc = expectProcessCreated();
-
-    yield ContentTask.spawn(aBrowser, TEST_URI, TEST_URI => {
-      content.document.location = TEST_URI;
-    });
-
-    yield epc;
-
-    let pid2 = yield getPID(aBrowser);
-
-    isnot(pid1, pid2);
-    is(true, yield getInLAProc(aBrowser));
-
-    yield BrowserTestUtils.browserLoaded(aBrowser);
-
-    yield ContentTask.spawn(aBrowser, null, () => content.document.location = "about:blank");
-
-    yield BrowserTestUtils.browserLoaded(aBrowser);
-
-    let pid3 = yield getPID(aBrowser);
-
-    // We should have been kicked out of the large-allocation process by the
-    // load, meaning we're back in a non-fresh process
-    is(false, yield getInLAProc(aBrowser));
-
-    epc = expectProcessCreated();
-
-    yield ContentTask.spawn(aBrowser, TEST_URI, TEST_URI => {
-      content.document.location = TEST_URI;
-    });
-
-    yield epc;
-
-    let pid4 = yield getPID(aBrowser);
-
-    isnot(pid1, pid4);
-    isnot(pid2, pid4);
-    is(true, yield getInLAProc(aBrowser));
-  });
-
-  // Load Large-Allocation then about:blank load, then back button press should load from bfcache.
-  yield BrowserTestUtils.withNewTab("about:blank", function*(aBrowser) {
-    info("Starting test 4");
-    let pid1 = yield getPID(aBrowser);
-    is(false, yield getInLAProc(aBrowser));
-
-    let epc = expectProcessCreated();
-
-    yield ContentTask.spawn(aBrowser, TEST_URI, TEST_URI => {
-      content.document.location = TEST_URI;
-    });
-
-    yield epc;
-
-    let pid2 = yield getPID(aBrowser);
-
-    isnot(pid1, pid2, "PIDs 1 and 2 should not match");
-    is(true, yield getInLAProc(aBrowser));
-
-    yield BrowserTestUtils.browserLoaded(aBrowser);
-
-    // Switch to about:blank, so we can navigate back
-    yield ContentTask.spawn(aBrowser, null, () => {
-      content.document.location = "about:blank";
-    });
-
-    yield BrowserTestUtils.browserLoaded(aBrowser);
-
-    let pid3 = yield getPID(aBrowser);
-
-    // We should have been kicked out of the large-allocation process by the
-    // load, meaning we're back in a non-large-allocation process.
-    is(false, yield getInLAProc(aBrowser));
-
-    epc = expectProcessCreated();
-
-    // Navigate back to the previous page. As the large alloation process was
-    // left, it won't be in bfcache and will have to be loaded fresh.
-    yield ContentTask.spawn(aBrowser, TEST_URI, TEST_URI => {
-      content.window.history.back();
-    });
-
-    yield epc;
-
-    let pid4 = yield getPID(aBrowser);
-
-    isnot(pid1, pid4, "PID 4 shouldn't match PID 1");
-    isnot(pid2, pid4, "PID 4 shouldn't match PID 2");
-    isnot(pid3, pid4, "PID 4 shouldn't match PID 3");
-    is(true, yield getInLAProc(aBrowser));
-  });
-
-  // Two consecutive large-allocation loads should create two processes.
-  yield BrowserTestUtils.withNewTab("about:blank", function*(aBrowser) {
-    info("Starting test 5");
-    let pid1 = yield getPID(aBrowser);
-    is(false, yield getInLAProc(aBrowser));
-
-    let ready = Promise.all([expectProcessCreated(),
-                             BrowserTestUtils.browserLoaded(aBrowser)]);
-
-    yield ContentTask.spawn(aBrowser, TEST_URI, TEST_URI => {
-      content.document.location = TEST_URI;
-    });
-
-    yield ready;
-
-    let pid2 = yield getPID(aBrowser);
-
-    isnot(pid1, pid2, "PIDs 1 and 2 should not match");
-    is(true, yield getInLAProc(aBrowser));
-
-    let epc = expectProcessCreated();
-
-    yield ContentTask.spawn(aBrowser, TEST_URI_2, TEST_URI_2 => {
-      content.document.location = TEST_URI_2;
-    });
-
-    yield epc;
-
-    // We just saw the creation of a new process. This is either the process we
-    // are interested in, or, in a multi-e10s situation, the normal content
-    // process which was created for the normal content to be loaded into as the
-    // browsing context was booted out of the fresh process. If we discover that
-    // this was not a fresh process, we'll need to wait for another process.
-    // Start listening now.
-    epc = expectProcessCreated();
-    if (!(yield getInLAProc(aBrowser))) {
-      yield epc;
-    } else {
-      epc.kill();
-    }
-
-    let pid3 = yield getPID(aBrowser);
-
-    isnot(pid1, pid3, "PIDs 1 and 3 should not match");
-    isnot(pid2, pid3, "PIDs 2 and 3 should not match");
-    is(true, yield getInLAProc(aBrowser));
-  });
-
-  // Opening a window from the large-allocation window should prevent the process switch.
-  yield BrowserTestUtils.withNewTab("about:blank", function*(aBrowser) {
-    info("Starting test 6");
-    let pid1 = yield getPID(aBrowser);
-    is(false, yield getInLAProc(aBrowser));
-
-    let ready = Promise.all([expectProcessCreated(),
-                             BrowserTestUtils.browserLoaded(aBrowser)]);
-
-    yield ContentTask.spawn(aBrowser, TEST_URI, TEST_URI => {
-      content.document.location = TEST_URI;
-    });
-
-    yield ready;
-
-    let pid2 = yield getPID(aBrowser);
-
-    isnot(pid1, pid2, "PIDs 1 and 2 should not match");
-    is(true, yield getInLAProc(aBrowser));
-
-    let stopExpectNoProcess = expectNoProcess();
-
-    yield ContentTask.spawn(aBrowser, null, () => {
-      this.__newWindow = content.window.open("about:blank");
-      content.document.location = "about:blank";
-    });
-
-    yield BrowserTestUtils.browserLoaded(aBrowser);
-
-    let pid3 = yield getPID(aBrowser);
-
-    is(pid3, pid2, "PIDs 2 and 3 should match");
-    is(true, yield getInLAProc(aBrowser));
-
-    stopExpectNoProcess();
-
-    yield ContentTask.spawn(aBrowser, null, () => {
-      ok(this.__newWindow, "The window should have been stored");
-      this.__newWindow.close();
-    });
-  });
-
-  yield BrowserTestUtils.withNewTab("about:blank", function*(aBrowser) {
-    info("Starting test 7");
-    yield SpecialPowers.pushPrefEnv({
-      set: [
-        ["dom.ipc.processCount.webLargeAllocation", 1]
-      ],
-    });
-
-    // Loading the first Large-Allocation tab should succeed as normal
-    let pid1 = yield getPID(aBrowser);
-    is(false, yield getInLAProc(aBrowser));
-
-    let ready = Promise.all([expectProcessCreated(),
-                             BrowserTestUtils.browserLoaded(aBrowser)]);
-
-    yield ContentTask.spawn(aBrowser, TEST_URI, TEST_URI => {
-      content.document.location = TEST_URI;
-    });
-
-    yield ready;
-
-    let pid2 = yield getPID(aBrowser);
-
-    isnot(pid1, pid2, "PIDs 1 and 2 should not match");
-    is(true, yield getInLAProc(aBrowser));
-
-    yield BrowserTestUtils.withNewTab("about:blank", function*(aBrowser) {
-      // The second one should load in a non-LA proc because the
-      // webLargeAllocation processes have been exhausted.
-      is(false, yield getInLAProc(aBrowser));
-
-      let ready = Promise.all([BrowserTestUtils.browserLoaded(aBrowser)]);
-      yield ContentTask.spawn(aBrowser, TEST_URI, TEST_URI => {
-        content.document.location = TEST_URI;
-      });
-      yield ready;
-
-      is(false, yield getInLAProc(aBrowser));
-    });
-  });
-
-  // XXX: Make sure to reset dom.ipc.processCount.webLargeAllocation if adding a
-  // test after the above test.
-});
new file mode 100644
--- /dev/null
+++ b/dom/tests/browser/browser_largeAllocation_non_win32.js
@@ -0,0 +1,39 @@
+let testDir = gTestPath.substr(0, gTestPath.lastIndexOf("/"));
+Services.scriptloader.loadSubScript(testDir + "/helper_largeAllocation.js", this);
+
+// Force-enabling the Large-Allocation header
+add_task(function*() {
+  info("Test 1 - force enabling the Large-Allocation header");
+  yield SpecialPowers.pushPrefEnv({
+    set: [
+      // Enable the header if it is disabled
+      ["dom.largeAllocationHeader.enabled", true],
+      // Force-enable process creation with large-allocation, such that non
+      // win32 builds can test the behavior.
+      ["dom.largeAllocation.forceEnable", true],
+      // Increase processCount.webLargeAllocation to avoid any races where
+      // processes aren't being cleaned up quickly enough.
+      ["dom.ipc.processCount.webLargeAllocation", 20]
+    ]
+  });
+
+  yield* largeAllocSuccessTests();
+});
+
+add_task(function*() {
+  info("Test 2 - not force enabling the Large-Allocation header");
+  yield SpecialPowers.pushPrefEnv({
+    set: [
+      // Enable the header if it is disabled
+      ["dom.largeAllocationHeader.enabled", true],
+      // Force-enable process creation with large-allocation, such that non
+      // win32 builds can test the behavior.
+      ["dom.largeAllocation.forceEnable", false],
+      // Increase processCount.webLargeAllocation to avoid any races where
+      // processes aren't being cleaned up quickly enough.
+      ["dom.ipc.processCount.webLargeAllocation", 20]
+    ]
+  });
+
+  yield* largeAllocFailTests();
+});
new file mode 100644
--- /dev/null
+++ b/dom/tests/browser/browser_largeAllocation_win32.js
@@ -0,0 +1,18 @@
+let testDir = gTestPath.substr(0, gTestPath.lastIndexOf("/"));
+Services.scriptloader.loadSubScript(testDir + "/helper_largeAllocation.js", this);
+
+add_task(function*() {
+  info("Test 1 - On win32 - no forceEnable pref");
+  yield SpecialPowers.pushPrefEnv({
+    set: [
+      // Enable the header if it is disabled
+      ["dom.largeAllocationHeader.enabled", true],
+      // Increase processCount.webLargeAllocation to avoid any races where
+      // processes aren't being cleaned up quickly enough.
+      ["dom.ipc.processCount.webLargeAllocation", 20]
+    ]
+  });
+
+  yield* largeAllocSuccessTests();
+});
+
new file mode 100644
--- /dev/null
+++ b/dom/tests/browser/helper_largeAllocation.js
@@ -0,0 +1,420 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+const TEST_URI = "http://example.com/browser/dom/tests/browser/test_largeAllocation.html";
+const TEST_URI_2 = "http://example.com/browser/dom/tests/browser/test_largeAllocation2.html";
+
+function expectProcessCreated() {
+  let os = Cc["@mozilla.org/observer-service;1"].getService(Ci.nsIObserverService);
+  let kill; // A kill function which will disable the promise.
+  let promise = new Promise((resolve, reject) => {
+    let topic = "ipc:content-created";
+    function observer() {
+      os.removeObserver(observer, topic);
+      ok(true, "Expect process created");
+      resolve();
+    }
+    os.addObserver(observer, topic, /* weak = */ false);
+    kill = () => {
+      os.removeObserver(observer, topic);
+      ok(true, "Expect process created killed");
+      reject();
+    };
+  });
+  promise.kill = kill;
+  return promise;
+}
+
+function expectNoProcess() {
+  let os = Cc["@mozilla.org/observer-service;1"].getService(Ci.nsIObserverService);
+  let topic = "ipc:content-created";
+  function observer() {
+    ok(false, "A process was created!");
+    os.removeObserver(observer, topic);
+  }
+  os.addObserver(observer, topic, /* weak = */ false);
+
+  return () => os.removeObserver(observer, topic);
+}
+
+function getPID(aBrowser) {
+  return ContentTask.spawn(aBrowser, null, () => {
+    const appinfo = Components.classes["@mozilla.org/xre/app-info;1"]
+            .getService(Components.interfaces.nsIXULRuntime);
+    return appinfo.processID;
+  });
+}
+
+function getInLAProc(aBrowser) {
+  return ContentTask.spawn(aBrowser, null, () => {
+    try {
+      return docShell.inLargeAllocProcess;
+    } catch (e) {
+      // This must be a non-remote browser, which means it is not fresh
+      return false;
+    }
+  });
+}
+
+function* largeAllocSuccessTests() {
+  // I'm terrible and put this set of tests into a single file, so I need a longer timeout
+  requestLongerTimeout(2);
+
+  // Check if we are on win32
+  let isWin32 = /Windows/.test(navigator.userAgent) && !/x64/.test(navigator.userAgent);
+
+  yield SpecialPowers.pushPrefEnv({
+    set: [
+      // Enable the header if it is disabled
+      ["dom.largeAllocationHeader.enabled", true],
+      // Force-enable process creation with large-allocation, such that non
+      // win32 builds can test the behavior.
+      ["dom.largeAllocation.forceEnable", !isWin32],
+      // Increase processCount.webLargeAllocation to avoid any races where
+      // processes aren't being cleaned up quickly enough.
+      ["dom.ipc.processCount.webLargeAllocation", 20]
+    ]
+  });
+
+  // A toplevel tab should be able to navigate cross process!
+  yield BrowserTestUtils.withNewTab("about:blank", function*(aBrowser) {
+    info("Starting test 0");
+    let pid1 = yield getPID(aBrowser);
+    is(false, yield getInLAProc(aBrowser));
+
+    let epc = expectProcessCreated();
+    yield ContentTask.spawn(aBrowser, TEST_URI, TEST_URI => {
+      content.document.location = TEST_URI;
+    });
+
+    // Wait for the new process to be created
+    yield epc;
+
+    let pid2 = yield getPID(aBrowser);
+
+    isnot(pid1, pid2, "The pids should be different between the initial load and the new load");
+    is(true, yield getInLAProc(aBrowser));
+  });
+
+  // When a Large-Allocation document is loaded in an iframe, the header should
+  // be ignored, and the tab should stay in the current process.
+  yield BrowserTestUtils.withNewTab("about:blank", function*(aBrowser) {
+    info("Starting test 1");
+    let pid1 = yield getPID(aBrowser);
+    is(false, yield getInLAProc(aBrowser));
+
+    // Fail the test if we create a process
+    let stopExpectNoProcess = expectNoProcess();
+
+    yield ContentTask.spawn(aBrowser, TEST_URI, TEST_URI => {
+      content.document.body.innerHTML = `<iframe src='${TEST_URI}'></iframe>`;
+
+      return new Promise(resolve => {
+        content.document.body.querySelector('iframe').onload = () => {
+          ok(true, "Iframe finished loading");
+          resolve();
+        };
+      });
+    });
+
+    let pid2 = yield getPID(aBrowser);
+
+    is(pid1, pid2, "The PID should not have changed");
+    is(false, yield getInLAProc(aBrowser));
+
+    stopExpectNoProcess();
+  });
+
+  // If you have an opener cross process navigation shouldn't work
+  yield BrowserTestUtils.withNewTab("http://example.com", function*(aBrowser) {
+    info("Starting test 2");
+    let pid1 = yield getPID(aBrowser);
+    is(false, yield getInLAProc(aBrowser));
+
+    // Fail the test if we create a process
+    let stopExpectNoProcess = expectNoProcess();
+
+    let loaded = ContentTask.spawn(aBrowser, TEST_URI, TEST_URI => {
+      content.document.body.innerHTML = '<button>CLICK ME</button>';
+
+      return new Promise(resolve => {
+        content.document.querySelector('button').onclick = e => {
+          let w = content.window.open(TEST_URI, '_blank');
+          w.onload = () => {
+            ok(true, "Window finished loading");
+            w.close();
+            resolve();
+          };
+        };
+      });
+    });
+
+    yield BrowserTestUtils.synthesizeMouseAtCenter("button", {}, aBrowser);
+
+    yield loaded;
+
+    let pid2 = yield getPID(aBrowser);
+
+    is(pid1, pid2, "The PID should not have changed");
+    is(false, yield getInLAProc(aBrowser));
+
+    stopExpectNoProcess();
+  });
+
+  // Load Large-Allocation twice with about:blank load in between
+  yield BrowserTestUtils.withNewTab("about:blank", function*(aBrowser) {
+    info("Starting test 3");
+    let pid1 = yield getPID(aBrowser);
+    is(false, yield getInLAProc(aBrowser));
+
+    let epc = expectProcessCreated();
+
+    yield ContentTask.spawn(aBrowser, TEST_URI, TEST_URI => {
+      content.document.location = TEST_URI;
+    });
+
+    yield epc;
+
+    let pid2 = yield getPID(aBrowser);
+
+    isnot(pid1, pid2);
+    is(true, yield getInLAProc(aBrowser));
+
+    yield BrowserTestUtils.browserLoaded(aBrowser);
+
+    yield ContentTask.spawn(aBrowser, null, () => content.document.location = "about:blank");
+
+    yield BrowserTestUtils.browserLoaded(aBrowser);
+
+    let pid3 = yield getPID(aBrowser);
+
+    // We should have been kicked out of the large-allocation process by the
+    // load, meaning we're back in a non-fresh process
+    is(false, yield getInLAProc(aBrowser));
+
+    epc = expectProcessCreated();
+
+    yield ContentTask.spawn(aBrowser, TEST_URI, TEST_URI => {
+      content.document.location = TEST_URI;
+    });
+
+    yield epc;
+
+    let pid4 = yield getPID(aBrowser);
+
+    isnot(pid1, pid4);
+    isnot(pid2, pid4);
+    is(true, yield getInLAProc(aBrowser));
+  });
+
+  // Load Large-Allocation then about:blank load, then back button press should load from bfcache.
+  yield BrowserTestUtils.withNewTab("about:blank", function*(aBrowser) {
+    info("Starting test 4");
+    let pid1 = yield getPID(aBrowser);
+    is(false, yield getInLAProc(aBrowser));
+
+    let epc = expectProcessCreated();
+
+    yield ContentTask.spawn(aBrowser, TEST_URI, TEST_URI => {
+      content.document.location = TEST_URI;
+    });
+
+    yield epc;
+
+    let pid2 = yield getPID(aBrowser);
+
+    isnot(pid1, pid2, "PIDs 1 and 2 should not match");
+    is(true, yield getInLAProc(aBrowser));
+
+    yield BrowserTestUtils.browserLoaded(aBrowser);
+
+    // Switch to about:blank, so we can navigate back
+    yield ContentTask.spawn(aBrowser, null, () => {
+      content.document.location = "about:blank";
+    });
+
+    yield BrowserTestUtils.browserLoaded(aBrowser);
+
+    let pid3 = yield getPID(aBrowser);
+
+    // We should have been kicked out of the large-allocation process by the
+    // load, meaning we're back in a non-large-allocation process.
+    is(false, yield getInLAProc(aBrowser));
+
+    epc = expectProcessCreated();
+
+    // Navigate back to the previous page. As the large alloation process was
+    // left, it won't be in bfcache and will have to be loaded fresh.
+    yield ContentTask.spawn(aBrowser, TEST_URI, TEST_URI => {
+      content.window.history.back();
+    });
+
+    yield epc;
+
+    let pid4 = yield getPID(aBrowser);
+
+    isnot(pid1, pid4, "PID 4 shouldn't match PID 1");
+    isnot(pid2, pid4, "PID 4 shouldn't match PID 2");
+    isnot(pid3, pid4, "PID 4 shouldn't match PID 3");
+    is(true, yield getInLAProc(aBrowser));
+  });
+
+  // Two consecutive large-allocation loads should create two processes.
+  yield BrowserTestUtils.withNewTab("about:blank", function*(aBrowser) {
+    info("Starting test 5");
+    let pid1 = yield getPID(aBrowser);
+    is(false, yield getInLAProc(aBrowser));
+
+    let ready = Promise.all([expectProcessCreated(),
+                             BrowserTestUtils.browserLoaded(aBrowser)]);
+
+    yield ContentTask.spawn(aBrowser, TEST_URI, TEST_URI => {
+      content.document.location = TEST_URI;
+    });
+
+    yield ready;
+
+    let pid2 = yield getPID(aBrowser);
+
+    isnot(pid1, pid2, "PIDs 1 and 2 should not match");
+    is(true, yield getInLAProc(aBrowser));
+
+    let epc = expectProcessCreated();
+
+    yield ContentTask.spawn(aBrowser, TEST_URI_2, TEST_URI_2 => {
+      content.document.location = TEST_URI_2;
+    });
+
+    yield epc;
+
+    // We just saw the creation of a new process. This is either the process we
+    // are interested in, or, in a multi-e10s situation, the normal content
+    // process which was created for the normal content to be loaded into as the
+    // browsing context was booted out of the fresh process. If we discover that
+    // this was not a fresh process, we'll need to wait for another process.
+    // Start listening now.
+    epc = expectProcessCreated();
+    if (!(yield getInLAProc(aBrowser))) {
+      yield epc;
+    } else {
+      epc.kill();
+    }
+
+    let pid3 = yield getPID(aBrowser);
+
+    isnot(pid1, pid3, "PIDs 1 and 3 should not match");
+    isnot(pid2, pid3, "PIDs 2 and 3 should not match");
+    is(true, yield getInLAProc(aBrowser));
+  });
+
+  // Opening a window from the large-allocation window should prevent the process switch.
+  yield BrowserTestUtils.withNewTab("about:blank", function*(aBrowser) {
+    info("Starting test 6");
+    let pid1 = yield getPID(aBrowser);
+    is(false, yield getInLAProc(aBrowser));
+
+    let ready = Promise.all([expectProcessCreated(),
+                             BrowserTestUtils.browserLoaded(aBrowser)]);
+
+    yield ContentTask.spawn(aBrowser, TEST_URI, TEST_URI => {
+      content.document.location = TEST_URI;
+    });
+
+    yield ready;
+
+    let pid2 = yield getPID(aBrowser);
+
+    isnot(pid1, pid2, "PIDs 1 and 2 should not match");
+    is(true, yield getInLAProc(aBrowser));
+
+    let stopExpectNoProcess = expectNoProcess();
+
+    yield ContentTask.spawn(aBrowser, null, () => {
+      this.__newWindow = content.window.open("about:blank");
+      content.document.location = "about:blank";
+    });
+
+    yield BrowserTestUtils.browserLoaded(aBrowser);
+
+    let pid3 = yield getPID(aBrowser);
+
+    is(pid3, pid2, "PIDs 2 and 3 should match");
+    is(true, yield getInLAProc(aBrowser));
+
+    stopExpectNoProcess();
+
+    yield ContentTask.spawn(aBrowser, null, () => {
+      ok(this.__newWindow, "The window should have been stored");
+      this.__newWindow.close();
+    });
+  });
+
+  yield BrowserTestUtils.withNewTab("about:blank", function*(aBrowser) {
+    info("Starting test 7");
+    yield SpecialPowers.pushPrefEnv({
+      set: [
+        ["dom.ipc.processCount.webLargeAllocation", 1]
+      ],
+    });
+
+    // Loading the first Large-Allocation tab should succeed as normal
+    let pid1 = yield getPID(aBrowser);
+    is(false, yield getInLAProc(aBrowser));
+
+    let ready = Promise.all([expectProcessCreated(),
+                             BrowserTestUtils.browserLoaded(aBrowser)]);
+
+    yield ContentTask.spawn(aBrowser, TEST_URI, TEST_URI => {
+      content.document.location = TEST_URI;
+    });
+
+    yield ready;
+
+    let pid2 = yield getPID(aBrowser);
+
+    isnot(pid1, pid2, "PIDs 1 and 2 should not match");
+    is(true, yield getInLAProc(aBrowser));
+
+    yield BrowserTestUtils.withNewTab("about:blank", function*(aBrowser) {
+      // The second one should load in a non-LA proc because the
+      // webLargeAllocation processes have been exhausted.
+      is(false, yield getInLAProc(aBrowser));
+
+      let ready = Promise.all([BrowserTestUtils.browserLoaded(aBrowser)]);
+      yield ContentTask.spawn(aBrowser, TEST_URI, TEST_URI => {
+        content.document.location = TEST_URI;
+      });
+      yield ready;
+
+      is(false, yield getInLAProc(aBrowser));
+    });
+  });
+
+  // XXX: Make sure to reset dom.ipc.processCount.webLargeAllocation if adding a
+  // test after the above test.
+}
+
+function* largeAllocFailTests() {
+  yield BrowserTestUtils.withNewTab("http://example.com", function*(aBrowser) {
+    info("Starting test 1");
+    let pid1 = yield getPID(aBrowser);
+    is(false, yield getInLAProc(aBrowser));
+
+    // Fail the test if we create a process
+    let stopExpectNoProcess = expectNoProcess();
+
+    yield ContentTask.spawn(aBrowser, TEST_URI, TEST_URI => {
+      content.document.location = TEST_URI;
+    });
+
+    yield BrowserTestUtils.browserLoaded(aBrowser);
+
+    let pid2 = yield getPID(aBrowser);
+
+    is(pid1, pid2, "The PID should not have changed");
+    is(false, yield getInLAProc(aBrowser));
+
+    stopExpectNoProcess();
+  });
+}