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

--HG--
extra : rebase_source : d43c411ae60aad3d5a3a496e6729de0b547b4acd
This commit is contained in:
imjching 2018-06-20 14:04:51 -04:00
Родитель 5d2f11ce8d
Коммит 4867775c4c
10 изменённых файлов: 248 добавлений и 2 удалений

Просмотреть файл

@ -32,6 +32,8 @@ 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]

Просмотреть файл

@ -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();
});

Просмотреть файл

@ -2746,6 +2746,8 @@ ContentChild::RecvRemoteType(const nsString& aRemoteType)
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"));
}

Просмотреть файл

@ -1857,8 +1857,8 @@ ContentParent::ShouldKeepProcessAlive() const
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;

Просмотреть файл

@ -43,6 +43,7 @@
#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"

Просмотреть файл

@ -192,6 +192,9 @@ 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;
}

Просмотреть файл

@ -43,6 +43,7 @@ namespace loader {
Parent,
Web,
Extension,
Privileged,
};
template <typename T>

Просмотреть файл

@ -20,6 +20,7 @@ class ProcessTypes:
Default = 0
Web = 1
Extension = 2
Privileged = 3
def __init__(self, val):
self.val = val
@ -32,6 +33,8 @@ class ProcessTypes:
res.append('Web')
if self.val & (1 << self.Extension):
res.append('Extension')
if self.val & (1 << self.Privileged):
res.append('Privileged')
return '|'.join(res)

Просмотреть файл

@ -3218,6 +3218,14 @@ 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);
@ -3254,6 +3262,9 @@ pref("browser.tabs.remote.separateFileUriProcess", true);
// 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);

Просмотреть файл

@ -13,6 +13,8 @@ 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");
@ -35,11 +37,14 @@ const NOT_REMOTE = null;
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.
@ -82,6 +87,7 @@ var E10SUtils = {
WEB_REMOTE_TYPE,
FILE_REMOTE_TYPE,
EXTENSION_REMOTE_TYPE,
PRIVILEGED_REMOTE_TYPE,
LARGE_ALLOCATION_REMOTE_TYPE,
canLoadURIInProcess(aURL, aProcess) {
@ -153,6 +159,11 @@ var E10SUtils = {
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;
}