Bug 1885580 - Add a MenuGroup component for the menu redesign r=android-reviewers,007
[gecko.git] / dom / serviceworkers / ServiceWorker.cpp
blobc554124fac759d592c4a6b9a1b9bb78dfa613eea
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
5 * You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "ServiceWorker.h"
9 #include "mozilla/dom/Document.h"
10 #include "nsGlobalWindowInner.h"
11 #include "nsPIDOMWindow.h"
12 #include "ServiceWorkerChild.h"
13 #include "ServiceWorkerCloneData.h"
14 #include "ServiceWorkerManager.h"
15 #include "ServiceWorkerPrivate.h"
16 #include "ServiceWorkerRegistration.h"
17 #include "ServiceWorkerUtils.h"
19 #include "mozilla/dom/ClientIPCTypes.h"
20 #include "mozilla/dom/ClientState.h"
21 #include "mozilla/dom/MessagePortBinding.h"
22 #include "mozilla/dom/Navigator.h"
23 #include "mozilla/dom/Promise.h"
24 #include "mozilla/dom/ServiceWorkerGlobalScopeBinding.h"
25 #include "mozilla/dom/WorkerPrivate.h"
26 #include "mozilla/ipc/BackgroundChild.h"
27 #include "mozilla/ipc/PBackgroundChild.h"
28 #include "mozilla/BasePrincipal.h"
29 #include "mozilla/StaticPrefs_dom.h"
30 #include "mozilla/StorageAccess.h"
32 #ifdef XP_WIN
33 # undef PostMessage
34 #endif
36 using mozilla::ipc::BackgroundChild;
37 using mozilla::ipc::PBackgroundChild;
39 namespace mozilla::dom {
41 // static
42 already_AddRefed<ServiceWorker> ServiceWorker::Create(
43 nsIGlobalObject* aOwner, const ServiceWorkerDescriptor& aDescriptor) {
44 RefPtr<ServiceWorker> ref = new ServiceWorker(aOwner, aDescriptor);
45 return ref.forget();
48 ServiceWorker::ServiceWorker(nsIGlobalObject* aGlobal,
49 const ServiceWorkerDescriptor& aDescriptor)
50 : DOMEventTargetHelper(aGlobal),
51 mDescriptor(aDescriptor),
52 mShutdown(false),
53 mLastNotifiedState(ServiceWorkerState::Installing) {
54 MOZ_ASSERT(NS_IsMainThread());
55 MOZ_DIAGNOSTIC_ASSERT(aGlobal);
57 PBackgroundChild* parentActor =
58 BackgroundChild::GetOrCreateForCurrentThread();
59 if (NS_WARN_IF(!parentActor)) {
60 Shutdown();
61 return;
64 RefPtr<ServiceWorkerChild> actor = ServiceWorkerChild::Create();
65 if (NS_WARN_IF(!actor)) {
66 Shutdown();
67 return;
70 PServiceWorkerChild* sentActor =
71 parentActor->SendPServiceWorkerConstructor(actor, aDescriptor.ToIPC());
72 if (NS_WARN_IF(!sentActor)) {
73 Shutdown();
74 return;
76 MOZ_DIAGNOSTIC_ASSERT(sentActor == actor);
78 mActor = std::move(actor);
79 mActor->SetOwner(this);
81 KeepAliveIfHasListenersFor(nsGkAtoms::onstatechange);
83 // The error event handler is required by the spec currently, but is not used
84 // anywhere. Don't keep the object alive in that case.
86 // Attempt to get an existing binding object for the registration
87 // associated with this ServiceWorker.
88 RefPtr<ServiceWorkerRegistration> reg =
89 aGlobal->GetServiceWorkerRegistration(ServiceWorkerRegistrationDescriptor(
90 mDescriptor.RegistrationId(), mDescriptor.RegistrationVersion(),
91 mDescriptor.PrincipalInfo(), mDescriptor.Scope(),
92 ServiceWorkerUpdateViaCache::Imports));
94 if (reg) {
95 MaybeAttachToRegistration(reg);
96 // Following codes are commented since GetRegistration has no
97 // implementation. If we can not get an existing binding object, probably
98 // need to create one to associate to it.
99 // https://bugzilla.mozilla.org/show_bug.cgi?id=1769652
101 } else {
103 RefPtr<ServiceWorker> self = this;
104 GetRegistration(
105 [self = std::move(self)](
106 const ServiceWorkerRegistrationDescriptor& aDescriptor) {
107 nsIGlobalObject* global = self->GetParentObject();
108 NS_ENSURE_TRUE_VOID(global);
109 RefPtr<ServiceWorkerRegistration> reg =
110 global->GetOrCreateServiceWorkerRegistration(aDescriptor);
111 self->MaybeAttachToRegistration(reg);
113 [](ErrorResult&& aRv) {
114 // do nothing
115 aRv.SuppressException();
121 ServiceWorker::~ServiceWorker() {
122 MOZ_ASSERT(NS_IsMainThread());
123 Shutdown();
126 NS_IMPL_CYCLE_COLLECTION_INHERITED(ServiceWorker, DOMEventTargetHelper,
127 mRegistration);
129 NS_IMPL_ADDREF_INHERITED(ServiceWorker, DOMEventTargetHelper)
130 NS_IMPL_RELEASE_INHERITED(ServiceWorker, DOMEventTargetHelper)
132 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ServiceWorker)
133 NS_INTERFACE_MAP_ENTRY_CONCRETE(ServiceWorker)
134 NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
136 JSObject* ServiceWorker::WrapObject(JSContext* aCx,
137 JS::Handle<JSObject*> aGivenProto) {
138 MOZ_ASSERT(NS_IsMainThread());
140 return ServiceWorker_Binding::Wrap(aCx, this, aGivenProto);
143 ServiceWorkerState ServiceWorker::State() const { return mDescriptor.State(); }
145 void ServiceWorker::SetState(ServiceWorkerState aState) {
146 NS_ENSURE_TRUE_VOID(aState >= mDescriptor.State());
147 mDescriptor.SetState(aState);
150 void ServiceWorker::MaybeDispatchStateChangeEvent() {
151 if (mDescriptor.State() <= mLastNotifiedState || !GetParentObject()) {
152 return;
154 mLastNotifiedState = mDescriptor.State();
156 DOMEventTargetHelper::DispatchTrustedEvent(u"statechange"_ns);
158 // Once we have transitioned to the redundant state then no
159 // more statechange events will occur. We can allow the DOM
160 // object to GC if script is not holding it alive.
161 if (mLastNotifiedState == ServiceWorkerState::Redundant) {
162 IgnoreKeepAliveIfHasListenersFor(nsGkAtoms::onstatechange);
166 void ServiceWorker::GetScriptURL(nsString& aURL) const {
167 CopyUTF8toUTF16(mDescriptor.ScriptURL(), aURL);
170 void ServiceWorker::PostMessage(JSContext* aCx, JS::Handle<JS::Value> aMessage,
171 const Sequence<JSObject*>& aTransferable,
172 ErrorResult& aRv) {
173 // Step 6.1 of
174 // https://w3c.github.io/ServiceWorker/#service-worker-postmessage-options
175 // invokes
176 // https://w3c.github.io/ServiceWorker/#run-service-worker
177 // which returns failure in step 3 if the ServiceWorker state is redundant.
178 // This will result in the "in parallel" step 6.1 of postMessage itself early
179 // returning without starting the ServiceWorker and without throwing an error.
180 if (State() == ServiceWorkerState::Redundant) {
181 return;
184 nsPIDOMWindowInner* window = GetOwner();
185 if (NS_WARN_IF(!window || !window->GetExtantDoc())) {
186 aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
187 return;
190 auto storageAllowed = StorageAllowedForWindow(window);
191 if (storageAllowed != StorageAccess::eAllow &&
192 (!StaticPrefs::privacy_partition_serviceWorkers() ||
193 !StoragePartitioningEnabled(
194 storageAllowed, window->GetExtantDoc()->CookieJarSettings()))) {
195 ServiceWorkerManager::LocalizeAndReportToAllClients(
196 mDescriptor.Scope(), "ServiceWorkerPostMessageStorageError",
197 nsTArray<nsString>{NS_ConvertUTF8toUTF16(mDescriptor.Scope())});
198 aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
199 return;
202 Maybe<ClientInfo> clientInfo = window->GetClientInfo();
203 Maybe<ClientState> clientState = window->GetClientState();
204 if (NS_WARN_IF(clientInfo.isNothing() || clientState.isNothing())) {
205 aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
206 return;
209 JS::Rooted<JS::Value> transferable(aCx, JS::UndefinedValue());
210 aRv = nsContentUtils::CreateJSValueFromSequenceOfObject(aCx, aTransferable,
211 &transferable);
212 if (aRv.Failed()) {
213 return;
216 // Window-to-SW messages do not allow memory sharing since they are not in the
217 // same agent cluster group, but we do not want to throw an error during the
218 // serialization. Because of this, ServiceWorkerCloneData will propagate an
219 // error message data if the SameProcess serialization is required. So that
220 // the receiver (service worker) knows that it needs to throw while
221 // deserialization and sharing memory objects are not propagated to the other
222 // process.
223 JS::CloneDataPolicy clonePolicy;
224 if (nsGlobalWindowInner::Cast(window)->IsSharedMemoryAllowed()) {
225 clonePolicy.allowSharedMemoryObjects();
228 RefPtr<ServiceWorkerCloneData> data = new ServiceWorkerCloneData();
229 data->Write(aCx, aMessage, transferable, clonePolicy, aRv);
230 if (aRv.Failed()) {
231 return;
234 // The value of CloneScope() is set while StructuredCloneData::Write(). If the
235 // aValue contiains a shared memory object, then the scope will be restricted
236 // and thus return SameProcess. If not, it will return DifferentProcess.
238 // When we postMessage a shared memory object from a window to a service
239 // worker, the object must be sent from a cross-origin isolated process to
240 // another one. So, we mark mark this data as an error message data if the
241 // scope is limited to same process.
242 if (data->CloneScope() ==
243 StructuredCloneHolder::StructuredCloneScope::SameProcess) {
244 data->SetAsErrorMessageData();
247 if (!mActor) {
248 return;
251 ClonedOrErrorMessageData clonedData;
252 if (!data->BuildClonedMessageData(clonedData)) {
253 return;
256 mActor->SendPostMessage(
257 clonedData,
258 ClientInfoAndState(clientInfo.ref().ToIPC(), clientState.ref().ToIPC()));
261 void ServiceWorker::PostMessage(JSContext* aCx, JS::Handle<JS::Value> aMessage,
262 const StructuredSerializeOptions& aOptions,
263 ErrorResult& aRv) {
264 PostMessage(aCx, aMessage, aOptions.mTransfer, aRv);
267 const ServiceWorkerDescriptor& ServiceWorker::Descriptor() const {
268 return mDescriptor;
271 void ServiceWorker::DisconnectFromOwner() {
272 DOMEventTargetHelper::DisconnectFromOwner();
275 void ServiceWorker::RevokeActor(ServiceWorkerChild* aActor) {
276 MOZ_DIAGNOSTIC_ASSERT(mActor);
277 MOZ_DIAGNOSTIC_ASSERT(mActor == aActor);
278 mActor->RevokeOwner(this);
279 mActor = nullptr;
281 mShutdown = true;
284 void ServiceWorker::MaybeAttachToRegistration(
285 ServiceWorkerRegistration* aRegistration) {
286 MOZ_DIAGNOSTIC_ASSERT(aRegistration);
287 MOZ_DIAGNOSTIC_ASSERT(!mRegistration);
289 // If the registration no longer actually references this ServiceWorker
290 // then we must be in the redundant state.
291 if (!aRegistration->Descriptor().HasWorker(mDescriptor)) {
292 SetState(ServiceWorkerState::Redundant);
293 MaybeDispatchStateChangeEvent();
294 return;
297 mRegistration = aRegistration;
300 void ServiceWorker::Shutdown() {
301 if (mShutdown) {
302 return;
304 mShutdown = true;
306 if (mActor) {
307 mActor->RevokeOwner(this);
308 mActor->MaybeStartTeardown();
309 mActor = nullptr;
313 } // namespace mozilla::dom