Bug 1469072 - Add infrastructure to move Activity Stream into its own content process. r=kmag,mconley
authorimjching <jlim@mozilla.com>
Wed, 20 Jun 2018 14:04:51 -0400
changeset 423988 22fd5e86fbf66c6cd182b435029ac2be54c26761
parent 423987 479c0a3c61fd2e8b86c958541d362560c97ae078
child 423989 d6ed3df9e0d64b2fa7bd153281a59f47bd4e7883
push id34197
push usercsabou@mozilla.com
push dateThu, 28 Jun 2018 09:44:02 +0000
treeherdermozilla-central@db455160668d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskmag, mconley
bugs1469072
milestone63.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 1469072 - Add infrastructure to move Activity Stream into its own content process. r=kmag,mconley Summary: This patch adds the infrastructure to move Activity Stream (about:newtab, about:home, and about:welcome) into its own special content process - the privileged content process. This feature of running Activity Stream in the privileged content process is disabled by default. (See "browser.tabs.remote.separatePrivilegedContentProcess" preference.) We can deal with other about: pages in a follow-up. Reviewers: mconley Tags: #secure-revision Bug #: 1469072 Differential Revision: https://phabricator.services.mozilla.com/D1731 MozReview-Commit-ID: 5gIrP4LxcIt
browser/base/content/test/tabs/browser.ini
browser/base/content/test/tabs/browser_new_tab_in_privileged_process_pref.js
dom/ipc/ContentChild.cpp
dom/ipc/ContentParent.cpp
dom/ipc/ContentParent.h
js/xpconnect/loader/ScriptPreloader.cpp
js/xpconnect/loader/ScriptPreloader.h
js/xpconnect/loader/script_cache.py
modules/libpref/init/all.js
toolkit/modules/E10SUtils.jsm
--- a/browser/base/content/test/tabs/browser.ini
+++ b/browser/base/content/test/tabs/browser.ini
@@ -27,16 +27,18 @@ support-files =
 [browser_multiselect_tabs_using_Ctrl.js]
 [browser_multiselect_tabs_using_Shift.js]
 [browser_navigatePinnedTab.js]
 [browser_new_file_whitelisted_http_tab.js]
 skip-if = !e10s # Test only relevant for e10s.
 [browser_new_tab_insert_position.js]
 skip-if = (debug && os == 'linux' && bits == 32) #Bug 1455882, disabled on Linux32 for almost permafailing
 support-files = file_new_tab_page.html
+[browser_new_tab_in_privileged_process_pref.js]
+skip-if = !e10s # Pref and test only relevant for e10s.
 [browser_new_web_tab_in_file_process_pref.js]
 skip-if = !e10s # Pref and test only relevant for e10s.
 [browser_newwindow_tabstrip_overflow.js]
 [browser_open_newtab_start_observer_notification.js]
 [browser_opened_file_tab_navigated_to_web.js]
 [browser_overflowScroll.js]
 [browser_pinnedTabs_clickOpen.js]
 [browser_pinnedTabs_closeByKeyboard.js]
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/tabs/browser_new_tab_in_privileged_process_pref.js
@@ -0,0 +1,212 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+/**
+ * Tests to ensure that Activity Stream loads in the privileged content process.
+ * Normal http web pages should load in the web content process.
+ * Ref: Bug 1469072.
+ */
+
+const ABOUT_BLANK = "about:blank";
+const ABOUT_HOME = "about:home";
+const ABOUT_NEWTAB = "about:newtab";
+const ABOUT_WELCOME = "about:welcome";
+const TEST_HTTP = "http://example.org/";
+
+/**
+ * Takes a xul:browser and makes sure that the remoteTypes for the browser in
+ * both the parent and the child processes are the same.
+ *
+ * @param {xul:browser} browser
+ *        A xul:browser.
+ * @param {string} expectedRemoteType
+ *        The expected remoteType value for the browser in both the parent
+ *        and child processes.
+ * @param {optional string} message
+ *        If provided, shows this string as the message when remoteType values
+ *        do not match. If not present, it uses the default message defined
+ *        in the function parameters.
+ */
+async function checkBrowserRemoteType(
+  browser,
+  expectedRemoteType,
+  message = `Ensures that tab runs in the ${expectedRemoteType} content process.`
+) {
+  // Check both parent and child to ensure that they have the correct remoteType.
+  is(browser.remoteType, expectedRemoteType, message);
+  is(browser.messageManager.remoteType, expectedRemoteType,
+    "Parent and child process should agree on the remote type.");
+}
+
+add_task(async function setup() {
+  await SpecialPowers.pushPrefEnv({
+    set: [
+      ["browser.newtab.preload", false],
+      ["browser.tabs.remote.separatePrivilegedContentProcess", true],
+      ["dom.ipc.processCount.privileged", 1],
+      ["dom.ipc.keepProcessesAlive.privileged", 1],
+    ]
+  });
+});
+
+/*
+ * Test to ensure that the Activity Stream tabs open in privileged content
+ * process. We will first open an about:newtab page that acts as a reference to
+ * the privileged content process. With the reference, we can then open Activity
+ * Stream links in a new tab and ensure that the new tab opens in the same
+ * privileged content process as our reference.
+ */
+add_task(async function activity_stream_in_privileged_content_process() {
+  Services.ppmm.releaseCachedProcesses();
+
+  await BrowserTestUtils.withNewTab(ABOUT_NEWTAB, async function(browser1) {
+    await checkBrowserRemoteType(browser1, E10SUtils.PRIVILEGED_REMOTE_TYPE);
+
+    // Note the processID for about:newtab for comparison later.
+    let privilegedPid = browser1.frameLoader.tabParent.osPid;
+
+    for (let url of [
+      ABOUT_NEWTAB,
+      ABOUT_WELCOME,
+      ABOUT_HOME,
+      `${ABOUT_NEWTAB}#foo`,
+      `${ABOUT_WELCOME}#bar`,
+      `${ABOUT_HOME}#baz`,
+      `${ABOUT_NEWTAB}?q=foo`,
+      `${ABOUT_WELCOME}?q=bar`,
+      `${ABOUT_HOME}?q=baz`
+    ]) {
+      await BrowserTestUtils.withNewTab(url, async function(browser2) {
+        is(browser2.frameLoader.tabParent.osPid, privilegedPid,
+          "Check that about:newtab tabs are in the same privileged content process.");
+      });
+    }
+  });
+
+  Services.ppmm.releaseCachedProcesses();
+});
+
+/*
+ * Test to ensure that a process switch occurs when navigating between normal
+ * web pages and Activity Stream pages in the same tab.
+ */
+add_task(async function process_switching_through_loading_in_the_same_tab() {
+  Services.ppmm.releaseCachedProcesses();
+
+  await BrowserTestUtils.withNewTab(TEST_HTTP, async function(browser) {
+    await checkBrowserRemoteType(browser, E10SUtils.WEB_REMOTE_TYPE);
+
+    for (let [url, remoteType] of [
+      [ABOUT_NEWTAB, E10SUtils.PRIVILEGED_REMOTE_TYPE],
+      [ABOUT_BLANK, E10SUtils.PRIVILEGED_REMOTE_TYPE],
+      [TEST_HTTP, E10SUtils.WEB_REMOTE_TYPE],
+      [ABOUT_HOME, E10SUtils.PRIVILEGED_REMOTE_TYPE],
+      [TEST_HTTP, E10SUtils.WEB_REMOTE_TYPE],
+      [ABOUT_WELCOME, E10SUtils.PRIVILEGED_REMOTE_TYPE],
+      [TEST_HTTP, E10SUtils.WEB_REMOTE_TYPE],
+      [ABOUT_BLANK, E10SUtils.WEB_REMOTE_TYPE],
+      [`${ABOUT_NEWTAB}#foo`, E10SUtils.PRIVILEGED_REMOTE_TYPE],
+      [TEST_HTTP, E10SUtils.WEB_REMOTE_TYPE],
+      [`${ABOUT_WELCOME}#bar`, E10SUtils.PRIVILEGED_REMOTE_TYPE],
+      [TEST_HTTP, E10SUtils.WEB_REMOTE_TYPE],
+      [`${ABOUT_HOME}#baz`, E10SUtils.PRIVILEGED_REMOTE_TYPE],
+      [TEST_HTTP, E10SUtils.WEB_REMOTE_TYPE],
+      [`${ABOUT_NEWTAB}?q=foo`, E10SUtils.PRIVILEGED_REMOTE_TYPE],
+      [TEST_HTTP, E10SUtils.WEB_REMOTE_TYPE],
+      [`${ABOUT_WELCOME}?q=bar`, E10SUtils.PRIVILEGED_REMOTE_TYPE],
+      [TEST_HTTP, E10SUtils.WEB_REMOTE_TYPE],
+      [`${ABOUT_HOME}?q=baz`, E10SUtils.PRIVILEGED_REMOTE_TYPE],
+      [TEST_HTTP, E10SUtils.WEB_REMOTE_TYPE]
+    ]) {
+      BrowserTestUtils.loadURI(browser, url);
+      await BrowserTestUtils.browserLoaded(browser, false, url);
+      await checkBrowserRemoteType(browser, remoteType);
+    }
+  });
+
+  Services.ppmm.releaseCachedProcesses();
+});
+
+/*
+ * Test to ensure that a process switch occurs when navigating between normal
+ * web pages and Activity Stream pages using the browser's navigation features
+ * such as history and location change.
+ */
+add_task(async function process_switching_through_navigation_features() {
+  Services.ppmm.releaseCachedProcesses();
+
+  await BrowserTestUtils.withNewTab(ABOUT_NEWTAB, async function(browser) {
+    await checkBrowserRemoteType(browser, E10SUtils.PRIVILEGED_REMOTE_TYPE);
+
+    // Note the processID for about:newtab for comparison later.
+    let privilegedPid = browser.frameLoader.tabParent.osPid;
+
+    // Check that about:newtab opened from JS in about:newtab page is in the same process.
+    let promiseTabOpened = BrowserTestUtils.waitForNewTab(gBrowser, ABOUT_NEWTAB, true);
+    await ContentTask.spawn(browser, ABOUT_NEWTAB, uri => {
+      content.open(uri, "_blank");
+    });
+    let newTab = await promiseTabOpened;
+    registerCleanupFunction(async function() {
+      BrowserTestUtils.removeTab(newTab);
+    });
+    browser = newTab.linkedBrowser;
+    is(browser.frameLoader.tabParent.osPid, privilegedPid,
+      "Check that new tab opened from about:newtab is loaded in privileged content process.");
+
+    // Check that reload does not break the privileged content process affinity.
+    BrowserReload();
+    await BrowserTestUtils.browserLoaded(browser, false, ABOUT_NEWTAB);
+    is(browser.frameLoader.tabParent.osPid, privilegedPid,
+      "Check that about:newtab is still in privileged content process after reload.");
+
+    // Load http webpage
+    BrowserTestUtils.loadURI(browser, TEST_HTTP);
+    await BrowserTestUtils.browserLoaded(browser, false, TEST_HTTP);
+    await checkBrowserRemoteType(browser, E10SUtils.WEB_REMOTE_TYPE);
+
+    // Check that using the history back feature switches back to privileged content process.
+    let promiseLocation = BrowserTestUtils.waitForLocationChange(gBrowser, ABOUT_NEWTAB);
+    browser.goBack();
+    await promiseLocation;
+    // We will need to ensure that the process flip has fully completed so that
+    // the navigation history data will be available when we do browser.goForward();
+    await BrowserTestUtils.waitForEvent(newTab, "SSTabRestored");
+    is(browser.frameLoader.tabParent.osPid, privilegedPid,
+      "Check that about:newtab is still in privileged content process after history goBack.");
+
+    // Check that using the history forward feature switches back to the web content process.
+    promiseLocation = BrowserTestUtils.waitForLocationChange(gBrowser, TEST_HTTP);
+    browser.goForward();
+    await promiseLocation;
+    // We will need to ensure that the process flip has fully completed so that
+    // the navigation history data will be available when we do browser.gotoIndex(0);
+    await BrowserTestUtils.waitForEvent(newTab, "SSTabRestored");
+    await checkBrowserRemoteType(browser, E10SUtils.WEB_REMOTE_TYPE,
+      "Check that tab runs in the web content process after using history goForward.");
+
+    // Check that goto history index does not break the affinity.
+    promiseLocation = BrowserTestUtils.waitForLocationChange(gBrowser, ABOUT_NEWTAB);
+    browser.gotoIndex(0);
+    await promiseLocation;
+    is(browser.frameLoader.tabParent.osPid, privilegedPid,
+      "Check that about:newtab is in privileged content process after history gotoIndex.");
+
+    BrowserTestUtils.loadURI(browser, TEST_HTTP);
+    await BrowserTestUtils.browserLoaded(browser, false, TEST_HTTP);
+    await checkBrowserRemoteType(browser, E10SUtils.WEB_REMOTE_TYPE);
+
+    // Check that location change causes a change in process type as well.
+    await ContentTask.spawn(browser, ABOUT_NEWTAB, uri => {
+      content.location = uri;
+    });
+    await BrowserTestUtils.browserLoaded(browser, false, ABOUT_NEWTAB);
+    is(browser.frameLoader.tabParent.osPid, privilegedPid,
+      "Check that about:newtab is in privileged content process after location change.");
+  });
+
+  Services.ppmm.releaseCachedProcesses();
+});
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -2741,16 +2741,18 @@ ContentChild::RecvRemoteType(const nsStr
   mRemoteType.Assign(aRemoteType);
 
   // For non-default ("web") types, update the process name so about:memory's
   // process names are more obvious.
   if (aRemoteType.EqualsLiteral(FILE_REMOTE_TYPE)) {
     SetProcessName(NS_LITERAL_STRING("file:// Content"));
   } else if (aRemoteType.EqualsLiteral(EXTENSION_REMOTE_TYPE)) {
     SetProcessName(NS_LITERAL_STRING("WebExtensions"));
+  } else if (aRemoteType.EqualsLiteral(PRIVILEGED_REMOTE_TYPE)) {
+    SetProcessName(NS_LITERAL_STRING("Privileged Content"));
   } else if (aRemoteType.EqualsLiteral(LARGE_ALLOCATION_REMOTE_TYPE)) {
     SetProcessName(NS_LITERAL_STRING("Large Allocation Web Content"));
   }
 
   return IPC_OK();
 }
 
 const nsAString&
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -1852,18 +1852,18 @@ ContentParent::ShouldKeepProcessAlive() 
     return false;
   }
 
   auto contentParents = sBrowserContentParents->Get(mRemoteType);
   if (!contentParents) {
     return false;
   }
 
-  // We might want to keep alive some content processes alive during test runs,
-  // for performance reasons. This should never be used in production.
+  // We might want to keep some content processes alive for performance reasons.
+  // e.g. test runs and privileged content process for some about: pages.
   // We don't want to alter behavior if the pref is not set, so default to 0.
   int32_t processesToKeepAlive = 0;
 
   nsAutoCString keepAlivePref("dom.ipc.keepProcessesAlive.");
   keepAlivePref.Append(NS_ConvertUTF16toUTF8(mRemoteType));
   if (NS_FAILED(Preferences::GetInt(keepAlivePref.get(), &processesToKeepAlive))) {
     return false;
   }
--- a/dom/ipc/ContentParent.h
+++ b/dom/ipc/ContentParent.h
@@ -38,16 +38,17 @@
 
 // These must match the similar ones in E10SUtils.jsm.
 // Process names as reported by about:memory are defined in
 // ContentChild:RecvRemoteType.  Add your value there too or it will be called
 // "Web Content".
 #define DEFAULT_REMOTE_TYPE "web"
 #define FILE_REMOTE_TYPE "file"
 #define EXTENSION_REMOTE_TYPE "extension"
+#define PRIVILEGED_REMOTE_TYPE "privileged"
 
 // This must start with the DEFAULT_REMOTE_TYPE above.
 #define LARGE_ALLOCATION_REMOTE_TYPE "webLargeAllocation"
 
 class nsConsoleService;
 class nsIContentProcessInfo;
 class nsICycleCollectorLogSink;
 class nsIDumpGCAndCCLogsCallback;
--- a/js/xpconnect/loader/ScriptPreloader.cpp
+++ b/js/xpconnect/loader/ScriptPreloader.cpp
@@ -187,16 +187,19 @@ ScriptPreloader::InitContentChild(Conten
 }
 
 ProcessType
 ScriptPreloader::GetChildProcessType(const nsAString& remoteType)
 {
     if (remoteType.EqualsLiteral(EXTENSION_REMOTE_TYPE)) {
         return ProcessType::Extension;
     }
+    if (remoteType.EqualsLiteral(PRIVILEGED_REMOTE_TYPE)) {
+        return ProcessType::Privileged;
+    }
     return ProcessType::Web;
 }
 
 
 namespace {
 
 static void
 TraceOp(JSTracer* trc, void* data)
--- a/js/xpconnect/loader/ScriptPreloader.h
+++ b/js/xpconnect/loader/ScriptPreloader.h
@@ -38,16 +38,17 @@ namespace ipc {
 namespace loader {
     class InputBuffer;
     class ScriptCacheChild;
 
     enum class ProcessType : uint8_t {
         Parent,
         Web,
         Extension,
+        Privileged,
     };
 
     template <typename T>
     struct Matcher
     {
         virtual bool Matches(T) = 0;
     };
 }
--- a/js/xpconnect/loader/script_cache.py
+++ b/js/xpconnect/loader/script_cache.py
@@ -15,28 +15,31 @@ def usage():
 
     sys.exit(1)
 
 
 class ProcessTypes:
     Default = 0
     Web = 1
     Extension = 2
+    Privileged = 3
 
     def __init__(self, val):
         self.val = val
 
     def __str__(self):
         res = []
         if self.val & (1 << self.Default):
             res.append('Parent')
         if self.val & (1 << self.Web):
             res.append('Web')
         if self.val & (1 << self.Extension):
             res.append('Extension')
+        if self.val & (1 << self.Privileged):
+            res.append('Privileged')
         return '|'.join(res)
 
 
 class InputBuffer(object):
 
     def __init__(self, data):
         self.data = data
         self.offset = 0
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -3213,16 +3213,24 @@ pref("dom.ipc.plugins.forcedirect.enable
 pref("dom.ipc.processCount", 4);
 
 // Default to allow only one file:// URL content process.
 pref("dom.ipc.processCount.file", 1);
 
 // WebExtensions only support a single extension process.
 pref("dom.ipc.processCount.extension", 1);
 
+// Privileged content only supports a single content process.
+pref("dom.ipc.processCount.privileged", 1);
+
+// Keep a single privileged content process alive for performance reasons.
+// e.g. we do not want to throw content processes out every time we navigate
+// away from about:newtab.
+pref("dom.ipc.keepProcessesAlive.privileged", 1);
+
 // Whether a native event loop should be used in the content process.
 #if defined(XP_WIN)
 pref("dom.ipc.useNativeEventProcessing.content", false);
 #else
 pref("dom.ipc.useNativeEventProcessing.content", true);
 #endif
 
 // Quantum DOM scheduling:
@@ -3249,16 +3257,19 @@ pref("browser.tabs.remote.separateFileUr
 
 // Pref that enables top level web content pages that are opened from file://
 // URI pages to run in the file content process.
 // This has been added in case breaking any window references between these
 // sorts of pages, which we have to do when we run them in the normal web
 // content process, causes compatibility issues.
 pref("browser.tabs.remote.allowLinkedWebInFileUriProcess", true);
 
+// Pref to control whether we use separate privileged content processes.
+pref("browser.tabs.remote.separatePrivilegedContentProcess", false);
+
 // Enable the use of display-lists for SVG hit-testing and painting.
 pref("svg.display-lists.hit-testing.enabled", true);
 pref("svg.display-lists.painting.enabled", true);
 
 // Is support for the <marker orient="auto-start-reverse"> feature enabled?
 pref("svg.marker-improvements.enabled", true);
 
 // Is support for the new getBBox method from SVG 2 enabled?
--- a/toolkit/modules/E10SUtils.jsm
+++ b/toolkit/modules/E10SUtils.jsm
@@ -8,16 +8,18 @@ var EXPORTED_SYMBOLS = ["E10SUtils"];
 
 ChromeUtils.import("resource://gre/modules/Services.jsm");
 ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
 
 XPCOMUtils.defineLazyPreferenceGetter(this, "useSeparateFileUriProcess",
                                       "browser.tabs.remote.separateFileUriProcess", false);
 XPCOMUtils.defineLazyPreferenceGetter(this, "allowLinkedWebInFileUriProcess",
                                       "browser.tabs.remote.allowLinkedWebInFileUriProcess", false);
+XPCOMUtils.defineLazyPreferenceGetter(this, "useSeparatePrivilegedContentProcess",
+                                      "browser.tabs.remote.separatePrivilegedContentProcess", false);
 ChromeUtils.defineModuleGetter(this, "Utils",
                                "resource://gre/modules/sessionstore/Utils.jsm");
 
 function getAboutModule(aURL) {
   // Needs to match NS_GetAboutModuleName
   let moduleName = aURL.pathQueryRef.replace(/[#?].*/, "").toLowerCase();
   let contract = "@mozilla.org/network/protocol/about;1?what=" + moduleName;
   try {
@@ -30,21 +32,24 @@ function getAboutModule(aURL) {
 }
 
 const NOT_REMOTE = null;
 
 // These must match any similar ones in ContentParent.h.
 const WEB_REMOTE_TYPE = "web";
 const FILE_REMOTE_TYPE = "file";
 const EXTENSION_REMOTE_TYPE = "extension";
+const PRIVILEGED_REMOTE_TYPE = "privileged";
 
 // This must start with the WEB_REMOTE_TYPE above.
 const LARGE_ALLOCATION_REMOTE_TYPE = "webLargeAllocation";
 const DEFAULT_REMOTE_TYPE = WEB_REMOTE_TYPE;
 
+const ACTIVITY_STREAM_PAGES = new Set(["home", "newtab", "welcome"]);
+
 function validatedWebRemoteType(aPreferredRemoteType, aTargetUri, aCurrentUri) {
   // If the domain is whitelisted to allow it to use file:// URIs, then we have
   // to run it in a file content process, in case it uses file:// sub-resources.
   const sm = Services.scriptSecurityManager;
   if (sm.inFileURIWhitelist(aTargetUri)) {
     return FILE_REMOTE_TYPE;
   }
 
@@ -77,16 +82,17 @@ function validatedWebRemoteType(aPreferr
 }
 
 var E10SUtils = {
   DEFAULT_REMOTE_TYPE,
   NOT_REMOTE,
   WEB_REMOTE_TYPE,
   FILE_REMOTE_TYPE,
   EXTENSION_REMOTE_TYPE,
+  PRIVILEGED_REMOTE_TYPE,
   LARGE_ALLOCATION_REMOTE_TYPE,
 
   canLoadURIInProcess(aURL, aProcess) {
     let remoteType = aProcess == Ci.nsIXULRuntime.PROCESS_TYPE_CONTENT
                      ? DEFAULT_REMOTE_TYPE : NOT_REMOTE;
     return remoteType == this.getRemoteTypeForURI(aURL, true, remoteType);
   },
 
@@ -148,16 +154,21 @@ var E10SUtils = {
         // If the module doesn't exist then an error page will be loading, that
         // should be ok to load in any process
         if (!module) {
           return aPreferredRemoteType;
         }
 
         let flags = module.getURIFlags(aURI);
         if (flags & Ci.nsIAboutModule.URI_MUST_LOAD_IN_CHILD) {
+          // Load Activity Stream in a separate process.
+          if (useSeparatePrivilegedContentProcess &&
+              ACTIVITY_STREAM_PAGES.has(aURI.filePath)) {
+            return PRIVILEGED_REMOTE_TYPE;
+          }
           return DEFAULT_REMOTE_TYPE;
         }
 
         // If the about page can load in parent or child, it should be safe to
         // load in any remote type.
         if (flags & Ci.nsIAboutModule.URI_CAN_LOAD_IN_CHILD) {
           return aPreferredRemoteType;
         }