Bug 1083281: Use flags to determine which chrome and about URIs are loaded in the content process. r=mconley, r=bsmedberg, r=jduell, r=mratcliffe
authorDave Townsend <dtownsend@oxymoronical.com>
Thu, 05 Feb 2015 08:09:15 -0800
changeset 227663 5dcd284d63af6cf02a0ecb7a5bf7a73afef03b64
parent 227662 d0d66a5cad388d510a0803534eaf4334c258b6da
child 227664 3fb22d2c7fd7c27907e70bb1a90efd436cc393a9
push id11179
push userdtownsend@mozilla.com
push dateThu, 05 Feb 2015 16:17:55 +0000
treeherderfx-team@5dcd284d63af [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmconley, bsmedberg, jduell, mratcliffe
bugs1083281
milestone38.0a1
Bug 1083281: Use flags to determine which chrome and about URIs are loaded in the content process. r=mconley, r=bsmedberg, r=jduell, r=mratcliffe
browser/base/content/browser.js
browser/base/content/tabbrowser.xml
browser/base/content/test/general/browser.ini
browser/base/content/test/general/browser_aboutAccounts.js
browser/base/content/test/general/browser_bug1025195_switchToTabHavingURI_aOpenParams.js
browser/base/content/test/general/browser_e10s_about_process.js
browser/base/content/test/general/browser_e10s_chrome_process.js
browser/base/content/test/general/test_process_flags_chrome.html
browser/components/about/AboutRedirector.cpp
browser/components/sessionstore/test/browser_frametree.js
browser/components/sessionstore/test/head.js
browser/devtools/styleeditor/test/browser_styleeditor_loading.js
browser/modules/E10SUtils.jsm
chrome/nsChromeRegistry.cpp
chrome/nsChromeRegistry.h
chrome/nsChromeRegistryChrome.cpp
chrome/nsChromeRegistryChrome.h
chrome/nsChromeRegistryContent.cpp
chrome/nsChromeRegistryContent.h
chrome/nsIChromeRegistry.idl
docshell/base/nsAboutRedirector.cpp
netwerk/protocol/about/nsAboutBlank.cpp
netwerk/protocol/about/nsIAboutModule.idl
testing/mochitest/runtests.py
xpcom/components/ManifestParser.cpp
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -818,34 +818,36 @@ function _loadURIWithFlags(browser, uri,
   if (!uri) {
     uri = "about:blank";
   }
 
   if (!(flags & browser.webNavigation.LOAD_FLAGS_FROM_EXTERNAL)) {
     browser.userTypedClear++;
   }
 
-  let shouldBeRemote = gMultiProcessBrowser &&
-                       E10SUtils.shouldBrowserBeRemote(uri);
+  let process = browser.isRemoteBrowser ? Ci.nsIXULRuntime.PROCESS_TYPE_CONTENT
+                                        : Ci.nsIXULRuntime.PROCESS_TYPE_DEFAULT;
+  let mustChangeProcess = gMultiProcessBrowser &&
+                          !E10SUtils.canLoadURIInProcess(uri, process);
   try {
-    if (browser.isRemoteBrowser == shouldBeRemote) {
+    if (!mustChangeProcess) {
       browser.webNavigation.loadURI(uri, flags, referrer, postdata, null);
     } else {
       LoadInOtherProcess(browser, {
         uri: uri,
         flags: flags,
         referrer: referrer ? referrer.spec : null,
       });
     }
   } catch (e) {
     // If anything goes wrong just switch remoteness manually and load the URI.
     // We might lose history that way but at least the browser loaded a page.
     // This might be necessary if SessionStore wasn't initialized yet i.e.
     // when the homepage is a non-remote page.
-    gBrowser.updateBrowserRemoteness(browser, shouldBeRemote);
+    gBrowser.updateBrowserRemotenessByURL(browser, uri);
     browser.webNavigation.loadURI(uri, flags, referrer, postdata, null);
   } finally {
     if (browser.userTypedClear) {
       browser.userTypedClear--;
     }
   }
 }
 
--- a/browser/base/content/tabbrowser.xml
+++ b/browser/base/content/tabbrowser.xml
@@ -1509,19 +1509,28 @@
         </body>
       </method>
 
       <method name="updateBrowserRemotenessByURL">
         <parameter name="aBrowser"/>
         <parameter name="aURL"/>
         <body>
           <![CDATA[
-            let shouldBeRemote = gMultiProcessBrowser &&
-                                 E10SUtils.shouldBrowserBeRemote(aURL);
-            return this.updateBrowserRemoteness(aBrowser, shouldBeRemote);
+            if (!gMultiProcessBrowser)
+              return this.updateBrowserRemoteness(aBrowser, false);
+
+            let process = aBrowser.isRemoteBrowser ? Ci.nsIXULRuntime.PROCESS_TYPE_CONTENT
+                                                   : Ci.nsIXULRuntime.PROCESS_TYPE_DEFAULT;
+
+            // If this URL can't load in the browser's current process then flip
+            // it to the other process
+            if (!E10SUtils.canLoadURIInProcess(aURL, process))
+              return this.updateBrowserRemoteness(aBrowser, !aBrowser.isRemoteBrowser);
+
+            return false;
           ]]>
         </body>
       </method>
 
       <field name="_preloadedBrowser">null</field>
       <method name="_getPreloadedBrowser">
         <body>
           <![CDATA[
@@ -1685,17 +1694,20 @@
             var t = document.createElementNS(NS_XUL, "tab");
 
             var uriIsAboutBlank = !aURI || aURI == "about:blank";
 
             t.setAttribute("crop", "end");
             t.setAttribute("onerror", "this.removeAttribute('image');");
             t.className = "tabbrowser-tab";
 
-            let remote = gMultiProcessBrowser && E10SUtils.shouldBrowserBeRemote(aURI);
+            // The new browser should be remote if this is an e10s window and
+            // the uri to load can be loaded remotely.
+            let remote = gMultiProcessBrowser &&
+                         E10SUtils.canLoadURIInProcess(aURI, Ci.nsIXULRuntime.PROCESS_TYPE_CONTENT);
             if (remote)
               t.setAttribute("remote", "true");
 
             this.tabContainer._unlockTabSizing();
 
             // When overflowing, new tabs are scrolled into view smoothly, which
             // doesn't go well together with the width transition. So we skip the
             // transition in that case.
--- a/browser/base/content/test/general/browser.ini
+++ b/browser/base/content/test/general/browser.ini
@@ -74,16 +74,17 @@ support-files =
   searchSuggestionEngine.xml
   subtst_contextmenu.html
   test-mixedcontent-securityerrors.html
   test_bug435035.html
   test_bug462673.html
   test_bug628179.html
   test_bug839103.html
   test_bug959531.html
+  test_process_flags_chrome.html
   test_wyciwyg_copying.html
   title_test.svg
   video.ogg
   web_video.html
   web_video1.ogv
   web_video1.ogv^headers^
   zoom_test.html
   test_no_mcb_on_http_site_img.html
@@ -475,16 +476,18 @@ skip-if = e10s # Bug 940206 - nsIWebCont
 skip-if = e10s
 [browser_bug1024133-switchtab-override-keynav.js]
 skip-if = e10s
 [browser_bug1025195_switchToTabHavingURI_aOpenParams.js]
 [browser_addCertException.js]
 skip-if = e10s # Bug 1100687 - test directly manipulates content (content.document.getElementById)
 [browser_bug1045809.js]
 [browser_e10s_switchbrowser.js]
+[browser_e10s_about_process.js]
+[browser_e10s_chrome_process.js]
 [browser_blockHPKP.js]
 skip-if = e10s # bug 1100687 - test directly manipulates content (content.document.getElementById)
 [browser_mcb_redirect.js]
 skip-if = e10s # bug 1084504 - [e10s] Mixed content detection does not take redirection into account
 [browser_windowactivation.js]
 [browser_contextmenu_childprocess.js]
 [browser_bug963945.js]
 [browser_readerMode.js]
--- a/browser/base/content/test/general/browser_aboutAccounts.js
+++ b/browser/base/content/test/general/browser_aboutAccounts.js
@@ -212,20 +212,20 @@ let gTests = [
     let profD = Services.dirsvc.get("ProfD", Ci.nsIFile);
     let mockDir = profD.clone();
     mockDir.append("about-accounts-mock-profd");
     mockDir.createUnique(Ci.nsIFile.DIRECTORY_TYPE, FileUtils.PERMS_DIRECTORY);
     let fxAccountsStorage = OS.Path.join(mockDir.path, fxAccountsCommon.DEFAULT_STORAGE_FILENAME);
     yield OS.File.writeAtomic(fxAccountsStorage, JSON.stringify(signedInUser));
     info("Wrote file " + fxAccountsStorage);
 
-    // this is a little subtle - we load about:blank so we get a tab, then
-    // we send a message which does both (a) load the URL we want and (b) mocks
-    // the default profile path used by about:accounts.
-    let tab = yield promiseNewTabLoadEvent("about:blank?");
+    // this is a little subtle - we load about:robots so we get a non-remote
+    // tab, then we send a message which does both (a) load the URL we want and
+    // (b) mocks the default profile path used by about:accounts.
+    let tab = yield promiseNewTabLoadEvent("about:robots");
     let readyPromise = promiseOneMessage(tab, "test:load-with-mocked-profile-path-response");
 
     let mm = tab.linkedBrowser.messageManager;
     mm.sendAsyncMessage("test:load-with-mocked-profile-path", {
       url: "about:accounts",
       profilePath: mockDir.path,
     });
 
@@ -256,17 +256,17 @@ let gTests = [
     Services.prefs.setBoolPref(pref, true);
 
     let profD = Services.dirsvc.get("ProfD", Ci.nsIFile);
     let mockDir = profD.clone();
     mockDir.append("about-accounts-mock-profd");
     mockDir.createUnique(Ci.nsIFile.DIRECTORY_TYPE, FileUtils.PERMS_DIRECTORY);
     // but leave it empty, so we don't think a user is logged in.
 
-    let tab = yield promiseNewTabLoadEvent("about:blank?");
+    let tab = yield promiseNewTabLoadEvent("about:robots");
     let readyPromise = promiseOneMessage(tab, "test:load-with-mocked-profile-path-response");
 
     let mm = tab.linkedBrowser.messageManager;
     mm.sendAsyncMessage("test:load-with-mocked-profile-path", {
       url: "about:accounts",
       profilePath: mockDir.path,
     });
 
--- a/browser/base/content/test/general/browser_bug1025195_switchToTabHavingURI_aOpenParams.js
+++ b/browser/base/content/test/general/browser_bug1025195_switchToTabHavingURI_aOpenParams.js
@@ -8,18 +8,23 @@ add_task(function test_ignoreFragment() 
   let tabRefAboutMozilla = gBrowser.addTab("about:mozilla");
   yield promiseTabLoaded(tabRefAboutMozilla);
 
   gBrowser.selectedTab = tabRefAboutMozilla;
   let numTabsAtStart = gBrowser.tabs.length;
 
   switchTab("about:home#1", true);
   switchTab("about:mozilla", true);
+
+  let hashChangePromise = new Promise(resolve => {
+    tabRefAboutHome.linkedBrowser.contentWindow.addEventListener("hashchange", resolve, false);
+  });
   switchTab("about:home#2", true, { ignoreFragment: true });
   is(tabRefAboutHome, gBrowser.selectedTab, "The same about:home tab should be switched to");
+  yield hashChangePromise;
   is(gBrowser.currentURI.ref, "2", "The ref should be updated to the new ref");
   switchTab("about:mozilla", true);
   switchTab("about:home#1", false);
   isnot(tabRefAboutHome, gBrowser.selectedTab, "Selected tab should not be initial about:blank tab");
   is(gBrowser.tabs.length, numTabsAtStart + 1, "Should have one new tab opened");
   switchTab("about:about", false, { ignoreFragment: true });
   cleanupTestTabs();
 });
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/general/browser_e10s_about_process.js
@@ -0,0 +1,114 @@
+const CHROME_PROCESS = Ci.nsIXULRuntime.PROCESS_TYPE_DEFAULT;
+const CONTENT_PROCESS = Ci.nsIXULRuntime.PROCESS_TYPE_CONTENT;
+
+const CHROME = {
+  id: "cb34538a-d9da-40f3-b61a-069f0b2cb9fb",
+  path: "test-chrome",
+  flags: 0,
+}
+const CANREMOTE = {
+  id: "2480d3e1-9ce4-4b84-8ae3-910b9a95cbb3",
+  path: "test-allowremote",
+  flags: Ci.nsIAboutModule.URI_CAN_LOAD_IN_CHILD,
+}
+const MUSTREMOTE = {
+  id: "f849cee5-e13e-44d2-981d-0fb3884aaead",
+  path: "test-mustremote",
+  flags: Ci.nsIAboutModule.URI_MUST_LOAD_IN_CHILD,
+}
+
+const TEST_MODULES = [
+  CHROME,
+  CANREMOTE,
+  MUSTREMOTE
+]
+
+function AboutModule() {
+}
+
+AboutModule.prototype = {
+  newChannel: function(aURI, aLoadInfo) {
+    throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
+  },
+
+  getURIFlags: function(aURI) {
+    for (let module of TEST_MODULES) {
+      if (aURI.path.startsWith(module.path)) {
+        return module.flags;
+      }
+    }
+
+    ok(false, "Called getURIFlags for an unknown page " + aURI.spec);
+    return 0;
+  },
+
+  getIndexedDBOriginPostfix: function(aURI) {
+    return null;
+  },
+
+  QueryInterface: XPCOMUtils.generateQI([Ci.nsIAboutModule])
+};
+
+let AboutModuleFactory = {
+  createInstance: function(aOuter, aIID) {
+    if (aOuter)
+      throw Components.results.NS_ERROR_NO_AGGREGATION;
+    return new AboutModule().QueryInterface(aIID);
+  },
+
+  lockFactory: function(aLock) {
+    throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
+  },
+
+  QueryInterface: XPCOMUtils.generateQI([Ci.nsIFactory])
+};
+
+add_task(function* init() {
+  let registrar = Components.manager.QueryInterface(Ci.nsIComponentRegistrar);
+  for (let module of TEST_MODULES) {
+    registrar.registerFactory(Components.ID(module.id), "",
+                              "@mozilla.org/network/protocol/about;1?what=" + module.path,
+                              AboutModuleFactory);
+  }
+});
+
+registerCleanupFunction(() => {
+  let registrar = Components.manager.QueryInterface(Ci.nsIComponentRegistrar);
+  for (let module of TEST_MODULES) {
+    registrar.unregisterFactory(Components.ID(module.id), AboutModuleFactory);
+  }
+});
+
+function test_url(url, chromeResult, contentResult) {
+  is(E10SUtils.canLoadURIInProcess(url, CHROME_PROCESS),
+     chromeResult, "Check URL in chrome process.");
+  is(E10SUtils.canLoadURIInProcess(url, CONTENT_PROCESS),
+     contentResult, "Check URL in content process.");
+
+  is(E10SUtils.canLoadURIInProcess(url + "#foo", CHROME_PROCESS),
+     chromeResult, "Check URL with ref in chrome process.");
+  is(E10SUtils.canLoadURIInProcess(url + "#foo", CONTENT_PROCESS),
+     contentResult, "Check URL with ref in content process.");
+
+  is(E10SUtils.canLoadURIInProcess(url + "?foo", CHROME_PROCESS),
+     chromeResult, "Check URL with query in chrome process.");
+  is(E10SUtils.canLoadURIInProcess(url + "?foo", CONTENT_PROCESS),
+     contentResult, "Check URL with query in content process.");
+
+  is(E10SUtils.canLoadURIInProcess(url + "?foo#bar", CHROME_PROCESS),
+     chromeResult, "Check URL with query and ref in chrome process.");
+  is(E10SUtils.canLoadURIInProcess(url + "?foo#bar", CONTENT_PROCESS),
+     contentResult, "Check URL with query and ref in content process.");
+}
+
+add_task(function* test_chrome() {
+  test_url("about:" + CHROME.path, true, false);
+});
+
+add_task(function* test_any() {
+  test_url("about:" + CANREMOTE.path, true, true);
+});
+
+add_task(function* test_remote() {
+  test_url("about:" + MUSTREMOTE.path, false, true);
+});
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/general/browser_e10s_chrome_process.js
@@ -0,0 +1,150 @@
+// Returns a function suitable for add_task which loads startURL, runs
+// transitionTask and waits for endURL to load, checking that the URLs were
+// loaded in the correct process.
+function makeTest(name, startURL, startProcessIsRemote, endURL, endProcessIsRemote, transitionTask) {
+  return function*() {
+    info("Running test " + name + ", " + transitionTask.name);
+    let browser = gBrowser.selectedBrowser;
+
+    // In non-e10s nothing should be remote
+    if (!gMultiProcessBrowser) {
+      startProcessIsRemote = false;
+      endProcessIsRemote = false;
+    }
+
+    // Load the initial URL and make sure we are in the right initial process
+    info("Loading initial URL");
+    browser.loadURI(startURL);
+    yield waitForDocLoadComplete();
+
+    is(browser.currentURI.spec, startURL, "Shouldn't have been redirected");
+    is(browser.isRemoteBrowser, startProcessIsRemote, "Should be displayed in the right process");
+
+    let docLoadedPromise = waitForDocLoadComplete();
+    let asyncTask = Task.async(transitionTask);
+    let expectSyncChange = yield asyncTask(browser, endURL);
+    if (expectSyncChange) {
+      is(browser.isRemoteBrowser, endProcessIsRemote, "Should have switched to the right process synchronously");
+    }
+    yield docLoadedPromise;
+
+    is(browser.currentURI.spec, endURL, "Should have made it to the final URL");
+    is(browser.isRemoteBrowser, endProcessIsRemote, "Should be displayed in the right process");
+  }
+}
+
+const CHROME_PROCESS = Ci.nsIXULRuntime.PROCESS_TYPE_DEFAULT;
+const CONTENT_PROCESS = Ci.nsIXULRuntime.PROCESS_TYPE_CONTENT;
+const PATH = (getRootDirectory(gTestPath) + "test_process_flags_chrome.html").replace("chrome://mochitests", "");
+
+const CHROME = "chrome://mochitests" + PATH;
+const CANREMOTE = "chrome://mochitests-any" + PATH;
+const MUSTREMOTE = "chrome://mochitests-content" + PATH;
+
+add_task(function* init() {
+  gBrowser.selectedTab = gBrowser.addTab("about:blank");
+});
+
+registerCleanupFunction(() => {
+  gBrowser.removeCurrentTab();
+});
+
+function test_url(url, chromeResult, contentResult) {
+  is(E10SUtils.canLoadURIInProcess(url, CHROME_PROCESS),
+     chromeResult, "Check URL in chrome process.");
+  is(E10SUtils.canLoadURIInProcess(url, CONTENT_PROCESS),
+     contentResult, "Check URL in content process.");
+
+  is(E10SUtils.canLoadURIInProcess(url + "#foo", CHROME_PROCESS),
+     chromeResult, "Check URL with ref in chrome process.");
+  is(E10SUtils.canLoadURIInProcess(url + "#foo", CONTENT_PROCESS),
+     contentResult, "Check URL with ref in content process.");
+
+  is(E10SUtils.canLoadURIInProcess(url + "?foo", CHROME_PROCESS),
+     chromeResult, "Check URL with query in chrome process.");
+  is(E10SUtils.canLoadURIInProcess(url + "?foo", CONTENT_PROCESS),
+     contentResult, "Check URL with query in content process.");
+
+  is(E10SUtils.canLoadURIInProcess(url + "?foo#bar", CHROME_PROCESS),
+     chromeResult, "Check URL with query and ref in chrome process.");
+  is(E10SUtils.canLoadURIInProcess(url + "?foo#bar", CONTENT_PROCESS),
+     contentResult, "Check URL with query and ref in content process.");
+}
+
+add_task(function* test_chrome() {
+  test_url(CHROME, true, false);
+});
+
+add_task(function* test_any() {
+  test_url(CANREMOTE, true, true);
+});
+
+add_task(function* test_remote() {
+  test_url(MUSTREMOTE, false, true);
+});
+
+// The set of page transitions
+let TESTS = [
+  [
+    "chrome -> chrome",
+    CHROME, false,
+    CHROME, false,
+  ],
+  [
+    "chrome -> canremote",
+    CHROME, false,
+    CANREMOTE, false,
+  ],
+  [
+    "chrome -> mustremote",
+    CHROME, false,
+    MUSTREMOTE, true,
+  ],
+  [
+    "remote -> chrome",
+    MUSTREMOTE, true,
+    CHROME, false,
+  ],
+  [
+    "remote -> canremote",
+    MUSTREMOTE, true,
+    CANREMOTE, true,
+  ],
+  [
+    "remote -> mustremote",
+    MUSTREMOTE, true,
+    MUSTREMOTE, true,
+  ],
+];
+
+// The different ways to transition from one page to another
+let TRANSITIONS = [
+// Loads the new page by calling browser.loadURI directly
+function* loadURI(browser, uri) {
+  info("Calling browser.loadURI");
+  browser.loadURI(uri);
+  return true;
+},
+
+// Loads the new page by finding a link with the right href in the document and
+// clicking it
+function* clickLink(browser, uri) {
+  info("Clicking link");
+
+  function frame_script(uri) {
+    let link = content.document.querySelector("a[href='" + uri + "']");
+    link.click();
+  }
+
+  browser.messageManager.loadFrameScript("data:,(" + frame_script.toString() + ")(" + JSON.stringify(uri) + ");", false);
+
+  return false;
+},
+];
+
+// Creates a set of test tasks, one for each combination of TESTS and TRANSITIONS.
+for (let test of TESTS) {
+  for (let transition of TRANSITIONS) {
+    add_task(makeTest(...test, transition));
+  }
+}
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/general/test_process_flags_chrome.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+
+<html>
+<body>
+<p>chrome: test page</p>
+<p><a href="chrome://mochitests/content/browser/browser/base/content/test/general/test_process_flags_chrome.html">chrome</a></p>
+<p><a href="chrome://mochitests-any/content/browser/browser/base/content/test/general/test_process_flags_chrome.html">canremote</a></p>
+<p><a href="chrome://mochitests-content/content/browser/browser/base/content/test/general/test_process_flags_chrome.html">mustremote</a></p>
+</body>
+</html>
--- a/browser/components/about/AboutRedirector.cpp
+++ b/browser/components/about/AboutRedirector.cpp
@@ -37,16 +37,17 @@ static RedirEntry kRedirMap[] = {
 #ifdef MOZ_SAFE_BROWSING
   { "blocked", "chrome://browser/content/blockedSite.xhtml",
     nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT |
     nsIAboutModule::ALLOW_SCRIPT |
     nsIAboutModule::HIDE_FROM_ABOUTABOUT },
 #endif
   { "certerror", "chrome://browser/content/certerror/aboutCertError.xhtml",
     nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT |
+    nsIAboutModule::URI_CAN_LOAD_IN_CHILD |
     nsIAboutModule::ALLOW_SCRIPT |
     nsIAboutModule::HIDE_FROM_ABOUTABOUT },
   { "socialerror", "chrome://browser/content/aboutSocialError.xhtml",
     nsIAboutModule::ALLOW_SCRIPT |
     nsIAboutModule::HIDE_FROM_ABOUTABOUT },
   { "providerdirectory", "chrome://browser/content/aboutProviderDirectory.xhtml",
     nsIAboutModule::ALLOW_SCRIPT |
     nsIAboutModule::HIDE_FROM_ABOUTABOUT },
@@ -78,16 +79,17 @@ static RedirEntry kRedirMap[] = {
 #ifdef MOZ_SERVICES_SYNC
   { "sync-progress", "chrome://browser/content/sync/progress.xhtml",
     nsIAboutModule::ALLOW_SCRIPT },
   { "sync-tabs", "chrome://browser/content/sync/aboutSyncTabs.xul",
     nsIAboutModule::ALLOW_SCRIPT },
 #endif
   { "home", "chrome://browser/content/abouthome/aboutHome.xhtml",
     nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT |
+    nsIAboutModule::URI_MUST_LOAD_IN_CHILD |
     nsIAboutModule::ALLOW_SCRIPT |
     nsIAboutModule::ENABLE_INDEXED_DB },
   { "newtab", "chrome://browser/content/newtab/newTab.xul",
     nsIAboutModule::ALLOW_SCRIPT },
   { "permissions", "chrome://browser/content/preferences/aboutPermissions.xul",
     nsIAboutModule::ALLOW_SCRIPT },
   { "preferences", "chrome://browser/content/preferences/in-content/preferences.xul",
     nsIAboutModule::ALLOW_SCRIPT },
--- a/browser/components/sessionstore/test/browser_frametree.js
+++ b/browser/components/sessionstore/test/browser_frametree.js
@@ -1,15 +1,15 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
-const URL = ROOT + "browser_frametree_sample.html";
-const URL_FRAMESET = ROOT + "browser_frametree_sample_frameset.html";
+const URL = HTTPROOT + "browser_frametree_sample.html";
+const URL_FRAMESET = HTTPROOT + "browser_frametree_sample_frameset.html";
 
 /**
  * This ensures that loading a page normally, aborting a page load, reloading
  * a page, navigating using the bfcache, and ignoring frames that were
  * created dynamically work as expect. We expect the frame tree to be reset
  * when a page starts loading and we also expect a valid frame tree to exist
  * when it has stopped loading.
  */
--- a/browser/components/sessionstore/test/head.js
+++ b/browser/components/sessionstore/test/head.js
@@ -1,16 +1,17 @@
 /* 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 TAB_STATE_NEEDS_RESTORE = 1;
 const TAB_STATE_RESTORING = 2;
 
 const ROOT = getRootDirectory(gTestPath);
+const HTTPROOT = ROOT.replace("chrome://mochitests/content/", "http://example.com/");
 const FRAME_SCRIPTS = [
   ROOT + "content.js",
   ROOT + "content-forms.js"
 ];
 
 let mm = Cc["@mozilla.org/globalmessagemanager;1"]
            .getService(Ci.nsIMessageListenerManager);
 
--- a/browser/devtools/styleeditor/test/browser_styleeditor_loading.js
+++ b/browser/devtools/styleeditor/test/browser_styleeditor_loading.js
@@ -1,13 +1,13 @@
 /* vim: set ts=2 et sw=2 tw=80: */
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
-const TESTCASE_URI = TEST_BASE + "longload.html";
+const TESTCASE_URI = TEST_BASE_HTTP + "longload.html";
 
 
 function test()
 {
   waitForExplicitFinish();
 
   // launch Style Editor right when the tab is created (before load)
   // this checks that the Style Editor still launches correctly when it is opened
--- a/browser/modules/E10SUtils.jsm
+++ b/browser/modules/E10SUtils.jsm
@@ -5,48 +5,77 @@
 "use strict";
 
 this.EXPORTED_SYMBOLS = ["E10SUtils"];
 
 const {interfaces: Ci, utils: Cu, classes: Cc} = Components;
 
 Cu.import("resource://gre/modules/Services.jsm");
 
+function getAboutModule(aURL) {
+  // Needs to match NS_GetAboutModuleName
+  let moduleName = aURL.path.replace(/[#?].*/, "").toLowerCase();
+  let contract = "@mozilla.org/network/protocol/about;1?what=" + moduleName;
+  try {
+    return Cc[contract].getService(Ci.nsIAboutModule);
+  }
+  catch (e) {
+    // Either the about module isn't defined or it is broken. In either case
+    // ignore it.
+    return null;
+  }
+}
+
 this.E10SUtils = {
-  shouldBrowserBeRemote: function(aURL) {
+  canLoadURIInProcess: function(aURL, aProcess) {
     // loadURI in browser.xml treats null as about:blank
     if (!aURL)
       aURL = "about:blank";
 
-    if (aURL.startsWith("about:") &&
-        aURL.toLowerCase() != "about:home" &&
-        aURL.toLowerCase() != "about:blank" &&
-        !aURL.toLowerCase().startsWith("about:neterror") &&
-        !aURL.toLowerCase().startsWith("about:certerror")) {
+    let processIsRemote = aProcess == Ci.nsIXULRuntime.PROCESS_TYPE_CONTENT;
+
+    let canLoadRemote = true;
+    let mustLoadRemote = true;
+
+    if (aURL.startsWith("about:")) {
+      let url = Services.io.newURI(aURL, null, null);
+      let module = getAboutModule(url);
+      // If the module doesn't exist then an error page will be loading, that
+      // should be ok to load in either process
+      if (module) {
+        let flags = module.getURIFlags(url);
+        canLoadRemote = !!(flags & Ci.nsIAboutModule.URI_CAN_LOAD_IN_CHILD);
+        mustLoadRemote = !!(flags & Ci.nsIAboutModule.URI_MUST_LOAD_IN_CHILD);
+      }
+    }
+
+    if (aURL.startsWith("chrome:")) {
+      let url = Services.io.newURI(aURL, null, null);
+      let chromeReg = Cc["@mozilla.org/chrome/chrome-registry;1"].
+                      getService(Ci.nsIXULChromeRegistry);
+      canLoadRemote = chromeReg.canLoadURLRemotely(url);
+      mustLoadRemote = chromeReg.mustLoadURLRemotely(url);
+    }
+
+    if (mustLoadRemote)
+      return processIsRemote;
+
+    if (!canLoadRemote && processIsRemote)
       return false;
-    }
 
     return true;
   },
 
   shouldLoadURI: function(aDocShell, aURI, aReferrer) {
-    // about:blank is the initial document and can load anywhere
-    if (aURI.spec == "about:blank")
-      return true;
-
     // Inner frames should always load in the current process
     if (aDocShell.QueryInterface(Ci.nsIDocShellTreeItem).sameTypeParent)
       return true;
 
     // If the URI can be loaded in the current process then continue
-    let isRemote = Services.appinfo.processType == Services.appinfo.PROCESS_TYPE_CONTENT;
-    if (this.shouldBrowserBeRemote(aURI.spec) == isRemote)
-      return true;
-
-    return false;
+    return this.canLoadURIInProcess(aURI.spec, Services.appinfo.processType);
   },
 
   redirectLoad: function(aDocShell, aURI, aReferrer) {
     // Retarget the load to the correct process
     let messageManager = aDocShell.QueryInterface(Ci.nsIInterfaceRequestor)
                                   .getInterface(Ci.nsIContentFrameMessageManager);
     let sessionHistory = aDocShell.getInterface(Ci.nsIWebNavigation).sessionHistory;
 
--- a/chrome/nsChromeRegistry.cpp
+++ b/chrome/nsChromeRegistry.cpp
@@ -587,16 +587,80 @@ nsChromeRegistry::AllowContentToAccess(n
   rv = GetFlagsFromPackage(package, &flags);
 
   if (NS_SUCCEEDED(rv)) {
     *aResult = !!(flags & CONTENT_ACCESSIBLE);
   }
   return NS_OK;
 }
 
+NS_IMETHODIMP
+nsChromeRegistry::CanLoadURLRemotely(nsIURI *aURI, bool *aResult)
+{
+  nsresult rv;
+
+  *aResult = false;
+
+#ifdef DEBUG
+  bool isChrome;
+  aURI->SchemeIs("chrome", &isChrome);
+  NS_ASSERTION(isChrome, "Non-chrome URI passed to CanLoadURLRemotely!");
+#endif
+
+  nsCOMPtr<nsIURL> url = do_QueryInterface(aURI);
+  if (!url) {
+    NS_ERROR("Chrome URL doesn't implement nsIURL.");
+    return NS_ERROR_UNEXPECTED;
+  }
+
+  nsAutoCString package;
+  rv = url->GetHostPort(package);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  uint32_t flags;
+  rv = GetFlagsFromPackage(package, &flags);
+
+  if (NS_SUCCEEDED(rv)) {
+    *aResult = !!(flags & REMOTE_ALLOWED);
+  }
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsChromeRegistry::MustLoadURLRemotely(nsIURI *aURI, bool *aResult)
+{
+  nsresult rv;
+
+  *aResult = false;
+
+#ifdef DEBUG
+  bool isChrome;
+  aURI->SchemeIs("chrome", &isChrome);
+  NS_ASSERTION(isChrome, "Non-chrome URI passed to MustLoadURLRemotely!");
+#endif
+
+  nsCOMPtr<nsIURL> url = do_QueryInterface(aURI);
+  if (!url) {
+    NS_ERROR("Chrome URL doesn't implement nsIURL.");
+    return NS_ERROR_UNEXPECTED;
+  }
+
+  nsAutoCString package;
+  rv = url->GetHostPort(package);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  uint32_t flags;
+  rv = GetFlagsFromPackage(package, &flags);
+
+  if (NS_SUCCEEDED(rv)) {
+    *aResult = !!(flags & REMOTE_REQUIRED);
+  }
+  return NS_OK;
+}
+
 NS_IMETHODIMP_(bool)
 nsChromeRegistry::WrappersEnabled(nsIURI *aURI)
 {
   nsCOMPtr<nsIURL> chromeURL (do_QueryInterface(aURI));
   if (!chromeURL)
     return false;
 
   bool isChrome = false;
--- a/chrome/nsChromeRegistry.h
+++ b/chrome/nsChromeRegistry.h
@@ -48,16 +48,20 @@ public:
 
   // nsIXULChromeRegistry methods:
   NS_IMETHOD ReloadChrome() MOZ_OVERRIDE;
   NS_IMETHOD RefreshSkins() MOZ_OVERRIDE;
   NS_IMETHOD AllowScriptsForPackage(nsIURI* url,
                                     bool* _retval) MOZ_OVERRIDE;
   NS_IMETHOD AllowContentToAccess(nsIURI* url,
                                   bool* _retval) MOZ_OVERRIDE;
+  NS_IMETHOD CanLoadURLRemotely(nsIURI* url,
+                                bool* _retval) MOZ_OVERRIDE;
+  NS_IMETHOD MustLoadURLRemotely(nsIURI* url,
+                                 bool* _retval) MOZ_OVERRIDE;
 
   // nsIChromeRegistry methods:
   NS_IMETHOD_(bool) WrappersEnabled(nsIURI *aURI) MOZ_OVERRIDE;
   NS_IMETHOD ConvertChromeURL(nsIURI* aChromeURI, nsIURI* *aResult) MOZ_OVERRIDE;
 
   // nsChromeRegistry methods:
   nsChromeRegistry() : mInitialized(false) { }
 
@@ -115,50 +119,49 @@ public:
 
     NSLocationType mType;
     mozilla::FileLocation mFile;
     nsCOMPtr<nsIURI> mManifestURI;
     nsCOMPtr<nsIXPConnect> mXPConnect;
   };
 
   virtual void ManifestContent(ManifestProcessingContext& cx, int lineno,
-                               char *const * argv, bool platform,
-                               bool contentaccessible) = 0;
+                               char *const * argv, int flags) = 0;
   virtual void ManifestLocale(ManifestProcessingContext& cx, int lineno,
-                              char *const * argv, bool platform,
-                              bool contentaccessible) = 0;
+                              char *const * argv, int flags) = 0;
   virtual void ManifestSkin(ManifestProcessingContext& cx, int lineno,
-                            char *const * argv, bool platform,
-                            bool contentaccessible) = 0;
+                            char *const * argv, int flags) = 0;
   virtual void ManifestOverlay(ManifestProcessingContext& cx, int lineno,
-                               char *const * argv, bool platform,
-                               bool contentaccessible) = 0;
+                               char *const * argv, int flags) = 0;
   virtual void ManifestStyle(ManifestProcessingContext& cx, int lineno,
-                             char *const * argv, bool platform,
-                             bool contentaccessible) = 0;
+                             char *const * argv, int flags) = 0;
   virtual void ManifestOverride(ManifestProcessingContext& cx, int lineno,
-                                char *const * argv, bool platform,
-                                bool contentaccessible) = 0;
+                                char *const * argv, int flags) = 0;
   virtual void ManifestResource(ManifestProcessingContext& cx, int lineno,
-                                char *const * argv, bool platform,
-                                bool contentaccessible) = 0;
+                                char *const * argv, int flags) = 0;
 
   // Available flags
   enum {
     // This is a "platform" package (e.g. chrome://global-platform/).
     // Appends one of win/ unix/ mac/ to the base URI.
     PLATFORM_PACKAGE = 1 << 0,
 
     // This package should use the new XPCNativeWrappers to separate
     // content from chrome. This flag is currently unused (because we call
     // into xpconnect at registration time).
     XPCNATIVEWRAPPERS = 1 << 1,
 
     // Content script may access files in this package
-    CONTENT_ACCESSIBLE = 1 << 2
+    CONTENT_ACCESSIBLE = 1 << 2,
+
+    // Package may be loaded remotely
+    REMOTE_ALLOWED = 1 << 3,
+
+    // Package must be loaded remotely
+    REMOTE_REQUIRED = 1 << 4,
   };
 
   bool mInitialized;
 
   // "Override" table (chrome URI string -> real URI)
   nsInterfaceHashtable<nsURIHashKey, nsIURI> mOverrideTable;
 };
 
--- a/chrome/nsChromeRegistryChrome.cpp
+++ b/chrome/nsChromeRegistryChrome.cpp
@@ -767,18 +767,17 @@ SendManifestEntry(const ChromeRegistryIt
 
   for (uint32_t i = 0; i < parents.Length(); i++) {
     unused << parents[i]->SendRegisterChromeItem(aItem);
   }
 }
 
 void
 nsChromeRegistryChrome::ManifestContent(ManifestProcessingContext& cx, int lineno,
-                                        char *const * argv, bool platform,
-                                        bool contentaccessible)
+                                        char *const * argv, int flags)
 {
   char* package = argv[0];
   char* uri = argv[1];
 
   EnsureLowerCase(package);
 
   nsCOMPtr<nsIURI> resolved = cx.ResolveURI(uri);
   if (!resolved) {
@@ -792,34 +791,29 @@ nsChromeRegistryChrome::ManifestContent(
                           "During chrome registration, cannot register non-local URI '%s' as content.",
                           uri);
     return;
   }
 
   nsDependentCString packageName(package);
   PackageEntry* entry = mPackagesHash.LookupOrAdd(packageName);
   entry->baseURI = resolved;
-
-  if (platform)
-    entry->flags |= PLATFORM_PACKAGE;
-  if (contentaccessible)
-    entry->flags |= CONTENT_ACCESSIBLE;
+  entry->flags = flags;
 
   if (mDynamicRegistration) {
     ChromePackage chromePackage;
     ChromePackageFromPackageEntry(packageName, entry, &chromePackage,
                                   mSelectedLocale, mSelectedSkin);
     SendManifestEntry(chromePackage);
   }
 }
 
 void
 nsChromeRegistryChrome::ManifestLocale(ManifestProcessingContext& cx, int lineno,
-                                       char *const * argv, bool platform,
-                                       bool contentaccessible)
+                                       char *const * argv, int flags)
 {
   char* package = argv[0];
   char* provider = argv[1];
   char* uri = argv[2];
 
   EnsureLowerCase(package);
 
   nsCOMPtr<nsIURI> resolved = cx.ResolveURI(uri);
@@ -845,18 +839,17 @@ nsChromeRegistryChrome::ManifestLocale(M
     ChromePackageFromPackageEntry(packageName, entry, &chromePackage,
                                   mSelectedLocale, mSelectedSkin);
     SendManifestEntry(chromePackage);
   }
 }
 
 void
 nsChromeRegistryChrome::ManifestSkin(ManifestProcessingContext& cx, int lineno,
-                                     char *const * argv, bool platform,
-                                     bool contentaccessible)
+                                     char *const * argv, int flags)
 {
   char* package = argv[0];
   char* provider = argv[1];
   char* uri = argv[2];
 
   EnsureLowerCase(package);
 
   nsCOMPtr<nsIURI> resolved = cx.ResolveURI(uri);
@@ -882,18 +875,17 @@ nsChromeRegistryChrome::ManifestSkin(Man
     ChromePackageFromPackageEntry(packageName, entry, &chromePackage,
                                   mSelectedLocale, mSelectedSkin);
     SendManifestEntry(chromePackage);
   }
 }
 
 void
 nsChromeRegistryChrome::ManifestOverlay(ManifestProcessingContext& cx, int lineno,
-                                        char *const * argv, bool platform,
-                                        bool contentaccessible)
+                                        char *const * argv, int flags)
 {
   char* base = argv[0];
   char* overlay = argv[1];
 
   nsCOMPtr<nsIURI> baseuri = cx.ResolveURI(base);
   nsCOMPtr<nsIURI> overlayuri = cx.ResolveURI(overlay);
   if (!baseuri || !overlayuri) {
     LogMessageWithContext(cx.GetManifestURI(), lineno, nsIScriptError::warningFlag,
@@ -910,18 +902,17 @@ nsChromeRegistryChrome::ManifestOverlay(
   nsCOMPtr<nsIURI> baseuriWithoutHash;
   baseuri->CloneIgnoringRef(getter_AddRefs(baseuriWithoutHash));
 
   mOverlayHash.Add(baseuriWithoutHash, overlayuri);
 }
 
 void
 nsChromeRegistryChrome::ManifestStyle(ManifestProcessingContext& cx, int lineno,
-                                      char *const * argv, bool platform,
-                                      bool contentaccessible)
+                                      char *const * argv, int flags)
 {
   char* base = argv[0];
   char* overlay = argv[1];
 
   nsCOMPtr<nsIURI> baseuri = cx.ResolveURI(base);
   nsCOMPtr<nsIURI> overlayuri = cx.ResolveURI(overlay);
   if (!baseuri || !overlayuri) {
     LogMessageWithContext(cx.GetManifestURI(), lineno, nsIScriptError::warningFlag,
@@ -938,18 +929,17 @@ nsChromeRegistryChrome::ManifestStyle(Ma
   nsCOMPtr<nsIURI> baseuriWithoutHash;
   baseuri->CloneIgnoringRef(getter_AddRefs(baseuriWithoutHash));
 
   mStyleHash.Add(baseuriWithoutHash, overlayuri);
 }
 
 void
 nsChromeRegistryChrome::ManifestOverride(ManifestProcessingContext& cx, int lineno,
-                                         char *const * argv, bool platform,
-                                         bool contentaccessible)
+                                         char *const * argv, int flags)
 {
   char* chrome = argv[0];
   char* resolved = argv[1];
 
   nsCOMPtr<nsIURI> chromeuri = cx.ResolveURI(chrome);
   nsCOMPtr<nsIURI> resolveduri = cx.ResolveURI(resolved);
   if (!chromeuri || !resolveduri) {
     LogMessageWithContext(cx.GetManifestURI(), lineno, nsIScriptError::warningFlag,
@@ -973,18 +963,17 @@ nsChromeRegistryChrome::ManifestOverride
 
     OverrideMapping override = { serializedChrome, serializedOverride };
     SendManifestEntry(override);
   }
 }
 
 void
 nsChromeRegistryChrome::ManifestResource(ManifestProcessingContext& cx, int lineno,
-                                         char *const * argv, bool platform,
-                                         bool contentaccessible)
+                                         char *const * argv, int flags)
 {
   char* package = argv[0];
   char* uri = argv[1];
 
   EnsureLowerCase(package);
   nsDependentCString host(package);
 
   nsCOMPtr<nsIIOService> io = mozilla::services::GetIOService();
--- a/chrome/nsChromeRegistryChrome.h
+++ b/chrome/nsChromeRegistryChrome.h
@@ -161,31 +161,24 @@ class nsChromeRegistryChrome : public ns
 
   nsCString mSelectedLocale;
   nsCString mSelectedSkin;
 
   // Hash of package names ("global") to PackageEntry objects
   nsClassHashtable<nsCStringHashKey, PackageEntry> mPackagesHash;
 
   virtual void ManifestContent(ManifestProcessingContext& cx, int lineno,
-                               char *const * argv, bool platform,
-                               bool contentaccessible) MOZ_OVERRIDE;
+                               char *const * argv, int flags) MOZ_OVERRIDE;
   virtual void ManifestLocale(ManifestProcessingContext& cx, int lineno,
-                              char *const * argv, bool platform,
-                              bool contentaccessible) MOZ_OVERRIDE;
+                              char *const * argv, int flags) MOZ_OVERRIDE;
   virtual void ManifestSkin(ManifestProcessingContext& cx, int lineno,
-                            char *const * argv, bool platform,
-                            bool contentaccessible) MOZ_OVERRIDE;
+                            char *const * argv, int flags) MOZ_OVERRIDE;
   virtual void ManifestOverlay(ManifestProcessingContext& cx, int lineno,
-                               char *const * argv, bool platform,
-                               bool contentaccessible) MOZ_OVERRIDE;
+                               char *const * argv, int flags) MOZ_OVERRIDE;
   virtual void ManifestStyle(ManifestProcessingContext& cx, int lineno,
-                             char *const * argv, bool platform,
-                             bool contentaccessible) MOZ_OVERRIDE;
+                             char *const * argv, int flags) MOZ_OVERRIDE;
   virtual void ManifestOverride(ManifestProcessingContext& cx, int lineno,
-                                char *const * argv, bool platform,
-                                bool contentaccessible) MOZ_OVERRIDE;
+                                char *const * argv, int flags) MOZ_OVERRIDE;
   virtual void ManifestResource(ManifestProcessingContext& cx, int lineno,
-                                char *const * argv, bool platform,
-                                bool contentaccessible) MOZ_OVERRIDE;
+                                char *const * argv, int flags) MOZ_OVERRIDE;
 };
 
 #endif // nsChromeRegistryChrome_h
--- a/chrome/nsChromeRegistryContent.cpp
+++ b/chrome/nsChromeRegistryContent.cpp
@@ -252,65 +252,59 @@ nsChromeRegistryContent::GetXULOverlays(
 nsresult nsChromeRegistryContent::UpdateSelectedLocale()
 {
   CONTENT_NOT_IMPLEMENTED();
 }
 
 void
 nsChromeRegistryContent::ManifestContent(ManifestProcessingContext& cx,
                                          int lineno, char *const * argv,
-                                         bool platform, bool contentaccessible)
+                                         int flags)
 {
   CONTENT_NOTREACHED();
 }
 
 void
 nsChromeRegistryContent::ManifestLocale(ManifestProcessingContext& cx,
                                         int lineno,
-                                        char *const * argv, bool platform,
-                                        bool contentaccessible)
+                                        char *const * argv, int flags)
 {
   CONTENT_NOTREACHED();
 }
 
 void
 nsChromeRegistryContent::ManifestSkin(ManifestProcessingContext& cx,
                                       int lineno,
-                                      char *const * argv, bool platform,
-                                      bool contentaccessible)
+                                      char *const * argv, int flags)
 {
   CONTENT_NOTREACHED();
 }
 
 void
 nsChromeRegistryContent::ManifestOverlay(ManifestProcessingContext& cx, int lineno,
-                                         char *const * argv, bool platform,
-                                         bool contentaccessible)
+                                         char *const * argv, int flags)
 {
   CONTENT_NOTREACHED();
 }
 
 void
 nsChromeRegistryContent::ManifestStyle(ManifestProcessingContext& cx,
                                        int lineno,
-                                       char *const * argv, bool platform,
-                                       bool contentaccessible)
+                                       char *const * argv, int flags)
 {
   CONTENT_NOTREACHED();
 }
 
 void
 nsChromeRegistryContent::ManifestOverride(ManifestProcessingContext& cx,
                                           int lineno,
-                                          char *const * argv, bool platform,
-                                          bool contentaccessible)
+                                          char *const * argv, int flags)
 {
   CONTENT_NOTREACHED();
 }
 
 void
 nsChromeRegistryContent::ManifestResource(ManifestProcessingContext& cx,
                                           int lineno,
-                                          char *const * argv, bool platform,
-                                          bool contentaccessible)
+                                          char *const * argv, int flags)
 {
   CONTENT_NOTREACHED();
 }
--- a/chrome/nsChromeRegistryContent.h
+++ b/chrome/nsChromeRegistryContent.h
@@ -60,31 +60,24 @@ class nsChromeRegistryContent : public n
                      const nsCString& aProvider,
                      const nsCString& aPath) MOZ_OVERRIDE;
   nsresult GetFlagsFromPackage(const nsCString& aPackage, uint32_t* aFlags) MOZ_OVERRIDE;
 
   nsClassHashtable<nsCStringHashKey, PackageEntry> mPackagesHash;
   nsCString mLocale;
 
   virtual void ManifestContent(ManifestProcessingContext& cx, int lineno,
-                               char *const * argv, bool platform,
-                               bool contentaccessible) MOZ_OVERRIDE;
+                               char *const * argv, int flags) MOZ_OVERRIDE;
   virtual void ManifestLocale(ManifestProcessingContext& cx, int lineno,
-                              char *const * argv, bool platform,
-                              bool contentaccessible) MOZ_OVERRIDE;
+                              char *const * argv, int flags) MOZ_OVERRIDE;
   virtual void ManifestSkin(ManifestProcessingContext& cx, int lineno,
-                            char *const * argv, bool platform,
-                            bool contentaccessible) MOZ_OVERRIDE;
+                            char *const * argv, int flags) MOZ_OVERRIDE;
   virtual void ManifestOverlay(ManifestProcessingContext& cx, int lineno,
-                               char *const * argv, bool platform,
-                               bool contentaccessible) MOZ_OVERRIDE;
+                               char *const * argv, int flags) MOZ_OVERRIDE;
   virtual void ManifestStyle(ManifestProcessingContext& cx, int lineno,
-                             char *const * argv, bool platform,
-                             bool contentaccessible) MOZ_OVERRIDE;
+                             char *const * argv, int flags) MOZ_OVERRIDE;
   virtual void ManifestOverride(ManifestProcessingContext& cx, int lineno,
-                                char *const * argv, bool platform,
-                                bool contentaccessible) MOZ_OVERRIDE;
+                                char *const * argv, int flags) MOZ_OVERRIDE;
   virtual void ManifestResource(ManifestProcessingContext& cx, int lineno,
-                                char *const * argv, bool platform,
-                                bool contentaccessible) MOZ_OVERRIDE;
+                                char *const * argv, int flags) MOZ_OVERRIDE;
 };
 
 #endif // nsChromeRegistryContent_h
--- a/chrome/nsIChromeRegistry.idl
+++ b/chrome/nsIChromeRegistry.idl
@@ -40,17 +40,17 @@ interface nsIChromeRegistry : nsISupport
   void checkForNewChrome();
 
   /**
    * returns whether XPCNativeWrappers are enabled for aURI.
    */
   [notxpcom] boolean wrappersEnabled(in nsIURI aURI);
 };
 
-[scriptable, uuid(c2461347-2b8f-48c7-9d59-3a61fb868828)]
+[scriptable, uuid(93251ddf-5e85-4172-ac2a-31780562974f)]
 interface nsIXULChromeRegistry : nsIChromeRegistry
 {
   /* Should be called when locales change to reload all chrome (including XUL). */
   void reloadChrome();
   
   ACString getSelectedLocale(in ACString packageName);
   
   // Get the direction of the locale via the intl.uidirection.<locale> pref
@@ -68,16 +68,30 @@ interface nsIXULChromeRegistry : nsIChro
   boolean allowScriptsForPackage(in nsIURI url);
 
   /**
    * Content should only be allowed to load chrome JS from certain packages.
    * This method reflects the contentaccessible flag on packages.
    * Do not pass non-chrome URIs to this method.
    */
   boolean allowContentToAccess(in nsIURI url);
+
+  /**
+   * Returns true if the passed chrome URL is allowed to be loaded in a remote
+   * process. This reflects the remoteenabled flag on packages.
+   * Do not pass non-chrome URIs to this method.
+   */
+  boolean canLoadURLRemotely(in nsIURI url);
+
+  /**
+   * Returns true if the passed chrome URL must be loaded in a remote process.
+   * This reflects the remoterequired flag on packages.
+   * Do not pass non-chrome URIs to this method.
+   */
+  boolean mustLoadURLRemotely(in nsIURI url);
 };
 
 %{ C++
 
 #define NS_CHROMEREGISTRY_CONTRACTID \
   "@mozilla.org/chrome/chrome-registry;1"
 
 /**
--- a/docshell/base/nsAboutRedirector.cpp
+++ b/docshell/base/nsAboutRedirector.cpp
@@ -44,16 +44,17 @@ static RedirEntry kRedirMap[] = {
     { "logo", "chrome://branding/content/about.png",
       nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT},
     { "buildconfig", "chrome://global/content/buildconfig.html",
       nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT },
     { "license", "chrome://global/content/license.html",
       nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT },
     { "neterror", "chrome://global/content/netError.xhtml",
       nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT |
+      nsIAboutModule::URI_CAN_LOAD_IN_CHILD |
       nsIAboutModule::ALLOW_SCRIPT |
       nsIAboutModule::HIDE_FROM_ABOUTABOUT },
     { "memory", "chrome://global/content/aboutMemory.xhtml",
       nsIAboutModule::ALLOW_SCRIPT },
     { "compartments", "chrome://global/content/aboutCompartments.xhtml",
       nsIAboutModule::ALLOW_SCRIPT },
     { "addons", "chrome://mozapps/content/extensions/extensions.xul",
       nsIAboutModule::ALLOW_SCRIPT },
--- a/netwerk/protocol/about/nsAboutBlank.cpp
+++ b/netwerk/protocol/about/nsAboutBlank.cpp
@@ -50,16 +50,17 @@ nsAboutBlank::NewChannel(nsIURI* aURI,
     channel.forget(result);
     return rv;
 }
 
 NS_IMETHODIMP
 nsAboutBlank::GetURIFlags(nsIURI *aURI, uint32_t *result)
 {
     *result = nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT |
+              nsIAboutModule::URI_CAN_LOAD_IN_CHILD |
               nsIAboutModule::HIDE_FROM_ABOUTABOUT;
     return NS_OK;
 }
 
 NS_IMETHODIMP
 nsAboutBlank::GetIndexedDBOriginPostfix(nsIURI *aURI, nsAString &result)
 {
     SetDOMStringToNull(result);
--- a/netwerk/protocol/about/nsIAboutModule.idl
+++ b/netwerk/protocol/about/nsIAboutModule.idl
@@ -46,16 +46,26 @@ interface nsIAboutModule : nsISupports
     const unsigned long HIDE_FROM_ABOUTABOUT = (1 << 2);
 
     /**
      * A flag that indicates whether this about: URI wants Indexed DB enabled.
      */
     const unsigned long ENABLE_INDEXED_DB = (1 << 3);
 
     /**
+     * A flag that indicates that this URI can be loaded in a child process
+     */
+    const unsigned long URI_CAN_LOAD_IN_CHILD = (1 << 4);
+
+    /**
+     * A flag that indicates that this URI must be loaded in a child process
+     */
+    const unsigned long URI_MUST_LOAD_IN_CHILD = (1 << 5);
+
+    /**
      * A method to get the flags that apply to a given about: URI.  The URI
      * passed in is guaranteed to be one of the URIs that this module
      * registered to deal with.
      */
     unsigned long getURIFlags(in nsIURI aURI);
 
     /**
      * Returns the Indexed DB origin's postfix used for the given about: URI.
--- a/testing/mochitest/runtests.py
+++ b/testing/mochitest/runtests.py
@@ -792,16 +792,18 @@ class MochitestUtilsMixin(object):
     return dir
 
   def writeChromeManifest(self, options):
     manifest = os.path.join(options.profilePath, "tests.manifest")
     with open(manifest, "w") as manifestFile:
       # Register chrome directory.
       chrometestDir = self.getChromeTestDir(options)
       manifestFile.write("content mochitests %s contentaccessible=yes\n" % chrometestDir)
+      manifestFile.write("content mochitests-any %s contentaccessible=yes remoteenabled=yes\n" % chrometestDir)
+      manifestFile.write("content mochitests-content %s contentaccessible=yes remoterequired=yes\n" % chrometestDir)
 
       if options.testingModulesDir is not None:
         manifestFile.write("resource testing-common file:///%s\n" %
           options.testingModulesDir)
     return manifest
 
   def addChromeToProfile(self, options):
     "Adds MochiKit chrome tests to the profile."
--- a/xpcom/components/ManifestParser.cpp
+++ b/xpcom/components/ManifestParser.cpp
@@ -69,18 +69,17 @@ struct ManifestDirective
 
   // Function to handle this directive. This isn't a union because C++ still
   // hasn't learned how to initialize unions in a sane way.
   void (nsComponentManagerImpl::*mgrfunc)(
     nsComponentManagerImpl::ManifestProcessingContext& aCx,
     int aLineNo, char* const* aArgv);
   void (nsChromeRegistry::*regfunc)(
     nsChromeRegistry::ManifestProcessingContext& aCx,
-    int aLineNo, char* const* aArgv,
-    bool aPlatform, bool aContentAccessible);
+    int aLineNo, char* const* aArgv, int aFlags);
 #ifdef MOZ_B2G_LOADER
   // The function to handle the directive for XPT Only parsing.
   void (*xptonlyfunc)(
     nsComponentManagerImpl::XPTOnlyManifestProcessingContext& aCx,
     int aLineNo, char* const* aArgv);
 #else
   void* xptonlyfunc;
 #endif
@@ -482,16 +481,18 @@ ParseManifest(NSLocationType aType, File
   nsChromeRegistry::ManifestProcessingContext chromecx(aType, aFile);
 #ifdef MOZ_B2G_LOADER
   nsComponentManagerImpl::XPTOnlyManifestProcessingContext xptonlycx(aFile);
 #endif
   nsresult rv;
 
   NS_NAMED_LITERAL_STRING(kPlatform, "platform");
   NS_NAMED_LITERAL_STRING(kContentAccessible, "contentaccessible");
+  NS_NAMED_LITERAL_STRING(kRemoteEnabled, "remoteenabled");
+  NS_NAMED_LITERAL_STRING(kRemoteRequired, "remoterequired");
   NS_NAMED_LITERAL_STRING(kApplication, "application");
   NS_NAMED_LITERAL_STRING(kAppVersion, "appversion");
   NS_NAMED_LITERAL_STRING(kGeckoVersion, "platformversion");
   NS_NAMED_LITERAL_STRING(kOs, "os");
   NS_NAMED_LITERAL_STRING(kOsVersion, "osversion");
   NS_NAMED_LITERAL_STRING(kABI, "abi");
   NS_NAMED_LITERAL_STRING(kProcess, "process");
 #if defined(MOZ_WIDGET_ANDROID)
@@ -679,18 +680,17 @@ ParseManifest(NSLocationType aType, File
     TriState stApp = eUnspecified;
     TriState stOsVersion = eUnspecified;
     TriState stOs = eUnspecified;
     TriState stABI = eUnspecified;
     TriState stProcess = eUnspecified;
 #if defined(MOZ_WIDGET_ANDROID)
     TriState stTablet = eUnspecified;
 #endif
-    bool platform = false;
-    bool contentAccessible = false;
+    int flags = 0;
 
     while ((token = nsCRT::strtok(whitespace, kWhitespace, &whitespace)) &&
            ok) {
       ToLowerCase(token);
       NS_ConvertASCIItoUTF16 wtoken(token);
 
       if (CheckStringFlag(kApplication, wtoken, appID, stApp) ||
           CheckStringFlag(kOs, wtoken, osTarget, stOs) ||
@@ -705,20 +705,38 @@ ParseManifest(NSLocationType aType, File
 #if defined(MOZ_WIDGET_ANDROID)
       bool tablet = false;
       if (CheckFlag(kTablet, wtoken, tablet)) {
         stTablet = (tablet == isTablet) ? eOK : eBad;
         continue;
       }
 #endif
 
-      if (directive->contentflags &&
-          (CheckFlag(kPlatform, wtoken, platform) ||
-           CheckFlag(kContentAccessible, wtoken, contentAccessible))) {
-        continue;
+      if (directive->contentflags) {
+        bool flag;
+        if (CheckFlag(kPlatform, wtoken, flag)) {
+          if (flag)
+            flags |= nsChromeRegistry::PLATFORM_PACKAGE;
+          continue;
+        }
+        if (CheckFlag(kContentAccessible, wtoken, flag)) {
+          if (flag)
+            flags |= nsChromeRegistry::CONTENT_ACCESSIBLE;
+          continue;
+        }
+        if (CheckFlag(kRemoteEnabled, wtoken, flag)) {
+          if (flag)
+            flags |= nsChromeRegistry::REMOTE_ALLOWED;
+          continue;
+        }
+        if (CheckFlag(kRemoteRequired, wtoken, flag)) {
+          if (flag)
+            flags |= nsChromeRegistry::REMOTE_REQUIRED;
+          continue;
+        }
       }
 
       bool xpcNativeWrappers = true; // Dummy for CheckFlag.
       if (CheckFlag(kXPCNativeWrappers, wtoken, xpcNativeWrappers)) {
         LogMessageWithContext(aFile, line,
                               "Ignoring obsolete chrome registration modifier '%s'.",
                               token);
         continue;
@@ -760,17 +778,17 @@ ParseManifest(NSLocationType aType, File
         if (!nsChromeRegistry::gChromeRegistry) {
           LogMessageWithContext(aFile, line,
                                 "Chrome registry isn't available yet.");
           continue;
         }
       }
 
       (nsChromeRegistry::gChromeRegistry->*(directive->regfunc))(
-        chromecx, line, argv, platform, contentAccessible);
+        chromecx, line, argv, flags);
     } else if (directive->ischrome || !aChromeOnly) {
       if (directive->isContract) {
         CachedDirective* cd = contracts.AppendElement();
         cd->lineno = line;
         cd->argv[0] = argv[0];
         cd->argv[1] = argv[1];
       } else {
         (nsComponentManagerImpl::gComponentManager->*(directive->mgrfunc))(