mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-19 08:15:31 +00:00
Bug 1642676 - Ensure remote workers are launched in a child process based on the expected remoteType. r=asuth,mixedpuppy
Differential Revision: https://phabricator.services.mozilla.com/D61708
This commit is contained in:
parent
b6fb32bf71
commit
634a0f15cf
@ -42,6 +42,7 @@
|
||||
#include "mozilla/dom/InternalRequest.h"
|
||||
#include "mozilla/dom/ReferrerInfo.h"
|
||||
#include "mozilla/dom/RemoteWorkerControllerChild.h"
|
||||
#include "mozilla/dom/RemoteWorkerManager.h" // RemoteWorkerManager::GetRemoteType
|
||||
#include "mozilla/dom/ServiceWorkerBinding.h"
|
||||
#include "mozilla/ipc/BackgroundChild.h"
|
||||
#include "mozilla/ipc/IPCStreamUtils.h"
|
||||
@ -186,6 +187,12 @@ nsresult ServiceWorkerPrivateImpl::Initialize() {
|
||||
return rv;
|
||||
}
|
||||
|
||||
auto remoteType = RemoteWorkerManager::GetRemoteType(
|
||||
principal, WorkerType::WorkerTypeService);
|
||||
if (NS_WARN_IF(remoteType.isErr())) {
|
||||
return remoteType.unwrapErr();
|
||||
}
|
||||
|
||||
mRemoteWorkerData = RemoteWorkerData(
|
||||
NS_ConvertUTF8toUTF16(mOuter->mInfo->ScriptSpec()), baseScriptURL,
|
||||
baseScriptURL, /* name */ VoidString(),
|
||||
@ -204,7 +211,8 @@ nsresult ServiceWorkerPrivateImpl::Initialize() {
|
||||
// already_AddRefed<>. Let's set it to null.
|
||||
/* referrerInfo */ nullptr,
|
||||
|
||||
storageAccess, std::move(serviceWorkerData), regInfo->AgentClusterId());
|
||||
storageAccess, std::move(serviceWorkerData), regInfo->AgentClusterId(),
|
||||
remoteType.unwrap());
|
||||
|
||||
mRemoteWorkerData.referrerInfo() = MakeAndAddRef<ReferrerInfo>();
|
||||
|
||||
|
@ -33,6 +33,7 @@
|
||||
#include "mozilla/dom/FetchEventOpProxyChild.h"
|
||||
#include "mozilla/dom/IndexedDatabaseManager.h"
|
||||
#include "mozilla/dom/MessagePort.h"
|
||||
#include "mozilla/dom/RemoteWorkerManager.h" // RemoteWorkerManager::IsRemoteTypeAllowed
|
||||
#include "mozilla/dom/RemoteWorkerTypes.h"
|
||||
#include "mozilla/dom/ServiceWorkerDescriptor.h"
|
||||
#include "mozilla/dom/ServiceWorkerInterceptController.h"
|
||||
@ -313,6 +314,14 @@ nsresult RemoteWorkerChild::ExecWorkerOnMainThread(RemoteWorkerData&& aData) {
|
||||
|
||||
auto scopeExit = MakeScopeExit([&] { TransitionStateToTerminated(); });
|
||||
|
||||
// Verify the the RemoteWorker should be really allowed to run in this
|
||||
// process, and fail if it shouldn't (This shouldn't normally happen,
|
||||
// unless the RemoteWorkerData has been tempered in the process it was
|
||||
// sent from).
|
||||
if (!RemoteWorkerManager::IsRemoteTypeAllowed(aData)) {
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
||||
auto principalOrErr = PrincipalInfoToPrincipal(aData.principalInfo());
|
||||
if (NS_WARN_IF(principalOrErr.isErr())) {
|
||||
return principalOrErr.unwrapErr();
|
||||
|
@ -10,6 +10,7 @@
|
||||
|
||||
#include "mozilla/SchedulerGroup.h"
|
||||
#include "mozilla/ScopeExit.h"
|
||||
#include "mozilla/dom/ContentChild.h" // ContentChild::GetSingleton
|
||||
#include "mozilla/dom/RemoteWorkerParent.h"
|
||||
#include "mozilla/ipc/BackgroundParent.h"
|
||||
#include "mozilla/ipc/BackgroundUtils.h"
|
||||
@ -37,18 +38,6 @@ bool IsServiceWorker(const RemoteWorkerData& aData) {
|
||||
OptionalServiceWorkerData::TServiceWorkerData;
|
||||
}
|
||||
|
||||
// Respecting COOP and COEP requires processing headers in the parent
|
||||
// process in order to choose an appropriate content process, but the
|
||||
// workers' ScriptLoader processes headers in content processes. An
|
||||
// intermediary step that provides security guarantees is to simply never
|
||||
// allow SharedWorkers and ServiceWorkers to exist in a COOP+COEP process.
|
||||
// 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
|
||||
bool IsServiceWorkerRemoteType(const nsAString& aRemoteType) {
|
||||
return IsWebRemoteType(aRemoteType) && !IsWebCoopCoepRemoteType(aRemoteType);
|
||||
}
|
||||
|
||||
void TransmitPermissionsAndBlobURLsForPrincipalInfo(
|
||||
ContentParent* aContentParent, const PrincipalInfo& aPrincipalInfo) {
|
||||
AssertIsOnMainThread();
|
||||
@ -70,6 +59,140 @@ void TransmitPermissionsAndBlobURLsForPrincipalInfo(
|
||||
|
||||
} // namespace
|
||||
|
||||
// static
|
||||
bool RemoteWorkerManager::MatchRemoteType(const nsAString& processRemoteType,
|
||||
const nsAString& workerRemoteType) {
|
||||
if (processRemoteType.Equals(workerRemoteType)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Respecting COOP and COEP requires processing headers in the parent
|
||||
// process in order to choose an appropriate content process, but the
|
||||
// workers' ScriptLoader processes headers in content processes. An
|
||||
// intermediary step that provides security guarantees is to simply never
|
||||
// allow SharedWorkers and ServiceWorkers to exist in a COOP+COEP process.
|
||||
// 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;
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// static
|
||||
Result<nsString, 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);
|
||||
}
|
||||
|
||||
nsString remoteType;
|
||||
|
||||
// If Gecko is running in single process mode, there is no child process
|
||||
// to select, return without assigning any remoteType.
|
||||
if (!BrowserTabsRemoteAutostart()) {
|
||||
return remoteType;
|
||||
}
|
||||
|
||||
auto* contentChild = ContentChild::GetSingleton();
|
||||
|
||||
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.Assign(NS_LITERAL_STRING(EXTENSION_REMOTE_TYPE));
|
||||
} else if (separatePrivilegedMozilla) {
|
||||
bool isPrivilegedMozilla = false;
|
||||
aPrincipal->IsURIInPrefList("browser.tabs.remote.separatedMozillaDomains",
|
||||
&isPrivilegedMozilla);
|
||||
|
||||
if (isPrivilegedMozilla) {
|
||||
remoteType.Assign(NS_LITERAL_STRING(PRIVILEGEDMOZILLA_REMOTE_TYPE));
|
||||
} else {
|
||||
remoteType.Assign(aWorkerType == WorkerType::WorkerTypeShared &&
|
||||
contentChild
|
||||
? contentChild->GetRemoteType()
|
||||
: NS_LITERAL_STRING(DEFAULT_REMOTE_TYPE));
|
||||
}
|
||||
} else {
|
||||
remoteType.Assign(NS_LITERAL_STRING(DEFAULT_REMOTE_TYPE));
|
||||
}
|
||||
|
||||
return remoteType;
|
||||
}
|
||||
|
||||
// static
|
||||
bool RemoteWorkerManager::IsRemoteTypeAllowed(const RemoteWorkerData& aData) {
|
||||
AssertIsOnMainThread();
|
||||
|
||||
// If Gecko is running in single process mode, there is no child process
|
||||
// to select and we have to just consider it valid (if it should haven't
|
||||
// been launched it should have been already prevented before reaching
|
||||
// a RemoteWorkerChild instance).
|
||||
if (!BrowserTabsRemoteAutostart()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const auto& principalInfo = aData.principalInfo();
|
||||
|
||||
auto* contentChild = ContentChild::GetSingleton();
|
||||
if (!contentChild) {
|
||||
// If e10s isn't disabled, only workers related to the system principal
|
||||
// should be allowed to run in the parent process.
|
||||
return principalInfo.type() == PrincipalInfo::TSystemPrincipalInfo;
|
||||
}
|
||||
|
||||
auto principalOrErr = PrincipalInfoToPrincipal(principalInfo);
|
||||
if (NS_WARN_IF(principalOrErr.isErr())) {
|
||||
return false;
|
||||
}
|
||||
nsCOMPtr<nsIPrincipal> principal = principalOrErr.unwrap();
|
||||
|
||||
// Recompute the remoteType based on the principal, to double-check that it
|
||||
// has not been tempered to select a different child process than the one
|
||||
// expected.
|
||||
bool isServiceWorker = aData.serviceWorkerData().type() ==
|
||||
OptionalServiceWorkerData::TServiceWorkerData;
|
||||
auto remoteType = GetRemoteType(
|
||||
principal, isServiceWorker ? WorkerTypeService : WorkerTypeShared);
|
||||
if (NS_WARN_IF(remoteType.isErr())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return MatchRemoteType(remoteType.unwrap(), contentChild->GetRemoteType());
|
||||
}
|
||||
|
||||
/* static */
|
||||
already_AddRefed<RemoteWorkerManager> RemoteWorkerManager::GetOrCreate() {
|
||||
AssertIsInMainProcess();
|
||||
@ -111,27 +234,24 @@ void RemoteWorkerManager::RegisterActor(RemoteWorkerServiceParent* aActor) {
|
||||
MOZ_ASSERT(!mChildActors.Contains(aActor));
|
||||
mChildActors.AppendElement(aActor);
|
||||
|
||||
if (!mPendings.IsEmpty()) {
|
||||
const auto& remoteType = GetRemoteTypeForActor(aActor);
|
||||
nsTArray<Pending> unlaunched;
|
||||
|
||||
RefPtr<ContentParent> contentParent =
|
||||
BackgroundParent::GetContentParent(aActor->Manager());
|
||||
auto scopeExit =
|
||||
MakeScopeExit([&] { NS_ReleaseOnMainThread(contentParent.forget()); });
|
||||
const auto& remoteType = contentParent->GetRemoteType();
|
||||
|
||||
if (!mPendings.IsEmpty()) {
|
||||
// Flush pending launching.
|
||||
for (Pending& p : mPendings) {
|
||||
if (p.mController->IsTerminated()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (IsServiceWorker(p.mData) && !IsServiceWorkerRemoteType(remoteType)) {
|
||||
const auto& workerRemoteType = p.mData.remoteType();
|
||||
|
||||
if (MatchRemoteType(remoteType, workerRemoteType)) {
|
||||
LaunchInternal(p.mController, aActor, p.mData);
|
||||
} else {
|
||||
unlaunched.AppendElement(std::move(p));
|
||||
continue;
|
||||
}
|
||||
|
||||
LaunchInternal(p.mController, aActor, p.mData);
|
||||
}
|
||||
|
||||
std::swap(mPendings, unlaunched);
|
||||
@ -227,6 +347,28 @@ void RemoteWorkerManager::AsyncCreationFailed(
|
||||
NS_DispatchToCurrentThread(r.forget());
|
||||
}
|
||||
|
||||
/* static */
|
||||
nsString RemoteWorkerManager::GetRemoteTypeForActor(
|
||||
const RemoteWorkerServiceParent* aActor) {
|
||||
AssertIsInMainProcess();
|
||||
AssertIsOnBackgroundThread();
|
||||
|
||||
MOZ_ASSERT(aActor);
|
||||
|
||||
RefPtr<ContentParent> contentParent =
|
||||
BackgroundParent::GetContentParent(aActor->Manager());
|
||||
auto scopeExit =
|
||||
MakeScopeExit([&] { NS_ReleaseOnMainThread(contentParent.forget()); });
|
||||
|
||||
if (NS_WARN_IF(!contentParent)) {
|
||||
return EmptyString();
|
||||
}
|
||||
|
||||
nsString aRemoteType(contentParent->GetRemoteType());
|
||||
|
||||
return aRemoteType;
|
||||
}
|
||||
|
||||
template <typename Callback>
|
||||
void RemoteWorkerManager::ForEachActor(Callback&& aCallback) const {
|
||||
AssertIsOnBackgroundThread();
|
||||
@ -290,11 +432,13 @@ RemoteWorkerManager::SelectTargetActorForServiceWorker(
|
||||
|
||||
RemoteWorkerServiceParent* actor = nullptr;
|
||||
|
||||
const auto& workerRemoteType = aData.remoteType();
|
||||
|
||||
ForEachActor([&](RemoteWorkerServiceParent* aActor,
|
||||
RefPtr<ContentParent>&& aContentParent) {
|
||||
const auto& remoteType = aContentParent->GetRemoteType();
|
||||
|
||||
if (IsServiceWorkerRemoteType(remoteType)) {
|
||||
if (MatchRemoteType(remoteType, workerRemoteType)) {
|
||||
auto lock = aContentParent->mRemoteWorkerActorData.Lock();
|
||||
|
||||
if (lock->mCount || !lock->mShutdownStarted) {
|
||||
@ -327,7 +471,7 @@ RemoteWorkerManager::SelectTargetActorForServiceWorker(
|
||||
|
||||
RemoteWorkerServiceParent*
|
||||
RemoteWorkerManager::SelectTargetActorForSharedWorker(
|
||||
base::ProcessId aProcessId) const {
|
||||
base::ProcessId aProcessId, const RemoteWorkerData& aData) const {
|
||||
AssertIsOnBackgroundThread();
|
||||
MOZ_ASSERT(!mChildActors.IsEmpty());
|
||||
|
||||
@ -335,7 +479,10 @@ RemoteWorkerManager::SelectTargetActorForSharedWorker(
|
||||
|
||||
ForEachActor([&](RemoteWorkerServiceParent* aActor,
|
||||
RefPtr<ContentParent>&& aContentParent) {
|
||||
if (IsWebCoopCoepRemoteType(aContentParent->GetRemoteType())) {
|
||||
bool matchRemoteType =
|
||||
MatchRemoteType(aContentParent->GetRemoteType(), aData.remoteType());
|
||||
|
||||
if (!matchRemoteType) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -378,8 +525,9 @@ RemoteWorkerServiceParent* RemoteWorkerManager::SelectTargetActor(
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return IsServiceWorker(aData) ? SelectTargetActorForServiceWorker(aData)
|
||||
: SelectTargetActorForSharedWorker(aProcessId);
|
||||
return IsServiceWorker(aData)
|
||||
? SelectTargetActorForServiceWorker(aData)
|
||||
: SelectTargetActorForSharedWorker(aProcessId, aData);
|
||||
}
|
||||
|
||||
void RemoteWorkerManager::LaunchNewContentProcess(
|
||||
@ -401,7 +549,8 @@ void RemoteWorkerManager::LaunchNewContentProcess(
|
||||
principalInfo = aData.principalInfo(),
|
||||
bgEventTarget = std::move(bgEventTarget),
|
||||
self = RefPtr<RemoteWorkerManager>(this)](
|
||||
const CallbackParamType& aValue) mutable {
|
||||
const CallbackParamType& aValue,
|
||||
const nsString& remoteType) mutable {
|
||||
if (aValue.IsResolve()) {
|
||||
if (isServiceWorker) {
|
||||
TransmitPermissionsAndBlobURLsForPrincipalInfo(aValue.ResolveValue(),
|
||||
@ -413,12 +562,21 @@ void RemoteWorkerManager::LaunchNewContentProcess(
|
||||
NS_ProxyRelease(__func__, bgEventTarget, self.forget());
|
||||
} else {
|
||||
// The "real" failure callback.
|
||||
nsCOMPtr<nsIRunnable> r =
|
||||
NS_NewRunnableFunction(__func__, [self = std::move(self)] {
|
||||
nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(
|
||||
__func__, [self = std::move(self), remoteType] {
|
||||
nsTArray<Pending> uncancelled;
|
||||
auto pendings = std::move(self->mPendings);
|
||||
|
||||
for (const auto& pending : pendings) {
|
||||
const auto& workerRemoteType = pending.mData.remoteType();
|
||||
if (self->MatchRemoteType(remoteType, workerRemoteType)) {
|
||||
pending.mController->CreationFailed();
|
||||
} else {
|
||||
uncancelled.AppendElement(pending);
|
||||
}
|
||||
}
|
||||
|
||||
std::swap(self->mPendings, uncancelled);
|
||||
});
|
||||
|
||||
bgEventTarget->Dispatch(r.forget(), NS_DISPATCH_NORMAL);
|
||||
@ -426,14 +584,19 @@ void RemoteWorkerManager::LaunchNewContentProcess(
|
||||
};
|
||||
|
||||
nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(
|
||||
__func__, [callback = std::move(processLaunchCallback)]() mutable {
|
||||
__func__, [callback = std::move(processLaunchCallback),
|
||||
workerRemoteType = aData.remoteType()]() mutable {
|
||||
auto remoteType = workerRemoteType.IsEmpty()
|
||||
? NS_LITERAL_STRING(DEFAULT_REMOTE_TYPE)
|
||||
: workerRemoteType;
|
||||
|
||||
ContentParent::GetNewOrUsedBrowserProcessAsync(
|
||||
/* aFrameElement = */ nullptr,
|
||||
/* aRemoteType = */ NS_LITERAL_STRING(DEFAULT_REMOTE_TYPE))
|
||||
/* aRemoteType = */ remoteType)
|
||||
->Then(GetCurrentSerialEventTarget(), __func__,
|
||||
[callback = std::move(callback)](
|
||||
const CallbackParamType& aValue) mutable {
|
||||
callback(aValue);
|
||||
[callback = std::move(callback),
|
||||
remoteType](const CallbackParamType& aValue) mutable {
|
||||
callback(aValue, remoteType);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -11,6 +11,7 @@
|
||||
#include "mozilla/RefPtr.h"
|
||||
#include "mozilla/dom/ContentParent.h"
|
||||
#include "mozilla/dom/RemoteWorkerTypes.h"
|
||||
#include "mozilla/dom/WorkerPrivate.h" // WorkerType enum
|
||||
#include "nsISupportsImpl.h"
|
||||
#include "nsTArray.h"
|
||||
|
||||
@ -34,6 +35,22 @@ class RemoteWorkerManager final {
|
||||
void Launch(RemoteWorkerController* aController,
|
||||
const RemoteWorkerData& aData, base::ProcessId aProcessId);
|
||||
|
||||
static bool MatchRemoteType(const nsAString& processRemoteType,
|
||||
const nsAString& workerRemoteType);
|
||||
|
||||
/**
|
||||
* Get the child process RemoteType where a RemoteWorker should be
|
||||
* launched.
|
||||
*/
|
||||
static Result<nsString, nsresult> GetRemoteType(
|
||||
const nsCOMPtr<nsIPrincipal>& aPrincipal, WorkerType aWorkerType);
|
||||
|
||||
/**
|
||||
* Verify if a remote worker should be allowed to run in the current
|
||||
* child process remoteType.
|
||||
*/
|
||||
static bool IsRemoteTypeAllowed(const RemoteWorkerData& aData);
|
||||
|
||||
private:
|
||||
RemoteWorkerManager();
|
||||
~RemoteWorkerManager();
|
||||
@ -45,7 +62,7 @@ class RemoteWorkerManager final {
|
||||
const RemoteWorkerData& aData) const;
|
||||
|
||||
RemoteWorkerServiceParent* SelectTargetActorForSharedWorker(
|
||||
base::ProcessId aProcessId) const;
|
||||
base::ProcessId aProcessId, const RemoteWorkerData& aData) const;
|
||||
|
||||
void LaunchInternal(RemoteWorkerController* aController,
|
||||
RemoteWorkerServiceParent* aTargetActor,
|
||||
@ -56,6 +73,9 @@ class RemoteWorkerManager final {
|
||||
|
||||
void AsyncCreationFailed(RemoteWorkerController* aController);
|
||||
|
||||
static nsString GetRemoteTypeForActor(
|
||||
const RemoteWorkerServiceParent* aActor);
|
||||
|
||||
// Iterate through all RemoteWorkerServiceParent actors, starting from a
|
||||
// random index (as if iterating through a circular array).
|
||||
//
|
||||
|
@ -70,6 +70,9 @@ struct RemoteWorkerData
|
||||
OptionalServiceWorkerData serviceWorkerData;
|
||||
|
||||
nsID agentClusterId;
|
||||
|
||||
// Child process remote type where the worker should only run on.
|
||||
nsString remoteType;
|
||||
};
|
||||
|
||||
// ErrorData/ErrorDataNote correspond to WorkerErrorReport/WorkerErrorNote
|
||||
|
65
dom/workers/remoteworkers/gtest/TestMatchRemoteType.cpp
Normal file
65
dom/workers/remoteworkers/gtest/TestMatchRemoteType.cpp
Normal file
@ -0,0 +1,65 @@
|
||||
/* -*- 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 nsString processRemoteType;
|
||||
const nsString workerRemoteType;
|
||||
const bool shouldMatch;
|
||||
} tests[] = {
|
||||
// Test exact matches between process and worker remote types.
|
||||
{NS_LITERAL_STRING(DEFAULT_REMOTE_TYPE),
|
||||
NS_LITERAL_STRING(DEFAULT_REMOTE_TYPE), true},
|
||||
{NS_LITERAL_STRING(EXTENSION_REMOTE_TYPE),
|
||||
NS_LITERAL_STRING(EXTENSION_REMOTE_TYPE), true},
|
||||
{NS_LITERAL_STRING(PRIVILEGEDMOZILLA_REMOTE_TYPE),
|
||||
NS_LITERAL_STRING(PRIVILEGEDMOZILLA_REMOTE_TYPE), true},
|
||||
|
||||
// Test workers with remoteType "web" not launched on non-web or coop+coep
|
||||
// processes.
|
||||
{NS_LITERAL_STRING(PRIVILEGEDMOZILLA_REMOTE_TYPE),
|
||||
NS_LITERAL_STRING(DEFAULT_REMOTE_TYPE), false},
|
||||
{NS_LITERAL_STRING(PRIVILEGEDABOUT_REMOTE_TYPE),
|
||||
NS_LITERAL_STRING(DEFAULT_REMOTE_TYPE), false},
|
||||
{NS_LITERAL_STRING(EXTENSION_REMOTE_TYPE),
|
||||
NS_LITERAL_STRING(DEFAULT_REMOTE_TYPE), false},
|
||||
{NS_LITERAL_STRING(FILE_REMOTE_TYPE),
|
||||
NS_LITERAL_STRING(DEFAULT_REMOTE_TYPE), false},
|
||||
{NS_LITERAL_STRING(WITH_COOP_COEP_REMOTE_TYPE_PREFIX),
|
||||
NS_LITERAL_STRING(DEFAULT_REMOTE_TYPE), false},
|
||||
|
||||
// Test workers with remoteType "web" launched in web child processes.
|
||||
{NS_LITERAL_STRING(LARGE_ALLOCATION_REMOTE_TYPE),
|
||||
NS_LITERAL_STRING(DEFAULT_REMOTE_TYPE), true},
|
||||
{NS_LITERAL_STRING(FISSION_WEB_REMOTE_TYPE),
|
||||
NS_LITERAL_STRING(DEFAULT_REMOTE_TYPE), true},
|
||||
|
||||
// Test empty remoteType default to "web" remoteType.
|
||||
{NS_LITERAL_STRING(DEFAULT_REMOTE_TYPE), EmptyString(), true},
|
||||
{NS_LITERAL_STRING(WITH_COOP_COEP_REMOTE_TYPE_PREFIX), EmptyString(),
|
||||
false},
|
||||
{NS_LITERAL_STRING(PRIVILEGEDMOZILLA_REMOTE_TYPE), EmptyString(), false},
|
||||
{NS_LITERAL_STRING(EXTENSION_REMOTE_TYPE), EmptyString(), false},
|
||||
};
|
||||
|
||||
for (const auto& test : tests) {
|
||||
auto message =
|
||||
nsPrintfCString(R"(MatchRemoteType("%s", "%s") should return %s)",
|
||||
NS_ConvertUTF16toUTF8(test.processRemoteType).get(),
|
||||
NS_ConvertUTF16toUTF8(test.workerRemoteType).get(),
|
||||
test.shouldMatch ? "true" : "false");
|
||||
ASSERT_EQ(RemoteWorkerManager::MatchRemoteType(test.processRemoteType,
|
||||
test.workerRemoteType),
|
||||
test.shouldMatch)
|
||||
<< message;
|
||||
}
|
||||
}
|
13
dom/workers/remoteworkers/gtest/moz.build
Normal file
13
dom/workers/remoteworkers/gtest/moz.build
Normal 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 = [
|
||||
'TestMatchRemoteType.cpp',
|
||||
]
|
||||
|
||||
include('/ipc/chromium/chromium-config.mozbuild')
|
||||
|
||||
FINAL_LIBRARY = 'xul-gtest'
|
@ -9,6 +9,7 @@ EXPORTS.mozilla.dom += [
|
||||
'RemoteWorkerController.h',
|
||||
'RemoteWorkerControllerChild.h',
|
||||
'RemoteWorkerControllerParent.h',
|
||||
'RemoteWorkerManager.h',
|
||||
'RemoteWorkerParent.h',
|
||||
'RemoteWorkerService.h',
|
||||
'RemoteWorkerServiceChild.h',
|
||||
@ -39,6 +40,8 @@ IPDL_SOURCES += [
|
||||
'RemoteWorkerTypes.ipdlh',
|
||||
]
|
||||
|
||||
TEST_DIRS += ['gtest']
|
||||
|
||||
include('/ipc/chromium/chromium-config.mozbuild')
|
||||
|
||||
FINAL_LIBRARY = 'xul'
|
||||
|
@ -13,6 +13,7 @@
|
||||
#include "mozilla/dom/MessageChannel.h"
|
||||
#include "mozilla/dom/MessagePort.h"
|
||||
#include "mozilla/dom/PMessagePort.h"
|
||||
#include "mozilla/dom/RemoteWorkerManager.h" // RemoteWorkerManager::GetRemoteType
|
||||
#include "mozilla/dom/RemoteWorkerTypes.h"
|
||||
#include "mozilla/dom/SharedWorkerBinding.h"
|
||||
#include "mozilla/dom/SharedWorkerChild.h"
|
||||
@ -198,13 +199,21 @@ already_AddRefed<SharedWorker> SharedWorker::Constructor(
|
||||
MOZ_ASSERT(loadInfo.mCookieJarSettings);
|
||||
net::CookieJarSettings::Cast(loadInfo.mCookieJarSettings)->Serialize(cjsData);
|
||||
|
||||
auto remoteType = RemoteWorkerManager::GetRemoteType(
|
||||
loadInfo.mPrincipal, WorkerType::WorkerTypeShared);
|
||||
if (NS_WARN_IF(remoteType.isErr())) {
|
||||
aRv.Throw(remoteType.unwrapErr());
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
RemoteWorkerData remoteWorkerData(
|
||||
nsString(aScriptURL), baseURL, resolvedScriptURL, name,
|
||||
loadingPrincipalInfo, principalInfo, partitionedPrincipalInfo,
|
||||
loadInfo.mUseRegularPrincipal,
|
||||
loadInfo.mHasStorageAccessPermissionGranted, cjsData, loadInfo.mDomain,
|
||||
isSecureContext, ipcClientInfo, loadInfo.mReferrerInfo, storageAllowed,
|
||||
void_t() /* OptionalServiceWorkerData */, agentClusterId);
|
||||
void_t() /* OptionalServiceWorkerData */, agentClusterId,
|
||||
remoteType.unwrap());
|
||||
|
||||
PSharedWorkerChild* pActor = actorChild->SendPSharedWorkerConstructor(
|
||||
remoteWorkerData, loadInfo.mWindowID, portIdentifier.release());
|
||||
|
@ -18,3 +18,7 @@ support-files =
|
||||
file_use_counter_worker.js
|
||||
file_use_counter_shared_worker.js
|
||||
file_use_counter_service_worker.js
|
||||
[browser_privilegedmozilla_remoteworker.js]
|
||||
support-files =
|
||||
file_service_worker.js
|
||||
file_service_worker_container.html
|
||||
|
117
dom/workers/test/browser_privilegedmozilla_remoteworker.js
Normal file
117
dom/workers/test/browser_privilegedmozilla_remoteworker.js
Normal file
@ -0,0 +1,117 @@
|
||||
add_task(async function setup() {
|
||||
await SpecialPowers.pushPrefEnv({
|
||||
set: [
|
||||
["browser.tabs.remote.separatePrivilegedMozillaWebContentProcess", true],
|
||||
["browser.tabs.remote.separatedMozillaDomains", "example.org"],
|
||||
["dom.ipc.processCount.web", 1],
|
||||
["dom.ipc.processCount.privilegedmozilla", 1],
|
||||
["dom.serviceWorkers.enabled", true],
|
||||
["dom.serviceWorkers.testing.enabled", true],
|
||||
["dom.serviceworkers.parent_intercept", true],
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
// This test attempts to verify proper placement of spawned remoteworkers
|
||||
// by spawning them and then verifying that they were spawned in the expected
|
||||
// process by way of nsIWorkerDebuggerManager enumeration.
|
||||
//
|
||||
// Unfortunately, there's no other way to introspect where a worker global was
|
||||
// spawned at this time. (devtools just ends up enumerating all workers in all
|
||||
// processes and we don't want to depend on devtools in this test).
|
||||
//
|
||||
// As a result, this test currently only tests situations where it's known that
|
||||
// a remote worker will be spawned in the same process that is initiating its
|
||||
// spawning.
|
||||
//
|
||||
// This should be enhanced in the future.
|
||||
add_task(async function test_serviceworker() {
|
||||
const basePath = "browser/dom/workers/test";
|
||||
const pagePath = `${basePath}/file_service_worker_container.html`;
|
||||
const scriptPath = `${basePath}/file_service_worker.js`;
|
||||
|
||||
Services.ppmm.releaseCachedProcesses();
|
||||
|
||||
async function runWorkerInProcess() {
|
||||
function getActiveWorkerURLs() {
|
||||
const wdm = Cc[
|
||||
"@mozilla.org/dom/workers/workerdebuggermanager;1"
|
||||
].getService(Ci.nsIWorkerDebuggerManager);
|
||||
|
||||
const workerDebuggerUrls = Array.from(
|
||||
wdm.getWorkerDebuggerEnumerator()
|
||||
).map(wd => {
|
||||
return wd.url;
|
||||
});
|
||||
|
||||
return workerDebuggerUrls;
|
||||
}
|
||||
|
||||
return new Promise(resolve => {
|
||||
content.navigator.serviceWorker.ready.then(({ active }) => {
|
||||
const { port1, port2 } = new content.MessageChannel();
|
||||
active.postMessage("webpage->serviceworker", [port2]);
|
||||
port1.onmessage = evt => {
|
||||
resolve({
|
||||
msg: evt.data,
|
||||
workerUrls: getActiveWorkerURLs(),
|
||||
});
|
||||
};
|
||||
});
|
||||
}).then(async res => {
|
||||
// Unregister the service worker used in this test.
|
||||
const registration = await content.navigator.serviceWorker.ready;
|
||||
await registration.unregister();
|
||||
return res;
|
||||
});
|
||||
}
|
||||
|
||||
const testCaseList = [
|
||||
// TODO: find a reasonable way to test the non-privileged scenario
|
||||
// (because more than 1 process is usually available and the worker
|
||||
// can be launched in a different one from the one where the tab
|
||||
// is running).
|
||||
/*{
|
||||
remoteType: "web",
|
||||
hostname: "example.com",
|
||||
},*/
|
||||
{
|
||||
remoteType: "privilegedmozilla",
|
||||
hostname: `example.org`,
|
||||
},
|
||||
];
|
||||
|
||||
for (const testCase of testCaseList) {
|
||||
const { remoteType, hostname } = testCase;
|
||||
|
||||
info(`Test remote serviceworkers launch selects a ${remoteType} process`);
|
||||
|
||||
const tab = await BrowserTestUtils.openNewForegroundTab({
|
||||
gBrowser,
|
||||
url: `https://${hostname}/${pagePath}`,
|
||||
});
|
||||
|
||||
is(
|
||||
tab.linkedBrowser.remoteType,
|
||||
remoteType,
|
||||
`Got the expected remoteType for ${hostname} tab`
|
||||
);
|
||||
|
||||
const results = await SpecialPowers.spawn(
|
||||
tab.linkedBrowser,
|
||||
[],
|
||||
runWorkerInProcess
|
||||
);
|
||||
|
||||
Assert.deepEqual(
|
||||
results,
|
||||
{
|
||||
msg: "serviceworker-reply",
|
||||
workerUrls: [`https://${hostname}/${scriptPath}`],
|
||||
},
|
||||
`Got the expected results for ${hostname} tab`
|
||||
);
|
||||
|
||||
BrowserTestUtils.removeTab(tab);
|
||||
}
|
||||
});
|
3
dom/workers/test/file_service_worker.js
Normal file
3
dom/workers/test/file_service_worker.js
Normal file
@ -0,0 +1,3 @@
|
||||
self.onmessage = evt => {
|
||||
evt.ports[0].postMessage("serviceworker-reply");
|
||||
};
|
15
dom/workers/test/file_service_worker_container.html
Normal file
15
dom/workers/test/file_service_worker_container.html
Normal file
@ -0,0 +1,15 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<script>
|
||||
(async function () {
|
||||
await navigator.serviceWorker.register("file_service_worker.js");
|
||||
await navigator.serviceWorker.ready;
|
||||
})();
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
Service Worker Container
|
||||
</body>
|
||||
</html>
|
@ -0,0 +1,57 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
// This test attemps to verify that:
|
||||
// - SharedWorkers can be created and successfully spawned by web extensions
|
||||
// when web-extensions run in their own child process.
|
||||
// - SharedWorkers cannot be created by web extensions when web extensions
|
||||
// are being run in the main process (because SharedWorkers are only
|
||||
// allowed to be spawned in the parent process if they have a system principal).
|
||||
add_task(async function test_spawn_shared_worker() {
|
||||
const background = WebExtensionPolicy.useRemoteWebExtensions
|
||||
? async function() {
|
||||
const worker = new SharedWorker("worker.js");
|
||||
await new Promise(resolve => {
|
||||
worker.port.onmessage = resolve;
|
||||
worker.port.postMessage("bgpage->worker");
|
||||
});
|
||||
browser.test.sendMessage("test-shared-worker:done");
|
||||
}
|
||||
: function() {
|
||||
// This test covers the builds where the extensions are still
|
||||
// running in the main process (it just checks that we don't
|
||||
// allow it).
|
||||
browser.test.assertThrows(
|
||||
() => {
|
||||
try {
|
||||
new SharedWorker("worker.js");
|
||||
} catch (e) {
|
||||
// assertThrows is currently failing to match the error message
|
||||
// automatically, let's cheat a little bit for now.
|
||||
throw new Error(`${e}`);
|
||||
}
|
||||
},
|
||||
/NS_ERROR_ABORT/,
|
||||
"Got the expected failure in non-remote mode"
|
||||
);
|
||||
|
||||
browser.test.sendMessage("test-shared-worker:done");
|
||||
};
|
||||
const extension = ExtensionTestUtils.loadExtension({
|
||||
background,
|
||||
files: {
|
||||
"worker.js": function() {
|
||||
self.onconnect = evt => {
|
||||
const port = evt.ports[0];
|
||||
port.onmessage = () => port.postMessage("worker-reply");
|
||||
};
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
await extension.startup();
|
||||
await extension.awaitMessage("test-shared-worker:done");
|
||||
await extension.unload();
|
||||
});
|
@ -140,6 +140,7 @@ skip-if = ccov && os == 'linux' # bug 1607581
|
||||
[test_ext_same_site_cookies.js]
|
||||
[test_ext_sandbox_var.js]
|
||||
[test_ext_schema.js]
|
||||
[test_ext_shared_workers.js]
|
||||
[test_ext_shutdown_cleanup.js]
|
||||
[test_ext_simple.js]
|
||||
[test_ext_startupData.js]
|
||||
|
Loading…
Reference in New Issue
Block a user