summaryrefslogtreecommitdiffstats
path: root/dom/workers/sharedworkers
diff options
context:
space:
mode:
Diffstat (limited to 'dom/workers/sharedworkers')
-rw-r--r--dom/workers/sharedworkers/PSharedWorker.ipdl31
-rw-r--r--dom/workers/sharedworkers/SharedWorker.cpp398
-rw-r--r--dom/workers/sharedworkers/SharedWorker.h90
-rw-r--r--dom/workers/sharedworkers/SharedWorkerChild.cpp131
-rw-r--r--dom/workers/sharedworkers/SharedWorkerChild.h55
-rw-r--r--dom/workers/sharedworkers/SharedWorkerManager.cpp298
-rw-r--r--dom/workers/sharedworkers/SharedWorkerManager.h151
-rw-r--r--dom/workers/sharedworkers/SharedWorkerParent.cpp165
-rw-r--r--dom/workers/sharedworkers/SharedWorkerParent.h78
-rw-r--r--dom/workers/sharedworkers/SharedWorkerService.cpp260
-rw-r--r--dom/workers/sharedworkers/SharedWorkerService.h69
-rw-r--r--dom/workers/sharedworkers/moz.build28
12 files changed, 1754 insertions, 0 deletions
diff --git a/dom/workers/sharedworkers/PSharedWorker.ipdl b/dom/workers/sharedworkers/PSharedWorker.ipdl
new file mode 100644
index 0000000000..31c34e1b94
--- /dev/null
+++ b/dom/workers/sharedworkers/PSharedWorker.ipdl
@@ -0,0 +1,31 @@
+/* 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 PSharedWorker
+{
+ manager PBackground;
+
+parent:
+ async Close();
+ async Suspend();
+ async Resume();
+ async Freeze();
+ async Thaw();
+
+child:
+ async Error(ErrorValue value);
+ 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..1b548fc00b
--- /dev/null
+++ b/dom/workers/sharedworkers/SharedWorker.cpp
@@ -0,0 +1,398 @@
+/* -*- 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/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 "nsContentUtils.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);
+
+ auto storageAllowed = StorageAllowedForWindow(window);
+ if (storageAllowed == StorageAccess::eDeny) {
+ aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
+ return nullptr;
+ }
+
+ if (ShouldPartitionStorage(storageAllowed) &&
+ !StoragePartitioningEnabled(
+ storageAllowed, window->GetExtantDoc()->CookieJarSettings())) {
+ aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
+ return nullptr;
+ }
+
+ // Assert that the principal private browsing state matches the
+ // StorageAccess value.
+#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
+ if (storageAllowed == StorageAccess::ePrivateBrowsing) {
+ nsCOMPtr<Document> doc = window->GetExtantDoc();
+ nsCOMPtr<nsIPrincipal> principal = doc ? doc->NodePrincipal() : nullptr;
+ uint32_t privateBrowsingId = 0;
+ if (principal) {
+ MOZ_ALWAYS_SUCCEEDS(principal->GetPrivateBrowsingId(&privateBrowsingId));
+ }
+ MOZ_DIAGNOSTIC_ASSERT(privateBrowsingId != 0);
+ }
+#endif // MOZ_DIAGNOSTIC_ASSERT_ENABLED
+
+ nsAutoString name;
+ if (aOptions.IsString()) {
+ name = aOptions.GetAsString();
+ } else {
+ MOZ_ASSERT(aOptions.IsWorkerOptions());
+ name = aOptions.GetAsWorkerOptions().mName;
+ }
+
+ JSContext* cx = aGlobal.Context();
+
+ WorkerLoadInfo loadInfo;
+ aRv = WorkerPrivate::GetLoadInfo(cx, window, nullptr, aScriptURL, false,
+ WorkerPrivate::OverrideLoadGroup,
+ WorkerTypeShared, &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.
+ if (ShouldPartitionStorage(storageAllowed)) {
+ nsCOMPtr<nsIScriptObjectPrincipal> sop = do_QueryInterface(window);
+ if (!sop) {
+ aRv.Throw(NS_ERROR_FAILURE);
+ return nullptr;
+ }
+
+ nsIPrincipal* windowPrincipal = sop->GetPrincipal();
+ if (!windowPrincipal) {
+ aRv.Throw(NS_ERROR_UNEXPECTED);
+ return nullptr;
+ }
+
+ nsIPrincipal* windowPartitionedPrincipal = sop->PartitionedPrincipal();
+ if (!windowPartitionedPrincipal) {
+ aRv.Throw(NS_ERROR_UNEXPECTED);
+ 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.
+ PBackgroundChild* actorChild = BackgroundChild::GetOrCreateForCurrentThread();
+
+ 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 = nsContentUtils::GenerateUUID();
+
+ net::CookieJarSettingsArgs cjsData;
+ 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,
+ remoteType.unwrap());
+
+ PSharedWorkerChild* pActor = actorChild->SendPSharedWorkerConstructor(
+ remoteWorkerData, loadInfo.mWindowID, portIdentifier.release());
+
+ RefPtr<SharedWorkerChild> actor = static_cast<SharedWorkerChild*>(pActor);
+ MOZ_ASSERT(actor);
+
+ 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::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..af42db120b
--- /dev/null
+++ b/dom/workers/sharedworkers/SharedWorker.h
@@ -0,0 +1,90 @@
+/* -*- 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;
+
+class SharedWorker final : public DOMEventTargetHelper {
+ typedef mozilla::ErrorResult ErrorResult;
+ typedef mozilla::dom::GlobalObject 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 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..e3395d91fe
--- /dev/null
+++ b/dom/workers/sharedworkers/SharedWorkerChild.cpp
@@ -0,0 +1,131 @@
+/* -*- 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/ScriptSettings.h"
+#include "mozilla/dom/SharedWorker.h"
+#include "mozilla/dom/WorkerError.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::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::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..12832a317b
--- /dev/null
+++ b/dom/workers/sharedworkers/SharedWorkerChild.h
@@ -0,0 +1,55 @@
+/* -*- 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 {
+namespace dom {
+
+class SharedWorker;
+
+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 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 dom
+} // namespace mozilla
+
+#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..8568e8147f
--- /dev/null
+++ b/dom/workers/sharedworkers/SharedWorkerManager.cpp
@@ -0,0 +1,298 @@
+/* -*- 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 {
+namespace 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();
+
+ 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);
+
+ // 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->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::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 dom
+} // namespace mozilla
diff --git a/dom/workers/sharedworkers/SharedWorkerManager.h b/dom/workers/sharedworkers/SharedWorkerManager.h
new file mode 100644
index 0000000000..fab4f3d90d
--- /dev/null
+++ b/dom/workers/sharedworkers/SharedWorkerManager.h
@@ -0,0 +1,151 @@
+/* -*- 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 {
+namespace 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;
+};
+
+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 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;
+
+ // 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 dom
+} // namespace mozilla
+
+#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..cd2b5a0d16
--- /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(GetCurrentEventTarget()),
+ 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..6f89cfdd4b
--- /dev/null
+++ b/dom/workers/sharedworkers/SharedWorkerParent.h
@@ -0,0 +1,78 @@
+/* -*- 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 {
+namespace dom {
+
+class MessagePortIdentifier;
+class RemoteWorkerData;
+class SharedWorkerManagerWrapper;
+
+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 dom
+} // namespace mozilla
+
+#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..82a82a9f96
--- /dev/null
+++ b/dom/workers/sharedworkers/SharedWorkerService.cpp
@@ -0,0 +1,260 @@
+/* -*- 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(GetCurrentEventTarget()),
+ 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(!mManagerWrapper->Manager()->MaybeCreateRemoteWorker(
+ mData, mWindowID, mPortIdentifier, mActor->OtherPid()))) {
+ 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(
+ TaskCategory::Other,
+ 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(TaskCategory::Other, 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..39eb345768
--- /dev/null
+++ b/dom/workers/sharedworkers/SharedWorkerService.h
@@ -0,0 +1,69 @@
+/* -*- 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;
+
+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"