Bug 1345990 - Allow creation of new tabs in their own processes. draft
authorBlake Kaplan <mrbkap@gmail.com>
Wed, 26 Apr 2017 17:52:20 -0700
changeset 569871 c87277c6faab2461964ec2b2345681ece51a9376
parent 569870 239dedb3376eece13376ac028394c5e5c54c3774
child 626311 6e3d308bedd0c6e8efebb671a301af8ce3c5cc8b
push id56289
push userbmo:mrbkap@mozilla.com
push dateThu, 27 Apr 2017 23:18:20 +0000
bugs1345990
milestone55.0a1
Bug 1345990 - Allow creation of new tabs in their own processes. This uses XPCOM to replace the default process selector with one that always asks for a new process and then put the old one back again. This comes with a test to prove that it works. MozReview-Commit-ID: Bq6KP4VzP7W
dom/base/test/browser.ini
dom/base/test/browser_force_process_selector.js
testing/mochitest/BrowserTestUtils/BrowserTestUtils.jsm
--- a/dom/base/test/browser.ini
+++ b/dom/base/test/browser.ini
@@ -21,16 +21,18 @@ support-files =
   file_webaudioLoop.html
   plugin.js
 
 [browser_bug593387.js]
 [browser_bug902350.js]
 tags = mcb
 [browser_bug1011748.js]
 [browser_bug1058164.js]
+[browser_force_process_selector.js]
+skip-if = !e10s # this only makes sense with e10s-multi
 [browser_messagemanager_loadprocessscript.js]
 [browser_messagemanager_targetframeloader.js]
 [browser_messagemanager_unload.js]
 [browser_pagehide_on_tab_close.js]
 skip-if = e10s # this tests non-e10s behavior. it's not expected to work in e10s.
 [browser_state_notifications.js]
 skip-if = true # Bug 1271028
 [browser_use_counters.js]
new file mode 100644
--- /dev/null
+++ b/dom/base/test/browser_force_process_selector.js
@@ -0,0 +1,20 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+async function spawnNewAndTest(lastBrowser, recur) {
+  await BrowserTestUtils.withNewTab({ gBrowser, url: "about:blank", forceNewProcess: true },
+                                    function* (browser) {
+      // Make sure our new browser is in its own process.
+      isnot(lastBrowser.frameLoader.tabParent.osPid, browser.frameLoader.tabParent.osPid,
+            "new tab is in its own process");
+      if (recur) {
+        yield spawnNewAndTest(browser, recur - 1);
+      }
+  });
+}
+
+add_task(async function test() {
+  let firstBrowser = gBrowser.selectedBrowser;
+  let maxCount = Services.prefs.getIntPref("dom.ipc.processCount");
+  await spawnNewAndTest(firstBrowser, Math.min(maxCount + 1, 5));
+});
--- a/testing/mochitest/BrowserTestUtils/BrowserTestUtils.jsm
+++ b/testing/mochitest/BrowserTestUtils/BrowserTestUtils.jsm
@@ -28,16 +28,39 @@ Cu.import("resource://testing-common/Con
 Cc["@mozilla.org/globalmessagemanager;1"]
   .getService(Ci.nsIMessageListenerManager)
   .loadFrameScript(
     "chrome://mochikit/content/tests/BrowserTestUtils/content-utils.js", true);
 
 XPCOMUtils.defineLazyModuleGetter(this, "E10SUtils",
   "resource:///modules/E10SUtils.jsm");
 
+const PROCESSELECTOR_CONTRACTID = "@mozilla.org/ipc/processselector;1";
+const PROCESSELECTOR_CID =
+  Components.ID(Cc[PROCESSELECTOR_CONTRACTID].number);
+const OUR_PROCESSSELECTOR_CID =
+  Components.ID("{f9746211-3d53-4465-9aeb-ca0d96de0253}");
+
+// A process selector that always asks for a new process.
+function NewProcessSelector() {
+}
+
+NewProcessSelector.prototype = {
+  classID: OUR_PROCESSSELECTOR_CID,
+  QueryInterface: XPCOMUtils.generateQI([Ci.nsIContentProcessProvider]),
+
+  provideProcess() {
+    return Ci.nsIContentProcessProvider.NEW_PROCESS;
+  }
+};
+
+let registrar = Components.manager.QueryInterface(Ci.nsIComponentRegistrar);
+let selectorFactory = XPCOMUtils._getFactory(NewProcessSelector);
+registrar.registerFactory(OUR_PROCESSSELECTOR_CID, "", null, selectorFactory);
+
 // For now, we'll allow tests to use CPOWs in this module for
 // some cases.
 Cu.permitCPOWsInScope(this);
 
 var gSendCharCount = 0;
 var gSynthesizeKeyCount = 0;
 var gSynthesizeCompositionCount = 0;
 var gSynthesizeCompositionChangeCount = 0;
@@ -72,17 +95,17 @@ this.BrowserTestUtils = {
    */
   withNewTab: Task.async(function* (options, taskFn) {
     if (typeof(options) == "string") {
       options = {
         gBrowser: Services.wm.getMostRecentWindow("navigator:browser").gBrowser,
         url: options
       }
     }
-    let tab = yield BrowserTestUtils.openNewForegroundTab(options.gBrowser, options.url);
+    let tab = yield BrowserTestUtils.openNewForegroundTab(options);
     let originalWindow = tab.ownerGlobal;
     let result = yield taskFn(tab.linkedBrowser);
     let finalWindow = tab.ownerGlobal;
     if (originalWindow == finalWindow && !tab.closing && tab.linkedBrowser) {
       yield BrowserTestUtils.removeTab(tab);
     } else {
       Services.console.logStringMessage(
         "BrowserTestUtils.withNewTab: Tab was already closed before " +
@@ -143,29 +166,39 @@ this.BrowserTestUtils = {
         options.waitForStateStop = false;
       }
 
       tabbrowser = options.gBrowser;
     }
 
     let { opening: opening, waitForLoad: aWaitForLoad, waitForStateStop: aWaitForStateStop } = options;
 
+    // If we're asked to force a new process, replace the normal process
+    // selector with one that always asks for a new process.
+    if (options.forceNewProcess) {
+      registrar.registerFactory(OUR_PROCESSSELECTOR_CID, "", PROCESSELECTOR_CONTRACTID, null);
+    }
+
     let tab;
     let promises = [
       BrowserTestUtils.switchTab(tabbrowser, function () {
         if (typeof opening == "function") {
           opening();
           tab = tabbrowser.selectedTab;
         }
         else {
           tabbrowser.selectedTab = tab = tabbrowser.addTab(opening);
         }
       })
     ];
 
+    if (options.forceNewProcess) {
+      registrar.registerFactory(PROCESSELECTOR_CID, "", PROCESSELECTOR_CONTRACTID, null);
+    }
+
     if (aWaitForLoad) {
       promises.push(BrowserTestUtils.browserLoaded(tab.linkedBrowser));
     }
     if (aWaitForStateStop) {
       promises.push(BrowserTestUtils.browserStopped(tab.linkedBrowser));
     }
 
     return Promise.all(promises).then(() => tab);