Bug 1850589 - Migrate worker process selection to use ProcessIsolation as a backend, r=smaug,asuth

This new backend shares more logic in common with the existing logic in
ProcessIsolation for navigations. This means that it will have support
for precursor principals and alternative process isolation behaviors
like IsolateHighValue on Android.

The logic around custom protocols like web+ and ext+ URIs has been
removed in the new patch, as these will never be used as the principals
for globals. The handling in E10SUtils was just for handling predicting
remote types for URIs before the load has started, which is not relevant
for Workers.

In a future patch we may want to also switch over the prediction logic
which still uses E10SUtils to also use the ProcessIsolation backend,
however it is more different due to the need to handle arbitrary input
URLs, rather than result principals, so the benefit will be smaller.

Differential Revision: https://phabricator.services.mozilla.com/D187227
This commit is contained in:
Nika Layzell 2023-09-06 18:48:30 +00:00
parent 0969d3b902
commit d5ede54ea6
11 changed files with 516 additions and 781 deletions

View File

@ -122,9 +122,12 @@ CommaSeparatedPref sSeparatedMozillaDomains{
*/
enum class IsolationBehavior {
// This URI loads web content and should be treated as a content load, being
// isolated based on the response principal.
// isolated based on the response principal if enabled.
WebContent,
// Forcibly load in a process with the "web" remote type.
// Forcibly load in a process with the "web" remote type. This will ignore the
// response principal completely.
// This is generally reserved for internal documents which are loaded in
// content, but not in the privilegedabout content process.
ForceWebRemoteType,
// Load this URI in the privileged about content process.
PrivilegedAbout,
@ -182,6 +185,23 @@ static const char* IsolationBehaviorName(IsolationBehavior aBehavior) {
}
}
/**
* Returns a static string with the name of the given worker kind. For use in
* logging code.
*/
static const char* WorkerKindName(WorkerKind aWorkerKind) {
switch (aWorkerKind) {
case WorkerKindDedicated:
return "Dedicated";
case WorkerKindShared:
return "Shared";
case WorkerKindService:
return "Service";
default:
return "Unknown";
}
}
/**
* Check if a given URI has specialized process isolation behaviour, such as
* needing to be loaded within a specific type of content process.
@ -356,6 +376,19 @@ static nsAutoCString OriginString(nsIPrincipal* aPrincipal) {
return origin;
}
/**
* Trim the OriginAttributes from aPrincipal, and use it to create a
* OriginSuffix string appropriate to use within a remoteType string.
*/
static nsAutoCString OriginSuffixForRemoteType(nsIPrincipal* aPrincipal) {
nsAutoCString originSuffix;
OriginAttributes attrs = aPrincipal->OriginAttributesRef();
attrs.StripAttributes(OriginAttributes::STRIP_FIRST_PARTY_DOMAIN |
OriginAttributes::STRIP_PARITION_KEY);
attrs.CreateSuffix(originSuffix);
return originSuffix;
}
/**
* Given an about:reader URI, extract the "url" query parameter, and use it to
* construct a principal which should be used for process selection.
@ -390,14 +423,11 @@ static already_AddRefed<BasePrincipal> GetAboutReaderURLPrincipal(
* worker should be isolated.
*/
static bool ShouldIsolateSite(nsIPrincipal* aPrincipal,
CanonicalBrowsingContext* aTopBC) {
bool aUseRemoteSubframes) {
// If Fission is disabled, we never want to isolate. We check the toplevel BC
// if it's available, or the global pref if checking for shared or service
// workers.
if (aTopBC && !aTopBC->UseRemoteSubframes()) {
return false;
}
if (!aTopBC && !mozilla::FissionAutostart()) {
if (!aUseRemoteSubframes) {
return false;
}
@ -461,6 +491,45 @@ static bool ShouldIsolateSite(nsIPrincipal* aPrincipal,
}
}
static Result<nsCString, nsresult> SpecialBehaviorRemoteType(
IsolationBehavior aBehavior, const nsACString& aCurrentRemoteType,
WindowGlobalParent* aParentWindow) {
switch (aBehavior) {
case IsolationBehavior::ForceWebRemoteType:
return {WEB_REMOTE_TYPE};
case IsolationBehavior::PrivilegedAbout:
// The privileged about: content process cannot be disabled, as it
// causes various actors to break.
return {PRIVILEGEDABOUT_REMOTE_TYPE};
case IsolationBehavior::Extension:
if (ExtensionPolicyService::GetSingleton().UseRemoteExtensions()) {
return {EXTENSION_REMOTE_TYPE};
}
return {NOT_REMOTE_TYPE};
case IsolationBehavior::File:
if (StaticPrefs::browser_tabs_remote_separateFileUriProcess()) {
return {FILE_REMOTE_TYPE};
}
return {WEB_REMOTE_TYPE};
case IsolationBehavior::PrivilegedMozilla:
return {PRIVILEGEDMOZILLA_REMOTE_TYPE};
case IsolationBehavior::Parent:
return {NOT_REMOTE_TYPE};
case IsolationBehavior::Anywhere:
return {nsCString(aCurrentRemoteType)};
case IsolationBehavior::Inherit:
MOZ_DIAGNOSTIC_ASSERT(aParentWindow);
return {nsCString(aParentWindow->GetRemoteType())};
case IsolationBehavior::Error:
return Err(NS_ERROR_UNEXPECTED);
default:
MOZ_ASSERT_UNREACHABLE();
return Err(NS_ERROR_UNEXPECTED);
}
}
enum class WebProcessType {
Web,
WebIsolated,
@ -497,7 +566,7 @@ Result<NavigationIsolationOptions, nsresult> IsolationOptionsForNavigation(
// If we're loading a null principal, we can't easily make a process
// selection decision off ot it. Instead, we'll use our null principal's
// precursor principal to make process selection decisions.
bool principalIsSandboxed = false;
bool isNullPrincipalPrecursor = false;
nsCOMPtr<nsIPrincipal> resultOrPrecursor(resultPrincipal);
if (nsCOMPtr<nsIPrincipal> precursor =
resultOrPrecursor->GetPrecursorPrincipal()) {
@ -505,7 +574,7 @@ Result<NavigationIsolationOptions, nsresult> IsolationOptionsForNavigation(
("using null principal precursor origin %s",
OriginString(precursor).get()));
resultOrPrecursor = precursor;
principalIsSandboxed = true;
isNullPrincipalPrecursor = true;
}
NavigationIsolationOptions options;
@ -659,7 +728,7 @@ Result<NavigationIsolationOptions, nsresult> IsolationOptionsForNavigation(
// We don't want to load documents with sandboxed null principals, like
// `data:` URIs, in the parent process, even if they were created by a
// document which would otherwise be loaded in the parent process.
if (behavior == IsolationBehavior::Parent && principalIsSandboxed) {
if (behavior == IsolationBehavior::Parent && isNullPrincipalPrecursor) {
MOZ_LOG(gProcessIsolationLog, LogLevel::Debug,
("Ensuring sandboxed null-principal load doesn't occur in the "
"parent process"));
@ -701,51 +770,9 @@ Result<NavigationIsolationOptions, nsresult> IsolationOptionsForNavigation(
// If the load has any special remote type handling, do so at this point.
if (behavior != IsolationBehavior::WebContent) {
switch (behavior) {
case IsolationBehavior::ForceWebRemoteType:
options.mRemoteType = WEB_REMOTE_TYPE;
break;
case IsolationBehavior::PrivilegedAbout:
// The privileged about: content process cannot be disabled, as it
// causes various actors to break.
options.mRemoteType = PRIVILEGEDABOUT_REMOTE_TYPE;
break;
case IsolationBehavior::Extension:
if (ExtensionPolicyService::GetSingleton().UseRemoteExtensions()) {
options.mRemoteType = EXTENSION_REMOTE_TYPE;
} else {
options.mRemoteType = NOT_REMOTE_TYPE;
}
break;
case IsolationBehavior::File:
if (StaticPrefs::browser_tabs_remote_separateFileUriProcess()) {
options.mRemoteType = FILE_REMOTE_TYPE;
} else {
options.mRemoteType = WEB_REMOTE_TYPE;
}
break;
case IsolationBehavior::PrivilegedMozilla:
options.mRemoteType = PRIVILEGEDMOZILLA_REMOTE_TYPE;
break;
case IsolationBehavior::Parent:
options.mRemoteType = NOT_REMOTE_TYPE;
break;
case IsolationBehavior::Anywhere:
options.mRemoteType = aCurrentRemoteType;
break;
case IsolationBehavior::Inherit:
MOZ_DIAGNOSTIC_ASSERT(aParentWindow);
options.mRemoteType = aParentWindow->GetRemoteType();
break;
case IsolationBehavior::WebContent:
case IsolationBehavior::AboutReader:
MOZ_ASSERT_UNREACHABLE();
return Err(NS_ERROR_UNEXPECTED);
case IsolationBehavior::Error:
return Err(NS_ERROR_UNEXPECTED);
}
MOZ_TRY_VAR(
options.mRemoteType,
SpecialBehaviorRemoteType(behavior, aCurrentRemoteType, aParentWindow));
if (options.mRemoteType != aCurrentRemoteType &&
(options.mRemoteType.IsEmpty() || aCurrentRemoteType.IsEmpty())) {
@ -836,14 +863,10 @@ Result<NavigationIsolationOptions, nsresult> IsolationOptionsForNavigation(
}
}
nsAutoCString originSuffix;
OriginAttributes attrs = resultOrPrecursor->OriginAttributesRef();
attrs.StripAttributes(OriginAttributes::STRIP_FIRST_PARTY_DOMAIN |
OriginAttributes::STRIP_PARITION_KEY);
attrs.CreateSuffix(originSuffix);
nsAutoCString originSuffix = OriginSuffixForRemoteType(resultOrPrecursor);
WebProcessType webProcessType = WebProcessType::Web;
if (ShouldIsolateSite(resultOrPrecursor, aTopBC)) {
if (ShouldIsolateSite(resultOrPrecursor, aTopBC->UseRemoteSubframes())) {
webProcessType = WebProcessType::WebIsolated;
}
@ -887,6 +910,155 @@ Result<NavigationIsolationOptions, nsresult> IsolationOptionsForNavigation(
return options;
}
Result<WorkerIsolationOptions, nsresult> IsolationOptionsForWorker(
nsIPrincipal* aPrincipal, WorkerKind aWorkerKind,
const nsACString& aCurrentRemoteType, bool aUseRemoteSubframes) {
MOZ_LOG(gProcessIsolationLog, LogLevel::Verbose,
("IsolationOptionsForWorker principal:%s, kind:%s, current:%s",
OriginString(aPrincipal).get(), WorkerKindName(aWorkerKind),
PromiseFlatCString(aCurrentRemoteType).get()));
MOZ_ASSERT(NS_IsMainThread());
MOZ_RELEASE_ASSERT(
aWorkerKind == WorkerKindService || aWorkerKind == WorkerKindShared,
"Unexpected remote worker kind");
if (aWorkerKind == WorkerKindService &&
!aPrincipal->GetIsContentPrincipal()) {
MOZ_LOG(gProcessIsolationLog, LogLevel::Warning,
("Rejecting service worker with non-content principal"));
return Err(NS_ERROR_UNEXPECTED);
}
if (aPrincipal->GetIsExpandedPrincipal()) {
MOZ_LOG(gProcessIsolationLog, LogLevel::Warning,
("Rejecting remote worker with expanded principal"));
return Err(NS_ERROR_UNEXPECTED);
}
// In some cases, such as for null principals without precursors, we will want
// to load a shared worker in a process based on the current process. This is
// not done for service workers - process selection for those should function
// the same in all processes.
//
// We only allow the current remote type to be used if it is not a COOP+COEP
// remote type, in order to avoid loading a shared worker in one of these
// processes. Currently process selection for workers occurs before response
// headers are available, so we will never select to load a shared worker in a
// COOP+COEP content process.
nsCString preferredRemoteType = DEFAULT_REMOTE_TYPE;
if (aWorkerKind == WorkerKind::WorkerKindShared &&
!StringBeginsWith(aCurrentRemoteType,
WITH_COOP_COEP_REMOTE_TYPE_PREFIX)) {
preferredRemoteType = aCurrentRemoteType;
}
WorkerIsolationOptions options;
// If we're loading a null principal, we can't easily make a process
// selection decision off ot it. Instead, we'll use our null principal's
// precursor principal to make process selection decisions.
bool isNullPrincipalPrecursor = false;
nsCOMPtr<nsIPrincipal> resultOrPrecursor(aPrincipal);
if (nsCOMPtr<nsIPrincipal> precursor =
resultOrPrecursor->GetPrecursorPrincipal()) {
MOZ_LOG(gProcessIsolationLog, LogLevel::Verbose,
("using null principal precursor origin %s",
OriginString(precursor).get()));
resultOrPrecursor = precursor;
isNullPrincipalPrecursor = true;
}
IsolationBehavior behavior = IsolationBehavior::WebContent;
if (resultOrPrecursor->GetIsContentPrincipal()) {
nsCOMPtr<nsIURI> uri = resultOrPrecursor->GetURI();
behavior = IsolationBehaviorForURI(uri, /* aIsSubframe */ false,
/* aForChannelCreationURI */ false);
} else if (resultOrPrecursor->IsSystemPrincipal()) {
MOZ_ASSERT(aWorkerKind == WorkerKindShared);
// Allow system principal shared workers to load within either the
// parent process or privilegedabout process, depending on the
// responsible process.
if (preferredRemoteType == NOT_REMOTE_TYPE) {
MOZ_LOG(gProcessIsolationLog, LogLevel::Debug,
("Loading system principal shared worker in parent process"));
behavior = IsolationBehavior::Parent;
} else if (preferredRemoteType == PRIVILEGEDABOUT_REMOTE_TYPE) {
MOZ_LOG(gProcessIsolationLog, LogLevel::Debug,
("Loading system principal shared worker in privilegedabout "
"process"));
behavior = IsolationBehavior::PrivilegedAbout;
} else {
MOZ_LOG(gProcessIsolationLog, LogLevel::Warning,
("Cannot load system-principal shared worker in "
"non-privilegedabout content process"));
return Err(NS_ERROR_UNEXPECTED);
}
} else {
MOZ_ASSERT(resultOrPrecursor->GetIsNullPrincipal());
MOZ_ASSERT(aWorkerKind == WorkerKindShared);
if (preferredRemoteType == NOT_REMOTE_TYPE) {
MOZ_LOG(gProcessIsolationLog, LogLevel::Debug,
("Ensuring precursorless null principal shared worker loads in a "
"content process"));
behavior = IsolationBehavior::ForceWebRemoteType;
} else {
MOZ_LOG(gProcessIsolationLog, LogLevel::Debug,
("Loading precursorless null principal shared worker within "
"current remotetype: (%s)",
preferredRemoteType.get()));
behavior = IsolationBehavior::Anywhere;
}
}
if (behavior == IsolationBehavior::Parent && isNullPrincipalPrecursor) {
MOZ_LOG(gProcessIsolationLog, LogLevel::Debug,
("Ensuring sandboxed null-principal shared worker doesn't load in "
"the parent process"));
behavior = IsolationBehavior::ForceWebRemoteType;
}
if (behavior != IsolationBehavior::WebContent) {
MOZ_TRY_VAR(
options.mRemoteType,
SpecialBehaviorRemoteType(behavior, preferredRemoteType, nullptr));
MOZ_LOG(
gProcessIsolationLog, LogLevel::Debug,
("Selecting specific %s worker remote type (%s) due to a special case "
"isolation behavior %s",
WorkerKindName(aWorkerKind), options.mRemoteType.get(),
IsolationBehaviorName(behavior)));
return options;
}
// If we should be isolating this site, we can determine the correct fission
// remote type from the principal's site-origin.
if (ShouldIsolateSite(resultOrPrecursor, aUseRemoteSubframes)) {
nsAutoCString siteOriginNoSuffix;
MOZ_TRY(resultOrPrecursor->GetSiteOriginNoSuffix(siteOriginNoSuffix));
nsAutoCString originSuffix = OriginSuffixForRemoteType(resultOrPrecursor);
nsCString prefix = aWorkerKind == WorkerKindService
? SERVICEWORKER_REMOTE_TYPE
: FISSION_WEB_REMOTE_TYPE;
options.mRemoteType = prefix + "="_ns + siteOriginNoSuffix + originSuffix;
MOZ_LOG(gProcessIsolationLog, LogLevel::Debug,
("Isolating web content %s worker in remote type (%s)",
WorkerKindName(aWorkerKind), options.mRemoteType.get()));
} else {
options.mRemoteType = WEB_REMOTE_TYPE;
MOZ_LOG(gProcessIsolationLog, LogLevel::Debug,
("Loading web content %s worker in shared web remote type",
WorkerKindName(aWorkerKind)));
}
return options;
}
void AddHighValuePermission(nsIPrincipal* aResultPrincipal,
const nsACString& aPermissionType) {
RefPtr<PermissionManager> perms = PermissionManager::GetInstance();

View File

@ -12,6 +12,7 @@
#include "mozilla/Logging.h"
#include "mozilla/dom/RemoteType.h"
#include "mozilla/dom/SessionHistoryEntry.h"
#include "mozilla/dom/WorkerPrivate.h"
#include "nsString.h"
#include "nsIPrincipal.h"
#include "nsIURI.h"
@ -59,6 +60,22 @@ Result<NavigationIsolationOptions, nsresult> IsolationOptionsForNavigation(
const Maybe<uint64_t>& aChannelId,
const Maybe<nsCString>& aRemoteTypeOverride);
// WorkerIsolationOptions is passed back to the RemoteWorkerManager to store the
// destination process information for remote worker loads.
struct WorkerIsolationOptions {
nsCString mRemoteType;
};
/**
* Given a specific worker principal and kind, determines which process the
* remote worker load should complete in.
*
* This method is only intended for use with remote workers.
*/
Result<WorkerIsolationOptions, nsresult> IsolationOptionsForWorker(
nsIPrincipal* aPrincipal, WorkerKind aWorkerKind,
const nsACString& aCurrentRemoteType, bool aUseRemoteSubframes);
/**
* Adds a `highValue` permission to the permissions database, and make loads of
* that origin isolated.

View File

@ -0,0 +1,234 @@
/* -*- 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 "mozilla/BasePrincipal.h"
#include "mozilla/dom/ProcessIsolation.h"
#include "mozilla/dom/WorkerPrivate.h"
#include "mozilla/ExpandedPrincipal.h"
#include "mozilla/ExtensionPolicyService.h"
#include "mozilla/gtest/MozAssertions.h"
#include "mozilla/gtest/MozHelpers.h"
#include "mozilla/NullPrincipal.h"
#include "mozilla/SystemPrincipal.h"
#include "mozilla/StaticPrefs_browser.h"
using namespace mozilla;
using namespace mozilla::dom;
static nsCOMPtr<nsIPrincipal> MakeTestPrincipal(const char* aURI) {
nsCOMPtr<nsIURI> uri;
MOZ_ALWAYS_SUCCEEDS(NS_NewURI(getter_AddRefs(uri), aURI));
return BasePrincipal::CreateContentPrincipal(uri, {});
}
namespace {
struct RemoteTypes {
nsCString mIsolated;
nsCString mUnisolated;
};
struct WorkerExpectation {
nsCOMPtr<nsIPrincipal> mPrincipal;
WorkerKind mWorkerKind = WorkerKindShared;
Result<RemoteTypes, nsresult> mExpected = Err(NS_ERROR_FAILURE);
nsCString mCurrentRemoteType = "fakeRemoteType"_ns;
void Check(bool aUseRemoteSubframes) {
nsAutoCString origin;
ASSERT_NS_SUCCEEDED(mPrincipal->GetOrigin(origin));
nsPrintfCString describe(
"origin: %s, workerKind: %s, currentRemoteType: %s, "
"useRemoteSubframes: %d",
origin.get(), mWorkerKind == WorkerKindShared ? "shared" : "service",
mCurrentRemoteType.get(), aUseRemoteSubframes);
auto result = IsolationOptionsForWorker(
mPrincipal, mWorkerKind, mCurrentRemoteType, aUseRemoteSubframes);
ASSERT_EQ(result.isOk(), mExpected.isOk())
<< "Unexpected status (expected " << (mExpected.isOk() ? "ok" : "err")
<< ") for " << describe;
if (mExpected.isOk()) {
const nsCString& expected = aUseRemoteSubframes
? mExpected.inspect().mIsolated
: mExpected.inspect().mUnisolated;
ASSERT_EQ(result.inspect().mRemoteType, expected)
<< "Unexpected remote type (expected " << expected << ") for "
<< describe;
}
}
};
} // namespace
static nsCString WebIsolatedRemoteType(nsIPrincipal* aPrincipal) {
nsAutoCString origin;
MOZ_ALWAYS_SUCCEEDS(aPrincipal->GetSiteOrigin(origin));
return FISSION_WEB_REMOTE_TYPE + "="_ns + origin;
}
static nsCString CoopCoepRemoteType(nsIPrincipal* aPrincipal) {
nsAutoCString origin;
MOZ_ALWAYS_SUCCEEDS(aPrincipal->GetSiteOrigin(origin));
return WITH_COOP_COEP_REMOTE_TYPE + "="_ns + origin;
}
static nsCString ServiceWorkerIsolatedRemoteType(nsIPrincipal* aPrincipal) {
nsAutoCString origin;
MOZ_ALWAYS_SUCCEEDS(aPrincipal->GetSiteOrigin(origin));
return SERVICEWORKER_REMOTE_TYPE + "="_ns + origin;
}
TEST(ProcessIsolationTest, WorkerOptions)
{
// Forcibly enable the privileged mozilla content process for the duration of
// the test.
MOZ_ALWAYS_SUCCEEDS(Preferences::SetCString(
"browser.tabs.remote.separatedMozillaDomains", "addons.mozilla.org"));
MOZ_ALWAYS_SUCCEEDS(Preferences::SetBool(
"browser.tabs.remote.separatePrivilegedMozillaWebContentProcess", true));
auto cleanup = MakeScopeExit([&] {
MOZ_ALWAYS_SUCCEEDS(
Preferences::ClearUser("browser.tabs.remote.separatedMozillaDomains"));
MOZ_ALWAYS_SUCCEEDS(Preferences::ClearUser(
"browser.tabs.remote.separatePrivilegedMozillaWebContentProcess"));
});
nsCOMPtr<nsIPrincipal> systemPrincipal = SystemPrincipal::Get();
nsCOMPtr<nsIPrincipal> nullPrincipal =
NullPrincipal::CreateWithoutOriginAttributes();
nsCOMPtr<nsIPrincipal> secureComPrincipal =
MakeTestPrincipal("https://example.com");
nsCOMPtr<nsIPrincipal> secureOrgPrincipal =
MakeTestPrincipal("https://example.org");
nsCOMPtr<nsIPrincipal> insecureOrgPrincipal =
MakeTestPrincipal("http://example.org");
nsCOMPtr<nsIPrincipal> filePrincipal =
MakeTestPrincipal("file:///path/to/dir");
nsCOMPtr<nsIPrincipal> extensionPrincipal =
MakeTestPrincipal("moz-extension://fake-uuid");
nsCOMPtr<nsIPrincipal> privilegedMozillaPrincipal =
MakeTestPrincipal("https://addons.mozilla.org");
nsCOMPtr<nsIPrincipal> expandedPrincipal = ExpandedPrincipal::Create(
nsTArray{secureComPrincipal, extensionPrincipal}, {});
nsCOMPtr<nsIPrincipal> nullSecureComPrecursorPrincipal =
NullPrincipal::CreateWithInheritedAttributes(secureComPrincipal);
nsCString extensionRemoteType =
ExtensionPolicyService::GetSingleton().UseRemoteExtensions()
? EXTENSION_REMOTE_TYPE
: NOT_REMOTE_TYPE;
nsCString fileRemoteType =
StaticPrefs::browser_tabs_remote_separateFileUriProcess()
? FILE_REMOTE_TYPE
: WEB_REMOTE_TYPE;
WorkerExpectation expectations[] = {
// Neither service not shared workers can have expanded principals
{.mPrincipal = expandedPrincipal,
.mWorkerKind = WorkerKindService,
.mExpected = Err(NS_ERROR_UNEXPECTED)},
{.mPrincipal = expandedPrincipal,
.mWorkerKind = WorkerKindShared,
.mExpected = Err(NS_ERROR_UNEXPECTED)},
// Service workers cannot have system or null principals
{.mPrincipal = systemPrincipal,
.mWorkerKind = WorkerKindService,
.mExpected = Err(NS_ERROR_UNEXPECTED)},
{.mPrincipal = nullPrincipal,
.mWorkerKind = WorkerKindService,
.mExpected = Err(NS_ERROR_UNEXPECTED)},
{.mPrincipal = nullSecureComPrecursorPrincipal,
.mWorkerKind = WorkerKindService,
.mExpected = Err(NS_ERROR_UNEXPECTED)},
// Service workers with various content principals
{.mPrincipal = secureComPrincipal,
.mWorkerKind = WorkerKindService,
.mExpected =
RemoteTypes{ServiceWorkerIsolatedRemoteType(secureComPrincipal),
WEB_REMOTE_TYPE}},
{.mPrincipal = secureOrgPrincipal,
.mWorkerKind = WorkerKindService,
.mExpected =
RemoteTypes{ServiceWorkerIsolatedRemoteType(secureOrgPrincipal),
WEB_REMOTE_TYPE}},
{.mPrincipal = extensionPrincipal,
.mWorkerKind = WorkerKindService,
.mExpected = RemoteTypes{extensionRemoteType, extensionRemoteType}},
{.mPrincipal = privilegedMozillaPrincipal,
.mWorkerKind = WorkerKindService,
.mExpected = RemoteTypes{PRIVILEGEDMOZILLA_REMOTE_TYPE,
PRIVILEGEDMOZILLA_REMOTE_TYPE}},
// Shared Worker loaded from within a webCOOP+COEP remote type process,
// should load elsewhere.
{.mPrincipal = secureComPrincipal,
.mWorkerKind = WorkerKindShared,
.mExpected = RemoteTypes{WebIsolatedRemoteType(secureComPrincipal),
WEB_REMOTE_TYPE},
.mCurrentRemoteType = CoopCoepRemoteType(secureComPrincipal)},
// Even precursorless null principal should load elsewhere.
{.mPrincipal = nullPrincipal,
.mWorkerKind = WorkerKindShared,
.mExpected = RemoteTypes{WEB_REMOTE_TYPE, WEB_REMOTE_TYPE},
.mCurrentRemoteType = CoopCoepRemoteType(secureComPrincipal)},
// System principal shared workers can only load in the parent process or
// the privilegedabout remote type.
{.mPrincipal = systemPrincipal,
.mWorkerKind = WorkerKindShared,
.mExpected = RemoteTypes{NOT_REMOTE_TYPE, NOT_REMOTE_TYPE},
.mCurrentRemoteType = NOT_REMOTE_TYPE},
{.mPrincipal = systemPrincipal,
.mWorkerKind = WorkerKindShared,
.mExpected = RemoteTypes{PRIVILEGEDABOUT_REMOTE_TYPE,
PRIVILEGEDABOUT_REMOTE_TYPE},
.mCurrentRemoteType = PRIVILEGEDABOUT_REMOTE_TYPE},
{.mPrincipal = systemPrincipal,
.mWorkerKind = WorkerKindShared,
.mExpected = Err(NS_ERROR_UNEXPECTED)},
{.mPrincipal = systemPrincipal,
.mWorkerKind = WorkerKindShared,
.mExpected = Err(NS_ERROR_UNEXPECTED)},
// Content principals should load in the appropriate remote types,
// ignoring the current remote type.
{.mPrincipal = secureComPrincipal,
.mWorkerKind = WorkerKindShared,
.mExpected = RemoteTypes{WebIsolatedRemoteType(secureComPrincipal),
WEB_REMOTE_TYPE}},
{.mPrincipal = secureOrgPrincipal,
.mWorkerKind = WorkerKindShared,
.mExpected = RemoteTypes{WebIsolatedRemoteType(secureOrgPrincipal),
WEB_REMOTE_TYPE}},
{.mPrincipal = insecureOrgPrincipal,
.mWorkerKind = WorkerKindShared,
.mExpected = RemoteTypes{WebIsolatedRemoteType(insecureOrgPrincipal),
WEB_REMOTE_TYPE}},
{.mPrincipal = filePrincipal,
.mWorkerKind = WorkerKindShared,
.mExpected = RemoteTypes{fileRemoteType, fileRemoteType}},
{.mPrincipal = extensionPrincipal,
.mWorkerKind = WorkerKindShared,
.mExpected = RemoteTypes{extensionRemoteType, extensionRemoteType}},
{.mPrincipal = privilegedMozillaPrincipal,
.mWorkerKind = WorkerKindShared,
.mExpected = RemoteTypes{PRIVILEGEDMOZILLA_REMOTE_TYPE,
PRIVILEGEDMOZILLA_REMOTE_TYPE}},
{.mPrincipal = nullSecureComPrecursorPrincipal,
.mWorkerKind = WorkerKindShared,
.mExpected = RemoteTypes{WebIsolatedRemoteType(secureComPrincipal),
WEB_REMOTE_TYPE}},
};
for (auto& expectation : expectations) {
expectation.Check(true);
expectation.Check(false);
}
}

13
dom/ipc/gtest/moz.build Normal file
View File

@ -0,0 +1,13 @@
# -*- 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 += [
"ProcessIsolationTest.cpp",
]
include("/ipc/chromium/chromium-config.mozbuild")
FINAL_LIBRARY = "xul-gtest"

View File

@ -9,6 +9,8 @@ with Files("**"):
DIRS += ["jsactor"]
TEST_DIRS += ["gtest"]
XPIDL_SOURCES += [
"nsIDOMProcessChild.idl",
"nsIDOMProcessParent.idl",

View File

@ -11,20 +11,14 @@
#include "mozilla/SchedulerGroup.h"
#include "mozilla/ScopeExit.h"
#include "mozilla/dom/ContentChild.h" // ContentChild::GetSingleton
#include "mozilla/dom/ProcessIsolation.h"
#include "mozilla/dom/RemoteWorkerController.h"
#include "mozilla/dom/RemoteWorkerParent.h"
#include "mozilla/ipc/BackgroundParent.h"
#include "mozilla/ipc/BackgroundUtils.h"
#include "mozilla/ipc/PBackgroundParent.h"
#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
# include "mozilla/dom/DOMException.h"
# include "mozilla/CycleCollectedJSContext.h"
# include "mozilla/Sprintf.h" // SprintfLiteral
# include "nsIXPConnect.h" // nsIXPConnectWrappedJS
#endif
#include "mozilla/StaticPrefs_extensions.h"
#include "nsCOMPtr.h"
#include "nsIE10SUtils.h"
#include "nsImportModule.h"
#include "nsIXULRuntime.h"
#include "nsTArray.h"
@ -109,11 +103,11 @@ Result<nsCString, nsresult> RemoteWorkerManager::GetRemoteType(
MOZ_ASSERT_IF(aWorkerKind == WorkerKind::WorkerKindService,
aPrincipal->GetIsContentPrincipal());
nsCOMPtr<nsIE10SUtils> e10sUtils = do_ImportESModule(
"resource://gre/modules/E10SUtils.sys.mjs", "E10SUtils", fallible);
if (NS_WARN_IF(!e10sUtils)) {
LOG(("GetRemoteType Abort: could not import E10SUtils"));
return Err(NS_ERROR_DOM_ABORT_ERR);
// If E10S is fully disabled, there are no decisions to be made, and we need
// to finish the load in the parent process.
if (!BrowserTabsRemoteAutostart()) {
LOG(("GetRemoteType: Loading in parent process as e10s is disabled"));
return NOT_REMOTE_TYPE;
}
nsCString preferredRemoteType = DEFAULT_REMOTE_TYPE;
@ -127,115 +121,13 @@ Result<nsCString, nsresult> RemoteWorkerManager::GetRemoteType(
}
}
nsIE10SUtils::RemoteWorkerType workerType;
switch (aWorkerKind) {
case WorkerKind::WorkerKindService:
workerType = nsIE10SUtils::REMOTE_WORKER_TYPE_SERVICE;
break;
case WorkerKind::WorkerKindShared:
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;
nsresult rv = e10sUtils->GetRemoteTypeForWorkerPrincipal(
aPrincipal, workerType, isMultiprocess, isFission, preferredRemoteType,
remoteType);
if (NS_WARN_IF(NS_FAILED(rv))) {
LOG(
("GetRemoteType Abort: E10SUtils.getRemoteTypeForWorkerPrincipal "
"exception"));
#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
nsCString principalTypeOrScheme;
if (aPrincipal->IsSystemPrincipal()) {
principalTypeOrScheme = "system"_ns;
} else if (aPrincipal->GetIsExpandedPrincipal()) {
principalTypeOrScheme = "expanded"_ns;
} else if (aPrincipal->GetIsNullPrincipal()) {
principalTypeOrScheme = "null"_ns;
} else {
nsCOMPtr<nsIURI> uri = aPrincipal->GetURI();
nsresult rv2 = uri->GetScheme(principalTypeOrScheme);
if (NS_FAILED(rv2)) {
principalTypeOrScheme = "content"_ns;
}
}
nsCString processRemoteType = "parent"_ns;
if (auto* contentChild = ContentChild::GetSingleton()) {
// RemoteTypePrefix make sure that we are not going to include
// the full origin that may be part of the current remote type.
processRemoteType = RemoteTypePrefix(contentChild->GetRemoteType());
}
// Convert the error code into an error name.
nsAutoCString errorName;
GetErrorName(rv, errorName);
// Try to retrieve the line number from the exception.
nsAutoCString errorFilename("(unknown)"_ns);
uint32_t jsmErrorLineNumber = 0;
if (auto* context = CycleCollectedJSContext::Get()) {
if (RefPtr<Exception> exn = context->GetPendingException()) {
nsAutoString filename(u"(unknown)"_ns);
if (rv == NS_ERROR_XPC_JAVASCRIPT_ERROR_WITH_DETAILS) {
// When the failure is a Javascript Error, the line number retrieved
// from the Exception instance isn't going to be the E10SUtils.sys.mjs
// line that originated the failure, and so we fallback to retrieve it
// from the nsIScriptError.
nsCOMPtr<nsIScriptError> scriptError =
do_QueryInterface(exn->GetData());
if (scriptError) {
scriptError->GetLineNumber(&jsmErrorLineNumber);
scriptError->GetSourceName(filename);
}
} else {
nsCOMPtr<nsIXPConnectWrappedJS> wrapped =
do_QueryInterface(e10sUtils);
dom::AutoJSAPI jsapi;
if (jsapi.Init(wrapped->GetJSObjectGlobal())) {
auto* cx = jsapi.cx();
jsmErrorLineNumber = exn->LineNumber(cx);
exn->GetFilename(cx, filename);
}
}
errorFilename = NS_ConvertUTF16toUTF8(filename);
}
}
char buf[1024];
SprintfLiteral(
buf,
"workerType=%s, principal=%s, preferredRemoteType=%s, "
"processRemoteType=%s, errorName=%s, errorLocation=%s:%d",
aWorkerKind == WorkerKind::WorkerKindService ? "service" : "shared",
principalTypeOrScheme.get(),
PromiseFlatCString(RemoteTypePrefix(preferredRemoteType)).get(),
processRemoteType.get(), errorName.get(), errorFilename.get(),
jsmErrorLineNumber);
MOZ_CRASH_UNSAFE_PRINTF(
"E10SUtils.getRemoteTypeForWorkerPrincipal did throw: %s", buf);
#endif
auto result = IsolationOptionsForWorker(
aPrincipal, aWorkerKind, preferredRemoteType, FissionAutostart());
if (NS_WARN_IF(result.isErr())) {
LOG(("GetRemoteType Abort: IsolationOptionsForWorker failed"));
return Err(NS_ERROR_DOM_ABORT_ERR);
}
auto options = result.unwrap();
if (MOZ_LOG_TEST(gRemoteWorkerManagerLog, LogLevel::Verbose)) {
nsCString principalOrigin;
@ -245,10 +137,11 @@ Result<nsCString, nsresult> RemoteWorkerManager::GetRemoteType(
("GetRemoteType workerType=%s, principal=%s, "
"preferredRemoteType=%s, selectedRemoteType=%s",
aWorkerKind == WorkerKind::WorkerKindService ? "service" : "shared",
principalOrigin.get(), preferredRemoteType.get(), remoteType.get()));
principalOrigin.get(), preferredRemoteType.get(),
options.mRemoteType.get()));
}
return remoteType;
return options.mRemoteType;
}
// static

View File

@ -129,9 +129,7 @@ function validatedWebRemoteType(
aCurrentUri,
aResultPrincipal,
aRemoteSubframes,
aIsWorker = false,
aOriginAttributes = {},
aWorkerType = Ci.nsIE10SUtils.REMOTE_WORKER_TYPE_SHARED
aOriginAttributes = {}
) {
// To load into the Privileged Mozilla Content Process you must be https,
// and be an exact match or a subdomain of an allowlisted domain.
@ -151,15 +149,6 @@ 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 + "=")
@ -196,7 +185,7 @@ function validatedWebRemoteType(
// If the domain is allow listed 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 (!aIsWorker && sm.inFileURIAllowlist(aTargetUri)) {
if (sm.inFileURIAllowlist(aTargetUri)) {
return FILE_REMOTE_TYPE;
}
@ -233,12 +222,6 @@ function validatedWebRemoteType(
return aPreferredRemoteType;
}
if (
aIsWorker &&
aWorkerType === Ci.nsIE10SUtils.REMOTE_WORKER_TYPE_SERVICE
) {
return `${SERVICEWORKER_REMOTE_TYPE}=${targetPrincipal.siteOrigin}`;
}
return `${FISSION_WEB_REMOTE_TYPE}=${targetPrincipal.siteOrigin}`;
// else fall through and probably return WEB_REMOTE_TYPE
}
@ -254,12 +237,6 @@ 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,
];
export var E10SUtils = {
DEFAULT_REMOTE_TYPE,
NOT_REMOTE,
@ -420,9 +397,7 @@ export var E10SUtils = {
preferredRemoteType = DEFAULT_REMOTE_TYPE,
currentURI = null,
resultPrincipal = null,
isWorker = false,
originAttributes = {},
workerType = Ci.nsIE10SUtils.REMOTE_WORKER_TYPE_SHARED,
} = options;
if (!multiProcess) {
return NOT_REMOTE;
@ -550,15 +525,6 @@ export 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 (isWorker) {
throw Components.Exception(
"Unexpected remote worker with extension handled scheme",
Cr.NS_ERROR_UNEXPECTED
);
}
return WebExtensionPolicy.useRemoteWebExtensions
? EXTENSION_REMOTE_TYPE
: NOT_REMOTE;
@ -572,15 +538,6 @@ export 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 (isWorker) {
throw Components.Exception(
"Unexpected worker with a NestedURI",
Cr.NS_ERROR_UNEXPECTED
);
}
let innerURI = aURI.QueryInterface(Ci.nsINestedURI).innerURI;
return this.getRemoteTypeForURIObject(innerURI, options);
}
@ -596,91 +553,13 @@ export var E10SUtils = {
currentURI,
resultPrincipal,
remoteSubFrames,
isWorker,
originAttributes,
workerType
originAttributes
);
log.debug(` validatedWebRemoteType() returning: ${remoteType}`);
return remoteType;
}
},
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;
}
// 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.
if (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, {
multiProcess: aIsMultiProcess,
remoteSubFrames: aIsFission,
preferredRemoteType: aPreferredRemoteType,
resultPrincipal: aPrincipal,
originAttributes: aPrincipal.originAttributes,
isWorker: true,
workerType: aWorkerType,
});
}
// 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(

View File

@ -66,9 +66,6 @@ with Files("DeferredTask.sys.mjs"):
with Files("E10SUtils.sys.mjs"):
BUG_COMPONENT = ("Core", "Security: Process Sandboxing")
with Files("nsIE10SUtils.idl"):
BUG_COMPONENT = ("Core", "Security: Process Sandboxing")
with Files("Finder*.sys.mjs"):
BUG_COMPONENT = ("Toolkit", "Find Toolbar")
@ -301,7 +298,6 @@ DEFINES["TOPOBJDIR"] = TOPOBJDIR
XPIDL_SOURCES += [
"nsIBrowserWindowTracker.idl",
"nsIE10SUtils.idl",
"nsIRegion.idl",
]

View File

@ -1,46 +0,0 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* 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 "nsISupports.idl"
interface nsIPrincipal;
interface nsIURI;
/**
* C++ exposed interface for the `E10SUtils` object from the
* `resource://gre/modules/E10SUtils.sys.mjs` module.
*/
[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 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);
};

View File

@ -1,424 +0,0 @@
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
const { E10SUtils } = ChromeUtils.importESModule(
"resource://gre/modules/E10SUtils.sys.mjs"
);
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 {
EXTENSION_REMOTE_TYPE,
FILE_REMOTE_TYPE,
FISSION_WEB_REMOTE_TYPE,
NOT_REMOTE,
PRIVILEGEDABOUT_REMOTE_TYPE,
PRIVILEGEDMOZILLA_REMOTE_TYPE,
SERVICEWORKER_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 => webServiceWorker=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 = `${SERVICEWORKER_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 web coop+coep remote type
// we are going to select a web or fission remote type.
for (const [principal, preferredRemoteType] of [
[
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],
["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],
["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}`
);
}
});

View File

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