summaryrefslogtreecommitdiffstats
path: root/dom/workers/sharedworkers
diff options
context:
space:
mode:
Diffstat (limited to 'dom/workers/sharedworkers')
-rw-r--r--dom/workers/sharedworkers/PSharedWorker.ipdl40
-rw-r--r--dom/workers/sharedworkers/SharedWorker.cpp445
-rw-r--r--dom/workers/sharedworkers/SharedWorker.h96
-rw-r--r--dom/workers/sharedworkers/SharedWorkerChild.cpp181
-rw-r--r--dom/workers/sharedworkers/SharedWorkerChild.h61
-rw-r--r--dom/workers/sharedworkers/SharedWorkerManager.cpp348
-rw-r--r--dom/workers/sharedworkers/SharedWorkerManager.h164
-rw-r--r--dom/workers/sharedworkers/SharedWorkerParent.cpp165
-rw-r--r--dom/workers/sharedworkers/SharedWorkerParent.h81
-rw-r--r--dom/workers/sharedworkers/SharedWorkerService.cpp261
-rw-r--r--dom/workers/sharedworkers/SharedWorkerService.h74
-rw-r--r--dom/workers/sharedworkers/moz.build28
12 files changed, 1944 insertions, 0 deletions
diff --git a/dom/workers/sharedworkers/PSharedWorker.ipdl b/dom/workers/sharedworkers/PSharedWorker.ipdl
new file mode 100644
index 0000000000..2406a731da
--- /dev/null
+++ b/dom/workers/sharedworkers/PSharedWorker.ipdl
@@ -0,0 +1,40 @@
+/* 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 protocol PBackground;
+
+include RemoteWorkerTypes;
+
+namespace mozilla {
+namespace dom {
+
+/**
+ * Protocol for SharedWorker bindings to communicate with per-worker
+ * SharedWorkerManager instances in the parent via SharedWorkerChild /
+ * SharedWorkerParent and SharedWorkerService getting/creating the
+ * SharedWorkerManager if it doesn't already exist. Main-thread to PBackground.
+ */
+[ManualDealloc]
+protocol PSharedWorker
+{
+ manager PBackground;
+
+parent:
+ async Close();
+ async Suspend();
+ async Resume();
+ async Freeze();
+ async Thaw();
+
+child:
+ async Error(ErrorValue value);
+ async NotifyLock(bool aCreated);
+ async NotifyWebTransport(bool aCreated);
+ async Terminate();
+
+ async __delete__();
+};
+
+} // namespace dom
+} // namespace mozilla
diff --git a/dom/workers/sharedworkers/SharedWorker.cpp b/dom/workers/sharedworkers/SharedWorker.cpp
new file mode 100644
index 0000000000..d3bb3bfcaa
--- /dev/null
+++ b/dom/workers/sharedworkers/SharedWorker.cpp
@@ -0,0 +1,445 @@
+/* -*- 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 "SharedWorker.h"
+
+#include "mozilla/AntiTrackingUtils.h"
+#include "mozilla/AsyncEventDispatcher.h"
+#include "mozilla/BasePrincipal.h"
+#include "mozilla/EventDispatcher.h"
+#include "mozilla/dom/ClientInfo.h"
+#include "mozilla/dom/Event.h"
+#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"
+#include "mozilla/dom/WorkerBinding.h"
+#include "mozilla/dom/WorkerLoadInfo.h"
+#include "mozilla/dom/WorkerPrivate.h"
+#include "mozilla/ipc/BackgroundChild.h"
+#include "mozilla/ipc/BackgroundUtils.h"
+#include "mozilla/ipc/PBackgroundChild.h"
+#include "mozilla/ipc/URIUtils.h"
+#include "mozilla/net/CookieJarSettings.h"
+#include "mozilla/StorageAccess.h"
+#include "nsGlobalWindowInner.h"
+#include "nsPIDOMWindow.h"
+
+#ifdef XP_WIN
+# undef PostMessage
+#endif
+
+using namespace mozilla;
+using namespace mozilla::dom;
+using namespace mozilla::ipc;
+
+SharedWorker::SharedWorker(nsPIDOMWindowInner* aWindow,
+ SharedWorkerChild* aActor, MessagePort* aMessagePort)
+ : DOMEventTargetHelper(aWindow),
+ mWindow(aWindow),
+ mActor(aActor),
+ mMessagePort(aMessagePort),
+ mFrozen(false) {
+ AssertIsOnMainThread();
+ MOZ_ASSERT(aActor);
+ MOZ_ASSERT(aMessagePort);
+}
+
+SharedWorker::~SharedWorker() {
+ AssertIsOnMainThread();
+ Close();
+}
+
+// static
+already_AddRefed<SharedWorker> SharedWorker::Constructor(
+ const GlobalObject& aGlobal, const nsAString& aScriptURL,
+ const StringOrWorkerOptions& aOptions, ErrorResult& aRv) {
+ AssertIsOnMainThread();
+
+ nsCOMPtr<nsPIDOMWindowInner> window =
+ do_QueryInterface(aGlobal.GetAsSupports());
+ MOZ_ASSERT(window);
+
+ // Our current idiom is that storage-related APIs specialize for the system
+ // principal themselves, which is consistent with StorageAllowedForwindow not
+ // specializing for the system principal. Without this specialization we
+ // would end up with ePrivateBrowsing for system principaled private browsing
+ // windows which is explicitly not what we want. System Principal code always
+ // should have access to storage. It may make sense to enhance
+ // StorageAllowedForWindow in the future to handle this after comprehensive
+ // auditing.
+ nsCOMPtr<nsIPrincipal> principal = aGlobal.GetSubjectPrincipal();
+ StorageAccess storageAllowed;
+ if (principal && principal->IsSystemPrincipal()) {
+ storageAllowed = StorageAccess::eAllow;
+ } else {
+ storageAllowed = StorageAllowedForWindow(window);
+ }
+
+ if (storageAllowed == StorageAccess::eDeny) {
+ aRv.ThrowSecurityError("StorageAccess denied.");
+ return nullptr;
+ }
+
+ if (ShouldPartitionStorage(storageAllowed) &&
+ !StoragePartitioningEnabled(
+ storageAllowed, window->GetExtantDoc()->CookieJarSettings())) {
+ aRv.ThrowSecurityError("StoragePartitioning not enabled.");
+ return nullptr;
+ }
+
+ // Assert that the principal private browsing state matches the
+ // StorageAccess value.
+#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
+ if (storageAllowed == StorageAccess::ePrivateBrowsing) {
+ uint32_t privateBrowsingId = 0;
+ if (principal) {
+ MOZ_ALWAYS_SUCCEEDS(principal->GetPrivateBrowsingId(&privateBrowsingId));
+ }
+ MOZ_DIAGNOSTIC_ASSERT(privateBrowsingId != 0);
+ }
+#endif // MOZ_DIAGNOSTIC_ASSERT_ENABLED
+
+ PBackgroundChild* actorChild = BackgroundChild::GetOrCreateForCurrentThread();
+ if (!actorChild || !actorChild->CanSend()) {
+ aRv.ThrowSecurityError("PBackground not available.");
+ return nullptr;
+ }
+
+ nsAutoString name;
+ WorkerType workerType = WorkerType::Classic;
+ RequestCredentials credentials = RequestCredentials::Omit;
+ if (aOptions.IsString()) {
+ name = aOptions.GetAsString();
+ } else {
+ MOZ_ASSERT(aOptions.IsWorkerOptions());
+ name = aOptions.GetAsWorkerOptions().mName;
+ workerType = aOptions.GetAsWorkerOptions().mType;
+ credentials = aOptions.GetAsWorkerOptions().mCredentials;
+ }
+
+ JSContext* cx = aGlobal.Context();
+
+ WorkerLoadInfo loadInfo;
+ aRv = WorkerPrivate::GetLoadInfo(
+ cx, window, nullptr, aScriptURL, workerType, credentials, false,
+ WorkerPrivate::OverrideLoadGroup, WorkerKindShared, &loadInfo);
+ if (NS_WARN_IF(aRv.Failed())) {
+ return nullptr;
+ }
+
+ PrincipalInfo principalInfo;
+ aRv = PrincipalToPrincipalInfo(loadInfo.mPrincipal, &principalInfo);
+ if (NS_WARN_IF(aRv.Failed())) {
+ return nullptr;
+ }
+
+ PrincipalInfo loadingPrincipalInfo;
+ aRv = PrincipalToPrincipalInfo(loadInfo.mLoadingPrincipal,
+ &loadingPrincipalInfo);
+ if (NS_WARN_IF(aRv.Failed())) {
+ return nullptr;
+ }
+
+ // Here, the PartitionedPrincipal is always equal to the SharedWorker's
+ // principal because the channel is not opened yet, and, because of this, it's
+ // not classified. We need to force the correct originAttributes.
+ //
+ // The sharedWorker's principal could be a null principal, e.g. loading a
+ // data url. In this case, we don't need to force the OAs for the partitioned
+ // principal because creating storage from a null principal will fail anyway.
+ // We should only do this for content principals.
+ //
+ // You can find more details in StoragePrincipalHelper.h
+ if (ShouldPartitionStorage(storageAllowed) &&
+ BasePrincipal::Cast(loadInfo.mPrincipal)->IsContentPrincipal()) {
+ nsCOMPtr<nsIScriptObjectPrincipal> sop = do_QueryInterface(window);
+ if (!sop) {
+ aRv.ThrowSecurityError("ScriptObjectPrincipal not available.");
+ return nullptr;
+ }
+
+ nsIPrincipal* windowPrincipal = sop->GetPrincipal();
+ if (!windowPrincipal) {
+ aRv.ThrowSecurityError("WindowPrincipal not available.");
+ return nullptr;
+ }
+
+ nsIPrincipal* windowPartitionedPrincipal = sop->PartitionedPrincipal();
+ if (!windowPartitionedPrincipal) {
+ aRv.ThrowSecurityError("WindowPartitionedPrincipal not available.");
+ return nullptr;
+ }
+
+ if (!windowPrincipal->Equals(windowPartitionedPrincipal)) {
+ loadInfo.mPartitionedPrincipal =
+ BasePrincipal::Cast(loadInfo.mPrincipal)
+ ->CloneForcingOriginAttributes(
+ BasePrincipal::Cast(windowPartitionedPrincipal)
+ ->OriginAttributesRef());
+ }
+ }
+
+ PrincipalInfo partitionedPrincipalInfo;
+ if (loadInfo.mPrincipal->Equals(loadInfo.mPartitionedPrincipal)) {
+ partitionedPrincipalInfo = principalInfo;
+ } else {
+ aRv = PrincipalToPrincipalInfo(loadInfo.mPartitionedPrincipal,
+ &partitionedPrincipalInfo);
+ if (NS_WARN_IF(aRv.Failed())) {
+ return nullptr;
+ }
+ }
+
+ // We don't actually care about this MessageChannel, but we use it to 'steal'
+ // its 2 connected ports.
+ nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(window);
+ RefPtr<MessageChannel> channel = MessageChannel::Constructor(global, aRv);
+ if (NS_WARN_IF(aRv.Failed())) {
+ return nullptr;
+ }
+
+ UniqueMessagePortId portIdentifier;
+ channel->Port1()->CloneAndDisentangle(portIdentifier);
+
+ URIParams resolvedScriptURL;
+ SerializeURI(loadInfo.mResolvedScriptURI, resolvedScriptURL);
+
+ URIParams baseURL;
+ SerializeURI(loadInfo.mBaseURI, baseURL);
+
+ // Register this component to PBackground.
+ bool isSecureContext = JS::GetIsSecureContext(js::GetContextRealm(cx));
+
+ Maybe<IPCClientInfo> ipcClientInfo;
+ Maybe<ClientInfo> clientInfo = window->GetClientInfo();
+ if (clientInfo.isSome()) {
+ ipcClientInfo.emplace(clientInfo.value().ToIPC());
+ }
+
+ nsID agentClusterId = nsID::GenerateUUID();
+
+ net::CookieJarSettingsArgs cjsData;
+ MOZ_ASSERT(loadInfo.mCookieJarSettings);
+ net::CookieJarSettings::Cast(loadInfo.mCookieJarSettings)->Serialize(cjsData);
+
+ auto remoteType = RemoteWorkerManager::GetRemoteType(
+ loadInfo.mPrincipal, WorkerKind::WorkerKindShared);
+ if (NS_WARN_IF(remoteType.isErr())) {
+ aRv.Throw(remoteType.unwrapErr());
+ return nullptr;
+ }
+
+ Maybe<uint64_t> overriddenFingerprintingSettingsArg;
+ if (loadInfo.mOverriddenFingerprintingSettings.isSome()) {
+ overriddenFingerprintingSettingsArg.emplace(
+ uint64_t(loadInfo.mOverriddenFingerprintingSettings.ref()));
+ }
+
+ RemoteWorkerData remoteWorkerData(
+ nsString(aScriptURL), baseURL, resolvedScriptURL, name, workerType,
+ credentials, loadingPrincipalInfo, principalInfo,
+ partitionedPrincipalInfo, loadInfo.mUseRegularPrincipal,
+ loadInfo.mUsingStorageAccess, cjsData, loadInfo.mDomain, isSecureContext,
+ ipcClientInfo, loadInfo.mReferrerInfo, storageAllowed,
+ AntiTrackingUtils::IsThirdPartyWindow(window, nullptr),
+ loadInfo.mShouldResistFingerprinting, overriddenFingerprintingSettingsArg,
+ OriginTrials::FromWindow(nsGlobalWindowInner::Cast(window)),
+ void_t() /* OptionalServiceWorkerData */, agentClusterId,
+ remoteType.unwrap());
+
+ PSharedWorkerChild* pActor = actorChild->SendPSharedWorkerConstructor(
+ remoteWorkerData, loadInfo.mWindowID, portIdentifier.release());
+ if (!pActor) {
+ MOZ_ASSERT_UNREACHABLE("We already checked PBackground above.");
+ aRv.ThrowSecurityError("PBackground not available.");
+ return nullptr;
+ }
+
+ RefPtr<SharedWorkerChild> actor = static_cast<SharedWorkerChild*>(pActor);
+
+ RefPtr<SharedWorker> sharedWorker =
+ new SharedWorker(window, actor, channel->Port2());
+
+ // Let's inform the window about this SharedWorker.
+ nsGlobalWindowInner::Cast(window)->StoreSharedWorker(sharedWorker);
+ actor->SetParent(sharedWorker);
+
+ if (nsGlobalWindowInner::Cast(window)->IsSuspended()) {
+ sharedWorker->Suspend();
+ }
+
+ return sharedWorker.forget();
+}
+
+MessagePort* SharedWorker::Port() {
+ AssertIsOnMainThread();
+ return mMessagePort;
+}
+
+void SharedWorker::Freeze() {
+ AssertIsOnMainThread();
+ MOZ_ASSERT(!IsFrozen());
+
+ if (mFrozen) {
+ return;
+ }
+
+ mFrozen = true;
+
+ if (mActor) {
+ mActor->SendFreeze();
+ }
+}
+
+void SharedWorker::Thaw() {
+ AssertIsOnMainThread();
+ MOZ_ASSERT(IsFrozen());
+
+ if (!mFrozen) {
+ return;
+ }
+
+ mFrozen = false;
+
+ if (mActor) {
+ mActor->SendThaw();
+ }
+
+ if (!mFrozenEvents.IsEmpty()) {
+ nsTArray<RefPtr<Event>> events = std::move(mFrozenEvents);
+
+ for (uint32_t index = 0; index < events.Length(); index++) {
+ RefPtr<Event>& event = events[index];
+ MOZ_ASSERT(event);
+
+ RefPtr<EventTarget> target = event->GetTarget();
+ ErrorResult rv;
+ target->DispatchEvent(*event, rv);
+ if (rv.Failed()) {
+ NS_WARNING("Failed to dispatch event!");
+ }
+ }
+ }
+}
+
+void SharedWorker::QueueEvent(Event* aEvent) {
+ AssertIsOnMainThread();
+ MOZ_ASSERT(aEvent);
+ MOZ_ASSERT(IsFrozen());
+
+ mFrozenEvents.AppendElement(aEvent);
+}
+
+void SharedWorker::Close() {
+ AssertIsOnMainThread();
+
+ if (mWindow) {
+ nsGlobalWindowInner::Cast(mWindow)->ForgetSharedWorker(this);
+ mWindow = nullptr;
+ }
+
+ if (mActor) {
+ mActor->SendClose();
+ mActor->SetParent(nullptr);
+ mActor = nullptr;
+ }
+
+ if (mMessagePort) {
+ mMessagePort->Close();
+ }
+}
+
+void SharedWorker::Suspend() {
+ if (mActor) {
+ mActor->SendSuspend();
+ }
+}
+
+void SharedWorker::Resume() {
+ if (mActor) {
+ mActor->SendResume();
+ }
+}
+
+void SharedWorker::PostMessage(JSContext* aCx, JS::Handle<JS::Value> aMessage,
+ const Sequence<JSObject*>& aTransferable,
+ ErrorResult& aRv) {
+ AssertIsOnMainThread();
+ MOZ_ASSERT(mMessagePort);
+
+ mMessagePort->PostMessage(aCx, aMessage, aTransferable, aRv);
+}
+
+NS_IMPL_ADDREF_INHERITED(SharedWorker, DOMEventTargetHelper)
+NS_IMPL_RELEASE_INHERITED(SharedWorker, DOMEventTargetHelper)
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(SharedWorker)
+NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
+
+NS_IMPL_CYCLE_COLLECTION_CLASS(SharedWorker)
+
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(SharedWorker,
+ DOMEventTargetHelper)
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWindow)
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMessagePort)
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFrozenEvents)
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(SharedWorker,
+ DOMEventTargetHelper)
+ NS_IMPL_CYCLE_COLLECTION_UNLINK(mWindow)
+ NS_IMPL_CYCLE_COLLECTION_UNLINK(mMessagePort)
+ NS_IMPL_CYCLE_COLLECTION_UNLINK(mFrozenEvents)
+NS_IMPL_CYCLE_COLLECTION_UNLINK_END
+
+JSObject* SharedWorker::WrapObject(JSContext* aCx,
+ JS::Handle<JSObject*> aGivenProto) {
+ AssertIsOnMainThread();
+
+ return SharedWorker_Binding::Wrap(aCx, this, aGivenProto);
+}
+
+void SharedWorker::GetEventTargetParent(EventChainPreVisitor& aVisitor) {
+ AssertIsOnMainThread();
+
+ if (IsFrozen()) {
+ RefPtr<Event> event = aVisitor.mDOMEvent;
+ if (!event) {
+ event = EventDispatcher::CreateEvent(aVisitor.mEvent->mOriginalTarget,
+ aVisitor.mPresContext,
+ aVisitor.mEvent, u""_ns);
+ }
+
+ QueueEvent(event);
+
+ aVisitor.mCanHandle = false;
+ aVisitor.SetParentTarget(nullptr, false);
+ return;
+ }
+
+ DOMEventTargetHelper::GetEventTargetParent(aVisitor);
+}
+
+void SharedWorker::DisconnectFromOwner() {
+ Close();
+ DOMEventTargetHelper::DisconnectFromOwner();
+}
+
+void SharedWorker::ErrorPropagation(nsresult aError) {
+ AssertIsOnMainThread();
+ MOZ_ASSERT(mActor);
+ MOZ_ASSERT(NS_FAILED(aError));
+
+ RefPtr<AsyncEventDispatcher> errorEvent =
+ new AsyncEventDispatcher(this, u"error"_ns, CanBubble::eNo);
+ errorEvent->PostDOMEvent();
+
+ Close();
+}
diff --git a/dom/workers/sharedworkers/SharedWorker.h b/dom/workers/sharedworkers/SharedWorker.h
new file mode 100644
index 0000000000..be61de8889
--- /dev/null
+++ b/dom/workers/sharedworkers/SharedWorker.h
@@ -0,0 +1,96 @@
+/* -*- 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/. */
+
+#ifndef mozilla_dom_workers_sharedworker_h__
+#define mozilla_dom_workers_sharedworker_h__
+
+#include "mozilla/dom/WorkerCommon.h"
+#include "mozilla/dom/BindingDeclarations.h"
+#include "mozilla/DOMEventTargetHelper.h"
+
+#ifdef XP_WIN
+# undef PostMessage
+#endif
+
+class nsPIDOMWindowInner;
+
+namespace mozilla {
+class EventChainPreVisitor;
+
+namespace dom {
+class MessagePort;
+class StringOrWorkerOptions;
+class Event;
+
+class SharedWorkerChild;
+
+/**
+ * DOM binding. Holds a SharedWorkerChild. Must exist on the main thread because
+ * we only allow top-level windows to create SharedWorkers.
+ */
+class SharedWorker final : public DOMEventTargetHelper {
+ using ErrorResult = mozilla::ErrorResult;
+ using GlobalObject = mozilla::dom::GlobalObject;
+
+ RefPtr<nsPIDOMWindowInner> mWindow;
+ RefPtr<SharedWorkerChild> mActor;
+ RefPtr<MessagePort> mMessagePort;
+ nsTArray<RefPtr<Event>> mFrozenEvents;
+ bool mFrozen;
+
+ public:
+ static already_AddRefed<SharedWorker> Constructor(
+ const GlobalObject& aGlobal, const nsAString& aScriptURL,
+ const StringOrWorkerOptions& aOptions, ErrorResult& aRv);
+
+ MessagePort* Port();
+
+ bool IsFrozen() const { return mFrozen; }
+
+ void QueueEvent(Event* aEvent);
+
+ NS_DECL_ISUPPORTS_INHERITED
+ NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(SharedWorker, DOMEventTargetHelper)
+
+ IMPL_EVENT_HANDLER(error)
+
+ virtual JSObject* WrapObject(JSContext* aCx,
+ JS::Handle<JSObject*> aGivenProto) override;
+
+ void GetEventTargetParent(EventChainPreVisitor& aVisitor) override;
+
+ void DisconnectFromOwner() override;
+
+ void ErrorPropagation(nsresult aError);
+
+ // Methods called from the window.
+
+ void Close();
+
+ void Suspend();
+
+ void Resume();
+
+ void Freeze();
+
+ void Thaw();
+
+ private:
+ SharedWorker(nsPIDOMWindowInner* aWindow, SharedWorkerChild* aActor,
+ MessagePort* aMessagePort);
+
+ // This class is reference-counted and will be destroyed from Release().
+ ~SharedWorker();
+
+ // Only called by MessagePort.
+ void PostMessage(JSContext* aCx, JS::Handle<JS::Value> aMessage,
+ const Sequence<JSObject*>& aTransferable, ErrorResult& aRv);
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_workers_sharedworker_h__
diff --git a/dom/workers/sharedworkers/SharedWorkerChild.cpp b/dom/workers/sharedworkers/SharedWorkerChild.cpp
new file mode 100644
index 0000000000..21ae74681b
--- /dev/null
+++ b/dom/workers/sharedworkers/SharedWorkerChild.cpp
@@ -0,0 +1,181 @@
+/* -*- 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 "SharedWorkerChild.h"
+#include "mozilla/dom/ErrorEvent.h"
+#include "mozilla/dom/ErrorEventBinding.h"
+#include "mozilla/dom/Exceptions.h"
+#include "mozilla/dom/RootedDictionary.h"
+#include "mozilla/dom/SecurityPolicyViolationEvent.h"
+#include "mozilla/dom/SecurityPolicyViolationEventBinding.h"
+#include "mozilla/dom/ScriptSettings.h"
+#include "mozilla/dom/SharedWorker.h"
+#include "mozilla/dom/WebTransport.h"
+#include "mozilla/dom/WindowGlobalChild.h"
+#include "mozilla/dom/WorkerError.h"
+#include "mozilla/dom/locks/LockManagerChild.h"
+
+namespace mozilla {
+
+using namespace ipc;
+
+namespace dom {
+
+SharedWorkerChild::SharedWorkerChild() : mParent(nullptr), mActive(true) {}
+
+SharedWorkerChild::~SharedWorkerChild() = default;
+
+void SharedWorkerChild::ActorDestroy(ActorDestroyReason aWhy) {
+ mActive = false;
+}
+
+void SharedWorkerChild::SendClose() {
+ if (mActive) {
+ // This is the last message.
+ mActive = false;
+ PSharedWorkerChild::SendClose();
+ }
+}
+
+void SharedWorkerChild::SendSuspend() {
+ if (mActive) {
+ PSharedWorkerChild::SendSuspend();
+ }
+}
+
+void SharedWorkerChild::SendResume() {
+ if (mActive) {
+ PSharedWorkerChild::SendResume();
+ }
+}
+
+void SharedWorkerChild::SendFreeze() {
+ if (mActive) {
+ PSharedWorkerChild::SendFreeze();
+ }
+}
+
+void SharedWorkerChild::SendThaw() {
+ if (mActive) {
+ PSharedWorkerChild::SendThaw();
+ }
+}
+
+IPCResult SharedWorkerChild::RecvError(const ErrorValue& aValue) {
+ if (!mParent) {
+ return IPC_OK();
+ }
+
+ if (aValue.type() == ErrorValue::Tnsresult) {
+ mParent->ErrorPropagation(aValue.get_nsresult());
+ return IPC_OK();
+ }
+
+ nsPIDOMWindowInner* window = mParent->GetOwner();
+ uint64_t innerWindowId = window ? window->WindowID() : 0;
+
+ if (aValue.type() == ErrorValue::TCSPViolation) {
+ SecurityPolicyViolationEventInit violationEventInit;
+ if (NS_WARN_IF(
+ !violationEventInit.Init(aValue.get_CSPViolation().json()))) {
+ return IPC_OK();
+ }
+
+ if (NS_WARN_IF(!window)) {
+ return IPC_OK();
+ }
+
+ RefPtr<EventTarget> eventTarget = window->GetExtantDoc();
+ if (NS_WARN_IF(!eventTarget)) {
+ return IPC_OK();
+ }
+
+ RefPtr<Event> event = SecurityPolicyViolationEvent::Constructor(
+ eventTarget, u"securitypolicyviolation"_ns, violationEventInit);
+ event->SetTrusted(true);
+
+ eventTarget->DispatchEvent(*event);
+ return IPC_OK();
+ }
+
+ if (aValue.type() == ErrorValue::TErrorData &&
+ aValue.get_ErrorData().isWarning()) {
+ // Don't fire any events for warnings. Just log to console.
+ WorkerErrorReport::LogErrorToConsole(aValue.get_ErrorData(), innerWindowId);
+ return IPC_OK();
+ }
+
+ AutoJSAPI jsapi;
+ jsapi.Init();
+
+ RefPtr<Event> event;
+ if (aValue.type() == ErrorValue::TErrorData) {
+ const ErrorData& errorData = aValue.get_ErrorData();
+ RootedDictionary<ErrorEventInit> errorInit(jsapi.cx());
+ errorInit.mBubbles = false;
+ errorInit.mCancelable = true;
+ errorInit.mMessage = errorData.message();
+ errorInit.mFilename = errorData.filename();
+ errorInit.mLineno = errorData.lineNumber();
+ errorInit.mColno = errorData.columnNumber();
+
+ event = ErrorEvent::Constructor(mParent, u"error"_ns, errorInit);
+ } else {
+ event = Event::Constructor(mParent, u"error"_ns, EventInit());
+ }
+ event->SetTrusted(true);
+
+ ErrorResult res;
+ bool defaultActionEnabled =
+ mParent->DispatchEvent(*event, CallerType::System, res);
+ if (res.Failed()) {
+ ThrowAndReport(window, res.StealNSResult());
+ return IPC_OK();
+ }
+
+ if (aValue.type() != ErrorValue::TErrorData) {
+ MOZ_ASSERT(aValue.type() == ErrorValue::Tvoid_t);
+ return IPC_OK();
+ }
+
+ if (defaultActionEnabled) {
+ WorkerErrorReport::LogErrorToConsole(aValue.get_ErrorData(), innerWindowId);
+ }
+
+ return IPC_OK();
+}
+
+IPCResult SharedWorkerChild::RecvNotifyLock(bool aCreated) {
+ if (!mParent) {
+ return IPC_OK();
+ }
+
+ locks::LockManagerChild::NotifyBFCacheOnMainThread(mParent->GetOwner(),
+ aCreated);
+
+ return IPC_OK();
+}
+
+IPCResult SharedWorkerChild::RecvNotifyWebTransport(bool aCreated) {
+ if (!mParent) {
+ return IPC_OK();
+ }
+
+ WebTransport::NotifyBFCacheOnMainThread(mParent->GetOwner(), aCreated);
+
+ return IPC_OK();
+}
+
+IPCResult SharedWorkerChild::RecvTerminate() {
+ if (mParent) {
+ mParent->Close();
+ }
+
+ return IPC_OK();
+}
+
+} // namespace dom
+} // namespace mozilla
diff --git a/dom/workers/sharedworkers/SharedWorkerChild.h b/dom/workers/sharedworkers/SharedWorkerChild.h
new file mode 100644
index 0000000000..c899589320
--- /dev/null
+++ b/dom/workers/sharedworkers/SharedWorkerChild.h
@@ -0,0 +1,61 @@
+/* -*- 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/. */
+
+#ifndef mozilla_dom_dom_SharedWorkerChild_h
+#define mozilla_dom_dom_SharedWorkerChild_h
+
+#include "mozilla/dom/PSharedWorkerChild.h"
+#include "nsISupportsImpl.h"
+
+namespace mozilla::dom {
+
+class SharedWorker;
+
+/**
+ * Held by SharedWorker bindings to remotely control sharedworker lifecycle and
+ * receive error and termination reports.
+ */
+class SharedWorkerChild final : public mozilla::dom::PSharedWorkerChild {
+ friend class PSharedWorkerChild;
+
+ public:
+ NS_INLINE_DECL_REFCOUNTING(SharedWorkerChild)
+
+ SharedWorkerChild();
+
+ void SetParent(SharedWorker* aSharedWorker) { mParent = aSharedWorker; }
+
+ void SendClose();
+
+ void SendSuspend();
+
+ void SendResume();
+
+ void SendFreeze();
+
+ void SendThaw();
+
+ private:
+ ~SharedWorkerChild();
+
+ mozilla::ipc::IPCResult RecvError(const ErrorValue& aValue);
+
+ mozilla::ipc::IPCResult RecvNotifyLock(bool aCreated);
+
+ mozilla::ipc::IPCResult RecvNotifyWebTransport(bool aCreated);
+
+ mozilla::ipc::IPCResult RecvTerminate();
+
+ void ActorDestroy(ActorDestroyReason aWhy) override;
+
+ // Raw pointer because mParent is set to null when released.
+ SharedWorker* MOZ_NON_OWNING_REF mParent;
+ bool mActive;
+};
+
+} // namespace mozilla::dom
+
+#endif // mozilla_dom_dom_SharedWorkerChild_h
diff --git a/dom/workers/sharedworkers/SharedWorkerManager.cpp b/dom/workers/sharedworkers/SharedWorkerManager.cpp
new file mode 100644
index 0000000000..37dbc700d9
--- /dev/null
+++ b/dom/workers/sharedworkers/SharedWorkerManager.cpp
@@ -0,0 +1,348 @@
+/* -*- 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 "SharedWorkerManager.h"
+#include "SharedWorkerParent.h"
+#include "SharedWorkerService.h"
+#include "mozilla/dom/MessagePort.h"
+#include "mozilla/dom/PSharedWorker.h"
+#include "mozilla/ipc/BackgroundParent.h"
+#include "mozilla/ipc/URIUtils.h"
+#include "mozilla/dom/RemoteWorkerController.h"
+#include "nsIConsoleReportCollector.h"
+#include "nsIPrincipal.h"
+#include "nsProxyRelease.h"
+
+namespace mozilla::dom {
+
+// static
+already_AddRefed<SharedWorkerManagerHolder> SharedWorkerManager::Create(
+ SharedWorkerService* aService, nsIEventTarget* aPBackgroundEventTarget,
+ const RemoteWorkerData& aData, nsIPrincipal* aLoadingPrincipal,
+ const OriginAttributes& aEffectiveStoragePrincipalAttrs) {
+ MOZ_ASSERT(NS_IsMainThread());
+
+ RefPtr<SharedWorkerManager> manager =
+ new SharedWorkerManager(aPBackgroundEventTarget, aData, aLoadingPrincipal,
+ aEffectiveStoragePrincipalAttrs);
+
+ RefPtr<SharedWorkerManagerHolder> holder =
+ new SharedWorkerManagerHolder(manager, aService);
+ return holder.forget();
+}
+
+SharedWorkerManager::SharedWorkerManager(
+ nsIEventTarget* aPBackgroundEventTarget, const RemoteWorkerData& aData,
+ nsIPrincipal* aLoadingPrincipal,
+ const OriginAttributes& aEffectiveStoragePrincipalAttrs)
+ : mPBackgroundEventTarget(aPBackgroundEventTarget),
+ mLoadingPrincipal(aLoadingPrincipal),
+ mDomain(aData.domain()),
+ mEffectiveStoragePrincipalAttrs(aEffectiveStoragePrincipalAttrs),
+ mResolvedScriptURL(DeserializeURI(aData.resolvedScriptURL())),
+ mName(aData.name()),
+ mIsSecureContext(aData.isSecureContext()),
+ mSuspended(false),
+ mFrozen(false) {
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(aLoadingPrincipal);
+}
+
+SharedWorkerManager::~SharedWorkerManager() {
+ NS_ReleaseOnMainThread("SharedWorkerManager::mLoadingPrincipal",
+ mLoadingPrincipal.forget());
+ NS_ProxyRelease("SharedWorkerManager::mRemoteWorkerController",
+ mPBackgroundEventTarget, mRemoteWorkerController.forget());
+}
+
+bool SharedWorkerManager::MaybeCreateRemoteWorker(
+ const RemoteWorkerData& aData, uint64_t aWindowID,
+ UniqueMessagePortId& aPortIdentifier, base::ProcessId aProcessId) {
+ ::mozilla::ipc::AssertIsOnBackgroundThread();
+
+ // Creating remote workers may result in creating new processes, but during
+ // parent shutdown that would add just noise, so better bail out.
+ if (AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdownConfirmed)) {
+ return false;
+ }
+
+ if (!mRemoteWorkerController) {
+ mRemoteWorkerController =
+ RemoteWorkerController::Create(aData, this, aProcessId);
+ if (NS_WARN_IF(!mRemoteWorkerController)) {
+ return false;
+ }
+ }
+
+ if (aWindowID) {
+ mRemoteWorkerController->AddWindowID(aWindowID);
+ }
+
+ mRemoteWorkerController->AddPortIdentifier(aPortIdentifier.release());
+ return true;
+}
+
+already_AddRefed<SharedWorkerManagerHolder>
+SharedWorkerManager::MatchOnMainThread(
+ SharedWorkerService* aService, const nsACString& aDomain,
+ nsIURI* aScriptURL, const nsAString& aName, nsIPrincipal* aLoadingPrincipal,
+ const OriginAttributes& aEffectiveStoragePrincipalAttrs) {
+ MOZ_ASSERT(NS_IsMainThread());
+
+ bool urlEquals;
+ if (NS_FAILED(aScriptURL->Equals(mResolvedScriptURL, &urlEquals))) {
+ return nullptr;
+ }
+
+ bool match =
+ aDomain == mDomain && urlEquals && aName == mName &&
+ // We want to be sure that the window's principal subsumes the
+ // SharedWorker's loading principal and vice versa.
+ mLoadingPrincipal->Subsumes(aLoadingPrincipal) &&
+ aLoadingPrincipal->Subsumes(mLoadingPrincipal) &&
+ mEffectiveStoragePrincipalAttrs == aEffectiveStoragePrincipalAttrs;
+ if (!match) {
+ return nullptr;
+ }
+
+ RefPtr<SharedWorkerManagerHolder> holder =
+ new SharedWorkerManagerHolder(this, aService);
+ return holder.forget();
+}
+
+void SharedWorkerManager::AddActor(SharedWorkerParent* aParent) {
+ ::mozilla::ipc::AssertIsOnBackgroundThread();
+ MOZ_ASSERT(aParent);
+ MOZ_ASSERT(!mActors.Contains(aParent));
+
+ mActors.AppendElement(aParent);
+
+ if (mLockCount) {
+ Unused << aParent->SendNotifyLock(true);
+ }
+
+ if (mWebTransportCount) {
+ Unused << aParent->SendNotifyWebTransport(true);
+ }
+
+ // NB: We don't update our Suspended/Frozen state here, yet. The aParent is
+ // responsible for doing so from SharedWorkerParent::ManagerCreated.
+ // XXX But we could avoid iterating all of our actors because if aParent is
+ // not frozen and we are, we would just need to thaw ourselves.
+}
+
+void SharedWorkerManager::RemoveActor(SharedWorkerParent* aParent) {
+ ::mozilla::ipc::AssertIsOnBackgroundThread();
+ MOZ_ASSERT(aParent);
+ MOZ_ASSERT(mActors.Contains(aParent));
+
+ uint64_t windowID = aParent->WindowID();
+ if (windowID) {
+ mRemoteWorkerController->RemoveWindowID(windowID);
+ }
+
+ mActors.RemoveElement(aParent);
+
+ if (!mActors.IsEmpty()) {
+ // Our remaining actors could be all suspended or frozen.
+ UpdateSuspend();
+ UpdateFrozen();
+ return;
+ }
+}
+
+void SharedWorkerManager::Terminate() {
+ ::mozilla::ipc::AssertIsOnBackgroundThread();
+ MOZ_ASSERT(mActors.IsEmpty());
+ MOZ_ASSERT(mHolders.IsEmpty());
+
+ // mRemoteWorkerController creation can fail. If the creation fails
+ // mRemoteWorkerController is nullptr and we should stop termination here.
+ if (!mRemoteWorkerController) {
+ return;
+ }
+
+ mRemoteWorkerController->Terminate();
+ mRemoteWorkerController = nullptr;
+}
+
+void SharedWorkerManager::UpdateSuspend() {
+ ::mozilla::ipc::AssertIsOnBackgroundThread();
+ MOZ_ASSERT(mRemoteWorkerController);
+
+ uint32_t suspended = 0;
+
+ for (SharedWorkerParent* actor : mActors) {
+ if (actor->IsSuspended()) {
+ ++suspended;
+ }
+ }
+
+ // Call Suspend only when all of our actors' windows are suspended and call
+ // Resume only when one of them resumes.
+ if ((mSuspended && suspended == mActors.Length()) ||
+ (!mSuspended && suspended != mActors.Length())) {
+ return;
+ }
+
+ if (!mSuspended) {
+ mSuspended = true;
+ mRemoteWorkerController->Suspend();
+ } else {
+ mSuspended = false;
+ mRemoteWorkerController->Resume();
+ }
+}
+
+void SharedWorkerManager::UpdateFrozen() {
+ ::mozilla::ipc::AssertIsOnBackgroundThread();
+ MOZ_ASSERT(mRemoteWorkerController);
+
+ uint32_t frozen = 0;
+
+ for (SharedWorkerParent* actor : mActors) {
+ if (actor->IsFrozen()) {
+ ++frozen;
+ }
+ }
+
+ // Similar to UpdateSuspend, above, we only want to be frozen when all of our
+ // actors are frozen.
+ if ((mFrozen && frozen == mActors.Length()) ||
+ (!mFrozen && frozen != mActors.Length())) {
+ return;
+ }
+
+ if (!mFrozen) {
+ mFrozen = true;
+ mRemoteWorkerController->Freeze();
+ } else {
+ mFrozen = false;
+ mRemoteWorkerController->Thaw();
+ }
+}
+
+bool SharedWorkerManager::IsSecureContext() const { return mIsSecureContext; }
+
+void SharedWorkerManager::CreationFailed() {
+ ::mozilla::ipc::AssertIsOnBackgroundThread();
+
+ for (SharedWorkerParent* actor : mActors) {
+ Unused << actor->SendError(NS_ERROR_FAILURE);
+ }
+}
+
+void SharedWorkerManager::CreationSucceeded() {
+ ::mozilla::ipc::AssertIsOnBackgroundThread();
+ // Nothing to do here.
+}
+
+void SharedWorkerManager::ErrorReceived(const ErrorValue& aValue) {
+ ::mozilla::ipc::AssertIsOnBackgroundThread();
+
+ for (SharedWorkerParent* actor : mActors) {
+ Unused << actor->SendError(aValue);
+ }
+}
+
+void SharedWorkerManager::LockNotified(bool aCreated) {
+ ::mozilla::ipc::AssertIsOnBackgroundThread();
+ MOZ_ASSERT_IF(!aCreated, mLockCount > 0);
+
+ mLockCount += aCreated ? 1 : -1;
+
+ // Notify only when we either:
+ // 1. Got a new lock when nothing were there
+ // 2. Lost all locks
+ if ((aCreated && mLockCount == 1) || !mLockCount) {
+ for (SharedWorkerParent* actor : mActors) {
+ Unused << actor->SendNotifyLock(aCreated);
+ }
+ }
+};
+
+void SharedWorkerManager::WebTransportNotified(bool aCreated) {
+ ::mozilla::ipc::AssertIsOnBackgroundThread();
+ MOZ_ASSERT_IF(!aCreated, mWebTransportCount > 0);
+
+ mWebTransportCount += aCreated ? 1 : -1;
+
+ // Notify only when we either:
+ // 1. Got a first WebTransport
+ // 2. The last WebTransport goes away
+ if ((aCreated && mWebTransportCount == 1) || mWebTransportCount == 0) {
+ for (SharedWorkerParent* actor : mActors) {
+ Unused << actor->SendNotifyWebTransport(aCreated);
+ }
+ }
+};
+
+void SharedWorkerManager::Terminated() {
+ ::mozilla::ipc::AssertIsOnBackgroundThread();
+
+ for (SharedWorkerParent* actor : mActors) {
+ Unused << actor->SendTerminate();
+ }
+}
+
+void SharedWorkerManager::RegisterHolder(SharedWorkerManagerHolder* aHolder) {
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(aHolder);
+ MOZ_ASSERT(!mHolders.Contains(aHolder));
+
+ mHolders.AppendElement(aHolder);
+}
+
+void SharedWorkerManager::UnregisterHolder(SharedWorkerManagerHolder* aHolder) {
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(aHolder);
+ MOZ_ASSERT(mHolders.Contains(aHolder));
+
+ mHolders.RemoveElement(aHolder);
+
+ if (!mHolders.IsEmpty()) {
+ return;
+ }
+
+ // Time to go.
+
+ aHolder->Service()->RemoveWorkerManagerOnMainThread(this);
+
+ RefPtr<SharedWorkerManager> self = this;
+ mPBackgroundEventTarget->Dispatch(
+ NS_NewRunnableFunction(
+ "SharedWorkerService::RemoveWorkerManagerOnMainThread",
+ [self]() { self->Terminate(); }),
+ NS_DISPATCH_NORMAL);
+}
+
+SharedWorkerManagerHolder::SharedWorkerManagerHolder(
+ SharedWorkerManager* aManager, SharedWorkerService* aService)
+ : mManager(aManager), mService(aService) {
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(aManager);
+ MOZ_ASSERT(aService);
+
+ aManager->RegisterHolder(this);
+}
+
+SharedWorkerManagerHolder::~SharedWorkerManagerHolder() {
+ MOZ_ASSERT(NS_IsMainThread());
+ mManager->UnregisterHolder(this);
+}
+
+SharedWorkerManagerWrapper::SharedWorkerManagerWrapper(
+ already_AddRefed<SharedWorkerManagerHolder> aHolder)
+ : mHolder(aHolder) {
+ MOZ_ASSERT(NS_IsMainThread());
+}
+
+SharedWorkerManagerWrapper::~SharedWorkerManagerWrapper() {
+ NS_ReleaseOnMainThread("SharedWorkerManagerWrapper::mHolder",
+ mHolder.forget());
+}
+
+} // namespace mozilla::dom
diff --git a/dom/workers/sharedworkers/SharedWorkerManager.h b/dom/workers/sharedworkers/SharedWorkerManager.h
new file mode 100644
index 0000000000..fceabca4d4
--- /dev/null
+++ b/dom/workers/sharedworkers/SharedWorkerManager.h
@@ -0,0 +1,164 @@
+/* -*- 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/. */
+
+#ifndef mozilla_dom_SharedWorkerManager_h
+#define mozilla_dom_SharedWorkerManager_h
+
+#include "SharedWorkerParent.h"
+#include "mozilla/dom/RemoteWorkerController.h"
+#include "mozilla/dom/quota/CheckedUnsafePtr.h"
+#include "nsISupportsImpl.h"
+#include "nsTArray.h"
+
+class nsIPrincipal;
+
+namespace mozilla::dom {
+
+class UniqueMessagePortId;
+class RemoteWorkerData;
+class SharedWorkerManager;
+class SharedWorkerService;
+
+// Main-thread only object that keeps a manager and the service alive.
+// When the last SharedWorkerManagerHolder is released, the corresponding
+// manager unregisters itself from the service and terminates the worker.
+class SharedWorkerManagerHolder final
+ : public SupportsCheckedUnsafePtr<CheckIf<DiagnosticAssertEnabled>> {
+ public:
+ NS_INLINE_DECL_REFCOUNTING(SharedWorkerManagerHolder);
+
+ SharedWorkerManagerHolder(SharedWorkerManager* aManager,
+ SharedWorkerService* aService);
+
+ SharedWorkerManager* Manager() const { return mManager; }
+
+ SharedWorkerService* Service() const { return mService; }
+
+ private:
+ ~SharedWorkerManagerHolder();
+
+ const RefPtr<SharedWorkerManager> mManager;
+ const RefPtr<SharedWorkerService> mService;
+};
+
+// Thread-safe wrapper for SharedWorkerManagerHolder.
+class SharedWorkerManagerWrapper final {
+ public:
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(SharedWorkerManagerWrapper);
+
+ explicit SharedWorkerManagerWrapper(
+ already_AddRefed<SharedWorkerManagerHolder> aHolder);
+
+ SharedWorkerManager* Manager() const { return mHolder->Manager(); }
+
+ private:
+ ~SharedWorkerManagerWrapper();
+
+ RefPtr<SharedWorkerManagerHolder> mHolder;
+};
+
+/**
+ * PBackground instance that corresponds to a single logical Shared Worker that
+ * exists somewhere in the process tree. Referenced/owned by multiple
+ * SharedWorkerParent instances on the PBackground thread. Holds/owns a single
+ * RemoteWorkerController to interact with the actual shared worker thread,
+ * wherever it is located. Creates the RemoteWorkerController via
+ * RemoteWorkerController::Create which uses RemoteWorkerManager::Launch under
+ * the hood.
+ */
+class SharedWorkerManager final : public RemoteWorkerObserver {
+ public:
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(SharedWorkerManager, override);
+
+ // Called on main-thread thread methods
+
+ static already_AddRefed<SharedWorkerManagerHolder> Create(
+ SharedWorkerService* aService, nsIEventTarget* aPBackgroundEventTarget,
+ const RemoteWorkerData& aData, nsIPrincipal* aLoadingPrincipal,
+ const OriginAttributes& aEffectiveStoragePrincipalAttrs);
+
+ // Returns a holder if this manager matches. The holder blocks the shutdown of
+ // the manager.
+ already_AddRefed<SharedWorkerManagerHolder> MatchOnMainThread(
+ SharedWorkerService* aService, const nsACString& aDomain,
+ nsIURI* aScriptURL, const nsAString& aName,
+ nsIPrincipal* aLoadingPrincipal,
+ const OriginAttributes& aEffectiveStoragePrincipalAttrs);
+
+ // RemoteWorkerObserver
+
+ void CreationFailed() override;
+
+ void CreationSucceeded() override;
+
+ void ErrorReceived(const ErrorValue& aValue) override;
+
+ void LockNotified(bool aCreated) final;
+
+ void WebTransportNotified(bool aCreated) final;
+
+ void Terminated() override;
+
+ // Called on PBackground thread methods
+
+ bool MaybeCreateRemoteWorker(const RemoteWorkerData& aData,
+ uint64_t aWindowID,
+ UniqueMessagePortId& aPortIdentifier,
+ base::ProcessId aProcessId);
+
+ void AddActor(SharedWorkerParent* aParent);
+
+ void RemoveActor(SharedWorkerParent* aParent);
+
+ void UpdateSuspend();
+
+ void UpdateFrozen();
+
+ bool IsSecureContext() const;
+
+ void Terminate();
+
+ // Called on main-thread only.
+
+ void RegisterHolder(SharedWorkerManagerHolder* aHolder);
+
+ void UnregisterHolder(SharedWorkerManagerHolder* aHolder);
+
+ private:
+ SharedWorkerManager(nsIEventTarget* aPBackgroundEventTarget,
+ const RemoteWorkerData& aData,
+ nsIPrincipal* aLoadingPrincipal,
+ const OriginAttributes& aEffectiveStoragePrincipalAttrs);
+
+ ~SharedWorkerManager();
+
+ nsCOMPtr<nsIEventTarget> mPBackgroundEventTarget;
+
+ nsCOMPtr<nsIPrincipal> mLoadingPrincipal;
+ const nsCString mDomain;
+ const OriginAttributes mEffectiveStoragePrincipalAttrs;
+ const nsCOMPtr<nsIURI> mResolvedScriptURL;
+ const nsString mName;
+ const bool mIsSecureContext;
+ bool mSuspended;
+ bool mFrozen;
+ uint32_t mLockCount = 0;
+ uint32_t mWebTransportCount = 0;
+
+ // Raw pointers because SharedWorkerParent unregisters itself in
+ // ActorDestroy().
+ nsTArray<CheckedUnsafePtr<SharedWorkerParent>> mActors;
+
+ RefPtr<RemoteWorkerController> mRemoteWorkerController;
+
+ // Main-thread only. Raw Pointers because holders keep the manager alive and
+ // they unregister themselves in their DTOR.
+ nsTArray<CheckedUnsafePtr<SharedWorkerManagerHolder>> mHolders;
+};
+
+} // namespace mozilla::dom
+
+#endif // mozilla_dom_SharedWorkerManager_h
diff --git a/dom/workers/sharedworkers/SharedWorkerParent.cpp b/dom/workers/sharedworkers/SharedWorkerParent.cpp
new file mode 100644
index 0000000000..38e56f5100
--- /dev/null
+++ b/dom/workers/sharedworkers/SharedWorkerParent.cpp
@@ -0,0 +1,165 @@
+/* -*- 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 "SharedWorkerParent.h"
+#include "SharedWorkerManager.h"
+#include "SharedWorkerService.h"
+#include "mozilla/dom/RemoteWorkerTypes.h"
+#include "mozilla/ipc/BackgroundParent.h"
+#include "mozilla/ipc/BackgroundUtils.h"
+#include "mozilla/Unused.h"
+
+namespace mozilla {
+
+using namespace ipc;
+
+namespace dom {
+
+SharedWorkerParent::SharedWorkerParent()
+ : mBackgroundEventTarget(GetCurrentSerialEventTarget()),
+ mStatus(eInit),
+ mSuspended(false),
+ mFrozen(false) {
+ AssertIsOnBackgroundThread();
+}
+
+SharedWorkerParent::~SharedWorkerParent() = default;
+
+void SharedWorkerParent::ActorDestroy(IProtocol::ActorDestroyReason aReason) {
+ AssertIsOnBackgroundThread();
+
+ if (mWorkerManagerWrapper) {
+ mWorkerManagerWrapper->Manager()->RemoveActor(this);
+ mWorkerManagerWrapper = nullptr;
+ }
+}
+
+void SharedWorkerParent::Initialize(
+ const RemoteWorkerData& aData, uint64_t aWindowID,
+ const MessagePortIdentifier& aPortIdentifier) {
+ AssertIsOnBackgroundThread();
+ MOZ_ASSERT(mStatus == eInit);
+
+ mWindowID = aWindowID;
+
+ mStatus = ePending;
+
+ RefPtr<SharedWorkerService> service = SharedWorkerService::GetOrCreate();
+ MOZ_ASSERT(service);
+ service->GetOrCreateWorkerManager(this, aData, aWindowID, aPortIdentifier);
+}
+
+IPCResult SharedWorkerParent::RecvClose() {
+ AssertIsOnBackgroundThread();
+ MOZ_ASSERT(mStatus == ePending || mStatus == eActive);
+
+ mStatus = eClosed;
+
+ if (mWorkerManagerWrapper) {
+ mWorkerManagerWrapper->Manager()->RemoveActor(this);
+ mWorkerManagerWrapper = nullptr;
+ }
+
+ Unused << Send__delete__(this);
+ return IPC_OK();
+}
+
+IPCResult SharedWorkerParent::RecvSuspend() {
+ AssertIsOnBackgroundThread();
+ MOZ_ASSERT(!mSuspended);
+ MOZ_ASSERT(mStatus == ePending || mStatus == eActive);
+
+ mSuspended = true;
+
+ if (mStatus == eActive) {
+ MOZ_ASSERT(mWorkerManagerWrapper);
+ mWorkerManagerWrapper->Manager()->UpdateSuspend();
+ }
+
+ return IPC_OK();
+}
+
+IPCResult SharedWorkerParent::RecvResume() {
+ AssertIsOnBackgroundThread();
+ MOZ_ASSERT(mSuspended);
+ MOZ_ASSERT(mStatus == ePending || mStatus == eActive);
+
+ mSuspended = false;
+
+ if (mStatus == eActive) {
+ MOZ_ASSERT(mWorkerManagerWrapper);
+ mWorkerManagerWrapper->Manager()->UpdateSuspend();
+ }
+
+ return IPC_OK();
+}
+
+IPCResult SharedWorkerParent::RecvFreeze() {
+ AssertIsOnBackgroundThread();
+ MOZ_ASSERT(!mFrozen);
+ MOZ_ASSERT(mStatus == ePending || mStatus == eActive);
+
+ mFrozen = true;
+
+ if (mStatus == eActive) {
+ MOZ_ASSERT(mWorkerManagerWrapper);
+ mWorkerManagerWrapper->Manager()->UpdateFrozen();
+ }
+
+ return IPC_OK();
+}
+
+IPCResult SharedWorkerParent::RecvThaw() {
+ AssertIsOnBackgroundThread();
+ MOZ_ASSERT(mFrozen);
+ MOZ_ASSERT(mStatus == ePending || mStatus == eActive);
+
+ mFrozen = false;
+
+ if (mStatus == eActive) {
+ MOZ_ASSERT(mWorkerManagerWrapper);
+ mWorkerManagerWrapper->Manager()->UpdateFrozen();
+ }
+
+ return IPC_OK();
+}
+
+void SharedWorkerParent::ManagerCreated(
+ already_AddRefed<SharedWorkerManagerWrapper> aWorkerManagerWrapper) {
+ AssertIsOnBackgroundThread();
+ MOZ_ASSERT(!mWorkerManagerWrapper);
+ MOZ_ASSERT(mStatus == ePending || mStatus == eClosed);
+
+ RefPtr<SharedWorkerManagerWrapper> wrapper = aWorkerManagerWrapper;
+
+ // Already gone.
+ if (mStatus == eClosed) {
+ wrapper->Manager()->RemoveActor(this);
+ return;
+ }
+
+ mStatus = eActive;
+ mWorkerManagerWrapper = wrapper;
+
+ mWorkerManagerWrapper->Manager()->UpdateFrozen();
+ mWorkerManagerWrapper->Manager()->UpdateSuspend();
+}
+
+void SharedWorkerParent::ErrorPropagation(nsresult aError) {
+ AssertIsOnBackgroundThread();
+ MOZ_ASSERT(NS_FAILED(aError));
+ MOZ_ASSERT(mStatus == ePending || mStatus == eClosed);
+
+ // Already gone.
+ if (mStatus == eClosed) {
+ return;
+ }
+
+ Unused << SendError(aError);
+}
+
+} // namespace dom
+} // namespace mozilla
diff --git a/dom/workers/sharedworkers/SharedWorkerParent.h b/dom/workers/sharedworkers/SharedWorkerParent.h
new file mode 100644
index 0000000000..c91e66cc2e
--- /dev/null
+++ b/dom/workers/sharedworkers/SharedWorkerParent.h
@@ -0,0 +1,81 @@
+/* -*- 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/. */
+
+#ifndef mozilla_dom_dom_SharedWorkerParent_h
+#define mozilla_dom_dom_SharedWorkerParent_h
+
+#include "mozilla/dom/PSharedWorkerParent.h"
+#include "mozilla/dom/quota/CheckedUnsafePtr.h"
+#include "mozilla/ipc/BackgroundUtils.h"
+#include "nsISupportsImpl.h"
+
+namespace mozilla::dom {
+
+class MessagePortIdentifier;
+class RemoteWorkerData;
+class SharedWorkerManagerWrapper;
+
+/**
+ * PBackground actor that relays life-cycle events (freeze/thaw, suspend/resume,
+ * close) to the PBackground SharedWorkerManager and relays error/termination
+ * back to the child.
+ */
+class SharedWorkerParent final
+ : public mozilla::dom::PSharedWorkerParent,
+ public SupportsCheckedUnsafePtr<CheckIf<DiagnosticAssertEnabled>> {
+ public:
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(SharedWorkerParent)
+
+ SharedWorkerParent();
+
+ void Initialize(const RemoteWorkerData& aData, uint64_t aWindowID,
+ const MessagePortIdentifier& aPortIdentifier);
+
+ void ManagerCreated(
+ already_AddRefed<SharedWorkerManagerWrapper> aWorkerManagerWrapper);
+
+ void ErrorPropagation(nsresult aError);
+
+ mozilla::ipc::IPCResult RecvClose();
+
+ mozilla::ipc::IPCResult RecvSuspend();
+
+ mozilla::ipc::IPCResult RecvResume();
+
+ mozilla::ipc::IPCResult RecvFreeze();
+
+ mozilla::ipc::IPCResult RecvThaw();
+
+ bool IsSuspended() const { return mSuspended; }
+
+ bool IsFrozen() const { return mFrozen; }
+
+ uint64_t WindowID() const { return mWindowID; }
+
+ private:
+ ~SharedWorkerParent();
+
+ void ActorDestroy(IProtocol::ActorDestroyReason aReason) override;
+
+ nsCOMPtr<nsIEventTarget> mBackgroundEventTarget;
+ RefPtr<SharedWorkerManagerWrapper> mWorkerManagerWrapper;
+
+ enum {
+ eInit,
+ ePending,
+ eActive,
+ eClosed,
+ } mStatus;
+
+ uint64_t mWindowID;
+
+ bool mSuspended;
+ bool mFrozen;
+};
+
+} // namespace mozilla::dom
+
+#endif // mozilla_dom_dom_SharedWorkerParent_h
diff --git a/dom/workers/sharedworkers/SharedWorkerService.cpp b/dom/workers/sharedworkers/SharedWorkerService.cpp
new file mode 100644
index 0000000000..c7e3ca1d4c
--- /dev/null
+++ b/dom/workers/sharedworkers/SharedWorkerService.cpp
@@ -0,0 +1,261 @@
+/* -*- 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 "SharedWorkerService.h"
+#include "mozilla/dom/MessagePort.h"
+#include "mozilla/dom/RemoteWorkerTypes.h"
+#include "mozilla/dom/SharedWorkerManager.h"
+#include "mozilla/ipc/BackgroundParent.h"
+#include "mozilla/ipc/URIUtils.h"
+#include "mozilla/BasePrincipal.h"
+#include "mozilla/ClearOnShutdown.h"
+#include "mozilla/SchedulerGroup.h"
+#include "mozilla/StaticMutex.h"
+#include "nsIPrincipal.h"
+#include "nsProxyRelease.h"
+
+namespace mozilla {
+
+using namespace ipc;
+
+namespace dom {
+
+namespace {
+
+StaticMutex sSharedWorkerMutex;
+
+StaticRefPtr<SharedWorkerService> sSharedWorkerService;
+
+class GetOrCreateWorkerManagerRunnable final : public Runnable {
+ public:
+ GetOrCreateWorkerManagerRunnable(SharedWorkerService* aService,
+ SharedWorkerParent* aActor,
+ const RemoteWorkerData& aData,
+ uint64_t aWindowID,
+ const MessagePortIdentifier& aPortIdentifier)
+ : Runnable("GetOrCreateWorkerManagerRunnable"),
+ mBackgroundEventTarget(GetCurrentSerialEventTarget()),
+ mService(aService),
+ mActor(aActor),
+ mData(aData),
+ mWindowID(aWindowID),
+ mPortIdentifier(aPortIdentifier) {}
+
+ NS_IMETHOD
+ Run() {
+ mService->GetOrCreateWorkerManagerOnMainThread(
+ mBackgroundEventTarget, mActor, mData, mWindowID, mPortIdentifier);
+
+ return NS_OK;
+ }
+
+ private:
+ nsCOMPtr<nsIEventTarget> mBackgroundEventTarget;
+ RefPtr<SharedWorkerService> mService;
+ RefPtr<SharedWorkerParent> mActor;
+ RemoteWorkerData mData;
+ uint64_t mWindowID;
+ UniqueMessagePortId mPortIdentifier;
+};
+
+class WorkerManagerCreatedRunnable final : public Runnable {
+ public:
+ WorkerManagerCreatedRunnable(
+ already_AddRefed<SharedWorkerManagerWrapper> aManagerWrapper,
+ SharedWorkerParent* aActor, const RemoteWorkerData& aData,
+ uint64_t aWindowID, UniqueMessagePortId& aPortIdentifier)
+ : Runnable("WorkerManagerCreatedRunnable"),
+ mManagerWrapper(aManagerWrapper),
+ mActor(aActor),
+ mData(aData),
+ mWindowID(aWindowID),
+ mPortIdentifier(std::move(aPortIdentifier)) {}
+
+ NS_IMETHOD
+ Run() {
+ AssertIsOnBackgroundThread();
+
+ if (NS_WARN_IF(
+ !mActor->CanSend() ||
+ !mManagerWrapper->Manager()->MaybeCreateRemoteWorker(
+ mData, mWindowID, mPortIdentifier, mActor->OtherPid()))) {
+ // If we cannot send, the error won't arrive, but we may log something.
+ mActor->ErrorPropagation(NS_ERROR_FAILURE);
+ return NS_OK;
+ }
+
+ mManagerWrapper->Manager()->AddActor(mActor);
+ mActor->ManagerCreated(mManagerWrapper.forget());
+ return NS_OK;
+ }
+
+ private:
+ RefPtr<SharedWorkerManagerWrapper> mManagerWrapper;
+ RefPtr<SharedWorkerParent> mActor;
+ RemoteWorkerData mData;
+ uint64_t mWindowID;
+ UniqueMessagePortId mPortIdentifier;
+};
+
+class ErrorPropagationRunnable final : public Runnable {
+ public:
+ ErrorPropagationRunnable(SharedWorkerParent* aActor, nsresult aError)
+ : Runnable("ErrorPropagationRunnable"), mActor(aActor), mError(aError) {}
+
+ NS_IMETHOD
+ Run() {
+ AssertIsOnBackgroundThread();
+ mActor->ErrorPropagation(mError);
+ return NS_OK;
+ }
+
+ private:
+ RefPtr<SharedWorkerParent> mActor;
+ nsresult mError;
+};
+
+} // namespace
+
+/* static */
+already_AddRefed<SharedWorkerService> SharedWorkerService::GetOrCreate() {
+ AssertIsOnBackgroundThread();
+
+ StaticMutexAutoLock lock(sSharedWorkerMutex);
+
+ if (!sSharedWorkerService) {
+ sSharedWorkerService = new SharedWorkerService();
+ // ClearOnShutdown can only be called on main thread
+ nsresult rv = SchedulerGroup::Dispatch(NS_NewRunnableFunction(
+ "RegisterSharedWorkerServiceClearOnShutdown", []() {
+ StaticMutexAutoLock lock(sSharedWorkerMutex);
+ MOZ_ASSERT(sSharedWorkerService);
+ ClearOnShutdown(&sSharedWorkerService);
+ }));
+ Unused << NS_WARN_IF(NS_FAILED(rv));
+ }
+
+ RefPtr<SharedWorkerService> instance = sSharedWorkerService;
+ return instance.forget();
+}
+
+/* static */
+SharedWorkerService* SharedWorkerService::Get() {
+ StaticMutexAutoLock lock(sSharedWorkerMutex);
+
+ MOZ_ASSERT(sSharedWorkerService);
+ return sSharedWorkerService;
+}
+
+void SharedWorkerService::GetOrCreateWorkerManager(
+ SharedWorkerParent* aActor, const RemoteWorkerData& aData,
+ uint64_t aWindowID, const MessagePortIdentifier& aPortIdentifier) {
+ AssertIsOnBackgroundThread();
+
+ // The real check happens on main-thread.
+ RefPtr<GetOrCreateWorkerManagerRunnable> r =
+ new GetOrCreateWorkerManagerRunnable(this, aActor, aData, aWindowID,
+ aPortIdentifier);
+
+ nsresult rv = SchedulerGroup::Dispatch(r.forget());
+ Unused << NS_WARN_IF(NS_FAILED(rv));
+}
+
+void SharedWorkerService::GetOrCreateWorkerManagerOnMainThread(
+ nsIEventTarget* aBackgroundEventTarget, SharedWorkerParent* aActor,
+ const RemoteWorkerData& aData, uint64_t aWindowID,
+ UniqueMessagePortId& aPortIdentifier) {
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(aBackgroundEventTarget);
+ MOZ_ASSERT(aActor);
+
+ auto partitionedPrincipalOrErr =
+ PrincipalInfoToPrincipal(aData.partitionedPrincipalInfo());
+ if (NS_WARN_IF(partitionedPrincipalOrErr.isErr())) {
+ ErrorPropagationOnMainThread(aBackgroundEventTarget, aActor,
+ partitionedPrincipalOrErr.unwrapErr());
+ return;
+ }
+
+ auto loadingPrincipalOrErr =
+ PrincipalInfoToPrincipal(aData.loadingPrincipalInfo());
+ if (NS_WARN_IF(loadingPrincipalOrErr.isErr())) {
+ ErrorPropagationOnMainThread(aBackgroundEventTarget, aActor,
+ loadingPrincipalOrErr.unwrapErr());
+ return;
+ }
+
+ RefPtr<SharedWorkerManagerHolder> managerHolder;
+
+ nsCOMPtr<nsIPrincipal> loadingPrincipal = loadingPrincipalOrErr.unwrap();
+ nsCOMPtr<nsIPrincipal> partitionedPrincipal =
+ partitionedPrincipalOrErr.unwrap();
+
+ nsCOMPtr<nsIPrincipal> effectiveStoragePrincipal = partitionedPrincipal;
+ if (aData.useRegularPrincipal()) {
+ effectiveStoragePrincipal = loadingPrincipal;
+ }
+
+ // Let's see if there is already a SharedWorker to share.
+ nsCOMPtr<nsIURI> resolvedScriptURL =
+ DeserializeURI(aData.resolvedScriptURL());
+ for (SharedWorkerManager* workerManager : mWorkerManagers) {
+ managerHolder = workerManager->MatchOnMainThread(
+ this, aData.domain(), resolvedScriptURL, aData.name(), loadingPrincipal,
+ BasePrincipal::Cast(effectiveStoragePrincipal)->OriginAttributesRef());
+ if (managerHolder) {
+ break;
+ }
+ }
+
+ // Let's create a new one.
+ if (!managerHolder) {
+ managerHolder = SharedWorkerManager::Create(
+ this, aBackgroundEventTarget, aData, loadingPrincipal,
+ BasePrincipal::Cast(effectiveStoragePrincipal)->OriginAttributesRef());
+
+ mWorkerManagers.AppendElement(managerHolder->Manager());
+ } else {
+ // We are attaching the actor to an existing one.
+ if (managerHolder->Manager()->IsSecureContext() !=
+ aData.isSecureContext()) {
+ ErrorPropagationOnMainThread(aBackgroundEventTarget, aActor,
+ NS_ERROR_DOM_SECURITY_ERR);
+ return;
+ }
+ }
+
+ RefPtr<SharedWorkerManagerWrapper> wrapper =
+ new SharedWorkerManagerWrapper(managerHolder.forget());
+
+ RefPtr<WorkerManagerCreatedRunnable> r = new WorkerManagerCreatedRunnable(
+ wrapper.forget(), aActor, aData, aWindowID, aPortIdentifier);
+ aBackgroundEventTarget->Dispatch(r.forget(), NS_DISPATCH_NORMAL);
+}
+
+void SharedWorkerService::ErrorPropagationOnMainThread(
+ nsIEventTarget* aBackgroundEventTarget, SharedWorkerParent* aActor,
+ nsresult aError) {
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(aBackgroundEventTarget);
+ MOZ_ASSERT(aActor);
+ MOZ_ASSERT(NS_FAILED(aError));
+
+ RefPtr<ErrorPropagationRunnable> r =
+ new ErrorPropagationRunnable(aActor, aError);
+ aBackgroundEventTarget->Dispatch(r.forget(), NS_DISPATCH_NORMAL);
+}
+
+void SharedWorkerService::RemoveWorkerManagerOnMainThread(
+ SharedWorkerManager* aManager) {
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(aManager);
+ MOZ_ASSERT(mWorkerManagers.Contains(aManager));
+
+ mWorkerManagers.RemoveElement(aManager);
+}
+
+} // namespace dom
+} // namespace mozilla
diff --git a/dom/workers/sharedworkers/SharedWorkerService.h b/dom/workers/sharedworkers/SharedWorkerService.h
new file mode 100644
index 0000000000..c4671163bd
--- /dev/null
+++ b/dom/workers/sharedworkers/SharedWorkerService.h
@@ -0,0 +1,74 @@
+/* -*- 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/. */
+
+#ifndef mozilla_dom_SharedWorkerService_h
+#define mozilla_dom_SharedWorkerService_h
+
+#include "mozilla/dom/quota/CheckedUnsafePtr.h"
+#include "nsISupportsImpl.h"
+#include "nsTArray.h"
+
+class nsIEventTarget;
+
+namespace mozilla {
+
+namespace ipc {
+class PrincipalInfo;
+}
+
+namespace dom {
+
+class MessagePortIdentifier;
+class RemoteWorkerData;
+class SharedWorkerManager;
+class SharedWorkerParent;
+class UniqueMessagePortId;
+
+/**
+ * PBackground service that creates and tracks the per-worker
+ * SharedWorkerManager instances, allowing rendezvous between SharedWorkerParent
+ * instances and the SharedWorkerManagers they want to talk to (1:1).
+ */
+class SharedWorkerService final {
+ public:
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(SharedWorkerService);
+
+ // This can be called on PBackground thread only.
+ static already_AddRefed<SharedWorkerService> GetOrCreate();
+
+ // The service, if already created, is available on any thread using this
+ // method.
+ static SharedWorkerService* Get();
+
+ // PBackground method only.
+ void GetOrCreateWorkerManager(SharedWorkerParent* aActor,
+ const RemoteWorkerData& aData,
+ uint64_t aWindowID,
+ const MessagePortIdentifier& aPortIdentifier);
+
+ void GetOrCreateWorkerManagerOnMainThread(
+ nsIEventTarget* aBackgroundEventTarget, SharedWorkerParent* aActor,
+ const RemoteWorkerData& aData, uint64_t aWindowID,
+ UniqueMessagePortId& aPortIdentifier);
+
+ void RemoveWorkerManagerOnMainThread(SharedWorkerManager* aManager);
+
+ private:
+ SharedWorkerService() = default;
+ ~SharedWorkerService() = default;
+
+ void ErrorPropagationOnMainThread(nsIEventTarget* aBackgroundEventTarget,
+ SharedWorkerParent* aActor,
+ nsresult aError);
+
+ // Touched on main-thread only.
+ nsTArray<RefPtr<SharedWorkerManager>> mWorkerManagers;
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_SharedWorkerService_h
diff --git a/dom/workers/sharedworkers/moz.build b/dom/workers/sharedworkers/moz.build
new file mode 100644
index 0000000000..2b83bc9525
--- /dev/null
+++ b/dom/workers/sharedworkers/moz.build
@@ -0,0 +1,28 @@
+# -*- 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/.
+
+EXPORTS.mozilla.dom += [
+ "SharedWorker.h",
+ "SharedWorkerChild.h",
+ "SharedWorkerManager.h",
+ "SharedWorkerParent.h",
+]
+
+UNIFIED_SOURCES += [
+ "SharedWorker.cpp",
+ "SharedWorkerChild.cpp",
+ "SharedWorkerManager.cpp",
+ "SharedWorkerParent.cpp",
+ "SharedWorkerService.cpp",
+]
+
+IPDL_SOURCES += [
+ "PSharedWorker.ipdl",
+]
+
+include("/ipc/chromium/chromium-config.mozbuild")
+
+FINAL_LIBRARY = "xul"