Bug 1568597 - Make RemoteWorkerManager::GetRemoteType/MatchRemoteType fission-aware. r=asuth,nika

Differential Revision: https://phabricator.services.mozilla.com/D81373
This commit is contained in:
Luca Greco 2020-08-25 10:21:55 +00:00
Родитель fa53a76810
Коммит 74b570aff1
9 изменённых файлов: 685 добавлений и 174 удалений

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

@ -17,11 +17,18 @@
#include "mozilla/ipc/PBackgroundParent.h"
#include "mozilla/StaticPrefs_extensions.h"
#include "nsCOMPtr.h"
#include "nsIE10SUtils.h"
#include "nsImportModule.h"
#include "nsIXULRuntime.h"
#include "nsTArray.h"
#include "nsThreadUtils.h"
#include "RemoteWorkerServiceParent.h"
mozilla::LazyLogModule gRemoteWorkerManagerLog("RemoteWorkerManager");
#define LOG(fmt) \
MOZ_LOG(gRemoteWorkerManagerLog, mozilla::LogLevel::Verbose, fmt)
namespace mozilla {
using namespace ipc;
@ -63,9 +70,9 @@ void TransmitPermissionsAndBlobURLsForPrincipalInfo(
// static
bool RemoteWorkerManager::MatchRemoteType(const nsACString& processRemoteType,
const nsACString& workerRemoteType) {
if (processRemoteType.Equals(workerRemoteType)) {
return true;
}
LOG(("MatchRemoteType [processRemoteType=%s, workerRemoteType=%s]",
PromiseFlatCString(processRemoteType).get(),
PromiseFlatCString(workerRemoteType).get()));
// Respecting COOP and COEP requires processing headers in the parent
// process in order to choose an appropriate content process, but the
@ -75,18 +82,17 @@ bool RemoteWorkerManager::MatchRemoteType(const nsACString& processRemoteType,
// The ultimate goal is to allow these worker types to be put in such
// processes based on their script response headers.
// https://bugzilla.mozilla.org/show_bug.cgi?id=1595206
if (IsWebCoopCoepRemoteType(processRemoteType)) {
return false;
}
//
// RemoteWorkerManager::GetRemoteType should not select this remoteType
// and so workerRemoteType is not expected to be set to a coop+coep
// remoteType and here we can just assert that it is not happening.
MOZ_ASSERT(!IsWebCoopCoepRemoteType(workerRemoteType));
// A worker for a non privileged child process can be launched in
// any web child process that is not COOP and COEP.
if ((workerRemoteType.IsEmpty() || IsWebRemoteType(workerRemoteType)) &&
IsWebRemoteType(processRemoteType)) {
return true;
}
// For similar reasons to the ones related to COOP+COEP processes,
// we don't expect workerRemoteType to be set to a large allocation one.
MOZ_ASSERT(workerRemoteType != LARGE_ALLOCATION_REMOTE_TYPE);
return false;
return processRemoteType.Equals(workerRemoteType);
}
// static
@ -94,59 +100,74 @@ Result<nsCString, nsresult> RemoteWorkerManager::GetRemoteType(
const nsCOMPtr<nsIPrincipal>& aPrincipal, WorkerType aWorkerType) {
AssertIsOnMainThread();
if (aWorkerType != WorkerType::WorkerTypeService &&
aWorkerType != WorkerType::WorkerTypeShared) {
// This methods isn't expected to be called for worker type that
// aren't remote workers (currently Service and Shared workers).
return Err(NS_ERROR_UNEXPECTED);
MOZ_ASSERT_IF(aWorkerType == WorkerType::WorkerTypeService,
aPrincipal->GetIsContentPrincipal());
nsCOMPtr<nsIE10SUtils> e10sUtils =
do_ImportModule("resource://gre/modules/E10SUtils.jsm", "E10SUtils");
if (NS_WARN_IF(!e10sUtils)) {
LOG(("GetRemoteType Abort: could not import E10SUtils"));
return Err(NS_ERROR_DOM_ABORT_ERR);
}
nsCString preferredRemoteType = DEFAULT_REMOTE_TYPE;
if (aWorkerType == WorkerType::WorkerTypeShared) {
if (auto* contentChild = ContentChild::GetSingleton()) {
// For a shared worker set the preferred remote type to the content
// child process remote type.
preferredRemoteType = contentChild->GetRemoteType();
} else if (aPrincipal->IsSystemPrincipal()) {
preferredRemoteType = NOT_REMOTE_TYPE;
}
}
nsIE10SUtils::RemoteWorkerType workerType;
switch (aWorkerType) {
case WorkerType::WorkerTypeService:
workerType = nsIE10SUtils::REMOTE_WORKER_TYPE_SERVICE;
break;
case WorkerType::WorkerTypeShared:
workerType = nsIE10SUtils::REMOTE_WORKER_TYPE_SHARED;
break;
default:
// This method isn't expected to be called for worker types that
// aren't remote workers (currently Service and Shared workers).
LOG(("GetRemoteType Error on unexpected worker type"));
MOZ_DIAGNOSTIC_ASSERT(false, "Unexpected worker type");
return Err(NS_ERROR_DOM_ABORT_ERR);
}
// Here we do not have access to the window and so we can't use its
// useRemoteTabs and useRemoteSubframes flags (for the service
// worker there may not even be a window associated to the worker
// yet), and so we have to use the prefs instead.
bool isMultiprocess = BrowserTabsRemoteAutostart();
bool isFission = FissionAutostart();
nsCString remoteType = NOT_REMOTE_TYPE;
// If Gecko is running in single process mode, there is no child process
// to select, return without assigning any remoteType.
if (!BrowserTabsRemoteAutostart()) {
return remoteType;
nsresult rv = e10sUtils->GetRemoteTypeForWorkerPrincipal(
aPrincipal, workerType, isMultiprocess, isFission, preferredRemoteType,
remoteType);
if (NS_WARN_IF(NS_FAILED(rv))) {
LOG(
("GetRemoteType Abort: E10SUtils.getRemoteTypeForWorkerPrincipal "
"exception"));
MOZ_DIAGNOSTIC_ASSERT(
false, "E10SUtils.getRemoteTypeForworkerPrincipal did throw");
return Err(NS_ERROR_DOM_ABORT_ERR);
}
auto* contentChild = ContentChild::GetSingleton();
if (MOZ_LOG_TEST(gRemoteWorkerManagerLog, LogLevel::Verbose)) {
nsCString principalOrigin;
aPrincipal->GetOrigin(principalOrigin);
bool isSystem = !!BasePrincipal::Cast(aPrincipal)->IsSystemPrincipal();
bool isMozExtension =
!isSystem && !!BasePrincipal::Cast(aPrincipal)->AddonPolicy();
if (aWorkerType == WorkerType::WorkerTypeShared && !contentChild &&
!isSystem) {
// Prevent content principals SharedWorkers to be launched in the main
// process while running in multiprocess mode.
//
// NOTE this also prevents moz-extension SharedWorker to be created
// while the extension process is disabled by prefs, allowing it would
// also trigger an assertion failure in
// RemoteWorkerManager::SelectorTargetActor, due to an unexpected
// content-principal parent-process workers while e10s is on).
return Err(NS_ERROR_ABORT);
}
bool separatePrivilegedMozilla = Preferences::GetBool(
"browser.tabs.remote.separatePrivilegedMozillaWebContentProcess", false);
if (isMozExtension) {
remoteType = EXTENSION_REMOTE_TYPE;
} else if (separatePrivilegedMozilla) {
bool isPrivilegedMozilla = false;
aPrincipal->IsURIInPrefList("browser.tabs.remote.separatedMozillaDomains",
&isPrivilegedMozilla);
if (isPrivilegedMozilla) {
remoteType = PRIVILEGEDMOZILLA_REMOTE_TYPE;
} else if (aWorkerType == WorkerType::WorkerTypeShared && contentChild) {
remoteType = contentChild->GetRemoteType();
} else {
remoteType = DEFAULT_REMOTE_TYPE;
}
} else {
remoteType = DEFAULT_REMOTE_TYPE;
LOG(
("GetRemoteType workerType=%s, principal=%s, "
"preferredRemoteType=%s, selectedRemoteType=%s",
aWorkerType == WorkerType::WorkerTypeService ? "service" : "shared",
principalOrigin.get(), preferredRemoteType.get(), remoteType.get()));
}
return remoteType;
@ -202,6 +223,7 @@ bool RemoteWorkerManager::IsRemoteTypeAllowed(const RemoteWorkerData& aData) {
auto remoteType = GetRemoteType(
principal, isServiceWorker ? WorkerTypeService : WorkerTypeShared);
if (NS_WARN_IF(remoteType.isErr())) {
LOG(("IsRemoteTypeAllowed: Error to retrieve remote type"));
return false;
}
@ -262,6 +284,8 @@ void RemoteWorkerManager::RegisterActor(RemoteWorkerServiceParent* aActor) {
const auto& workerRemoteType = p.mData.remoteType();
if (MatchRemoteType(remoteType, workerRemoteType)) {
LOG(("RegisterActor - Launch Pending, workerRemoteType=%s",
workerRemoteType.get()));
LaunchInternal(p.mController, aActor, p.mData);
} else {
unlaunched.AppendElement(std::move(p));
@ -278,6 +302,8 @@ void RemoteWorkerManager::RegisterActor(RemoteWorkerServiceParent* aActor) {
if (mPendings.IsEmpty()) {
Release();
}
LOG(("RegisterActor - mPendings length: %zu", mPendings.Length()));
}
}
@ -577,6 +603,7 @@ void RemoteWorkerManager::LaunchNewContentProcess(
const CallbackParamType& aValue,
const nsCString& remoteType) mutable {
if (aValue.IsResolve()) {
LOG(("LaunchNewContentProcess: successfully got child process"));
if (isServiceWorker) {
TransmitPermissionsAndBlobURLsForPrincipalInfo(aValue.ResolveValue(),
principalInfo);
@ -595,6 +622,10 @@ void RemoteWorkerManager::LaunchNewContentProcess(
for (const auto& pending : pendings) {
const auto& workerRemoteType = pending.mData.remoteType();
if (self->MatchRemoteType(remoteType, workerRemoteType)) {
LOG(
("LaunchNewContentProcess: Cancel pending with "
"workerRemoteType=%s",
workerRemoteType.get()));
pending.mController->CreationFailed();
} else {
uncancelled.AppendElement(pending);
@ -608,6 +639,8 @@ void RemoteWorkerManager::LaunchNewContentProcess(
}
};
LOG(("LaunchNewContentProcess: remoteType=%s", aData.remoteType().get()));
nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(
__func__, [callback = std::move(processLaunchCallback),
workerRemoteType = aData.remoteType()]() mutable {

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

@ -1,53 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* 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/. */
#include "gtest/gtest.h"
#include "../RemoteWorkerManager.h"
using namespace mozilla::dom;
TEST(RemoteWorkerManager, TestMatchRemoteType)
{
static const struct {
const nsCString processRemoteType;
const nsCString workerRemoteType;
const bool shouldMatch;
} tests[] = {
// Test exact matches between process and worker remote types.
{DEFAULT_REMOTE_TYPE, DEFAULT_REMOTE_TYPE, true},
{EXTENSION_REMOTE_TYPE, EXTENSION_REMOTE_TYPE, true},
{PRIVILEGEDMOZILLA_REMOTE_TYPE, PRIVILEGEDMOZILLA_REMOTE_TYPE, true},
// Test workers with remoteType "web" not launched on non-web or coop+coep
// processes.
{PRIVILEGEDMOZILLA_REMOTE_TYPE, DEFAULT_REMOTE_TYPE, false},
{PRIVILEGEDABOUT_REMOTE_TYPE, DEFAULT_REMOTE_TYPE, false},
{EXTENSION_REMOTE_TYPE, DEFAULT_REMOTE_TYPE, false},
{FILE_REMOTE_TYPE, DEFAULT_REMOTE_TYPE, false},
{WITH_COOP_COEP_REMOTE_TYPE_PREFIX, DEFAULT_REMOTE_TYPE, false},
// Test workers with remoteType "web" launched in web child processes.
{LARGE_ALLOCATION_REMOTE_TYPE, DEFAULT_REMOTE_TYPE, true},
{FISSION_WEB_REMOTE_TYPE, DEFAULT_REMOTE_TYPE, true},
// Test empty remoteType default to "web" remoteType.
{DEFAULT_REMOTE_TYPE, NOT_REMOTE_TYPE, true},
{WITH_COOP_COEP_REMOTE_TYPE_PREFIX, NOT_REMOTE_TYPE, false},
{PRIVILEGEDMOZILLA_REMOTE_TYPE, NOT_REMOTE_TYPE, false},
{EXTENSION_REMOTE_TYPE, NOT_REMOTE_TYPE, false},
};
for (const auto& test : tests) {
auto message = nsPrintfCString(
R"(MatchRemoteType("%s", "%s") should return %s)",
test.processRemoteType.get(), test.workerRemoteType.get(),
test.shouldMatch ? "true" : "false");
ASSERT_EQ(RemoteWorkerManager::MatchRemoteType(test.processRemoteType,
test.workerRemoteType),
test.shouldMatch)
<< message;
}
}

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

@ -1,13 +0,0 @@
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
# vim: set filetype=python:
# 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/.
UNIFIED_SOURCES = [
'TestMatchRemoteType.cpp',
]
include('/ipc/chromium/chromium-config.mozbuild')
FINAL_LIBRARY = 'xul-gtest'

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

@ -40,8 +40,6 @@ IPDL_SOURCES += [
'RemoteWorkerTypes.ipdlh',
]
TEST_DIRS += ['gtest']
include('/ipc/chromium/chromium-config.mozbuild')
FINAL_LIBRARY = 'xul'

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

@ -21,9 +21,7 @@ do_get_profile(true);
AddonTestUtils.init(this);
const server = createHttpServer({
hosts: ["localhost", "example.org"],
});
const server = createHttpServer({ hosts: ["localhost"] });
server.registerPathHandler("/sw.js", (request, response) => {
info(`/sw.js is being requested: ${JSON.stringify(request)}`);
@ -41,23 +39,8 @@ add_task(async function setup_prefs() {
// Enable nsIServiceWorkerManager.registerForTest.
Services.prefs.setBoolPref("dom.serviceWorkers.testing.enabled", true);
// Configure prefs to configure example.org as a domain to load
// in a privilegedmozilla content child process.
Services.prefs.setBoolPref(
"browser.tabs.remote.separatePrivilegedMozillaWebContentProcess",
true
);
Services.prefs.setCharPref(
"browser.tabs.remote.separatedMozillaDomains",
"example.org"
);
registerCleanupFunction(() => {
Services.prefs.clearUserPref("dom.serviceWorkers.testing.enabled");
Services.prefs.clearUserPref(
"browser.tabs.remote.separatePrivilegedMozillaWebContentProcess"
);
Services.prefs.clearUserPref("browser.tabs.remote.separatedMozillaDomains");
});
});
@ -96,24 +79,9 @@ add_task(async function launch_remoteworkers_in_new_processes() {
})}`
);
// A test service worker that should spawn a privilegedmozilla child process.
const swRegInfoPriv = await swm.registerForTest(
ssm.createContentPrincipal(Services.io.newURI("http://example.org"), {}),
"http://example.org/scope",
"http://example.org/sw.js"
);
swRegInfoPriv.QueryInterface(Ci.nsIServiceWorkerRegistrationInfo);
info(
`privilegedmozilla service worker registered: ${JSON.stringify({
principal: swRegInfoPriv.principal.spec,
scope: swRegInfoPriv.scope,
})}`
);
info("Wait new process to be launched");
await TestUtils.waitForCondition(() => {
return Services.ppmm.childCount - initialChildCount >= 2;
return Services.ppmm.childCount - initialChildCount >= 1;
}, "wait for a new child processes to be started");
// Wait both workers to become active to be sure that. besides spawning
@ -122,13 +90,7 @@ add_task(async function launch_remoteworkers_in_new_processes() {
// pass successfull the IsRemoteTypeAllowed check in RemoteworkerChild).
info("Wait for webcontent worker to become active");
await TestUtils.waitForCondition(
() => swRegInfoPriv.activeWorker,
`wait workers for scope ${swRegInfoPriv.scope} to be active`
);
info("Wait for privilegedmozille worker to become active");
await TestUtils.waitForCondition(
() => swRegInfoPriv.activeWorker,
`wait workers for scope ${swRegInfoPriv.scope} to be active`
() => swRegInfoWeb.activeWorker,
`wait workers for scope ${swRegInfoWeb.scope} to be active`
);
});

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

@ -169,7 +169,8 @@ function validatedWebRemoteType(
aTargetUri,
aCurrentUri,
aResultPrincipal,
aRemoteSubframes
aRemoteSubframes,
aIsWorker = false
) {
// To load into the Privileged Mozilla Content Process you must be https,
// and be an exact match or a subdomain of an allowlisted domain.
@ -189,6 +190,15 @@ function validatedWebRemoteType(
// If we're in the parent and we were passed a web-handled scheme,
// transform it now to avoid trying to load it in the wrong process.
if (aRemoteSubframes && hasPotentiallyWebHandledScheme(aTargetUri)) {
// We shouldn't even get to this for a worker, throw an unexpected error
// if we do.
if (aIsWorker) {
throw Components.Exception(
"Unexpected remote worker with a web handled scheme",
Cr.NS_ERROR_UNEXPECTED
);
}
if (
Services.appinfo.processType != Services.appinfo.PROCESS_TYPE_DEFAULT &&
Services.appinfo.remoteType.startsWith(FISSION_WEB_REMOTE_TYPE + "=")
@ -198,6 +208,7 @@ function validatedWebRemoteType(
// we know the "real" URL to which we'll redirect.
return Services.appinfo.remoteType;
}
// This doesn't work (throws) in the child - see
// https://bugzilla.mozilla.org/show_bug.cgi?id=1589082
// Even if it did, it'd cause sync IPC
@ -222,7 +233,7 @@ function validatedWebRemoteType(
// 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.inFileURIAllowlist(aTargetUri)) {
if (!aIsWorker && sm.inFileURIAllowlist(aTargetUri)) {
return FILE_REMOTE_TYPE;
}
@ -264,7 +275,11 @@ function validatedWebRemoteType(
return aPreferredRemoteType;
}
if (aPreferredRemoteType == FILE_REMOTE_TYPE && !aRemoteSubframes) {
if (
aPreferredRemoteType == FILE_REMOTE_TYPE &&
!aRemoteSubframes &&
!aIsWorker
) {
E10SUtils.log().debug("checking allowLinkedWebInFileUriProcess");
if (!aCurrentUri) {
E10SUtils.log().debug("No aCurrentUri");
@ -294,6 +309,12 @@ function validatedWebRemoteType(
return WEB_REMOTE_TYPE;
}
// remoteTypes allowed to host system-principal remote workers.
const SYSTEM_WORKERS_REMOTE_TYPES_ALLOWED = [
NOT_REMOTE,
PRIVILEGEDABOUT_REMOTE_TYPE,
];
var E10SUtils = {
DEFAULT_REMOTE_TYPE,
NOT_REMOTE,
@ -304,6 +325,7 @@ var E10SUtils = {
PRIVILEGEDABOUT_REMOTE_TYPE,
PRIVILEGEDMOZILLA_REMOTE_TYPE,
LARGE_ALLOCATION_REMOTE_TYPE,
FISSION_WEB_REMOTE_TYPE,
useCrossOriginOpenerPolicy() {
return useCrossOriginOpenerPolicy;
@ -429,7 +451,8 @@ var E10SUtils = {
aPreferredRemoteType = DEFAULT_REMOTE_TYPE,
aCurrentUri = null,
aResultPrincipal = null,
aIsSubframe = false
aIsSubframe = false,
aIsWorker = false
) {
if (!aMultiProcess) {
return NOT_REMOTE;
@ -527,6 +550,15 @@ var E10SUtils = {
// Protocols that redirect to http(s) will just flip back to a
// regular content process after the redirect.
if (aURI.scheme.startsWith("ext+")) {
// We shouldn't even get to this for a worker, throw an unexpected error
// if we do.
if (aIsWorker) {
throw Components.Exception(
"Unexpected remote worker with extension handled scheme",
Cr.NS_ERROR_UNEXPECTED
);
}
return WebExtensionPolicy.useRemoteWebExtensions
? EXTENSION_REMOTE_TYPE
: NOT_REMOTE;
@ -540,6 +572,15 @@ var E10SUtils = {
// innermost URI. Any URIs like this will need to be handled in the
// cases above, so we don't still end up using the fake inner URI here.
if (aURI instanceof Ci.nsINestedURI) {
// We shouldn't even get to this for a worker, throw an unexpected error
// if we do.
if (aIsWorker) {
throw Components.Exception(
"Unexpected worker with a NestedURI",
Cr.NS_ERROR_UNEXPECTED
);
}
let innerURI = aURI.QueryInterface(Ci.nsINestedURI).innerURI;
return this.getRemoteTypeForURIObject(
innerURI,
@ -561,7 +602,8 @@ var E10SUtils = {
aURI,
aCurrentUri,
aResultPrincipal,
aRemoteSubframes
aRemoteSubframes,
aIsWorker
);
log.debug(` validatedWebRemoteType() returning: ${remoteType}`);
return remoteType;
@ -625,6 +667,90 @@ var E10SUtils = {
);
},
getRemoteTypeForWorkerPrincipal(
aPrincipal,
aWorkerType,
aIsMultiProcess,
aIsFission,
aPreferredRemoteType = DEFAULT_REMOTE_TYPE
) {
if (aPrincipal.isExpandedPrincipal) {
// Explicitly disallow expanded principals:
// The worker principal is based on the worker script, an expanded principal
// is not expected.
throw new Error("Unexpected expanded principal worker");
}
if (
aWorkerType === Ci.nsIE10SUtils.REMOTE_WORKER_TYPE_SERVICE &&
!aPrincipal.isContentPrincipal
) {
// Fails earlier on service worker with a non content principal.
throw new Error("Unexpected system or null principal service worker");
}
if (!aIsMultiProcess) {
// Return earlier when multiprocess is disabled.
return NOT_REMOTE;
}
if (
// We don't want to launch workers in a large allocation remote type,
// change it to the web remote type (then getRemoteTypeForURIObject
// may change it to an isolated one).
aPreferredRemoteType === LARGE_ALLOCATION_REMOTE_TYPE ||
// Similarly to the large allocation remote type, we don't want to
// launch the shared worker in a web coop+coep remote type even if
// was registered from a frame loaded in a child process with that
// remote type.
aPreferredRemoteType?.startsWith(WEB_REMOTE_COOP_COEP_TYPE_PREFIX)
) {
aPreferredRemoteType = DEFAULT_REMOTE_TYPE;
}
// System principal shared workers are allowed to run in the main process
// or in the privilegedabout child process. Early return the preferred remote type
// if it is one where a system principal worked is allowed to run.
if (
aPrincipal.isSystemPrincipal &&
SYSTEM_WORKERS_REMOTE_TYPES_ALLOWED.includes(aPreferredRemoteType)
) {
return aPreferredRemoteType;
}
// Allow null principal shared workers to run in the same process type where they
// have been registered (the preferredRemoteType), but return the DEFAULT_REMOTE_TYPE
// if the preferred remote type was NOT_REMOTE.
if (aPrincipal.isNullPrincipal) {
return aPreferredRemoteType === NOT_REMOTE
? DEFAULT_REMOTE_TYPE
: aPreferredRemoteType;
}
// Sanity check, there shouldn't be any system or null principal after this point.
if (aPrincipal.isContentPrincipal) {
// For content principal, get a remote type based on the worker principal URI
// (which is based on the worker script url) and an initial preferredRemoteType
// (only set for shared worker, based on the remote type where the shared worker
// was registered from).
return E10SUtils.getRemoteTypeForURIObject(
aPrincipal.URI,
aIsMultiProcess,
aIsFission,
aPreferredRemoteType,
null,
aPrincipal,
false, // aIsSubFrame
true // aIsWorker
);
}
// Throw explicitly if we were unable to get a remoteType for the worker.
throw new Error(
"Failed to get a remoteType for a non content principal worker"
);
},
makeInputStream(data) {
if (typeof data == "string") {
let stream = Cc["@mozilla.org/io/string-input-stream;1"].createInstance(

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

@ -15,6 +15,11 @@ interface nsIURI;
*/
[scriptable, uuid(1e18680e-052d-4509-a17e-678f5c495e02)]
interface nsIE10SUtils : nsISupports {
cenum RemoteWorkerType: 8 {
REMOTE_WORKER_TYPE_SHARED,
REMOTE_WORKER_TYPE_SERVICE,
};
/**
* Determine what remote type should be used to load a document with the given
* principal.
@ -39,4 +44,28 @@ interface nsIE10SUtils : nsISupports {
in AUTF8String aPreferredRemoteType,
in nsIPrincipal aCurrentPrincipal,
in boolean aIsSubframe);
/**
* Determine what remote type should be used to launch a worker script with
* the given principal.
*
* @param aPrincipal
* The result principal for the document being loaded.
* @param aWorkerTypeName
* The type of remote worker being launched (Ci.nsIE10SUtils.REMOTE_WORKER_TYPE_*).
* @param aIsMultiProcess
* A boolean to indicate if e10s enabled.
* @param aIsFission
* A boolean to indicate if fission is enabled.
* @param aPreferredRemoteType
* If multiple remote types are compatible with the worker,
* prefer staying in this remote type.
*
* @return The remote type to launch the worker in.
*/
AUTF8String getRemoteTypeForWorkerPrincipal(in nsIPrincipal aPrincipal,
in nsIE10SUtils_RemoteWorkerType aWorkerType,
in boolean aIsMultiProcess,
in boolean aIsFission,
in AUTF8String aPreferredRemoteType);
};

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

@ -0,0 +1,428 @@
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
const { E10SUtils } = ChromeUtils.import(
"resource://gre/modules/E10SUtils.jsm"
);
const URI_SECURE_COM = Services.io.newURI("https://example.com");
const URI_SECURE_ORG = Services.io.newURI("https://example.org");
const URI_INSECURE_ORG = Services.io.newURI("http://example.org");
const URI_FILE = Services.io.newURI("file:///path/to/dir");
const URI_EXTENSION = Services.io.newURI("moz-extension://fake-uuid");
const URI_EXT_PROTOCOL = Services.io.newURI("ext+custom://fake-url");
const URI_WEB_PROTOCOL = Services.io.newURI("web+custom://fake-url");
const URI_PRIVILEGEDMOZILLA = Services.io.newURI("https://addons.mozilla.org");
const fakeContentScriptSandbox = Cu.Sandbox(
["https://example.org", "moz-extension://fake-uuid"],
{}
);
const ssm = Services.scriptSecurityManager;
const systemPrincipal = ssm.getSystemPrincipal();
const nullPrincipal = ssm.createNullPrincipal({});
const principalSecureCom = ssm.createContentPrincipal(URI_SECURE_COM, {});
const principalSecureOrg = ssm.createContentPrincipal(URI_SECURE_ORG, {});
const principalInsecureOrg = ssm.createContentPrincipal(URI_INSECURE_ORG, {});
const principalFile = ssm.createContentPrincipal(URI_FILE, {});
const principalExtension = ssm.createContentPrincipal(URI_EXTENSION, {});
const principalExpanded = Cu.getObjectPrincipal(fakeContentScriptSandbox);
const principalExtProtocol = ssm.createContentPrincipal(URI_EXT_PROTOCOL, {});
const principalWebProtocol = ssm.createContentPrincipal(URI_WEB_PROTOCOL, {});
const principalPrivilegedMozilla = ssm.createContentPrincipal(
URI_PRIVILEGEDMOZILLA,
{}
);
const {
DEFAULT_REMOTE_TYPE,
EXTENSION_REMOTE_TYPE,
FILE_REMOTE_TYPE,
FISSION_WEB_REMOTE_TYPE,
LARGE_ALLOCATION_REMOTE_TYPE,
NOT_REMOTE,
PRIVILEGEDABOUT_REMOTE_TYPE,
PRIVILEGEDMOZILLA_REMOTE_TYPE,
WEB_REMOTE_COOP_COEP_TYPE_PREFIX,
WEB_REMOTE_TYPE,
} = E10SUtils;
const {
REMOTE_WORKER_TYPE_SHARED,
REMOTE_WORKER_TYPE_SERVICE,
} = Ci.nsIE10SUtils;
// Test ServiceWorker remoteType selection with multiprocess and/or site isolation enabled.
add_task(function test_get_remote_type_for_service_worker() {
// ServiceWorkers with system or null principal are unexpected and we expect
// the method call to throw.
for (const principal of [systemPrincipal, nullPrincipal]) {
Assert.throws(
() =>
E10SUtils.getRemoteTypeForWorkerPrincipal(
principal,
REMOTE_WORKER_TYPE_SERVICE,
true,
false
),
/Unexpected system or null principal/,
`Did raise an exception on "${principal.origin}" principal ServiceWorker`
);
}
// ServiceWorker test cases:
// - e10s + fission disabled:
// - extension principal + any preferred remote type => extension remote type
// - content principal + any preferred remote type => web remote type
// - fission enabled:
// - extension principal + any preferred remote type => extension remote type
// - content principal + any preferred remote type => webIsolated=siteOrigin remote type
function* getTestCase(fission = false) {
const TEST_PRINCIPALS = [
principalSecureCom,
principalSecureOrg,
principalExtension,
];
const PREFERRED_REMOTE_TYPES = [
E10SUtils.DEFAULT_REMOTE_TYPE,
E10SUtils.WEB_REMOTE_TYPE,
"fakeRemoteType",
];
for (const principal of TEST_PRINCIPALS) {
for (const preferred of PREFERRED_REMOTE_TYPES) {
const msg = `ServiceWorker, principal=${
principal.origin
}, preferredRemoteType=${preferred}, ${fission ? "fission" : "e10s"}`;
yield [
msg,
principal,
REMOTE_WORKER_TYPE_SERVICE,
true,
fission,
preferred,
];
}
}
}
// Test cases for e10s mode + fission disabled.
for (const testCase of getTestCase(false)) {
const [msg, principal, ...args] = testCase;
let expected = E10SUtils.WEB_REMOTE_TYPE;
if (principal == principalExtension) {
expected = WebExtensionPolicy.useRemoteWebExtensions
? E10SUtils.EXTENSION_REMOTE_TYPE
: E10SUtils.NOT_REMOTE;
}
equal(
E10SUtils.getRemoteTypeForWorkerPrincipal(principal, ...args),
expected,
msg
);
}
// Test cases for e10s mode + fission enabled.
for (const testCase of getTestCase(true)) {
const [msg, principal, ...args] = testCase;
let expected = `${FISSION_WEB_REMOTE_TYPE}=${principal.siteOrigin}`;
if (principal == principalExtension) {
expected = WebExtensionPolicy.useRemoteWebExtensions
? E10SUtils.EXTENSION_REMOTE_TYPE
: E10SUtils.NOT_REMOTE;
}
equal(
E10SUtils.getRemoteTypeForWorkerPrincipal(principal, ...args),
expected,
msg
);
}
});
// Test SharedWorker remoteType selection with multiprocess and/or site isolation enabled.
add_task(function test_get_remote_type_for_shared_worker() {
// Verify that for shared worker registered from a large allocation or web
// coop+coep remote types we are going to select a web or fission remote type.
for (const [principal, preferredRemoteType] of [
[principalSecureCom, LARGE_ALLOCATION_REMOTE_TYPE],
[
principalSecureCom,
`${WEB_REMOTE_COOP_COEP_TYPE_PREFIX}=${principalSecureCom.siteOrigin}`,
],
]) {
equal(
E10SUtils.getRemoteTypeForWorkerPrincipal(
principal,
REMOTE_WORKER_TYPE_SHARED,
true,
false,
preferredRemoteType
),
WEB_REMOTE_TYPE,
`Got WEB_REMOTE_TYPE on preferred ${preferredRemoteType} and fission disabled`
);
equal(
E10SUtils.getRemoteTypeForWorkerPrincipal(
principal,
REMOTE_WORKER_TYPE_SHARED,
true,
true,
preferredRemoteType
),
`${FISSION_WEB_REMOTE_TYPE}=${principal.siteOrigin}`,
`Got WEB_REMOTE_TYPE on preferred ${preferredRemoteType} and fission enabled`
);
}
// For System principal shared worker we do select NOT_REMOTE or the preferred
// remote type if is one of the explicitly allowed (NOT_REMOTE and
// PRIVILEGEDABOUT_REMOTE_TYPE).
for (const [principal, preferredRemoteType] of [
[systemPrincipal, NOT_REMOTE],
[systemPrincipal, PRIVILEGEDABOUT_REMOTE_TYPE],
]) {
equal(
E10SUtils.getRemoteTypeForWorkerPrincipal(
principal,
REMOTE_WORKER_TYPE_SHARED,
true,
true,
preferredRemoteType
),
preferredRemoteType,
`Selected the preferred ${preferredRemoteType} on system principal shared worker`
);
}
Assert.throws(
() =>
E10SUtils.getRemoteTypeForWorkerPrincipal(
systemPrincipal,
REMOTE_WORKER_TYPE_SHARED,
true,
true,
"fakeRemoteType"
),
/Failed to get a remoteType/,
"Does fail explicitly on system worker for arbitrary preferredRemoteType"
);
// Behavior NOT_REMOTE preferredRemoteType for content principals with
// multiprocess enabled.
for (const [principal, expectedRemoteType] of [
[
principalSecureCom,
`${FISSION_WEB_REMOTE_TYPE}=${principalSecureCom.siteOrigin}`,
],
[
principalExtension,
WebExtensionPolicy.useRemoteWebExtensions
? EXTENSION_REMOTE_TYPE
: NOT_REMOTE,
],
]) {
equal(
E10SUtils.getRemoteTypeForWorkerPrincipal(
principal,
REMOTE_WORKER_TYPE_SHARED,
true,
true,
NOT_REMOTE
),
expectedRemoteType,
`Got ${expectedRemoteType} for content principal ${principal.siteOrigin}`
);
}
// Shared worker registered for web+custom urls.
for (const [preferredRemoteType, expectedRemoteType] of [
[WEB_REMOTE_TYPE, WEB_REMOTE_TYPE],
[LARGE_ALLOCATION_REMOTE_TYPE, WEB_REMOTE_TYPE],
["fakeRemoteType", "fakeRemoteType"],
// This seems to be actually failing with a SecurityError
// as soon as the SharedWorker constructor is being called with
// a web+...:// url from an extension principal:
//
// [EXTENSION_REMOTE_TYPE, EXTENSION_REMOTE_TYPE],
]) {
equal(
E10SUtils.getRemoteTypeForWorkerPrincipal(
principalWebProtocol,
REMOTE_WORKER_TYPE_SHARED,
true,
true,
preferredRemoteType
),
expectedRemoteType,
"Selected expected process for web+custom:// shared worker"
);
}
// Shared worker registered for ext+custom urls.
for (const [preferredRemoteType, expectedRemoteType] of [
[WEB_REMOTE_TYPE, WEB_REMOTE_TYPE],
[LARGE_ALLOCATION_REMOTE_TYPE, WEB_REMOTE_TYPE],
["fakeRemoteType", "fakeRemoteType"],
// This seems to be actually prevented by failing a ClientIsValidPrincipalInfo
// check (but only when the remote worker is being launched in the child process
// and so after a remote Type has been selected).
// [EXTENSION_REMOTE_TYPE, EXTENSION_REMOTE_TYPE],
]) {
equal(
E10SUtils.getRemoteTypeForWorkerPrincipal(
principalExtProtocol,
REMOTE_WORKER_TYPE_SHARED,
true,
true,
preferredRemoteType
),
expectedRemoteType,
"Selected expected process for ext+custom:// shared worker"
);
}
// Shared worker with a file principal.
// NOTE: on android useSeparateFileUriProcess will be false and file uri are
// going to run in the default web process.
const expectedFileRemoteType = Services.prefs.getBoolPref(
"browser.tabs.remote.separateFileUriProcess",
false
)
? FILE_REMOTE_TYPE
: WEB_REMOTE_TYPE;
for (const [preferredRemoteType, expectedRemoteType] of [
[WEB_REMOTE_TYPE, expectedFileRemoteType],
["fakeRemoteType", expectedFileRemoteType],
]) {
equal(
E10SUtils.getRemoteTypeForWorkerPrincipal(
principalFile,
REMOTE_WORKER_TYPE_SHARED,
true,
true,
preferredRemoteType
),
expectedRemoteType,
"Got expected remote type on file principal shared worker"
);
}
// Shared worker related to a privilegedmozilla domain.
// NOTE: separatePrivilegedMozilla will be false on android builds.
const usePrivilegedMozilla = Services.prefs.getBoolPref(
"browser.tabs.remote.separatePrivilegedMozillaWebContentProcess",
false
);
const expectedRemoteType = usePrivilegedMozilla
? PRIVILEGEDMOZILLA_REMOTE_TYPE
: `${FISSION_WEB_REMOTE_TYPE}=https://mozilla.org`;
for (const preferredRemoteType of [
PRIVILEGEDMOZILLA_REMOTE_TYPE,
"fakeRemoteType",
]) {
equal(
E10SUtils.getRemoteTypeForWorkerPrincipal(
principalPrivilegedMozilla,
REMOTE_WORKER_TYPE_SHARED,
true,
true,
preferredRemoteType
),
expectedRemoteType,
"Got expected remote type on privilegedmozilla principal shared worker"
);
}
});
// Test that we do throw on expanded principals.
add_task(function test_get_remote_type_throws_on_expanded_principals() {
for (const workerType of [
REMOTE_WORKER_TYPE_SHARED,
REMOTE_WORKER_TYPE_SERVICE,
]) {
Assert.throws(
() =>
E10SUtils.getRemoteTypeForWorkerPrincipal(
principalExpanded,
workerType,
true,
false
),
/Unexpected expanded principal/,
"Did raise an exception as expected"
);
}
});
// Test that NO_REMOTE is the remote type selected when multiprocess is disabled,
// there is no other checks special behaviors on particular principal or worker type.
add_task(function test_get_remote_type_multiprocess_disabled() {
function* getTestCase() {
const TEST_PRINCIPALS = [
systemPrincipal,
nullPrincipal,
principalSecureCom,
principalSecureOrg,
principalInsecureOrg,
principalFile,
principalExtension,
];
const PREFERRED_REMOTE_TYPES = [
E10SUtils.DEFAULT_REMOTE_TYPE,
E10SUtils.WEB_REMOTE_TYPE,
"fakeRemoteType",
];
for (const principal of TEST_PRINCIPALS) {
for (const preferred of PREFERRED_REMOTE_TYPES) {
const msg = `SharedWorker, principal=${principal.origin}, preferredRemoteType=${preferred}`;
yield [
msg,
principal,
REMOTE_WORKER_TYPE_SHARED,
false,
false,
preferred,
];
}
}
for (const principal of TEST_PRINCIPALS) {
// system and null principals are disallowed for service workers, we throw
// if passed to the E10SUtils method and we cover this scenario with a
// separate test.
if (principal.isSystemPrincipal || principal.isNullPrincipal) {
continue;
}
for (const preferred of PREFERRED_REMOTE_TYPES) {
const msg = `ServiceWorker with principal ${principal.origin} and preferredRemoteType ${preferred}`;
yield [
msg,
principal,
REMOTE_WORKER_TYPE_SERVICE,
false,
false,
preferred,
];
}
}
}
for (const testCase of getTestCase()) {
const [msg, ...args] = testCase;
equal(
E10SUtils.getRemoteTypeForWorkerPrincipal(...args),
E10SUtils.NOT_REMOTE,
`Expect NOT_REMOTE on disabled multiprocess: ${msg}`
);
}
});

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

@ -17,6 +17,7 @@ support-files =
[test_CreditCard.js]
[test_DeferredTask.js]
skip-if = toolkit == 'android' || (os == 'mac' && os_version=='10.14') # osx: Bug 1550141;
[test_E10SUtils_workers_remote_types.js]
[test_FileUtils.js]
skip-if = toolkit == 'android'
[test_FinderIterator.js]