summaryrefslogtreecommitdiffstats
path: root/dom/workers/sharedworkers/SharedWorkerManager.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'dom/workers/sharedworkers/SharedWorkerManager.cpp')
-rw-r--r--dom/workers/sharedworkers/SharedWorkerManager.cpp348
1 files changed, 348 insertions, 0 deletions
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