Bug 1521808 - Implement process switching based on Cross-Opener-Origin-Policy header r=nika,qdot

* New topLevel loads get the nsILoadInfo.openerPolicy of the current top level document
* Parsing the Cross-Opener-Origin-Policy of a channel will update mLoadInfo.openerPolicy and this value will get propagated to the child process.
* SessionStore now checks nsIHttpChannel.hasCrossOriginOpenerPolicyMismatch (preffed off) and performs a process switch if needed

Differential Revision: https://phabricator.services.mozilla.com/D19000

--HG--
rename : toolkit/components/remotebrowserutils/tests/browser/browser_httpResponseProcessSelection.js => toolkit/components/remotebrowserutils/tests/browser/browser_httpCrossOriginOpenerPolicy.js
extra : moz-landing-system : lando
This commit is contained in:
Valentin Gosu 2019-02-15 12:14:49 +00:00
Родитель 646e6529a2
Коммит 3d45f28bb0
8 изменённых файлов: 114 добавлений и 5 удалений

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

@ -2315,7 +2315,8 @@ var SessionStoreInternal = {
// Examine the channel response to see if we should change the process
// performing the given load.
onMayChangeProcess(aChannel) {
if (!E10SUtils.useHttpResponseProcessSelection()) {
if (!E10SUtils.useHttpResponseProcessSelection() &&
!E10SUtils.useCrossOriginOpenerPolicy()) {
return;
}
@ -2370,6 +2371,7 @@ var SessionStoreInternal = {
let browser = tabParent.ownerElement;
if (!browser) {
debug(`[process-switch]: TabParent has no ownerElement - ignoring`);
return;
}
let tabbrowser = browser.ownerGlobal.gBrowser;
@ -2391,7 +2393,9 @@ var SessionStoreInternal = {
true,
browser.remoteType,
currentPrincipal);
if (browser.remoteType == remoteType) {
if (browser.remoteType == remoteType &&
(!E10SUtils.useCrossOriginOpenerPolicy() ||
!aChannel.hasCrossOriginOpenerPolicyMismatch())) {
debug(`[process-switch]: type (${remoteType}) is compatible - ignoring`);
return;
}

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

@ -9805,8 +9805,6 @@ nsresult nsDocShell::DoURILoad(nsDocShellLoadState* aLoadState,
securityFlags |= nsILoadInfo::SEC_SANDBOXED;
}
// TODO: pass openerPolicy through loadInfo?
RefPtr<LoadInfo> loadInfo =
(contentPolicyType == nsIContentPolicy::TYPE_DOCUMENT)
? new LoadInfo(loadingWindow, aLoadState->TriggeringPrincipal(),
@ -9830,6 +9828,12 @@ nsresult nsDocShell::DoURILoad(nsDocShellLoadState* aLoadState,
(contentPolicyType == nsIContentPolicy::TYPE_DOCUMENT ||
GetIsMozBrowser());
if (isTopLevelDoc && GetDocument() && GetDocument()->GetChannel()) {
nsCOMPtr<nsILoadInfo> oldLoadInfo =
GetDocument()->GetChannel()->GetLoadInfo();
loadInfo->SetOpenerPolicy(oldLoadInfo->GetOpenerPolicy());
}
OriginAttributes attrs;
// Inherit origin attributes from PrincipalToInherit if inheritAttrs is

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

@ -715,7 +715,8 @@ nsresult MergeParentLoadInfoForwarder(
aLoadInfo->MaybeIncreaseTainting(aForwarderArgs.tainting());
}
// TODO: merge openerPolicy
MOZ_ALWAYS_SUCCEEDS(
aLoadInfo->SetOpenerPolicy(aForwarderArgs.openerPolicy()));
MOZ_ALWAYS_SUCCEEDS(aLoadInfo->SetDocumentHasUserInteracted(
aForwarderArgs.documentHasUserInteracted()));

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

@ -7257,6 +7257,8 @@ nsHttpChannel::HasCrossOriginOpenerPolicyMismatch(bool *aMismatch) {
nsILoadInfo::CrossOriginOpenerPolicy resultPolicy =
GetCrossOriginOpenerPolicy(head);
mLoadInfo->SetOpenerPolicy(resultPolicy);
// We use the top window principal as the documentOrigin
if (!mTopWindowPrincipal) {
GetTopWindowPrincipal(getter_AddRefs(mTopWindowPrincipal));

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

@ -5,7 +5,9 @@ support-files =
print_postdata.sjs
307redirect.sjs
head.js
coop_header.sjs
[browser_RemoteWebNavigation.js]
[browser_httpResponseProcessSelection.js]
[browser_httpCrossOriginOpenerPolicy.js]
[browser_httpToFileHistory.js]

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

@ -0,0 +1,77 @@
/* eslint-disable mozilla/no-arbitrary-setTimeout */
"use strict";
const {E10SUtils} = ChromeUtils.import("resource://gre/modules/E10SUtils.jsm");
const PREF_NAME = "browser.tabs.remote.useCrossOriginOpenerPolicy";
function httpURL(filename, host = "https://example.com") {
let root = getRootDirectory(gTestPath)
.replace("chrome://mochitests/content", host);
return root + filename;
}
async function performLoad(browser, opts, action) {
let loadedPromise = BrowserTestUtils.browserStopped(
browser, opts.url, opts.maybeErrorPage);
await action();
await loadedPromise;
}
async function test_coop(start, target, expectedProcessSwitch) {
return BrowserTestUtils.withNewTab({
gBrowser,
url: start,
waitForStateStop: true,
}, async function(_browser) {
info(`Test tab ready: ${start}`);
await new Promise(resolve => setTimeout(resolve, 20));
let browser = gBrowser.selectedBrowser;
let firstProcessID = await ContentTask.spawn(browser, null, () => {
return Services.appinfo.processID;
});
info(`firstProcessID: ${firstProcessID}`);
await performLoad(browser, {
url: target,
maybeErrorPage: false,
}, async () => {
BrowserTestUtils.loadURI(browser, target);
if (expectedProcessSwitch) {
await BrowserTestUtils.waitForEvent(gBrowser.getTabForBrowser(browser), "SSTabRestored");
}
});
info(`Navigated to: ${target}`);
await new Promise(resolve => setTimeout(resolve, 20));
browser = gBrowser.selectedBrowser;
let secondProcessID = await ContentTask.spawn(browser, null, () => {
return Services.appinfo.processID;
});
info(`secondProcessID: ${secondProcessID}`);
if (expectedProcessSwitch) {
Assert.notEqual(firstProcessID, secondProcessID, `from: ${start} to ${target}`);
} else {
Assert.equal(firstProcessID, secondProcessID, `from: ${start} to ${target}`);
}
});
}
add_task(async function test_disabled() {
await SpecialPowers.pushPrefEnv({set: [[PREF_NAME, false]]});
await test_coop(httpURL("coop_header.sjs", "https://example.com"), httpURL("coop_header.sjs", "https://example.com"), false);
await test_coop(httpURL("coop_header.sjs?same-origin", "http://example.com"), httpURL("coop_header.sjs", "http://example.com"), false);
await test_coop(httpURL("coop_header.sjs", "http://example.com"), httpURL("coop_header.sjs?same-origin", "http://example.com"), false);
await test_coop(httpURL("coop_header.sjs?same-origin", "http://example.com"), httpURL("coop_header.sjs?same-site", "http://example.com"), false); // assuming we don't have fission yet :)
});
add_task(async function test_enabled() {
await SpecialPowers.pushPrefEnv({set: [[PREF_NAME, true]]});
await test_coop(httpURL("coop_header.sjs", "https://example.com"), httpURL("coop_header.sjs", "https://example.com"), false);
await test_coop(httpURL("coop_header.sjs", "https://example.com"), httpURL("coop_header.sjs?same-origin", "https://example.org"), true);
await test_coop(httpURL("coop_header.sjs?same-origin#1", "https://example.com"), httpURL("coop_header.sjs?same-origin#1", "https://example.org"), true);
await test_coop(httpURL("coop_header.sjs?same-origin#2", "https://example.com"), httpURL("coop_header.sjs?same-site#2", "https://example.org"), true);
});

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

@ -0,0 +1,13 @@
function handleRequest(request, response)
{
response.setStatusLine(request.httpVersion, 200, "OK");
let coop = request.queryString;
if (coop.length > 0) {
response.setHeader("Cross-Origin-Opener-Policy", unescape(coop), false);
}
response.setHeader("Content-Type", "text/html; charset=utf-8", false);
response.write("<!DOCTYPE html><html><body><p>Hello world</p></body></html>");
}

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

@ -17,9 +17,12 @@ XPCOMUtils.defineLazyPreferenceGetter(this, "useSeparatePrivilegedContentProcess
"browser.tabs.remote.separatePrivilegedContentProcess", false);
XPCOMUtils.defineLazyPreferenceGetter(this, "useHttpResponseProcessSelection",
"browser.tabs.remote.useHTTPResponseProcessSelection", false);
XPCOMUtils.defineLazyPreferenceGetter(this, "useCrossOriginOpenerPolicy",
"browser.tabs.remote.useCrossOriginOpenerPolicy", false);
XPCOMUtils.defineLazyServiceGetter(this, "serializationHelper",
"@mozilla.org/network/serialization-helper;1",
"nsISerializationHelper");
ChromeUtils.defineModuleGetter(this, "Utils",
"resource://gre/modules/sessionstore/Utils.jsm");
@ -98,6 +101,9 @@ var E10SUtils = {
useHttpResponseProcessSelection() {
return useHttpResponseProcessSelection;
},
useCrossOriginOpenerPolicy() {
return useCrossOriginOpenerPolicy;
},
canLoadURIInRemoteType(aURL, aRemoteType = DEFAULT_REMOTE_TYPE,
aPreferredRemoteType = undefined) {