Bug 1332343 - Fix browser_largeAllocation.js in multi-e10s. r=smaug, a=lizzard
authorMichael Layzell <michael@thelayzells.com>
Thu, 26 Jan 2017 12:07:24 -0500
changeset 358898 ce5e1c705787634cd9bfd3298f3ba9bad8c7815f
parent 358897 8483128d72e6509e552ce106ea450203b0502bf8
child 358899 77a99146995f55fa78caed883692ad9c4b838823
push id10672
push userryanvm@gmail.com
push dateThu, 02 Feb 2017 23:56:06 +0000
treeherdermozilla-aurora@c422e37f1667 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmaug, lizzard
bugs1332343
milestone53.0a2
Bug 1332343 - Fix browser_largeAllocation.js in multi-e10s. r=smaug, a=lizzard MozReview-Commit-ID: EdNYf1saOI0
dom/interfaces/base/nsITabChild.idl
dom/ipc/TabChild.cpp
dom/tests/browser/browser_largeAllocation.js
dom/tests/browser/test_largeAllocation.html
--- a/dom/interfaces/base/nsITabChild.idl
+++ b/dom/interfaces/base/nsITabChild.idl
@@ -29,10 +29,12 @@ interface nsITabChild : nsISupports
 
   [noscript] void remoteSizeShellTo(in int32_t width, in int32_t height,
                                     in int32_t shellItemWidth, in int32_t shellItemHeight);
 
   [noscript] void remoteDropLinks(in unsigned long linksCount,
                                   [array, size_is(linksCount)] in nsIDroppedLinkItem links);
 
   readonly attribute uint64_t tabId;
+
+  readonly attribute bool isInFreshProcess;
 };
 
--- a/dom/ipc/TabChild.cpp
+++ b/dom/ipc/TabChild.cpp
@@ -3105,16 +3105,24 @@ TabChild::RecvThemeChanged(nsTArray<Look
 mozilla::ipc::IPCResult
 TabChild::RecvSetFreshProcess()
 {
   MOZ_ASSERT(!sWasFreshProcess, "Can only be a fresh process once!");
   mIsFreshProcess = true;
   return IPC_OK();
 }
 
+NS_IMETHODIMP
+TabChild::GetIsInFreshProcess(bool* aResult)
+{
+  MOZ_ASSERT(aResult);
+  *aResult = mIsFreshProcess || sWasFreshProcess;
+  return NS_OK;
+}
+
 mozilla::plugins::PPluginWidgetChild*
 TabChild::AllocPPluginWidgetChild()
 {
     return new mozilla::plugins::PluginWidgetChild();
 }
 
 bool
 TabChild::DeallocPPluginWidgetChild(mozilla::plugins::PPluginWidgetChild* aActor)
--- a/dom/tests/browser/browser_largeAllocation.js
+++ b/dom/tests/browser/browser_largeAllocation.js
@@ -2,25 +2,33 @@
  * 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);
-  return new Promise(resolve => {
+  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);
@@ -33,16 +41,29 @@ function expectNoProcess() {
 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 getIsFreshProcess(aBrowser) {
+  return ContentTask.spawn(aBrowser, null, () => {
+    try {
+      return docShell.QueryInterface(Ci.nsIInterfaceRequestor)
+                     .getInterface(Ci.nsITabChild)
+                     .isInFreshProcess;
+    } 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
@@ -50,35 +71,38 @@ add_task(function*() {
       ["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 getIsFreshProcess(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 getIsFreshProcess(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 getIsFreshProcess(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 => {
@@ -87,24 +111,26 @@ add_task(function*() {
           resolve();
         };
       });
     });
 
     let pid2 = yield getPID(aBrowser);
 
     is(pid1, pid2, "The PID should not have changed");
+    is(false, yield getIsFreshProcess(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 getIsFreshProcess(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 => {
@@ -121,180 +147,201 @@ add_task(function*() {
 
     yield BrowserTestUtils.synthesizeMouseAtCenter("button", {}, aBrowser);
 
     yield loaded;
 
     let pid2 = yield getPID(aBrowser);
 
     is(pid1, pid2, "The PID should not have changed");
+    is(false, yield getIsFreshProcess(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 getIsFreshProcess(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);
-
-    epc = expectProcessCreated();
+    is(true, yield getIsFreshProcess(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 the first process.
-    is(pid1, pid3); // XXX: This may be flakey in multiple content process e10s?
+    // load, meaning we're back in a non-fresh process
+    is(false, yield getIsFreshProcess(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 getIsFreshProcess(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 getIsFreshProcess(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");
-
-    let stopExpectNoProcess = expectNoProcess();
+    is(true, yield getIsFreshProcess(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 the first process.
-    is(pid1, pid3, "PIDs 1 and 3 should match");
-
-    stopExpectNoProcess();
+    // load, meaning we're back in a non-large-allocation process.
+    is(false, yield getIsFreshProcess(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 getIsFreshProcess(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 getIsFreshProcess(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 getIsFreshProcess(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 getIsFreshProcess(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 1 and 3 should not match");
+    isnot(pid2, pid3, "PIDs 2 and 3 should not match");
+    is(true, yield getIsFreshProcess(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 getIsFreshProcess(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");
-
-    yield BrowserTestUtils.synthesizeMouse("a", 0, 0, {}, aBrowser);
+    is(true, yield getIsFreshProcess(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 getIsFreshProcess(aBrowser));
 
     stopExpectNoProcess();
 
-    is(gBrowser.tabs.length, 3, "There should be 3 tabs");
-
-    // Get rid of that other tab. It should always be the last one.
-    gBrowser.removeTab(gBrowser.tabs[2]);
+    yield ContentTask.spawn(aBrowser, null, () => {
+      ok(this.__newWindow, "The window should have been stored");
+      this.__newWindow.close();
+    });
   });
 });
--- a/dom/tests/browser/test_largeAllocation.html
+++ b/dom/tests/browser/test_largeAllocation.html
@@ -1,9 +1,4 @@
 <!doctype html>
 <html>
-  <script>
-    function onClick() {
-      window.open("about:blank");
-    }
-  </script>
-  <body><a onclick="onClick()">clicky</a>Loaded in a new process!</body>
+  <body>Loaded in a new process!</body>
 </html>