summaryrefslogtreecommitdiffstats
path: root/dom/serviceworkers/ServiceWorker.cpp
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 14:29:10 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 14:29:10 +0000
commit2aa4a82499d4becd2284cdb482213d541b8804dd (patch)
treeb80bf8bf13c3766139fbacc530efd0dd9d54394c /dom/serviceworkers/ServiceWorker.cpp
parentInitial commit. (diff)
downloadfirefox-2aa4a82499d4becd2284cdb482213d541b8804dd.tar.xz
firefox-2aa4a82499d4becd2284cdb482213d541b8804dd.zip
Adding upstream version 86.0.1.upstream/86.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'dom/serviceworkers/ServiceWorker.cpp')
-rw-r--r--dom/serviceworkers/ServiceWorker.cpp274
1 files changed, 274 insertions, 0 deletions
diff --git a/dom/serviceworkers/ServiceWorker.cpp b/dom/serviceworkers/ServiceWorker.cpp
new file mode 100644
index 0000000000..47c4e6ff44
--- /dev/null
+++ b/dom/serviceworkers/ServiceWorker.cpp
@@ -0,0 +1,274 @@
+/* -*- 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 "ServiceWorker.h"
+
+#include "mozilla/dom/Document.h"
+#include "nsGlobalWindowInner.h"
+#include "nsPIDOMWindow.h"
+#include "RemoteServiceWorkerImpl.h"
+#include "ServiceWorkerCloneData.h"
+#include "ServiceWorkerImpl.h"
+#include "ServiceWorkerManager.h"
+#include "ServiceWorkerPrivate.h"
+#include "ServiceWorkerRegistration.h"
+#include "ServiceWorkerUtils.h"
+
+#include "mozilla/dom/ClientIPCTypes.h"
+#include "mozilla/dom/ClientState.h"
+#include "mozilla/dom/MessagePortBinding.h"
+#include "mozilla/dom/Promise.h"
+#include "mozilla/dom/ServiceWorkerGlobalScopeBinding.h"
+#include "mozilla/dom/WorkerPrivate.h"
+#include "mozilla/StaticPrefs_dom.h"
+#include "mozilla/StorageAccess.h"
+#include "nsGlobalWindowInner.h"
+
+#ifdef XP_WIN
+# undef PostMessage
+#endif
+
+using mozilla::ErrorResult;
+using namespace mozilla::dom;
+
+namespace mozilla {
+namespace dom {
+
+bool ServiceWorkerVisible(JSContext* aCx, JSObject* aObj) {
+ if (NS_IsMainThread()) {
+ return StaticPrefs::dom_serviceWorkers_enabled();
+ }
+
+ return IS_INSTANCE_OF(ServiceWorkerGlobalScope, aObj);
+}
+
+// static
+already_AddRefed<ServiceWorker> ServiceWorker::Create(
+ nsIGlobalObject* aOwner, const ServiceWorkerDescriptor& aDescriptor) {
+ RefPtr<ServiceWorker> ref;
+ RefPtr<ServiceWorker::Inner> inner;
+
+ if (ServiceWorkerParentInterceptEnabled()) {
+ inner = new RemoteServiceWorkerImpl(aDescriptor);
+ } else {
+ RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
+ NS_ENSURE_TRUE(swm, nullptr);
+
+ RefPtr<ServiceWorkerRegistrationInfo> reg =
+ swm->GetRegistration(aDescriptor.PrincipalInfo(), aDescriptor.Scope());
+ NS_ENSURE_TRUE(reg, nullptr);
+
+ RefPtr<ServiceWorkerInfo> info = reg->GetByDescriptor(aDescriptor);
+ NS_ENSURE_TRUE(info, nullptr);
+
+ inner = new ServiceWorkerImpl(info, reg);
+ }
+
+ NS_ENSURE_TRUE(inner, nullptr);
+
+ ref = new ServiceWorker(aOwner, aDescriptor, inner);
+ return ref.forget();
+}
+
+ServiceWorker::ServiceWorker(nsIGlobalObject* aGlobal,
+ const ServiceWorkerDescriptor& aDescriptor,
+ ServiceWorker::Inner* aInner)
+ : DOMEventTargetHelper(aGlobal),
+ mDescriptor(aDescriptor),
+ mInner(aInner),
+ mLastNotifiedState(ServiceWorkerState::Installing) {
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_DIAGNOSTIC_ASSERT(aGlobal);
+ MOZ_DIAGNOSTIC_ASSERT(mInner);
+
+ KeepAliveIfHasListenersFor(u"statechange"_ns);
+
+ // The error event handler is required by the spec currently, but is not used
+ // anywhere. Don't keep the object alive in that case.
+
+ // This will update our state too.
+ mInner->AddServiceWorker(this);
+
+ // Attempt to get an existing binding object for the registration
+ // associated with this ServiceWorker.
+ RefPtr<ServiceWorkerRegistration> reg =
+ aGlobal->GetServiceWorkerRegistration(ServiceWorkerRegistrationDescriptor(
+ mDescriptor.RegistrationId(), mDescriptor.RegistrationVersion(),
+ mDescriptor.PrincipalInfo(), mDescriptor.Scope(),
+ ServiceWorkerUpdateViaCache::Imports));
+ if (reg) {
+ MaybeAttachToRegistration(reg);
+ } else {
+ RefPtr<ServiceWorker> self = this;
+
+ mInner->GetRegistration(
+ [self = std::move(self)](
+ const ServiceWorkerRegistrationDescriptor& aDescriptor) {
+ nsIGlobalObject* global = self->GetParentObject();
+ NS_ENSURE_TRUE_VOID(global);
+ RefPtr<ServiceWorkerRegistration> reg =
+ global->GetOrCreateServiceWorkerRegistration(aDescriptor);
+ self->MaybeAttachToRegistration(reg);
+ },
+ [](ErrorResult&& aRv) {
+ // do nothing
+ aRv.SuppressException();
+ });
+ }
+}
+
+ServiceWorker::~ServiceWorker() {
+ MOZ_ASSERT(NS_IsMainThread());
+ mInner->RemoveServiceWorker(this);
+}
+
+NS_IMPL_CYCLE_COLLECTION_INHERITED(ServiceWorker, DOMEventTargetHelper,
+ mRegistration);
+
+NS_IMPL_ADDREF_INHERITED(ServiceWorker, DOMEventTargetHelper)
+NS_IMPL_RELEASE_INHERITED(ServiceWorker, DOMEventTargetHelper)
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ServiceWorker)
+ NS_INTERFACE_MAP_ENTRY(ServiceWorker)
+NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
+
+JSObject* ServiceWorker::WrapObject(JSContext* aCx,
+ JS::Handle<JSObject*> aGivenProto) {
+ MOZ_ASSERT(NS_IsMainThread());
+
+ return ServiceWorker_Binding::Wrap(aCx, this, aGivenProto);
+}
+
+ServiceWorkerState ServiceWorker::State() const { return mDescriptor.State(); }
+
+void ServiceWorker::SetState(ServiceWorkerState aState) {
+ NS_ENSURE_TRUE_VOID(aState >= mDescriptor.State());
+ mDescriptor.SetState(aState);
+}
+
+void ServiceWorker::MaybeDispatchStateChangeEvent() {
+ if (mDescriptor.State() <= mLastNotifiedState || !GetParentObject()) {
+ return;
+ }
+ mLastNotifiedState = mDescriptor.State();
+
+ DOMEventTargetHelper::DispatchTrustedEvent(u"statechange"_ns);
+
+ // Once we have transitioned to the redundant state then no
+ // more statechange events will occur. We can allow the DOM
+ // object to GC if script is not holding it alive.
+ if (mLastNotifiedState == ServiceWorkerState::Redundant) {
+ IgnoreKeepAliveIfHasListenersFor(u"statechange"_ns);
+ }
+}
+
+void ServiceWorker::GetScriptURL(nsString& aURL) const {
+ CopyUTF8toUTF16(mDescriptor.ScriptURL(), aURL);
+}
+
+void ServiceWorker::PostMessage(JSContext* aCx, JS::Handle<JS::Value> aMessage,
+ const Sequence<JSObject*>& aTransferable,
+ ErrorResult& aRv) {
+ if (State() == ServiceWorkerState::Redundant) {
+ aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
+ return;
+ }
+
+ nsPIDOMWindowInner* window = GetOwner();
+ if (NS_WARN_IF(!window || !window->GetExtantDoc())) {
+ aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
+ return;
+ }
+
+ auto storageAllowed = StorageAllowedForWindow(window);
+ if (storageAllowed != StorageAccess::eAllow) {
+ ServiceWorkerManager::LocalizeAndReportToAllClients(
+ mDescriptor.Scope(), "ServiceWorkerPostMessageStorageError",
+ nsTArray<nsString>{NS_ConvertUTF8toUTF16(mDescriptor.Scope())});
+ aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
+ return;
+ }
+
+ Maybe<ClientInfo> clientInfo = window->GetClientInfo();
+ Maybe<ClientState> clientState = window->GetClientState();
+ if (NS_WARN_IF(clientInfo.isNothing() || clientState.isNothing())) {
+ aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
+ return;
+ }
+
+ JS::Rooted<JS::Value> transferable(aCx, JS::UndefinedValue());
+ aRv = nsContentUtils::CreateJSValueFromSequenceOfObject(aCx, aTransferable,
+ &transferable);
+ if (aRv.Failed()) {
+ return;
+ }
+
+ // Window-to-SW messages do not allow memory sharing since they are not in the
+ // same agent cluster group, but we do not want to throw an error during the
+ // serialization. Because of this, ServiceWorkerCloneData will propagate an
+ // error message data if the SameProcess serialization is required. So that
+ // the receiver (service worker) knows that it needs to throw while
+ // deserialization and sharing memory objects are not propagated to the other
+ // process.
+ JS::CloneDataPolicy clonePolicy;
+ if (nsGlobalWindowInner::Cast(window)->IsSharedMemoryAllowed()) {
+ clonePolicy.allowSharedMemoryObjects();
+ }
+
+ RefPtr<ServiceWorkerCloneData> data = new ServiceWorkerCloneData();
+ data->Write(aCx, aMessage, transferable, clonePolicy, aRv);
+ if (aRv.Failed()) {
+ return;
+ }
+
+ // The value of CloneScope() is set while StructuredCloneData::Write(). If the
+ // aValue contiains a shared memory object, then the scope will be restricted
+ // and thus return SameProcess. If not, it will return DifferentProcess.
+ //
+ // When we postMessage a shared memory object from a window to a service
+ // worker, the object must be sent from a cross-origin isolated process to
+ // another one. So, we mark mark this data as an error message data if the
+ // scope is limited to same process.
+ if (data->CloneScope() ==
+ StructuredCloneHolder::StructuredCloneScope::SameProcess) {
+ data->SetAsErrorMessageData();
+ }
+
+ mInner->PostMessage(std::move(data), clientInfo.ref(), clientState.ref());
+}
+
+void ServiceWorker::PostMessage(JSContext* aCx, JS::Handle<JS::Value> aMessage,
+ const PostMessageOptions& aOptions,
+ ErrorResult& aRv) {
+ PostMessage(aCx, aMessage, aOptions.mTransfer, aRv);
+}
+
+const ServiceWorkerDescriptor& ServiceWorker::Descriptor() const {
+ return mDescriptor;
+}
+
+void ServiceWorker::DisconnectFromOwner() {
+ DOMEventTargetHelper::DisconnectFromOwner();
+}
+
+void ServiceWorker::MaybeAttachToRegistration(
+ ServiceWorkerRegistration* aRegistration) {
+ MOZ_DIAGNOSTIC_ASSERT(aRegistration);
+ MOZ_DIAGNOSTIC_ASSERT(!mRegistration);
+
+ // If the registration no longer actually references this ServiceWorker
+ // then we must be in the redundant state.
+ if (!aRegistration->Descriptor().HasWorker(mDescriptor)) {
+ SetState(ServiceWorkerState::Redundant);
+ MaybeDispatchStateChangeEvent();
+ return;
+ }
+
+ mRegistration = aRegistration;
+}
+
+} // namespace dom
+} // namespace mozilla