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 id28236
push userryanvm@gmail.com
push dateThu, 05 Feb 2015 22:15:34 +0000
treeherdermozilla-central@71972672e292 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmconley, bsmedberg, jduell, mratcliffe
bugs1083281
milestone38.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
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))(