Bug 1494333 - index crons just like artifacts r=Callek
[gecko.git] / dom / serviceworkers / ServiceWorkerManager.cpp
blob732fac332cf26e6bb43b99b97e156644744fecfe
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
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "ServiceWorkerManager.h"
9 #include "nsAutoPtr.h"
10 #include "nsIConsoleService.h"
11 #include "nsIEffectiveTLDService.h"
12 #include "nsIScriptSecurityManager.h"
13 #include "nsIStreamLoader.h"
14 #include "nsIHttpChannel.h"
15 #include "nsIHttpChannelInternal.h"
16 #include "nsIHttpHeaderVisitor.h"
17 #include "nsINamed.h"
18 #include "nsINetworkInterceptController.h"
19 #include "nsIMutableArray.h"
20 #include "nsIScriptError.h"
21 #include "nsISimpleEnumerator.h"
22 #include "nsITimer.h"
23 #include "nsIUploadChannel2.h"
24 #include "nsServiceManagerUtils.h"
25 #include "nsDebug.h"
26 #include "nsISupportsPrimitives.h"
27 #include "nsIPermissionManager.h"
29 #include "jsapi.h"
31 #include "mozilla/BasePrincipal.h"
32 #include "mozilla/ClearOnShutdown.h"
33 #include "mozilla/ErrorNames.h"
34 #include "mozilla/LoadContext.h"
35 #include "mozilla/SystemGroup.h"
36 #include "mozilla/Telemetry.h"
37 #include "mozilla/dom/BindingUtils.h"
38 #include "mozilla/dom/ClientHandle.h"
39 #include "mozilla/dom/ClientManager.h"
40 #include "mozilla/dom/ClientSource.h"
41 #include "mozilla/dom/ConsoleUtils.h"
42 #include "mozilla/dom/ContentParent.h"
43 #include "mozilla/dom/DOMPrefs.h"
44 #include "mozilla/dom/ErrorEvent.h"
45 #include "mozilla/dom/Headers.h"
46 #include "mozilla/dom/InternalHeaders.h"
47 #include "mozilla/dom/Navigator.h"
48 #include "mozilla/dom/NotificationEvent.h"
49 #include "mozilla/dom/PromiseNativeHandler.h"
50 #include "mozilla/dom/Request.h"
51 #include "mozilla/dom/RootedDictionary.h"
52 #include "mozilla/dom/TypedArray.h"
53 #include "mozilla/dom/SharedWorker.h"
54 #include "mozilla/dom/WorkerPrivate.h"
55 #include "mozilla/dom/WorkerRunnable.h"
56 #include "mozilla/dom/WorkerScope.h"
57 #include "mozilla/ipc/BackgroundChild.h"
58 #include "mozilla/ipc/PBackgroundChild.h"
59 #include "mozilla/ipc/PBackgroundSharedTypes.h"
60 #include "mozilla/dom/ScriptLoader.h"
61 #include "mozilla/Unused.h"
62 #include "mozilla/EnumSet.h"
64 #include "nsContentUtils.h"
65 #include "nsNetUtil.h"
66 #include "nsProxyRelease.h"
67 #include "nsQueryObject.h"
68 #include "nsTArray.h"
70 #include "ServiceWorker.h"
71 #include "ServiceWorkerContainer.h"
72 #include "ServiceWorkerInfo.h"
73 #include "ServiceWorkerJobQueue.h"
74 #include "ServiceWorkerManagerChild.h"
75 #include "ServiceWorkerPrivate.h"
76 #include "ServiceWorkerRegisterJob.h"
77 #include "ServiceWorkerRegistrar.h"
78 #include "ServiceWorkerRegistration.h"
79 #include "ServiceWorkerScriptCache.h"
80 #include "ServiceWorkerEvents.h"
81 #include "ServiceWorkerUnregisterJob.h"
82 #include "ServiceWorkerUpdateJob.h"
83 #include "ServiceWorkerUpdaterChild.h"
84 #include "ServiceWorkerUtils.h"
86 #ifdef PostMessage
87 #undef PostMessage
88 #endif
90 using namespace mozilla;
91 using namespace mozilla::dom;
92 using namespace mozilla::ipc;
94 namespace mozilla {
95 namespace dom {
97 #define CLEAR_ORIGIN_DATA "clear-origin-attributes-data"
99 static_assert(nsIHttpChannelInternal::CORS_MODE_SAME_ORIGIN == static_cast<uint32_t>(RequestMode::Same_origin),
100 "RequestMode enumeration value should match Necko CORS mode value.");
101 static_assert(nsIHttpChannelInternal::CORS_MODE_NO_CORS == static_cast<uint32_t>(RequestMode::No_cors),
102 "RequestMode enumeration value should match Necko CORS mode value.");
103 static_assert(nsIHttpChannelInternal::CORS_MODE_CORS == static_cast<uint32_t>(RequestMode::Cors),
104 "RequestMode enumeration value should match Necko CORS mode value.");
105 static_assert(nsIHttpChannelInternal::CORS_MODE_NAVIGATE == static_cast<uint32_t>(RequestMode::Navigate),
106 "RequestMode enumeration value should match Necko CORS mode value.");
108 static_assert(nsIHttpChannelInternal::REDIRECT_MODE_FOLLOW == static_cast<uint32_t>(RequestRedirect::Follow),
109 "RequestRedirect enumeration value should make Necko Redirect mode value.");
110 static_assert(nsIHttpChannelInternal::REDIRECT_MODE_ERROR == static_cast<uint32_t>(RequestRedirect::Error),
111 "RequestRedirect enumeration value should make Necko Redirect mode value.");
112 static_assert(nsIHttpChannelInternal::REDIRECT_MODE_MANUAL == static_cast<uint32_t>(RequestRedirect::Manual),
113 "RequestRedirect enumeration value should make Necko Redirect mode value.");
114 static_assert(3 == static_cast<uint32_t>(RequestRedirect::EndGuard_),
115 "RequestRedirect enumeration value should make Necko Redirect mode value.");
117 static_assert(nsIHttpChannelInternal::FETCH_CACHE_MODE_DEFAULT == static_cast<uint32_t>(RequestCache::Default),
118 "RequestCache enumeration value should match Necko Cache mode value.");
119 static_assert(nsIHttpChannelInternal::FETCH_CACHE_MODE_NO_STORE == static_cast<uint32_t>(RequestCache::No_store),
120 "RequestCache enumeration value should match Necko Cache mode value.");
121 static_assert(nsIHttpChannelInternal::FETCH_CACHE_MODE_RELOAD == static_cast<uint32_t>(RequestCache::Reload),
122 "RequestCache enumeration value should match Necko Cache mode value.");
123 static_assert(nsIHttpChannelInternal::FETCH_CACHE_MODE_NO_CACHE == static_cast<uint32_t>(RequestCache::No_cache),
124 "RequestCache enumeration value should match Necko Cache mode value.");
125 static_assert(nsIHttpChannelInternal::FETCH_CACHE_MODE_FORCE_CACHE == static_cast<uint32_t>(RequestCache::Force_cache),
126 "RequestCache enumeration value should match Necko Cache mode value.");
127 static_assert(nsIHttpChannelInternal::FETCH_CACHE_MODE_ONLY_IF_CACHED == static_cast<uint32_t>(RequestCache::Only_if_cached),
128 "RequestCache enumeration value should match Necko Cache mode value.");
129 static_assert(6 == static_cast<uint32_t>(RequestCache::EndGuard_),
130 "RequestCache enumeration value should match Necko Cache mode value.");
132 static_assert(static_cast<uint16_t>(ServiceWorkerUpdateViaCache::Imports) ==
133 nsIServiceWorkerRegistrationInfo::UPDATE_VIA_CACHE_IMPORTS,
134 "nsIServiceWorkerRegistrationInfo::UPDATE_VIA_CACHE_*"
135 " should match ServiceWorkerUpdateViaCache enumeration.");
136 static_assert(static_cast<uint16_t>(ServiceWorkerUpdateViaCache::All) ==
137 nsIServiceWorkerRegistrationInfo::UPDATE_VIA_CACHE_ALL,
138 "nsIServiceWorkerRegistrationInfo::UPDATE_VIA_CACHE_*"
139 " should match ServiceWorkerUpdateViaCache enumeration.");
140 static_assert(static_cast<uint16_t>(ServiceWorkerUpdateViaCache::None) ==
141 nsIServiceWorkerRegistrationInfo::UPDATE_VIA_CACHE_NONE,
142 "nsIServiceWorkerRegistrationInfo::UPDATE_VIA_CACHE_*"
143 " should match ServiceWorkerUpdateViaCache enumeration.");
145 static StaticRefPtr<ServiceWorkerManager> gInstance;
147 struct ServiceWorkerManager::RegistrationDataPerPrincipal final
149 // Ordered list of scopes for glob matching.
150 // Each entry is an absolute URL representing the scope.
151 // Each value of the hash table is an array of an absolute URLs representing
152 // the scopes.
154 // An array is used for now since the number of controlled scopes per
155 // domain is expected to be relatively low. If that assumption was proved
156 // wrong this should be replaced with a better structure to avoid the
157 // memmoves associated with inserting stuff in the middle of the array.
158 nsTArray<nsCString> mOrderedScopes;
160 // Scope to registration.
161 // The scope should be a fully qualified valid URL.
162 nsRefPtrHashtable<nsCStringHashKey, ServiceWorkerRegistrationInfo> mInfos;
164 // Maps scopes to job queues.
165 nsRefPtrHashtable<nsCStringHashKey, ServiceWorkerJobQueue> mJobQueues;
167 // Map scopes to scheduled update timers.
168 nsInterfaceHashtable<nsCStringHashKey, nsITimer> mUpdateTimers;
171 namespace {
173 nsresult
174 PopulateRegistrationData(nsIPrincipal* aPrincipal,
175 const ServiceWorkerRegistrationInfo* aRegistration,
176 ServiceWorkerRegistrationData& aData)
178 MOZ_ASSERT(aPrincipal);
179 MOZ_ASSERT(aRegistration);
181 if (NS_WARN_IF(!BasePrincipal::Cast(aPrincipal)->IsCodebasePrincipal())) {
182 return NS_ERROR_FAILURE;
185 nsresult rv = PrincipalToPrincipalInfo(aPrincipal, &aData.principal());
186 if (NS_WARN_IF(NS_FAILED(rv))) {
187 return rv;
190 aData.scope() = aRegistration->Scope();
192 // TODO: When bug 1426401 is implemented we will need to handle more
193 // than just the active worker here.
194 RefPtr<ServiceWorkerInfo> active = aRegistration->GetActive();
195 MOZ_ASSERT(active);
196 if (NS_WARN_IF(!active)) {
197 return NS_ERROR_FAILURE;
200 aData.currentWorkerURL() = active->ScriptSpec();
201 aData.cacheName() = active->CacheName();
202 aData.currentWorkerHandlesFetch() = active->HandlesFetch();
204 aData.currentWorkerInstalledTime() = active->GetInstalledTime();
205 aData.currentWorkerActivatedTime() = active->GetActivatedTime();
207 aData.updateViaCache() =
208 static_cast<uint32_t>(aRegistration->GetUpdateViaCache());
210 aData.lastUpdateTime() = aRegistration->GetLastUpdateTime();
212 MOZ_ASSERT(ServiceWorkerRegistrationDataIsValid(aData));
214 return NS_OK;
217 class TeardownRunnable final : public Runnable
219 public:
220 explicit TeardownRunnable(ServiceWorkerManagerChild* aActor)
221 : Runnable("dom::ServiceWorkerManager::TeardownRunnable")
222 , mActor(aActor)
224 MOZ_ASSERT(mActor);
227 NS_IMETHOD Run() override
229 MOZ_ASSERT(mActor);
230 mActor->SendShutdown();
231 return NS_OK;
234 private:
235 ~TeardownRunnable() {}
237 RefPtr<ServiceWorkerManagerChild> mActor;
240 } // namespace
242 //////////////////////////
243 // ServiceWorkerManager //
244 //////////////////////////
246 NS_IMPL_ADDREF(ServiceWorkerManager)
247 NS_IMPL_RELEASE(ServiceWorkerManager)
249 NS_INTERFACE_MAP_BEGIN(ServiceWorkerManager)
250 NS_INTERFACE_MAP_ENTRY(nsIServiceWorkerManager)
251 NS_INTERFACE_MAP_ENTRY(nsIObserver)
252 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIServiceWorkerManager)
253 NS_INTERFACE_MAP_END
255 ServiceWorkerManager::ServiceWorkerManager()
256 : mActor(nullptr)
257 , mShuttingDown(false)
261 ServiceWorkerManager::~ServiceWorkerManager()
263 // The map will assert if it is not empty when destroyed.
264 mRegistrationInfos.Clear();
265 MOZ_ASSERT(!mActor);
268 void
269 ServiceWorkerManager::Init(ServiceWorkerRegistrar* aRegistrar)
271 nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
272 if (obs) {
273 DebugOnly<nsresult> rv;
274 rv = obs->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false /* ownsWeak */);
275 MOZ_ASSERT(NS_SUCCEEDED(rv));
278 if (XRE_IsParentProcess()) {
279 MOZ_DIAGNOSTIC_ASSERT(aRegistrar);
281 nsTArray<ServiceWorkerRegistrationData> data;
282 aRegistrar->GetRegistrations(data);
283 LoadRegistrations(data);
285 if (obs) {
286 DebugOnly<nsresult> rv;
287 rv = obs->AddObserver(this, CLEAR_ORIGIN_DATA, false /* ownsWeak */);
288 MOZ_ASSERT(NS_SUCCEEDED(rv));
292 PBackgroundChild* actorChild = BackgroundChild::GetOrCreateForCurrentThread();
293 if (NS_WARN_IF(!actorChild)) {
294 MaybeStartShutdown();
295 return;
298 PServiceWorkerManagerChild* actor =
299 actorChild->SendPServiceWorkerManagerConstructor();
300 if (!actor) {
301 MaybeStartShutdown();
302 return;
305 mActor = static_cast<ServiceWorkerManagerChild*>(actor);
308 RefPtr<GenericPromise>
309 ServiceWorkerManager::StartControllingClient(const ClientInfo& aClientInfo,
310 ServiceWorkerRegistrationInfo* aRegistrationInfo,
311 bool aControlClientHandle)
313 MOZ_DIAGNOSTIC_ASSERT(aRegistrationInfo->GetActive());
315 RefPtr<GenericPromise> ref;
316 RefPtr<ServiceWorkerManager> self(this);
318 const ServiceWorkerDescriptor& active =
319 aRegistrationInfo->GetActive()->Descriptor();
321 auto entry = mControlledClients.LookupForAdd(aClientInfo.Id());
322 if (entry) {
323 RefPtr<ServiceWorkerRegistrationInfo> old =
324 entry.Data()->mRegistrationInfo.forget();
326 if (aControlClientHandle) {
327 ref = entry.Data()->mClientHandle->Control(active);
328 } else {
329 ref = GenericPromise::CreateAndResolve(false, __func__);
332 entry.Data()->mRegistrationInfo = aRegistrationInfo;
334 if (old != aRegistrationInfo) {
335 StopControllingRegistration(old);
336 aRegistrationInfo->StartControllingClient();
339 Telemetry::Accumulate(Telemetry::SERVICE_WORKER_CONTROLLED_DOCUMENTS, 1);
341 // Always check to see if we failed to actually control the client. In
342 // that case removed the client from our list of controlled clients.
343 ref->Then(
344 SystemGroup::EventTargetFor(TaskCategory::Other), __func__,
345 [] (bool) {
346 // do nothing on success
347 }, [self, aClientInfo] (nsresult aRv) {
348 // failed to control, forget about this client
349 self->StopControllingClient(aClientInfo);
352 return ref;
355 RefPtr<ClientHandle> clientHandle =
356 ClientManager::CreateHandle(aClientInfo,
357 SystemGroup::EventTargetFor(TaskCategory::Other));
359 if (aControlClientHandle) {
360 ref = clientHandle->Control(active);
361 } else {
362 ref = GenericPromise::CreateAndResolve(false, __func__);
365 aRegistrationInfo->StartControllingClient();
367 entry.OrInsert([&] {
368 return new ControlledClientData(clientHandle, aRegistrationInfo);
371 clientHandle->OnDetach()->Then(
372 SystemGroup::EventTargetFor(TaskCategory::Other), __func__,
373 [self, aClientInfo] {
374 self->StopControllingClient(aClientInfo);
377 Telemetry::Accumulate(Telemetry::SERVICE_WORKER_CONTROLLED_DOCUMENTS, 1);
379 // Always check to see if we failed to actually control the client. In
380 // that case removed the client from our list of controlled clients.
381 ref->Then(
382 SystemGroup::EventTargetFor(TaskCategory::Other), __func__,
383 [] (bool) {
384 // do nothing on success
385 }, [self, aClientInfo] (nsresult aRv) {
386 // failed to control, forget about this client
387 self->StopControllingClient(aClientInfo);
390 return ref;
393 void
394 ServiceWorkerManager::StopControllingClient(const ClientInfo& aClientInfo)
396 auto entry = mControlledClients.Lookup(aClientInfo.Id());
397 if (!entry) {
398 return;
401 RefPtr<ServiceWorkerRegistrationInfo> reg =
402 entry.Data()->mRegistrationInfo.forget();
404 entry.Remove();
406 StopControllingRegistration(reg);
409 void
410 ServiceWorkerManager::MaybeStartShutdown()
412 MOZ_ASSERT(NS_IsMainThread());
414 if (mShuttingDown) {
415 return;
418 mShuttingDown = true;
420 for (auto it1 = mRegistrationInfos.Iter(); !it1.Done(); it1.Next()) {
421 for (auto it2 = it1.UserData()->mUpdateTimers.Iter(); !it2.Done(); it2.Next()) {
422 nsCOMPtr<nsITimer> timer = it2.UserData();
423 timer->Cancel();
425 it1.UserData()->mUpdateTimers.Clear();
427 for (auto it2 = it1.UserData()->mJobQueues.Iter(); !it2.Done(); it2.Next()) {
428 RefPtr<ServiceWorkerJobQueue> queue = it2.UserData();
429 queue->CancelAll();
431 it1.UserData()->mJobQueues.Clear();
434 nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
435 if (obs) {
436 obs->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID);
438 if (XRE_IsParentProcess()) {
439 obs->RemoveObserver(this, CLEAR_ORIGIN_DATA);
443 if (!mActor) {
444 return;
447 mActor->ManagerShuttingDown();
449 RefPtr<TeardownRunnable> runnable = new TeardownRunnable(mActor);
450 nsresult rv = NS_DispatchToMainThread(runnable);
451 Unused << NS_WARN_IF(NS_FAILED(rv));
452 mActor = nullptr;
455 class ServiceWorkerResolveWindowPromiseOnRegisterCallback final : public ServiceWorkerJob::Callback
457 RefPtr<ServiceWorkerRegistrationPromise::Private> mPromise;
459 ~ServiceWorkerResolveWindowPromiseOnRegisterCallback()
462 virtual void
463 JobFinished(ServiceWorkerJob* aJob, ErrorResult& aStatus) override
465 MOZ_ASSERT(NS_IsMainThread());
466 MOZ_ASSERT(aJob);
468 if (aStatus.Failed()) {
469 mPromise->Reject(std::move(aStatus), __func__);
470 return;
473 MOZ_ASSERT(aJob->GetType() == ServiceWorkerJob::Type::Register);
474 RefPtr<ServiceWorkerRegisterJob> registerJob =
475 static_cast<ServiceWorkerRegisterJob*>(aJob);
476 RefPtr<ServiceWorkerRegistrationInfo> reg = registerJob->GetRegistration();
478 mPromise->Resolve(reg->Descriptor(), __func__);
481 public:
482 ServiceWorkerResolveWindowPromiseOnRegisterCallback()
483 : mPromise(new ServiceWorkerRegistrationPromise::Private(__func__))
486 RefPtr<ServiceWorkerRegistrationPromise>
487 Promise() const
489 return mPromise;
492 NS_INLINE_DECL_REFCOUNTING(ServiceWorkerResolveWindowPromiseOnRegisterCallback, override)
495 namespace {
497 class PropagateSoftUpdateRunnable final : public Runnable
499 public:
500 PropagateSoftUpdateRunnable(const OriginAttributes& aOriginAttributes,
501 const nsAString& aScope)
502 : Runnable("dom::ServiceWorkerManager::PropagateSoftUpdateRunnable")
503 , mOriginAttributes(aOriginAttributes)
504 , mScope(aScope)
507 NS_IMETHOD Run() override
509 MOZ_ASSERT(NS_IsMainThread());
511 RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
512 if (swm) {
513 swm->PropagateSoftUpdate(mOriginAttributes, mScope);
516 return NS_OK;
519 private:
520 ~PropagateSoftUpdateRunnable()
523 const OriginAttributes mOriginAttributes;
524 const nsString mScope;
527 class PromiseResolverCallback final : public ServiceWorkerUpdateFinishCallback
529 public:
530 PromiseResolverCallback(ServiceWorkerUpdateFinishCallback* aCallback,
531 GenericPromise::Private* aPromise)
532 : mCallback(aCallback)
533 , mPromise(aPromise)
535 MOZ_DIAGNOSTIC_ASSERT(mPromise);
538 void UpdateSucceeded(ServiceWorkerRegistrationInfo* aInfo) override
540 MOZ_DIAGNOSTIC_ASSERT(mPromise);
542 if (mCallback) {
543 mCallback->UpdateSucceeded(aInfo);
546 MaybeResolve();
549 void UpdateFailed(ErrorResult& aStatus) override
551 MOZ_DIAGNOSTIC_ASSERT(mPromise);
553 if (mCallback) {
554 mCallback->UpdateFailed(aStatus);
557 MaybeResolve();
560 private:
561 ~PromiseResolverCallback()
563 MaybeResolve();
566 void
567 MaybeResolve()
569 if (mPromise) {
570 mPromise->Resolve(true, __func__);
571 mPromise = nullptr;
575 RefPtr<ServiceWorkerUpdateFinishCallback> mCallback;
576 RefPtr<GenericPromise::Private> mPromise;
579 // This runnable is used for 2 different tasks:
580 // - to postpone the SoftUpdate() until the IPC SWM actor is created
581 // (aInternalMethod == false)
582 // - to call the 'real' SoftUpdate when the ServiceWorkerUpdaterChild is
583 // notified by the parent (aInternalMethod == true)
584 class SoftUpdateRunnable final : public CancelableRunnable
586 public:
587 SoftUpdateRunnable(const OriginAttributes& aOriginAttributes,
588 const nsACString& aScope,
589 bool aInternalMethod,
590 GenericPromise::Private* aPromise)
591 : CancelableRunnable("dom::ServiceWorkerManager::SoftUpdateRunnable")
592 , mAttrs(aOriginAttributes)
593 , mScope(aScope)
594 , mInternalMethod(aInternalMethod)
595 , mPromise(aPromise)
598 NS_IMETHOD Run() override
600 MOZ_ASSERT(NS_IsMainThread());
602 RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
603 if (!swm) {
604 return NS_ERROR_FAILURE;
607 if (mInternalMethod) {
608 RefPtr<PromiseResolverCallback> callback =
609 new PromiseResolverCallback(nullptr, mPromise);
610 mPromise = nullptr;
612 swm->SoftUpdateInternal(mAttrs, mScope, callback);
613 } else {
614 swm->SoftUpdate(mAttrs, mScope);
617 return NS_OK;
620 nsresult
621 Cancel() override
623 mPromise = nullptr;
624 return NS_OK;
627 private:
628 ~SoftUpdateRunnable()
630 if (mPromise) {
631 mPromise->Resolve(true, __func__);
635 const OriginAttributes mAttrs;
636 const nsCString mScope;
637 bool mInternalMethod;
639 RefPtr<GenericPromise::Private> mPromise;
642 // This runnable is used for 3 different tasks:
643 // - to postpone the Update() until the IPC SWM actor is created
644 // (aType == ePostpone)
645 // - to call the 'real' Update when the ServiceWorkerUpdaterChild is
646 // notified by the parent (aType == eSuccess)
647 // - an error must be propagated (aType == eFailure)
648 class UpdateRunnable final : public CancelableRunnable
650 public:
651 enum Type {
652 ePostpone,
653 eSuccess,
654 eFailure,
657 UpdateRunnable(nsIPrincipal* aPrincipal,
658 const nsACString& aScope,
659 ServiceWorkerUpdateFinishCallback* aCallback,
660 Type aType,
661 GenericPromise::Private* aPromise)
662 : CancelableRunnable("dom::ServiceWorkerManager::UpdateRunnable")
663 , mPrincipal(aPrincipal)
664 , mScope(aScope)
665 , mCallback(aCallback)
666 , mType(aType)
667 , mPromise(aPromise)
670 NS_IMETHOD Run() override
672 MOZ_ASSERT(NS_IsMainThread());
674 RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
675 if (!swm) {
676 return NS_ERROR_FAILURE;
679 if (mType == ePostpone) {
680 swm->Update(mPrincipal, mScope, mCallback);
681 return NS_OK;
684 MOZ_ASSERT(mPromise);
686 RefPtr<PromiseResolverCallback> callback =
687 new PromiseResolverCallback(mCallback, mPromise);
688 mPromise = nullptr;
690 if (mType == eSuccess) {
691 swm->UpdateInternal(mPrincipal, mScope, callback);
692 return NS_OK;
695 ErrorResult error(NS_ERROR_DOM_ABORT_ERR);
696 callback->UpdateFailed(error);
697 return NS_OK;
700 nsresult
701 Cancel() override
703 mPromise = nullptr;
704 return NS_OK;
707 private:
708 ~UpdateRunnable()
710 if (mPromise) {
711 mPromise->Resolve(true, __func__);
715 nsCOMPtr<nsIPrincipal> mPrincipal;
716 const nsCString mScope;
717 RefPtr<ServiceWorkerUpdateFinishCallback> mCallback;
718 Type mType;
720 RefPtr<GenericPromise::Private> mPromise;
723 class ResolvePromiseRunnable final : public CancelableRunnable
725 public:
726 explicit ResolvePromiseRunnable(GenericPromise::Private* aPromise)
727 : CancelableRunnable("dom::ServiceWorkerManager::ResolvePromiseRunnable")
728 , mPromise(aPromise)
731 NS_IMETHOD
732 Run() override
734 MaybeResolve();
735 return NS_OK;
738 nsresult
739 Cancel() override
741 mPromise = nullptr;
742 return NS_OK;
745 private:
746 ~ResolvePromiseRunnable()
748 MaybeResolve();
751 void
752 MaybeResolve()
754 if (mPromise) {
755 mPromise->Resolve(true, __func__);
756 mPromise = nullptr;
760 RefPtr<GenericPromise::Private> mPromise;
763 } // namespace
765 RefPtr<ServiceWorkerRegistrationPromise>
766 ServiceWorkerManager::Register(const ClientInfo& aClientInfo,
767 const nsACString& aScopeURL,
768 const nsACString& aScriptURL,
769 ServiceWorkerUpdateViaCache aUpdateViaCache)
771 nsCOMPtr<nsIURI> scopeURI;
772 nsresult rv = NS_NewURI(getter_AddRefs(scopeURI), aScopeURL, nullptr, nullptr);
773 if (NS_FAILED(rv)) {
774 return ServiceWorkerRegistrationPromise::CreateAndReject(rv, __func__);
777 nsCOMPtr<nsIURI> scriptURI;
778 rv = NS_NewURI(getter_AddRefs(scriptURI), aScriptURL, nullptr, nullptr);
779 if (NS_FAILED(rv)) {
780 return ServiceWorkerRegistrationPromise::CreateAndReject(rv, __func__);
783 rv = ServiceWorkerScopeAndScriptAreValid(aClientInfo, scopeURI, scriptURI);
784 if (NS_FAILED(rv)) {
785 return ServiceWorkerRegistrationPromise::CreateAndReject(rv, __func__);
788 // If the previous validation step passed then we must have a principal.
789 nsCOMPtr<nsIPrincipal> principal = aClientInfo.GetPrincipal();
791 nsAutoCString scopeKey;
792 rv = PrincipalToScopeKey(principal, scopeKey);
793 if (NS_WARN_IF(NS_FAILED(rv))) {
794 return ServiceWorkerRegistrationPromise::CreateAndReject(rv, __func__);
797 RefPtr<ServiceWorkerJobQueue> queue = GetOrCreateJobQueue(scopeKey,
798 aScopeURL);
800 RefPtr<ServiceWorkerResolveWindowPromiseOnRegisterCallback> cb =
801 new ServiceWorkerResolveWindowPromiseOnRegisterCallback();
803 nsCOMPtr<nsILoadGroup> loadGroup = do_CreateInstance(NS_LOADGROUP_CONTRACTID);
804 RefPtr<ServiceWorkerRegisterJob> job = new ServiceWorkerRegisterJob(
805 principal, aScopeURL, aScriptURL, loadGroup,
806 static_cast<ServiceWorkerUpdateViaCache>(aUpdateViaCache)
809 job->AppendResultCallback(cb);
810 queue->ScheduleJob(job);
812 MOZ_ASSERT(NS_IsMainThread());
813 Telemetry::Accumulate(Telemetry::SERVICE_WORKER_REGISTRATIONS, 1);
815 return cb->Promise();
819 * Implements the async aspects of the getRegistrations algorithm.
821 class GetRegistrationsRunnable final : public Runnable
823 const ClientInfo mClientInfo;
824 RefPtr<ServiceWorkerRegistrationListPromise::Private> mPromise;
825 public:
826 explicit GetRegistrationsRunnable(const ClientInfo& aClientInfo)
827 : Runnable("dom::ServiceWorkerManager::GetRegistrationsRunnable")
828 , mClientInfo(aClientInfo)
829 , mPromise(new ServiceWorkerRegistrationListPromise::Private(__func__))
832 RefPtr<ServiceWorkerRegistrationListPromise>
833 Promise() const
835 return mPromise;
838 NS_IMETHOD
839 Run() override
841 auto scopeExit = MakeScopeExit([&] {
842 mPromise->Reject(NS_ERROR_DOM_INVALID_STATE_ERR, __func__);
845 RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
846 if (!swm) {
847 return NS_OK;
850 nsCOMPtr<nsIPrincipal> principal = mClientInfo.GetPrincipal();
851 if (!principal) {
852 return NS_OK;
855 nsTArray<ServiceWorkerRegistrationDescriptor> array;
857 if (NS_WARN_IF(!BasePrincipal::Cast(principal)->IsCodebasePrincipal())) {
858 return NS_OK;
861 nsAutoCString scopeKey;
862 nsresult rv = swm->PrincipalToScopeKey(principal, scopeKey);
863 if (NS_WARN_IF(NS_FAILED(rv))) {
864 return rv;
867 ServiceWorkerManager::RegistrationDataPerPrincipal* data;
868 if (!swm->mRegistrationInfos.Get(scopeKey, &data)) {
869 scopeExit.release();
870 mPromise->Resolve(array, __func__);
871 return NS_OK;
874 for (uint32_t i = 0; i < data->mOrderedScopes.Length(); ++i) {
875 RefPtr<ServiceWorkerRegistrationInfo> info =
876 data->mInfos.GetWeak(data->mOrderedScopes[i]);
877 if (info->IsPendingUninstall()) {
878 continue;
881 NS_ConvertUTF8toUTF16 scope(data->mOrderedScopes[i]);
883 nsCOMPtr<nsIURI> scopeURI;
884 nsresult rv = NS_NewURI(getter_AddRefs(scopeURI), scope, nullptr, nullptr);
885 if (NS_WARN_IF(NS_FAILED(rv))) {
886 break;
889 rv = principal->CheckMayLoad(scopeURI, true /* report */,
890 false /* allowIfInheritsPrincipal */);
891 if (NS_WARN_IF(NS_FAILED(rv))) {
892 continue;
895 array.AppendElement(info->Descriptor());
898 scopeExit.release();
899 mPromise->Resolve(array, __func__);
901 return NS_OK;
905 RefPtr<ServiceWorkerRegistrationListPromise>
906 ServiceWorkerManager::GetRegistrations(const ClientInfo& aClientInfo) const
908 RefPtr<GetRegistrationsRunnable> runnable =
909 new GetRegistrationsRunnable(aClientInfo);
910 MOZ_ALWAYS_SUCCEEDS(NS_DispatchToCurrentThread(runnable));
911 return runnable->Promise();;
915 * Implements the async aspects of the getRegistration algorithm.
917 class GetRegistrationRunnable final : public Runnable
919 const ClientInfo mClientInfo;
920 RefPtr<ServiceWorkerRegistrationPromise::Private> mPromise;
921 nsCString mURL;
923 public:
924 GetRegistrationRunnable(const ClientInfo& aClientInfo,
925 const nsACString& aURL)
926 : Runnable("dom::ServiceWorkerManager::GetRegistrationRunnable")
927 , mClientInfo(aClientInfo)
928 , mPromise(new ServiceWorkerRegistrationPromise::Private(__func__))
929 , mURL(aURL)
932 RefPtr<ServiceWorkerRegistrationPromise>
933 Promise() const
935 return mPromise;
938 NS_IMETHOD
939 Run() override
941 RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
942 if (!swm) {
943 mPromise->Reject(NS_ERROR_DOM_INVALID_STATE_ERR, __func__);
944 return NS_OK;
947 nsCOMPtr<nsIPrincipal> principal = mClientInfo.GetPrincipal();
948 if (!principal) {
949 mPromise->Reject(NS_ERROR_DOM_INVALID_STATE_ERR, __func__);
950 return NS_OK;
953 nsCOMPtr<nsIURI> uri;
954 nsresult rv = NS_NewURI(getter_AddRefs(uri), mURL, nullptr, nullptr);
955 if (NS_WARN_IF(NS_FAILED(rv))) {
956 mPromise->Reject(rv, __func__);
957 return NS_OK;
960 rv = principal->CheckMayLoad(uri, true /* report */,
961 false /* allowIfInheritsPrinciple */);
962 if (NS_FAILED(rv)) {
963 mPromise->Reject(NS_ERROR_DOM_SECURITY_ERR, __func__);
964 return NS_OK;
967 RefPtr<ServiceWorkerRegistrationInfo> registration =
968 swm->GetServiceWorkerRegistrationInfo(principal, uri);
970 if (!registration) {
971 // Reject with NS_OK means "not found".
972 mPromise->Reject(NS_OK, __func__);
973 return NS_OK;
976 mPromise->Resolve(registration->Descriptor(), __func__);
978 return NS_OK;
982 RefPtr<ServiceWorkerRegistrationPromise>
983 ServiceWorkerManager::GetRegistration(const ClientInfo& aClientInfo,
984 const nsACString& aURL) const
986 MOZ_ASSERT(NS_IsMainThread());
988 RefPtr<GetRegistrationRunnable> runnable =
989 new GetRegistrationRunnable(aClientInfo, aURL);
990 MOZ_ALWAYS_SUCCEEDS(NS_DispatchToCurrentThread(runnable));
992 return runnable->Promise();
995 NS_IMETHODIMP
996 ServiceWorkerManager::SendPushEvent(const nsACString& aOriginAttributes,
997 const nsACString& aScope,
998 uint32_t aDataLength,
999 uint8_t* aDataBytes,
1000 uint8_t optional_argc)
1002 if (optional_argc == 2) {
1003 nsTArray<uint8_t> data;
1004 if (!data.InsertElementsAt(0, aDataBytes, aDataLength, fallible)) {
1005 return NS_ERROR_OUT_OF_MEMORY;
1007 return SendPushEvent(aOriginAttributes, aScope, EmptyString(), Some(data));
1009 MOZ_ASSERT(optional_argc == 0);
1010 return SendPushEvent(aOriginAttributes, aScope, EmptyString(), Nothing());
1013 nsresult
1014 ServiceWorkerManager::SendPushEvent(const nsACString& aOriginAttributes,
1015 const nsACString& aScope,
1016 const nsAString& aMessageId,
1017 const Maybe<nsTArray<uint8_t>>& aData)
1019 OriginAttributes attrs;
1020 if (!attrs.PopulateFromSuffix(aOriginAttributes)) {
1021 return NS_ERROR_INVALID_ARG;
1024 ServiceWorkerInfo* serviceWorker = GetActiveWorkerInfoForScope(attrs, aScope);
1025 if (NS_WARN_IF(!serviceWorker)) {
1026 return NS_ERROR_FAILURE;
1029 RefPtr<ServiceWorkerRegistrationInfo> registration =
1030 GetRegistration(serviceWorker->Principal(), aScope);
1031 MOZ_DIAGNOSTIC_ASSERT(registration);
1033 return serviceWorker->WorkerPrivate()->SendPushEvent(aMessageId, aData,
1034 registration);
1037 NS_IMETHODIMP
1038 ServiceWorkerManager::SendPushSubscriptionChangeEvent(const nsACString& aOriginAttributes,
1039 const nsACString& aScope)
1041 OriginAttributes attrs;
1042 if (!attrs.PopulateFromSuffix(aOriginAttributes)) {
1043 return NS_ERROR_INVALID_ARG;
1046 ServiceWorkerInfo* info = GetActiveWorkerInfoForScope(attrs, aScope);
1047 if (!info) {
1048 return NS_ERROR_FAILURE;
1050 return info->WorkerPrivate()->SendPushSubscriptionChangeEvent();
1053 nsresult
1054 ServiceWorkerManager::SendNotificationEvent(const nsAString& aEventName,
1055 const nsACString& aOriginSuffix,
1056 const nsACString& aScope,
1057 const nsAString& aID,
1058 const nsAString& aTitle,
1059 const nsAString& aDir,
1060 const nsAString& aLang,
1061 const nsAString& aBody,
1062 const nsAString& aTag,
1063 const nsAString& aIcon,
1064 const nsAString& aData,
1065 const nsAString& aBehavior)
1067 OriginAttributes attrs;
1068 if (!attrs.PopulateFromSuffix(aOriginSuffix)) {
1069 return NS_ERROR_INVALID_ARG;
1072 ServiceWorkerInfo* info = GetActiveWorkerInfoForScope(attrs, aScope);
1073 if (!info) {
1074 return NS_ERROR_FAILURE;
1077 ServiceWorkerPrivate* workerPrivate = info->WorkerPrivate();
1078 return workerPrivate->SendNotificationEvent(aEventName, aID, aTitle, aDir,
1079 aLang, aBody, aTag,
1080 aIcon, aData, aBehavior,
1081 NS_ConvertUTF8toUTF16(aScope));
1084 NS_IMETHODIMP
1085 ServiceWorkerManager::SendNotificationClickEvent(const nsACString& aOriginSuffix,
1086 const nsACString& aScope,
1087 const nsAString& aID,
1088 const nsAString& aTitle,
1089 const nsAString& aDir,
1090 const nsAString& aLang,
1091 const nsAString& aBody,
1092 const nsAString& aTag,
1093 const nsAString& aIcon,
1094 const nsAString& aData,
1095 const nsAString& aBehavior)
1097 return SendNotificationEvent(NS_LITERAL_STRING(NOTIFICATION_CLICK_EVENT_NAME),
1098 aOriginSuffix, aScope, aID, aTitle, aDir, aLang,
1099 aBody, aTag, aIcon, aData, aBehavior);
1102 NS_IMETHODIMP
1103 ServiceWorkerManager::SendNotificationCloseEvent(const nsACString& aOriginSuffix,
1104 const nsACString& aScope,
1105 const nsAString& aID,
1106 const nsAString& aTitle,
1107 const nsAString& aDir,
1108 const nsAString& aLang,
1109 const nsAString& aBody,
1110 const nsAString& aTag,
1111 const nsAString& aIcon,
1112 const nsAString& aData,
1113 const nsAString& aBehavior)
1115 return SendNotificationEvent(NS_LITERAL_STRING(NOTIFICATION_CLOSE_EVENT_NAME),
1116 aOriginSuffix, aScope, aID, aTitle, aDir, aLang,
1117 aBody, aTag, aIcon, aData, aBehavior);
1120 RefPtr<ServiceWorkerRegistrationPromise>
1121 ServiceWorkerManager::WhenReady(const ClientInfo& aClientInfo)
1123 AssertIsOnMainThread();
1125 for (auto &prd : mPendingReadyList) {
1126 if (prd->mClientHandle->Info().Id() == aClientInfo.Id() &&
1127 prd->mClientHandle->Info().PrincipalInfo() == aClientInfo.PrincipalInfo()) {
1128 return prd->mPromise;
1132 RefPtr<ServiceWorkerRegistrationInfo> reg =
1133 GetServiceWorkerRegistrationInfo(aClientInfo);
1134 if (reg && reg->GetActive()) {
1135 return ServiceWorkerRegistrationPromise::CreateAndResolve(reg->Descriptor(),
1136 __func__);
1139 nsCOMPtr<nsISerialEventTarget> target =
1140 SystemGroup::EventTargetFor(TaskCategory::Other);
1142 RefPtr<ClientHandle> handle = ClientManager::CreateHandle(aClientInfo, target);
1143 mPendingReadyList.AppendElement(MakeUnique<PendingReadyData>(handle));
1145 RefPtr<ServiceWorkerManager> self(this);
1146 handle->OnDetach()->Then( target, __func__,
1147 [self = std::move(self), aClientInfo] {
1148 self->RemovePendingReadyPromise(aClientInfo);
1151 return mPendingReadyList.LastElement()->mPromise;
1154 void
1155 ServiceWorkerManager::CheckPendingReadyPromises()
1157 nsTArray<UniquePtr<PendingReadyData>> pendingReadyList;
1158 mPendingReadyList.SwapElements(pendingReadyList);
1159 for (uint32_t i = 0; i < pendingReadyList.Length(); ++i) {
1160 UniquePtr<PendingReadyData> prd(std::move(pendingReadyList[i]));
1162 RefPtr<ServiceWorkerRegistrationInfo> reg =
1163 GetServiceWorkerRegistrationInfo(prd->mClientHandle->Info());
1165 if (reg && reg->GetActive()) {
1166 prd->mPromise->Resolve(reg->Descriptor(), __func__);
1167 } else {
1168 mPendingReadyList.AppendElement(std::move(prd));
1173 void
1174 ServiceWorkerManager::RemovePendingReadyPromise(const ClientInfo& aClientInfo)
1176 nsTArray<UniquePtr<PendingReadyData>> pendingReadyList;
1177 mPendingReadyList.SwapElements(pendingReadyList);
1178 for (uint32_t i = 0; i < pendingReadyList.Length(); ++i) {
1179 UniquePtr<PendingReadyData> prd(std::move(pendingReadyList[i]));
1181 if (prd->mClientHandle->Info().Id() == aClientInfo.Id() &&
1182 prd->mClientHandle->Info().PrincipalInfo() == aClientInfo.PrincipalInfo()) {
1183 prd->mPromise->Reject(NS_ERROR_DOM_ABORT_ERR, __func__);
1184 } else {
1185 mPendingReadyList.AppendElement(std::move(prd));
1190 void
1191 ServiceWorkerManager::NoteInheritedController(const ClientInfo& aClientInfo,
1192 const ServiceWorkerDescriptor& aController)
1194 MOZ_ASSERT(NS_IsMainThread());
1196 nsCOMPtr<nsIPrincipal> principal =
1197 PrincipalInfoToPrincipal(aController.PrincipalInfo());
1198 NS_ENSURE_TRUE_VOID(principal);
1200 nsCOMPtr<nsIURI> scope;
1201 nsresult rv =
1202 NS_NewURI(getter_AddRefs(scope), aController.Scope(), nullptr, nullptr);
1203 NS_ENSURE_SUCCESS_VOID(rv);
1205 RefPtr<ServiceWorkerRegistrationInfo> registration =
1206 GetServiceWorkerRegistrationInfo(principal, scope);
1207 NS_ENSURE_TRUE_VOID(registration);
1208 NS_ENSURE_TRUE_VOID(registration->GetActive());
1210 StartControllingClient(aClientInfo, registration, false /* aControlClientHandle */);
1213 ServiceWorkerInfo*
1214 ServiceWorkerManager::GetActiveWorkerInfoForScope(const OriginAttributes& aOriginAttributes,
1215 const nsACString& aScope)
1217 MOZ_ASSERT(NS_IsMainThread());
1219 nsCOMPtr<nsIURI> scopeURI;
1220 nsresult rv = NS_NewURI(getter_AddRefs(scopeURI), aScope, nullptr, nullptr);
1221 if (NS_FAILED(rv)) {
1222 return nullptr;
1224 nsCOMPtr<nsIPrincipal> principal =
1225 BasePrincipal::CreateCodebasePrincipal(scopeURI, aOriginAttributes);
1226 RefPtr<ServiceWorkerRegistrationInfo> registration =
1227 GetServiceWorkerRegistrationInfo(principal, scopeURI);
1228 if (!registration) {
1229 return nullptr;
1232 return registration->GetActive();
1235 namespace {
1237 class UnregisterJobCallback final : public ServiceWorkerJob::Callback
1239 nsCOMPtr<nsIServiceWorkerUnregisterCallback> mCallback;
1241 ~UnregisterJobCallback()
1245 public:
1246 explicit UnregisterJobCallback(nsIServiceWorkerUnregisterCallback* aCallback)
1247 : mCallback(aCallback)
1249 MOZ_ASSERT(NS_IsMainThread());
1250 MOZ_ASSERT(mCallback);
1253 void
1254 JobFinished(ServiceWorkerJob* aJob, ErrorResult& aStatus) override
1256 MOZ_ASSERT(NS_IsMainThread());
1257 MOZ_ASSERT(aJob);
1259 if (aStatus.Failed()) {
1260 mCallback->UnregisterFailed();
1261 return;
1264 MOZ_ASSERT(aJob->GetType() == ServiceWorkerJob::Type::Unregister);
1265 RefPtr<ServiceWorkerUnregisterJob> unregisterJob =
1266 static_cast<ServiceWorkerUnregisterJob*>(aJob);
1267 mCallback->UnregisterSucceeded(unregisterJob->GetResult());
1270 NS_INLINE_DECL_REFCOUNTING(UnregisterJobCallback, override)
1273 } // anonymous namespace
1275 NS_IMETHODIMP
1276 ServiceWorkerManager::Unregister(nsIPrincipal* aPrincipal,
1277 nsIServiceWorkerUnregisterCallback* aCallback,
1278 const nsAString& aScope)
1280 MOZ_ASSERT(NS_IsMainThread());
1282 if (!aPrincipal) {
1283 return NS_ERROR_FAILURE;
1286 nsresult rv;
1288 // This is not accessible by content, and callers should always ensure scope is
1289 // a correct URI, so this is wrapped in DEBUG
1290 #ifdef DEBUG
1291 nsCOMPtr<nsIURI> scopeURI;
1292 rv = NS_NewURI(getter_AddRefs(scopeURI), aScope, nullptr, nullptr);
1293 if (NS_WARN_IF(NS_FAILED(rv))) {
1294 return NS_ERROR_DOM_SECURITY_ERR;
1296 #endif
1298 nsAutoCString scopeKey;
1299 rv = PrincipalToScopeKey(aPrincipal, scopeKey);
1300 if (NS_WARN_IF(NS_FAILED(rv))) {
1301 return rv;
1304 NS_ConvertUTF16toUTF8 scope(aScope);
1305 RefPtr<ServiceWorkerJobQueue> queue = GetOrCreateJobQueue(scopeKey, scope);
1307 RefPtr<ServiceWorkerUnregisterJob> job =
1308 new ServiceWorkerUnregisterJob(aPrincipal, scope, true /* send to parent */);
1310 if (aCallback) {
1311 RefPtr<UnregisterJobCallback> cb = new UnregisterJobCallback(aCallback);
1312 job->AppendResultCallback(cb);
1315 queue->ScheduleJob(job);
1316 return NS_OK;
1319 nsresult
1320 ServiceWorkerManager::NotifyUnregister(nsIPrincipal* aPrincipal,
1321 const nsAString& aScope)
1323 MOZ_ASSERT(NS_IsMainThread());
1324 MOZ_ASSERT(aPrincipal);
1326 nsresult rv;
1328 // This is not accessible by content, and callers should always ensure scope is
1329 // a correct URI, so this is wrapped in DEBUG
1330 #ifdef DEBUG
1331 nsCOMPtr<nsIURI> scopeURI;
1332 rv = NS_NewURI(getter_AddRefs(scopeURI), aScope, nullptr, nullptr);
1333 if (NS_WARN_IF(NS_FAILED(rv))) {
1334 return rv;
1336 #endif
1338 nsAutoCString scopeKey;
1339 rv = PrincipalToScopeKey(aPrincipal, scopeKey);
1340 if (NS_WARN_IF(NS_FAILED(rv))) {
1341 return rv;
1344 NS_ConvertUTF16toUTF8 scope(aScope);
1345 RefPtr<ServiceWorkerJobQueue> queue = GetOrCreateJobQueue(scopeKey, scope);
1347 RefPtr<ServiceWorkerUnregisterJob> job =
1348 new ServiceWorkerUnregisterJob(aPrincipal, scope,
1349 false /* send to parent */);
1351 queue->ScheduleJob(job);
1352 return NS_OK;
1355 void
1356 ServiceWorkerManager::WorkerIsIdle(ServiceWorkerInfo* aWorker)
1358 MOZ_ASSERT(NS_IsMainThread());
1359 MOZ_DIAGNOSTIC_ASSERT(aWorker);
1361 RefPtr<ServiceWorkerRegistrationInfo> reg =
1362 GetRegistration(aWorker->Principal(), aWorker->Scope());
1363 if (!reg) {
1364 return;
1367 if (reg->GetActive() != aWorker) {
1368 return;
1371 if (!reg->IsControllingClients() && reg->IsPendingUninstall()) {
1372 RemoveRegistration(reg);
1373 return;
1376 reg->TryToActivateAsync();
1379 already_AddRefed<ServiceWorkerJobQueue>
1380 ServiceWorkerManager::GetOrCreateJobQueue(const nsACString& aKey,
1381 const nsACString& aScope)
1383 MOZ_ASSERT(!aKey.IsEmpty());
1384 ServiceWorkerManager::RegistrationDataPerPrincipal* data;
1385 // XXX we could use LookupForAdd here to avoid a hashtable lookup, except that
1386 // leads to a false positive assertion, see bug 1370674 comment 7.
1387 if (!mRegistrationInfos.Get(aKey, &data)) {
1388 data = new RegistrationDataPerPrincipal();
1389 mRegistrationInfos.Put(aKey, data);
1392 RefPtr<ServiceWorkerJobQueue> queue =
1393 data->mJobQueues.LookupForAdd(aScope).OrInsert(
1394 []() { return new ServiceWorkerJobQueue(); });
1396 return queue.forget();
1399 /* static */
1400 already_AddRefed<ServiceWorkerManager>
1401 ServiceWorkerManager::GetInstance()
1403 // Note: We don't simply check gInstance for null-ness here, since otherwise
1404 // this can resurrect the ServiceWorkerManager pretty late during shutdown.
1405 static bool firstTime = true;
1406 if (firstTime) {
1407 RefPtr<ServiceWorkerRegistrar> swr;
1409 // Don't create the ServiceWorkerManager until the ServiceWorkerRegistrar is
1410 // initialized.
1411 if (XRE_IsParentProcess()) {
1412 swr = ServiceWorkerRegistrar::Get();
1413 if (!swr) {
1414 return nullptr;
1418 firstTime = false;
1420 MOZ_ASSERT(NS_IsMainThread());
1422 gInstance = new ServiceWorkerManager();
1423 gInstance->Init(swr);
1424 ClearOnShutdown(&gInstance);
1426 RefPtr<ServiceWorkerManager> copy = gInstance.get();
1427 return copy.forget();
1430 void
1431 ServiceWorkerManager::FinishFetch(ServiceWorkerRegistrationInfo* aRegistration)
1435 void
1436 ServiceWorkerManager::ReportToAllClients(const nsCString& aScope,
1437 const nsString& aMessage,
1438 const nsString& aFilename,
1439 const nsString& aLine,
1440 uint32_t aLineNumber,
1441 uint32_t aColumnNumber,
1442 uint32_t aFlags)
1444 ConsoleUtils::ReportForServiceWorkerScope(NS_ConvertUTF8toUTF16(aScope),
1445 aMessage,
1446 aFilename,
1447 aLineNumber,
1448 aColumnNumber,
1449 ConsoleUtils::eError);
1452 /* static */
1453 void
1454 ServiceWorkerManager::LocalizeAndReportToAllClients(
1455 const nsCString& aScope,
1456 const char* aStringKey,
1457 const nsTArray<nsString>& aParamArray,
1458 uint32_t aFlags,
1459 const nsString& aFilename,
1460 const nsString& aLine,
1461 uint32_t aLineNumber,
1462 uint32_t aColumnNumber)
1464 RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
1465 if (!swm) {
1466 return;
1469 nsresult rv;
1470 nsAutoString message;
1471 rv = nsContentUtils::FormatLocalizedString(nsContentUtils::eDOM_PROPERTIES,
1472 aStringKey, aParamArray, message);
1473 if (NS_SUCCEEDED(rv)) {
1474 swm->ReportToAllClients(aScope, message,
1475 aFilename, aLine, aLineNumber, aColumnNumber,
1476 aFlags);
1477 } else {
1478 NS_WARNING("Failed to format and therefore report localized error.");
1482 void
1483 ServiceWorkerManager::HandleError(JSContext* aCx,
1484 nsIPrincipal* aPrincipal,
1485 const nsCString& aScope,
1486 const nsString& aWorkerURL,
1487 const nsString& aMessage,
1488 const nsString& aFilename,
1489 const nsString& aLine,
1490 uint32_t aLineNumber,
1491 uint32_t aColumnNumber,
1492 uint32_t aFlags,
1493 JSExnType aExnType)
1495 MOZ_ASSERT(NS_IsMainThread());
1496 MOZ_ASSERT(aPrincipal);
1498 nsAutoCString scopeKey;
1499 nsresult rv = PrincipalToScopeKey(aPrincipal, scopeKey);
1500 if (NS_WARN_IF(NS_FAILED(rv))) {
1501 return;
1504 ServiceWorkerManager::RegistrationDataPerPrincipal* data;
1505 if (NS_WARN_IF(!mRegistrationInfos.Get(scopeKey, &data))) {
1506 return;
1509 // Always report any uncaught exceptions or errors to the console of
1510 // each client.
1511 ReportToAllClients(aScope, aMessage, aFilename, aLine, aLineNumber,
1512 aColumnNumber, aFlags);
1515 void
1516 ServiceWorkerManager::LoadRegistration(
1517 const ServiceWorkerRegistrationData& aRegistration)
1519 MOZ_ASSERT(NS_IsMainThread());
1521 nsCOMPtr<nsIPrincipal> principal =
1522 PrincipalInfoToPrincipal(aRegistration.principal());
1523 if (!principal) {
1524 return;
1527 RefPtr<ServiceWorkerRegistrationInfo> registration =
1528 GetRegistration(principal, aRegistration.scope());
1529 if (!registration) {
1530 registration =
1531 CreateNewRegistration(
1532 aRegistration.scope(),
1533 principal,
1534 static_cast<ServiceWorkerUpdateViaCache>(aRegistration.updateViaCache())
1536 } else {
1537 // If active worker script matches our expectations for a "current worker",
1538 // then we are done. Since scripts with the same URL might have different
1539 // contents such as updated scripts or scripts with different LoadFlags, we
1540 // use the CacheName to judje whether the two scripts are identical, where
1541 // the CacheName is an UUID generated when a new script is found.
1542 if (registration->GetActive() &&
1543 registration->GetActive()->CacheName() == aRegistration.cacheName()) {
1544 // No needs for updates.
1545 return;
1549 registration->SetLastUpdateTime(aRegistration.lastUpdateTime());
1551 nsLoadFlags importsLoadFlags = nsIChannel::LOAD_BYPASS_SERVICE_WORKER;
1552 importsLoadFlags |=
1553 aRegistration.updateViaCache() == static_cast<uint16_t>(ServiceWorkerUpdateViaCache::None)
1554 ? nsIRequest::LOAD_NORMAL
1555 : nsIRequest::VALIDATE_ALWAYS;
1557 const nsCString& currentWorkerURL = aRegistration.currentWorkerURL();
1558 if (!currentWorkerURL.IsEmpty()) {
1559 registration->SetActive(
1560 new ServiceWorkerInfo(registration->Principal(),
1561 registration->Scope(),
1562 registration->Id(),
1563 registration->Version(),
1564 currentWorkerURL,
1565 aRegistration.cacheName(),
1566 importsLoadFlags));
1567 registration->GetActive()->SetHandlesFetch(aRegistration.currentWorkerHandlesFetch());
1568 registration->GetActive()->SetInstalledTime(aRegistration.currentWorkerInstalledTime());
1569 registration->GetActive()->SetActivatedTime(aRegistration.currentWorkerActivatedTime());
1573 void
1574 ServiceWorkerManager::LoadRegistrations(
1575 const nsTArray<ServiceWorkerRegistrationData>& aRegistrations)
1577 MOZ_ASSERT(NS_IsMainThread());
1579 for (uint32_t i = 0, len = aRegistrations.Length(); i < len; ++i) {
1580 LoadRegistration(aRegistrations[i]);
1584 void
1585 ServiceWorkerManager::StoreRegistration(
1586 nsIPrincipal* aPrincipal,
1587 ServiceWorkerRegistrationInfo* aRegistration)
1589 MOZ_ASSERT(aPrincipal);
1590 MOZ_ASSERT(aRegistration);
1592 if (mShuttingDown) {
1593 return;
1596 ServiceWorkerRegistrationData data;
1597 nsresult rv = PopulateRegistrationData(aPrincipal, aRegistration, data);
1598 if (NS_WARN_IF(NS_FAILED(rv))) {
1599 return;
1602 PrincipalInfo principalInfo;
1603 if (NS_WARN_IF(NS_FAILED(PrincipalToPrincipalInfo(aPrincipal,
1604 &principalInfo)))) {
1605 return;
1608 mActor->SendRegister(data);
1611 already_AddRefed<ServiceWorkerRegistrationInfo>
1612 ServiceWorkerManager::GetServiceWorkerRegistrationInfo(const ClientInfo& aClientInfo) const
1614 nsCOMPtr<nsIPrincipal> principal = aClientInfo.GetPrincipal();
1615 NS_ENSURE_TRUE(principal, nullptr);
1617 nsCOMPtr<nsIURI> uri;
1618 nsresult rv = NS_NewURI(getter_AddRefs(uri), aClientInfo.URL(),
1619 nullptr, nullptr);
1620 NS_ENSURE_SUCCESS(rv, nullptr);
1622 return GetServiceWorkerRegistrationInfo(principal, uri);
1625 already_AddRefed<ServiceWorkerRegistrationInfo>
1626 ServiceWorkerManager::GetServiceWorkerRegistrationInfo(nsIPrincipal* aPrincipal,
1627 nsIURI* aURI) const
1629 MOZ_ASSERT(aPrincipal);
1630 MOZ_ASSERT(aURI);
1632 //XXXnsm Temporary fix until Bug 1171432 is fixed.
1633 if (NS_WARN_IF(BasePrincipal::Cast(aPrincipal)->AppId() == nsIScriptSecurityManager::UNKNOWN_APP_ID)) {
1634 return nullptr;
1637 nsAutoCString scopeKey;
1638 nsresult rv = PrincipalToScopeKey(aPrincipal, scopeKey);
1639 if (NS_FAILED(rv)) {
1640 return nullptr;
1643 return GetServiceWorkerRegistrationInfo(scopeKey, aURI);
1646 already_AddRefed<ServiceWorkerRegistrationInfo>
1647 ServiceWorkerManager::GetServiceWorkerRegistrationInfo(const nsACString& aScopeKey,
1648 nsIURI* aURI) const
1650 MOZ_ASSERT(aURI);
1652 nsAutoCString spec;
1653 nsresult rv = aURI->GetSpec(spec);
1654 if (NS_WARN_IF(NS_FAILED(rv))) {
1655 return nullptr;
1658 nsAutoCString scope;
1659 RegistrationDataPerPrincipal* data;
1660 if (!FindScopeForPath(aScopeKey, spec, &data, scope)) {
1661 return nullptr;
1664 MOZ_ASSERT(data);
1666 RefPtr<ServiceWorkerRegistrationInfo> registration;
1667 data->mInfos.Get(scope, getter_AddRefs(registration));
1668 // ordered scopes and registrations better be in sync.
1669 MOZ_ASSERT(registration);
1671 #ifdef DEBUG
1672 nsAutoCString origin;
1673 rv = registration->Principal()->GetOrigin(origin);
1674 MOZ_ASSERT(NS_SUCCEEDED(rv));
1675 MOZ_ASSERT(origin.Equals(aScopeKey));
1676 #endif
1678 if (registration->IsPendingUninstall()) {
1679 return nullptr;
1681 return registration.forget();
1684 /* static */ nsresult
1685 ServiceWorkerManager::PrincipalToScopeKey(nsIPrincipal* aPrincipal,
1686 nsACString& aKey)
1688 MOZ_ASSERT(aPrincipal);
1690 if (!BasePrincipal::Cast(aPrincipal)->IsCodebasePrincipal()) {
1691 return NS_ERROR_FAILURE;
1694 nsresult rv = aPrincipal->GetOrigin(aKey);
1695 if (NS_WARN_IF(NS_FAILED(rv))) {
1696 return rv;
1699 return NS_OK;
1702 /* static */ nsresult
1703 ServiceWorkerManager::PrincipalInfoToScopeKey(const PrincipalInfo& aPrincipalInfo,
1704 nsACString& aKey)
1706 if (aPrincipalInfo.type() != PrincipalInfo::TContentPrincipalInfo) {
1707 return NS_ERROR_FAILURE;
1710 auto content = aPrincipalInfo.get_ContentPrincipalInfo();
1712 nsAutoCString suffix;
1713 content.attrs().CreateSuffix(suffix);
1715 aKey = content.originNoSuffix();
1716 aKey.Append(suffix);
1718 return NS_OK;
1721 /* static */ void
1722 ServiceWorkerManager::AddScopeAndRegistration(const nsACString& aScope,
1723 ServiceWorkerRegistrationInfo* aInfo)
1725 MOZ_ASSERT(aInfo);
1726 MOZ_ASSERT(aInfo->Principal());
1728 RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
1729 if (!swm) {
1730 // browser shutdown
1731 return;
1734 nsAutoCString scopeKey;
1735 nsresult rv = swm->PrincipalToScopeKey(aInfo->Principal(), scopeKey);
1736 if (NS_WARN_IF(NS_FAILED(rv))) {
1737 return;
1740 MOZ_ASSERT(!scopeKey.IsEmpty());
1742 RegistrationDataPerPrincipal* data =
1743 swm->mRegistrationInfos.LookupForAdd(scopeKey).OrInsert(
1744 []() { return new RegistrationDataPerPrincipal(); });
1746 for (uint32_t i = 0; i < data->mOrderedScopes.Length(); ++i) {
1747 const nsCString& current = data->mOrderedScopes[i];
1749 // Perfect match!
1750 if (aScope.Equals(current)) {
1751 data->mInfos.Put(aScope, aInfo);
1752 swm->NotifyListenersOnRegister(aInfo);
1753 return;
1756 // Sort by length, with longest match first.
1757 // /foo/bar should be before /foo/
1758 // Similarly /foo/b is between the two.
1759 if (StringBeginsWith(aScope, current)) {
1760 data->mOrderedScopes.InsertElementAt(i, aScope);
1761 data->mInfos.Put(aScope, aInfo);
1762 swm->NotifyListenersOnRegister(aInfo);
1763 return;
1767 data->mOrderedScopes.AppendElement(aScope);
1768 data->mInfos.Put(aScope, aInfo);
1769 swm->NotifyListenersOnRegister(aInfo);
1772 /* static */ bool
1773 ServiceWorkerManager::FindScopeForPath(const nsACString& aScopeKey,
1774 const nsACString& aPath,
1775 RegistrationDataPerPrincipal** aData,
1776 nsACString& aMatch)
1778 MOZ_ASSERT(aData);
1780 RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
1782 if (!swm || !swm->mRegistrationInfos.Get(aScopeKey, aData)) {
1783 return false;
1786 for (uint32_t i = 0; i < (*aData)->mOrderedScopes.Length(); ++i) {
1787 const nsCString& current = (*aData)->mOrderedScopes[i];
1788 if (StringBeginsWith(aPath, current)) {
1789 aMatch = current;
1790 return true;
1794 return false;
1797 /* static */ bool
1798 ServiceWorkerManager::HasScope(nsIPrincipal* aPrincipal,
1799 const nsACString& aScope)
1801 RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
1802 if (!swm) {
1803 return false;
1806 nsAutoCString scopeKey;
1807 nsresult rv = PrincipalToScopeKey(aPrincipal, scopeKey);
1808 if (NS_WARN_IF(NS_FAILED(rv))) {
1809 return false;
1812 RegistrationDataPerPrincipal* data;
1813 if (!swm->mRegistrationInfos.Get(scopeKey, &data)) {
1814 return false;
1817 return data->mOrderedScopes.Contains(aScope);
1820 /* static */ void
1821 ServiceWorkerManager::RemoveScopeAndRegistration(ServiceWorkerRegistrationInfo* aRegistration)
1823 RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
1824 if (!swm) {
1825 return;
1828 nsAutoCString scopeKey;
1829 nsresult rv = swm->PrincipalToScopeKey(aRegistration->Principal(), scopeKey);
1830 if (NS_WARN_IF(NS_FAILED(rv))) {
1831 return;
1834 RegistrationDataPerPrincipal* data;
1835 if (!swm->mRegistrationInfos.Get(scopeKey, &data)) {
1836 return;
1839 if (auto entry = data->mUpdateTimers.Lookup(aRegistration->Scope())) {
1840 entry.Data()->Cancel();
1841 entry.Remove();
1844 // Verify there are no controlled clients for the purged registration.
1845 for (auto iter = swm->mControlledClients.Iter(); !iter.Done(); iter.Next()) {
1846 auto& reg = iter.UserData()->mRegistrationInfo;
1847 if (reg->Scope().Equals(aRegistration->Scope()) &&
1848 reg->Principal()->Equals(aRegistration->Principal())) {
1849 MOZ_DIAGNOSTIC_ASSERT(aRegistration->IsCorrupt(),
1850 "controlled client when removing non-corrupt registration");
1851 iter.Remove();
1852 break;
1856 RefPtr<ServiceWorkerRegistrationInfo> info;
1857 data->mInfos.Remove(aRegistration->Scope(), getter_AddRefs(info));
1858 data->mOrderedScopes.RemoveElement(aRegistration->Scope());
1859 swm->NotifyListenersOnUnregister(info);
1861 swm->MaybeRemoveRegistrationInfo(scopeKey);
1862 aRegistration->NotifyRemoved();
1865 void
1866 ServiceWorkerManager::MaybeRemoveRegistrationInfo(const nsACString& aScopeKey)
1868 if (auto entry = mRegistrationInfos.Lookup(aScopeKey)) {
1869 if (entry.Data()->mOrderedScopes.IsEmpty() &&
1870 entry.Data()->mJobQueues.Count() == 0) {
1871 entry.Remove();
1876 bool
1877 ServiceWorkerManager::StartControlling(const ClientInfo& aClientInfo,
1878 const ServiceWorkerDescriptor& aServiceWorker)
1880 MOZ_ASSERT(NS_IsMainThread());
1882 nsCOMPtr<nsIPrincipal> principal =
1883 PrincipalInfoToPrincipal(aServiceWorker.PrincipalInfo());
1884 NS_ENSURE_TRUE(principal, false);
1886 nsCOMPtr<nsIURI> scope;
1887 nsresult rv =
1888 NS_NewURI(getter_AddRefs(scope), aServiceWorker.Scope(), nullptr, nullptr);
1889 NS_ENSURE_SUCCESS(rv, false);
1891 RefPtr<ServiceWorkerRegistrationInfo> registration =
1892 GetServiceWorkerRegistrationInfo(principal, scope);
1893 NS_ENSURE_TRUE(registration, false);
1894 NS_ENSURE_TRUE(registration->GetActive(), false);
1896 StartControllingClient(aClientInfo, registration);
1898 return true;
1901 void
1902 ServiceWorkerManager::MaybeCheckNavigationUpdate(const ClientInfo& aClientInfo)
1904 MOZ_ASSERT(NS_IsMainThread());
1905 // We perform these success path navigation update steps when the
1906 // document tells us its more or less done loading. This avoids
1907 // slowing down page load and also lets pages consistently get
1908 // updatefound events when they fire.
1910 // 9.8.20 If respondWithEntered is false, then:
1911 // 9.8.22 Else: (respondWith was entered and succeeded)
1912 // If request is a non-subresource request, then: Invoke Soft Update
1913 // algorithm.
1914 ControlledClientData* data = mControlledClients.Get(aClientInfo.Id());
1915 if (data && data->mRegistrationInfo) {
1916 data->mRegistrationInfo->MaybeScheduleUpdate();
1920 void
1921 ServiceWorkerManager::StopControllingRegistration(ServiceWorkerRegistrationInfo* aRegistration)
1923 aRegistration->StopControllingClient();
1924 if (aRegistration->IsControllingClients() || !aRegistration->IsIdle()) {
1925 return;
1928 if (aRegistration->IsPendingUninstall()) {
1929 RemoveRegistration(aRegistration);
1930 return;
1933 // We use to aggressively terminate the worker at this point, but it
1934 // caused problems. There are more uses for a service worker than actively
1935 // controlled documents. We need to let the worker naturally terminate
1936 // in case its handling push events, message events, etc.
1937 aRegistration->TryToActivateAsync();
1940 NS_IMETHODIMP
1941 ServiceWorkerManager::GetScopeForUrl(nsIPrincipal* aPrincipal,
1942 const nsAString& aUrl, nsAString& aScope)
1944 MOZ_ASSERT(aPrincipal);
1946 nsCOMPtr<nsIURI> uri;
1947 nsresult rv = NS_NewURI(getter_AddRefs(uri), aUrl, nullptr, nullptr);
1948 if (NS_WARN_IF(NS_FAILED(rv))) {
1949 return NS_ERROR_FAILURE;
1952 RefPtr<ServiceWorkerRegistrationInfo> r =
1953 GetServiceWorkerRegistrationInfo(aPrincipal, uri);
1954 if (!r) {
1955 return NS_ERROR_FAILURE;
1958 aScope = NS_ConvertUTF8toUTF16(r->Scope());
1959 return NS_OK;
1962 namespace {
1964 class ContinueDispatchFetchEventRunnable : public Runnable
1966 RefPtr<ServiceWorkerPrivate> mServiceWorkerPrivate;
1967 nsCOMPtr<nsIInterceptedChannel> mChannel;
1968 nsCOMPtr<nsILoadGroup> mLoadGroup;
1969 bool mIsReload;
1970 public:
1971 ContinueDispatchFetchEventRunnable(
1972 ServiceWorkerPrivate* aServiceWorkerPrivate,
1973 nsIInterceptedChannel* aChannel,
1974 nsILoadGroup* aLoadGroup,
1975 bool aIsReload)
1976 : Runnable("dom::ServiceWorkerManager::ContinueDispatchFetchEventRunnable")
1977 , mServiceWorkerPrivate(aServiceWorkerPrivate)
1978 , mChannel(aChannel)
1979 , mLoadGroup(aLoadGroup)
1980 , mIsReload(aIsReload)
1982 MOZ_ASSERT(aServiceWorkerPrivate);
1983 MOZ_ASSERT(aChannel);
1986 void
1987 HandleError()
1989 MOZ_ASSERT(NS_IsMainThread());
1990 NS_WARNING("Unexpected error while dispatching fetch event!");
1991 nsresult rv = mChannel->ResetInterception();
1992 if (NS_FAILED(rv)) {
1993 NS_WARNING("Failed to resume intercepted network request");
1994 mChannel->CancelInterception(rv);
1998 NS_IMETHOD
1999 Run() override
2001 MOZ_ASSERT(NS_IsMainThread());
2003 nsCOMPtr<nsIChannel> channel;
2004 nsresult rv = mChannel->GetChannel(getter_AddRefs(channel));
2005 if (NS_WARN_IF(NS_FAILED(rv))) {
2006 HandleError();
2007 return NS_OK;
2010 // The channel might have encountered an unexpected error while ensuring
2011 // the upload stream is cloneable. Check here and reset the interception
2012 // if that happens.
2013 nsresult status;
2014 rv = channel->GetStatus(&status);
2015 if (NS_WARN_IF(NS_FAILED(rv) || NS_FAILED(status))) {
2016 HandleError();
2017 return NS_OK;
2020 nsString clientId;
2021 nsCOMPtr<nsILoadInfo> loadInfo = channel->GetLoadInfo();
2022 if (loadInfo) {
2023 Maybe<ClientInfo> clientInfo = loadInfo->GetClientInfo();
2024 if (clientInfo.isSome()) {
2025 char buf[NSID_LENGTH];
2026 clientInfo.ref().Id().ToProvidedString(buf);
2027 NS_ConvertASCIItoUTF16 uuid(buf);
2029 // Remove {} and the null terminator
2030 clientId.Assign(Substring(uuid, 1, NSID_LENGTH - 3));
2034 rv = mServiceWorkerPrivate->SendFetchEvent(mChannel, mLoadGroup, clientId,
2035 mIsReload);
2036 if (NS_WARN_IF(NS_FAILED(rv))) {
2037 HandleError();
2040 return NS_OK;
2044 } // anonymous namespace
2046 void
2047 ServiceWorkerManager::DispatchFetchEvent(nsIInterceptedChannel* aChannel,
2048 ErrorResult& aRv)
2050 MOZ_ASSERT(aChannel);
2051 MOZ_ASSERT(NS_IsMainThread());
2053 nsCOMPtr<nsIChannel> internalChannel;
2054 aRv = aChannel->GetChannel(getter_AddRefs(internalChannel));
2055 if (NS_WARN_IF(aRv.Failed())) {
2056 return;
2059 nsCOMPtr<nsILoadGroup> loadGroup;
2060 aRv = internalChannel->GetLoadGroup(getter_AddRefs(loadGroup));
2061 if (NS_WARN_IF(aRv.Failed())) {
2062 return;
2065 nsCOMPtr<nsILoadInfo> loadInfo = internalChannel->GetLoadInfo();
2066 if (NS_WARN_IF(!loadInfo)) {
2067 aRv.Throw(NS_ERROR_UNEXPECTED);
2068 return;
2071 RefPtr<ServiceWorkerInfo> serviceWorker;
2073 if (!nsContentUtils::IsNonSubresourceRequest(internalChannel)) {
2074 const Maybe<ServiceWorkerDescriptor>& controller = loadInfo->GetController();
2075 if (NS_WARN_IF(controller.isNothing())) {
2076 aRv.Throw(NS_ERROR_FAILURE);
2077 return;
2080 RefPtr<ServiceWorkerRegistrationInfo> registration =
2081 GetRegistration(controller.ref().PrincipalInfo(), controller.ref().Scope());
2082 if (NS_WARN_IF(!registration)) {
2083 aRv.Throw(NS_ERROR_FAILURE);
2084 return;
2087 serviceWorker = registration->GetActive();
2088 if (NS_WARN_IF(!serviceWorker) ||
2089 NS_WARN_IF(serviceWorker->Descriptor().Id() != controller.ref().Id())) {
2090 aRv.Throw(NS_ERROR_FAILURE);
2091 return;
2093 } else {
2094 nsCOMPtr<nsIURI> uri;
2095 aRv = aChannel->GetSecureUpgradedChannelURI(getter_AddRefs(uri));
2096 if (NS_WARN_IF(aRv.Failed())) {
2097 return;
2100 // non-subresource request means the URI contains the principal
2101 nsCOMPtr<nsIPrincipal> principal =
2102 BasePrincipal::CreateCodebasePrincipal(uri,
2103 loadInfo->GetOriginAttributes());
2105 RefPtr<ServiceWorkerRegistrationInfo> registration =
2106 GetServiceWorkerRegistrationInfo(principal, uri);
2107 if (NS_WARN_IF(!registration)) {
2108 aRv.Throw(NS_ERROR_FAILURE);
2109 return;
2112 // While we only enter this method if IsAvailable() previously saw
2113 // an active worker, it is possible for that worker to be removed
2114 // before we get to this point. Therefore we must handle a nullptr
2115 // active worker here.
2116 serviceWorker = registration->GetActive();
2117 if (NS_WARN_IF(!serviceWorker)) {
2118 aRv.Throw(NS_ERROR_FAILURE);
2119 return;
2122 // If there is a reserved client it should be marked as controlled before
2123 // the FetchEvent is dispatched.
2124 Maybe<ClientInfo> clientInfo = loadInfo->GetReservedClientInfo();
2126 // Also override the initial about:blank controller since the real
2127 // network load may be intercepted by a different service worker. If
2128 // the intial about:blank has a controller here its simply been
2129 // inherited from its parent.
2130 if (clientInfo.isNothing()) {
2131 clientInfo = loadInfo->GetInitialClientInfo();
2133 // TODO: We need to handle the case where the initial about:blank is
2134 // controlled, but the final document load is not. Right now
2135 // the spec does not really say what to do. There currently
2136 // is no way for the controller to be cleared from a client in
2137 // the spec or our implementation. We may want to force a
2138 // new inner window to be created instead of reusing the
2139 // initial about:blank global. See bug 1419620 and the spec
2140 // issue here: https://github.com/w3c/ServiceWorker/issues/1232
2143 if (clientInfo.isSome()) {
2144 // ClientChannelHelper is not called for STS upgrades that get
2145 // intercepted by a service worker when interception occurs in
2146 // the content process. Therefore the reserved client is not
2147 // properly cleared in that case leading to a situation where
2148 // a ClientSource with an http:// principal is controlled by
2149 // a ServiceWorker with an https:// principal.
2151 // This does not occur when interception is handled by the
2152 // simpler InterceptedHttpChannel approach in the parent.
2154 // As a temporary work around check for this principal mismatch
2155 // here and perform the ClientChannelHelper's replacement of
2156 // reserved client automatically.
2157 if (!XRE_IsParentProcess()) {
2158 nsCOMPtr<nsIPrincipal> clientPrincipal = clientInfo.ref().GetPrincipal();
2159 if (!clientPrincipal || !clientPrincipal->Equals(principal)) {
2160 UniquePtr<ClientSource> reservedClient =
2161 loadInfo->TakeReservedClientSource();
2163 nsCOMPtr<nsISerialEventTarget> target =
2164 reservedClient ? reservedClient->EventTarget()
2165 : SystemGroup::EventTargetFor(TaskCategory::Other);
2167 reservedClient.reset();
2168 reservedClient = ClientManager::CreateSource(ClientType::Window,
2169 target,
2170 principal);
2172 loadInfo->GiveReservedClientSource(std::move(reservedClient));
2174 clientInfo = loadInfo->GetReservedClientInfo();
2178 // First, attempt to mark the reserved client controlled directly. This
2179 // will update the controlled status in the ClientManagerService in the
2180 // parent. It will also eventually propagate back to the ClientSource.
2181 StartControllingClient(clientInfo.ref(), registration);
2184 uint32_t redirectMode = nsIHttpChannelInternal::REDIRECT_MODE_MANUAL;
2185 nsCOMPtr<nsIHttpChannelInternal> http = do_QueryInterface(internalChannel);
2186 MOZ_ALWAYS_SUCCEEDS(http->GetRedirectMode(&redirectMode));
2188 // Synthetic redirects for non-subresource requests with a "follow"
2189 // redirect mode may switch controllers. This is basically worker
2190 // scripts right now. In this case we need to explicitly clear the
2191 // controller to avoid assertions on the SetController() below.
2192 if (redirectMode == nsIHttpChannelInternal::REDIRECT_MODE_FOLLOW) {
2193 loadInfo->ClearController();
2196 // But we also note the reserved state on the LoadInfo. This allows the
2197 // ClientSource to be updated immediately after the nsIChannel starts.
2198 // This is necessary to have the correct controller in place for immediate
2199 // follow-on requests.
2200 loadInfo->SetController(serviceWorker->Descriptor());
2203 MOZ_DIAGNOSTIC_ASSERT(serviceWorker);
2205 nsCOMPtr<nsIRunnable> continueRunnable =
2206 new ContinueDispatchFetchEventRunnable(serviceWorker->WorkerPrivate(),
2207 aChannel, loadGroup,
2208 loadInfo->GetIsDocshellReload());
2210 // When this service worker was registered, we also sent down the permissions
2211 // for the runnable. They should have arrived by now, but we still need to
2212 // wait for them if they have not.
2213 nsCOMPtr<nsIRunnable> permissionsRunnable = NS_NewRunnableFunction(
2214 "dom::ServiceWorkerManager::DispatchFetchEvent", [=]() {
2215 nsCOMPtr<nsIPermissionManager> permMgr = services::GetPermissionManager();
2216 MOZ_ALWAYS_SUCCEEDS(permMgr->WhenPermissionsAvailable(serviceWorker->Principal(),
2217 continueRunnable));
2220 nsCOMPtr<nsIUploadChannel2> uploadChannel = do_QueryInterface(internalChannel);
2222 // If there is no upload stream, then continue immediately
2223 if (!uploadChannel) {
2224 MOZ_ALWAYS_SUCCEEDS(permissionsRunnable->Run());
2225 return;
2227 // Otherwise, ensure the upload stream can be cloned directly. This may
2228 // require some async copying, so provide a callback.
2229 aRv = uploadChannel->EnsureUploadStreamIsCloneable(permissionsRunnable);
2232 bool
2233 ServiceWorkerManager::IsAvailable(nsIPrincipal* aPrincipal,
2234 nsIURI* aURI)
2236 MOZ_ASSERT(aPrincipal);
2237 MOZ_ASSERT(aURI);
2239 RefPtr<ServiceWorkerRegistrationInfo> registration =
2240 GetServiceWorkerRegistrationInfo(aPrincipal, aURI);
2241 return registration && registration->GetActive();
2244 nsresult
2245 ServiceWorkerManager::GetClientRegistration(const ClientInfo& aClientInfo,
2246 ServiceWorkerRegistrationInfo** aRegistrationInfo)
2248 ControlledClientData* data = mControlledClients.Get(aClientInfo.Id());
2249 if (!data || !data->mRegistrationInfo) {
2250 return NS_ERROR_NOT_AVAILABLE;
2253 // If the document is controlled, the current worker MUST be non-null.
2254 if (!data->mRegistrationInfo->GetActive()) {
2255 return NS_ERROR_NOT_AVAILABLE;
2258 RefPtr<ServiceWorkerRegistrationInfo> ref = data->mRegistrationInfo;
2259 ref.forget(aRegistrationInfo);
2260 return NS_OK;
2263 void
2264 ServiceWorkerManager::SoftUpdate(const OriginAttributes& aOriginAttributes,
2265 const nsACString& aScope)
2267 MOZ_ASSERT(NS_IsMainThread());
2269 if (mShuttingDown) {
2270 return;
2273 if (ServiceWorkerParentInterceptEnabled()) {
2274 SoftUpdateInternal(aOriginAttributes, aScope, nullptr);
2275 return;
2278 RefPtr<GenericPromise::Private> promise =
2279 new GenericPromise::Private(__func__);
2281 RefPtr<CancelableRunnable> successRunnable =
2282 new SoftUpdateRunnable(aOriginAttributes, aScope, true, promise);
2284 RefPtr<CancelableRunnable> failureRunnable =
2285 new ResolvePromiseRunnable(promise);
2287 ServiceWorkerUpdaterChild* actor =
2288 new ServiceWorkerUpdaterChild(promise, successRunnable, failureRunnable);
2290 mActor->SendPServiceWorkerUpdaterConstructor(actor, aOriginAttributes,
2291 nsCString(aScope));
2294 namespace {
2296 class UpdateJobCallback final : public ServiceWorkerJob::Callback
2298 RefPtr<ServiceWorkerUpdateFinishCallback> mCallback;
2300 ~UpdateJobCallback() = default;
2302 public:
2303 explicit UpdateJobCallback(ServiceWorkerUpdateFinishCallback* aCallback)
2304 : mCallback(aCallback)
2306 MOZ_ASSERT(NS_IsMainThread());
2307 MOZ_ASSERT(mCallback);
2310 void
2311 JobFinished(ServiceWorkerJob* aJob, ErrorResult& aStatus) override
2313 MOZ_ASSERT(NS_IsMainThread());
2314 MOZ_ASSERT(aJob);
2316 if (aStatus.Failed()) {
2317 mCallback->UpdateFailed(aStatus);
2318 return;
2321 MOZ_DIAGNOSTIC_ASSERT(aJob->GetType() == ServiceWorkerJob::Type::Update);
2322 RefPtr<ServiceWorkerUpdateJob> updateJob =
2323 static_cast<ServiceWorkerUpdateJob*>(aJob);
2324 RefPtr<ServiceWorkerRegistrationInfo> reg = updateJob->GetRegistration();
2325 mCallback->UpdateSucceeded(reg);
2328 NS_INLINE_DECL_REFCOUNTING(UpdateJobCallback, override)
2331 } // anonymous namespace
2333 void
2334 ServiceWorkerManager::SoftUpdateInternal(const OriginAttributes& aOriginAttributes,
2335 const nsACString& aScope,
2336 ServiceWorkerUpdateFinishCallback* aCallback)
2338 MOZ_ASSERT(NS_IsMainThread());
2340 if (mShuttingDown) {
2341 return;
2344 nsCOMPtr<nsIURI> scopeURI;
2345 nsresult rv = NS_NewURI(getter_AddRefs(scopeURI), aScope);
2346 if (NS_WARN_IF(NS_FAILED(rv))) {
2347 return;
2350 nsCOMPtr<nsIPrincipal> principal =
2351 BasePrincipal::CreateCodebasePrincipal(scopeURI, aOriginAttributes);
2352 if (NS_WARN_IF(!principal)) {
2353 return;
2356 nsAutoCString scopeKey;
2357 rv = PrincipalToScopeKey(principal, scopeKey);
2358 if (NS_WARN_IF(NS_FAILED(rv))) {
2359 return;
2362 RefPtr<ServiceWorkerRegistrationInfo> registration =
2363 GetRegistration(scopeKey, aScope);
2364 if (NS_WARN_IF(!registration)) {
2365 return;
2368 // "If registration's uninstalling flag is set, abort these steps."
2369 if (registration->IsPendingUninstall()) {
2370 return;
2373 // "If registration's installing worker is not null, abort these steps."
2374 if (registration->GetInstalling()) {
2375 return;
2378 // "Let newestWorker be the result of running Get Newest Worker algorithm
2379 // passing registration as its argument.
2380 // If newestWorker is null, abort these steps."
2381 RefPtr<ServiceWorkerInfo> newest = registration->Newest();
2382 if (!newest) {
2383 return;
2386 // "If the registration queue for registration is empty, invoke Update algorithm,
2387 // or its equivalent, with client, registration as its argument."
2388 // TODO(catalinb): We don't implement the force bypass cache flag.
2389 // See: https://github.com/slightlyoff/ServiceWorker/issues/759
2390 RefPtr<ServiceWorkerJobQueue> queue = GetOrCreateJobQueue(scopeKey,
2391 aScope);
2393 RefPtr<ServiceWorkerUpdateJob> job =
2394 new ServiceWorkerUpdateJob(principal, registration->Scope(),
2395 newest->ScriptSpec(), nullptr,
2396 registration->GetUpdateViaCache());
2398 if (aCallback) {
2399 RefPtr<UpdateJobCallback> cb = new UpdateJobCallback(aCallback);
2400 job->AppendResultCallback(cb);
2403 queue->ScheduleJob(job);
2406 void
2407 ServiceWorkerManager::Update(nsIPrincipal* aPrincipal,
2408 const nsACString& aScope,
2409 ServiceWorkerUpdateFinishCallback* aCallback)
2411 MOZ_ASSERT(NS_IsMainThread());
2413 if (ServiceWorkerParentInterceptEnabled()) {
2414 UpdateInternal(aPrincipal, aScope, aCallback);
2415 return;
2418 RefPtr<GenericPromise::Private> promise =
2419 new GenericPromise::Private(__func__);
2421 RefPtr<CancelableRunnable> successRunnable =
2422 new UpdateRunnable(aPrincipal, aScope, aCallback,
2423 UpdateRunnable::eSuccess, promise);
2425 RefPtr<CancelableRunnable> failureRunnable =
2426 new UpdateRunnable(aPrincipal, aScope, aCallback,
2427 UpdateRunnable::eFailure, promise);
2429 ServiceWorkerUpdaterChild* actor =
2430 new ServiceWorkerUpdaterChild(promise, successRunnable, failureRunnable);
2432 mActor->SendPServiceWorkerUpdaterConstructor(actor,
2433 aPrincipal->OriginAttributesRef(),
2434 nsCString(aScope));
2437 namespace {
2439 void
2440 RejectUpdateWithInvalidStateError(ServiceWorkerUpdateFinishCallback& aCallback)
2442 ErrorResult error(NS_ERROR_DOM_INVALID_STATE_ERR);
2443 aCallback.UpdateFailed(error);
2445 // In case the callback does not consume the exception
2446 error.SuppressException();
2451 void
2452 ServiceWorkerManager::UpdateInternal(nsIPrincipal* aPrincipal,
2453 const nsACString& aScope,
2454 ServiceWorkerUpdateFinishCallback* aCallback)
2456 MOZ_ASSERT(aPrincipal);
2457 MOZ_ASSERT(aCallback);
2459 nsAutoCString scopeKey;
2460 nsresult rv = PrincipalToScopeKey(aPrincipal, scopeKey);
2461 if (NS_WARN_IF(NS_FAILED(rv))) {
2462 return;
2465 RefPtr<ServiceWorkerRegistrationInfo> registration =
2466 GetRegistration(scopeKey, aScope);
2467 if (NS_WARN_IF(!registration)) {
2468 return;
2471 // "Let newestWorker be the result of running Get Newest Worker algorithm
2472 // passing registration as its argument.
2473 // If newestWorker is null, return a promise rejected with "InvalidStateError"
2474 RefPtr<ServiceWorkerInfo> newest = registration->Newest();
2475 if (!newest) {
2476 RejectUpdateWithInvalidStateError(*aCallback);
2477 return;
2480 if (newest->State() == ServiceWorkerState::Installing) {
2481 RejectUpdateWithInvalidStateError(*aCallback);
2482 return;
2485 RefPtr<ServiceWorkerJobQueue> queue = GetOrCreateJobQueue(scopeKey, aScope);
2487 // "Invoke Update algorithm, or its equivalent, with client, registration as
2488 // its argument."
2489 RefPtr<ServiceWorkerUpdateJob> job =
2490 new ServiceWorkerUpdateJob(aPrincipal, registration->Scope(),
2491 newest->ScriptSpec(), nullptr,
2492 registration->GetUpdateViaCache());
2494 RefPtr<UpdateJobCallback> cb = new UpdateJobCallback(aCallback);
2495 job->AppendResultCallback(cb);
2497 queue->ScheduleJob(job);
2500 already_AddRefed<GenericPromise>
2501 ServiceWorkerManager::MaybeClaimClient(const ClientInfo& aClientInfo,
2502 ServiceWorkerRegistrationInfo* aWorkerRegistration)
2504 MOZ_DIAGNOSTIC_ASSERT(aWorkerRegistration);
2506 RefPtr<GenericPromise> ref;
2508 if (!aWorkerRegistration->GetActive()) {
2509 ref = GenericPromise::CreateAndReject(NS_ERROR_DOM_INVALID_STATE_ERR,
2510 __func__);
2511 return ref.forget();
2514 // Same origin check
2515 nsCOMPtr<nsIPrincipal> principal(aClientInfo.GetPrincipal());
2516 if (!aWorkerRegistration->Principal()->Equals(principal)) {
2517 ref = GenericPromise::CreateAndReject(NS_ERROR_DOM_SECURITY_ERR, __func__);
2518 return ref.forget();
2521 // The registration that should be controlling the client
2522 RefPtr<ServiceWorkerRegistrationInfo> matchingRegistration =
2523 GetServiceWorkerRegistrationInfo(aClientInfo);
2525 // The registration currently controlling the client
2526 RefPtr<ServiceWorkerRegistrationInfo> controllingRegistration;
2527 GetClientRegistration(aClientInfo, getter_AddRefs(controllingRegistration));
2529 if (aWorkerRegistration != matchingRegistration ||
2530 aWorkerRegistration == controllingRegistration) {
2531 ref = GenericPromise::CreateAndResolve(true, __func__);
2532 return ref.forget();
2535 ref = StartControllingClient(aClientInfo, aWorkerRegistration);
2536 return ref.forget();
2539 already_AddRefed<GenericPromise>
2540 ServiceWorkerManager::MaybeClaimClient(const ClientInfo& aClientInfo,
2541 const ServiceWorkerDescriptor& aServiceWorker)
2543 RefPtr<GenericPromise> ref;
2545 nsCOMPtr<nsIPrincipal> principal = aServiceWorker.GetPrincipal();
2546 if (!principal) {
2547 ref = GenericPromise::CreateAndResolve(false, __func__);
2548 return ref.forget();
2551 RefPtr<ServiceWorkerRegistrationInfo> registration =
2552 GetRegistration(principal, aServiceWorker.Scope());
2554 // While ServiceWorkerManager is distributed across child processes its
2555 // possible for us to sometimes get a claim for a new worker that has
2556 // not propagated to this process yet. For now, simply note that we
2557 // are done. The fix for this is to move the SWM to the parent process
2558 // so there are no consistency errors.
2559 if (NS_WARN_IF(!registration) || NS_WARN_IF(!registration->GetActive())) {
2560 ref = GenericPromise::CreateAndResolve(false, __func__);
2561 return ref.forget();
2564 ref = MaybeClaimClient(aClientInfo, registration);
2565 return ref.forget();
2568 void
2569 ServiceWorkerManager::SetSkipWaitingFlag(nsIPrincipal* aPrincipal,
2570 const nsCString& aScope,
2571 uint64_t aServiceWorkerID)
2573 RefPtr<ServiceWorkerRegistrationInfo> registration =
2574 GetRegistration(aPrincipal, aScope);
2575 if (NS_WARN_IF(!registration)) {
2576 return;
2579 RefPtr<ServiceWorkerInfo> worker =
2580 registration->GetServiceWorkerInfoById(aServiceWorkerID);
2582 if (NS_WARN_IF(!worker)) {
2583 return;
2586 worker->SetSkipWaitingFlag();
2588 if (worker->State() == ServiceWorkerState::Installed) {
2589 registration->TryToActivateAsync();
2593 void
2594 ServiceWorkerManager::UpdateClientControllers(ServiceWorkerRegistrationInfo* aRegistration)
2596 MOZ_ASSERT(NS_IsMainThread());
2598 RefPtr<ServiceWorkerInfo> activeWorker = aRegistration->GetActive();
2599 MOZ_DIAGNOSTIC_ASSERT(activeWorker);
2601 AutoTArray<RefPtr<ClientHandle>, 16> handleList;
2602 for (auto iter = mControlledClients.Iter(); !iter.Done(); iter.Next()) {
2603 if (iter.UserData()->mRegistrationInfo != aRegistration) {
2604 continue;
2607 handleList.AppendElement(iter.UserData()->mClientHandle);
2610 // Fire event after iterating mControlledClients is done to prevent
2611 // modification by reentering from the event handlers during iteration.
2612 for (auto& handle : handleList) {
2613 RefPtr<GenericPromise> p = handle->Control(activeWorker->Descriptor());
2615 RefPtr<ServiceWorkerManager> self = this;
2617 // If we fail to control the client, then automatically remove it
2618 // from our list of controlled clients.
2619 p->Then(
2620 SystemGroup::EventTargetFor(TaskCategory::Other), __func__,
2621 [] (bool) {
2622 // do nothing on success
2623 }, [self, clientInfo = handle->Info()] (nsresult aRv) {
2624 // failed to control, forget about this client
2625 self->StopControllingClient(clientInfo);
2630 already_AddRefed<ServiceWorkerRegistrationInfo>
2631 ServiceWorkerManager::GetRegistration(nsIPrincipal* aPrincipal,
2632 const nsACString& aScope) const
2634 MOZ_ASSERT(aPrincipal);
2636 nsAutoCString scopeKey;
2637 nsresult rv = PrincipalToScopeKey(aPrincipal, scopeKey);
2638 if (NS_WARN_IF(NS_FAILED(rv))) {
2639 return nullptr;
2642 return GetRegistration(scopeKey, aScope);
2645 already_AddRefed<ServiceWorkerRegistrationInfo>
2646 ServiceWorkerManager::GetRegistration(const PrincipalInfo& aPrincipalInfo,
2647 const nsACString& aScope) const
2649 nsAutoCString scopeKey;
2650 nsresult rv = PrincipalInfoToScopeKey(aPrincipalInfo, scopeKey);
2651 if (NS_WARN_IF(NS_FAILED(rv))) {
2652 return nullptr;
2655 return GetRegistration(scopeKey, aScope);
2658 NS_IMETHODIMP
2659 ServiceWorkerManager::GetRegistrationByPrincipal(nsIPrincipal* aPrincipal,
2660 const nsAString& aScope,
2661 nsIServiceWorkerRegistrationInfo** aInfo)
2663 MOZ_ASSERT(aPrincipal);
2664 MOZ_ASSERT(aInfo);
2666 nsCOMPtr<nsIURI> scopeURI;
2667 nsresult rv = NS_NewURI(getter_AddRefs(scopeURI), aScope, nullptr, nullptr);
2668 if (NS_FAILED(rv)) {
2669 return NS_ERROR_FAILURE;
2672 RefPtr<ServiceWorkerRegistrationInfo> info =
2673 GetServiceWorkerRegistrationInfo(aPrincipal, scopeURI);
2674 if (!info) {
2675 return NS_ERROR_FAILURE;
2677 info.forget(aInfo);
2679 return NS_OK;
2682 already_AddRefed<ServiceWorkerRegistrationInfo>
2683 ServiceWorkerManager::GetRegistration(const nsACString& aScopeKey,
2684 const nsACString& aScope) const
2686 RefPtr<ServiceWorkerRegistrationInfo> reg;
2688 RegistrationDataPerPrincipal* data;
2689 if (!mRegistrationInfos.Get(aScopeKey, &data)) {
2690 return reg.forget();
2693 data->mInfos.Get(aScope, getter_AddRefs(reg));
2694 return reg.forget();
2697 already_AddRefed<ServiceWorkerRegistrationInfo>
2698 ServiceWorkerManager::CreateNewRegistration(
2699 const nsCString& aScope,
2700 nsIPrincipal* aPrincipal,
2701 ServiceWorkerUpdateViaCache aUpdateViaCache)
2703 #ifdef DEBUG
2704 MOZ_ASSERT(NS_IsMainThread());
2705 nsCOMPtr<nsIURI> scopeURI;
2706 nsresult rv = NS_NewURI(getter_AddRefs(scopeURI), aScope, nullptr, nullptr);
2707 MOZ_ASSERT(NS_SUCCEEDED(rv));
2709 RefPtr<ServiceWorkerRegistrationInfo> tmp =
2710 GetRegistration(aPrincipal, aScope);
2711 MOZ_ASSERT(!tmp);
2712 #endif
2714 RefPtr<ServiceWorkerRegistrationInfo> registration =
2715 new ServiceWorkerRegistrationInfo(aScope, aPrincipal, aUpdateViaCache);
2717 // From now on ownership of registration is with
2718 // mServiceWorkerRegistrationInfos.
2719 AddScopeAndRegistration(aScope, registration);
2720 return registration.forget();
2723 void
2724 ServiceWorkerManager::MaybeRemoveRegistration(ServiceWorkerRegistrationInfo* aRegistration)
2726 MOZ_ASSERT(aRegistration);
2727 RefPtr<ServiceWorkerInfo> newest = aRegistration->Newest();
2728 if (!newest && HasScope(aRegistration->Principal(), aRegistration->Scope())) {
2729 RemoveRegistration(aRegistration);
2733 void
2734 ServiceWorkerManager::RemoveRegistration(ServiceWorkerRegistrationInfo* aRegistration)
2736 // Note, we do not need to call mActor->SendUnregister() here. There are a few
2737 // ways we can get here:
2738 // 1) Through a normal unregister which calls SendUnregister() in the unregister
2739 // job Start() method.
2740 // 2) Through origin storage being purged. These result in ForceUnregister()
2741 // starting unregister jobs which in turn call SendUnregister().
2742 // 3) Through the failure to install a new service worker. Since we don't store
2743 // the registration until install succeeds, we do not need to call
2744 // SendUnregister here.
2745 // Assert these conditions by testing for pending uninstall (cases 1 and 2) or
2746 // null workers (case 3).
2747 #ifdef DEBUG
2748 RefPtr<ServiceWorkerInfo> newest = aRegistration->Newest();
2749 MOZ_ASSERT(aRegistration->IsPendingUninstall() || !newest);
2750 #endif
2752 MOZ_ASSERT(HasScope(aRegistration->Principal(), aRegistration->Scope()));
2754 // When a registration is removed, we must clear its contents since the DOM
2755 // object may be held by content script.
2756 aRegistration->Clear();
2758 RemoveScopeAndRegistration(aRegistration);
2761 NS_IMETHODIMP
2762 ServiceWorkerManager::GetAllRegistrations(nsIArray** aResult)
2764 MOZ_ASSERT(NS_IsMainThread());
2766 nsCOMPtr<nsIMutableArray> array(do_CreateInstance(NS_ARRAY_CONTRACTID));
2767 if (!array) {
2768 return NS_ERROR_OUT_OF_MEMORY;
2771 for (auto it1 = mRegistrationInfos.Iter(); !it1.Done(); it1.Next()) {
2772 for (auto it2 = it1.UserData()->mInfos.Iter(); !it2.Done(); it2.Next()) {
2773 ServiceWorkerRegistrationInfo* reg = it2.UserData();
2774 MOZ_ASSERT(reg);
2776 if (reg->IsPendingUninstall()) {
2777 continue;
2780 array->AppendElement(reg);
2784 array.forget(aResult);
2785 return NS_OK;
2788 // MUST ONLY BE CALLED FROM Remove(), RemoveAll() and RemoveAllRegistrations()!
2789 void
2790 ServiceWorkerManager::ForceUnregister(RegistrationDataPerPrincipal* aRegistrationData,
2791 ServiceWorkerRegistrationInfo* aRegistration)
2793 MOZ_ASSERT(aRegistrationData);
2794 MOZ_ASSERT(aRegistration);
2796 RefPtr<ServiceWorkerJobQueue> queue;
2797 aRegistrationData->mJobQueues.Get(aRegistration->Scope(), getter_AddRefs(queue));
2798 if (queue) {
2799 queue->CancelAll();
2802 if (auto entry = aRegistrationData->mUpdateTimers.Lookup(aRegistration->Scope())) {
2803 entry.Data()->Cancel();
2804 entry.Remove();
2807 // Since Unregister is async, it is ok to call it in an enumeration.
2808 Unregister(aRegistration->Principal(), nullptr, NS_ConvertUTF8toUTF16(aRegistration->Scope()));
2811 void
2812 ServiceWorkerManager::Remove(const nsACString& aHost)
2814 MOZ_ASSERT(NS_IsMainThread());
2816 nsCOMPtr<nsIEffectiveTLDService> tldService =
2817 do_GetService(NS_EFFECTIVETLDSERVICE_CONTRACTID);
2818 if (NS_WARN_IF(!tldService)) {
2819 return;
2822 for (auto it1 = mRegistrationInfos.Iter(); !it1.Done(); it1.Next()) {
2823 ServiceWorkerManager::RegistrationDataPerPrincipal* data = it1.UserData();
2824 for (auto it2 = data->mInfos.Iter(); !it2.Done(); it2.Next()) {
2825 ServiceWorkerRegistrationInfo* reg = it2.UserData();
2826 nsCOMPtr<nsIURI> scopeURI;
2827 nsresult rv = NS_NewURI(getter_AddRefs(scopeURI), it2.Key(),
2828 nullptr, nullptr);
2829 if (NS_WARN_IF(NS_FAILED(rv))) {
2830 continue;
2833 nsAutoCString host;
2834 rv = scopeURI->GetHost(host);
2835 if (NS_WARN_IF(NS_FAILED(rv))) {
2836 continue;
2839 // This way subdomains are also cleared.
2840 bool hasRootDomain = false;
2841 rv = tldService->HasRootDomain(host, aHost, &hasRootDomain);
2842 if (NS_WARN_IF(NS_FAILED(rv))) {
2843 continue;
2846 if (hasRootDomain) {
2847 ForceUnregister(data, reg);
2853 void
2854 ServiceWorkerManager::PropagateRemove(const nsACString& aHost)
2856 MOZ_ASSERT(NS_IsMainThread());
2857 mActor->SendPropagateRemove(nsCString(aHost));
2860 void
2861 ServiceWorkerManager::RemoveAll()
2863 MOZ_ASSERT(NS_IsMainThread());
2865 for (auto it1 = mRegistrationInfos.Iter(); !it1.Done(); it1.Next()) {
2866 ServiceWorkerManager::RegistrationDataPerPrincipal* data = it1.UserData();
2867 for (auto it2 = data->mInfos.Iter(); !it2.Done(); it2.Next()) {
2868 ServiceWorkerRegistrationInfo* reg = it2.UserData();
2869 ForceUnregister(data, reg);
2874 void
2875 ServiceWorkerManager::PropagateRemoveAll()
2877 MOZ_ASSERT(NS_IsMainThread());
2878 MOZ_ASSERT(XRE_IsParentProcess());
2879 mActor->SendPropagateRemoveAll();
2882 void
2883 ServiceWorkerManager::RemoveAllRegistrations(OriginAttributesPattern* aPattern)
2885 MOZ_ASSERT(NS_IsMainThread());
2887 MOZ_ASSERT(aPattern);
2889 for (auto it1 = mRegistrationInfos.Iter(); !it1.Done(); it1.Next()) {
2890 ServiceWorkerManager::RegistrationDataPerPrincipal* data = it1.UserData();
2892 // We can use iteration because ForceUnregister (and Unregister) are
2893 // async. Otherwise doing some R/W operations on an hashtable during
2894 // iteration will crash.
2895 for (auto it2 = data->mInfos.Iter(); !it2.Done(); it2.Next()) {
2896 ServiceWorkerRegistrationInfo* reg = it2.UserData();
2898 MOZ_ASSERT(reg);
2899 MOZ_ASSERT(reg->Principal());
2901 bool matches =
2902 aPattern->Matches(reg->Principal()->OriginAttributesRef());
2903 if (!matches) {
2904 continue;
2907 ForceUnregister(data, reg);
2912 NS_IMETHODIMP
2913 ServiceWorkerManager::AddListener(nsIServiceWorkerManagerListener* aListener)
2915 MOZ_ASSERT(NS_IsMainThread());
2917 if (!aListener || mListeners.Contains(aListener)) {
2918 return NS_ERROR_INVALID_ARG;
2921 mListeners.AppendElement(aListener);
2923 return NS_OK;
2926 NS_IMETHODIMP
2927 ServiceWorkerManager::RemoveListener(nsIServiceWorkerManagerListener* aListener)
2929 MOZ_ASSERT(NS_IsMainThread());
2931 if (!aListener || !mListeners.Contains(aListener)) {
2932 return NS_ERROR_INVALID_ARG;
2935 mListeners.RemoveElement(aListener);
2937 return NS_OK;
2940 NS_IMETHODIMP
2941 ServiceWorkerManager::Observe(nsISupports* aSubject,
2942 const char* aTopic,
2943 const char16_t* aData)
2945 if (strcmp(aTopic, CLEAR_ORIGIN_DATA) == 0) {
2946 MOZ_ASSERT(XRE_IsParentProcess());
2947 OriginAttributesPattern pattern;
2948 MOZ_ALWAYS_TRUE(pattern.Init(nsAutoString(aData)));
2950 RemoveAllRegistrations(&pattern);
2951 return NS_OK;
2954 if (strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID) == 0) {
2955 MaybeStartShutdown();
2956 return NS_OK;
2959 MOZ_CRASH("Received message we aren't supposed to be registered for!");
2960 return NS_OK;
2963 NS_IMETHODIMP
2964 ServiceWorkerManager::PropagateSoftUpdate(JS::Handle<JS::Value> aOriginAttributes,
2965 const nsAString& aScope,
2966 JSContext* aCx)
2968 MOZ_ASSERT(NS_IsMainThread());
2970 OriginAttributes attrs;
2971 if (!aOriginAttributes.isObject() || !attrs.Init(aCx, aOriginAttributes)) {
2972 return NS_ERROR_INVALID_ARG;
2975 PropagateSoftUpdate(attrs, aScope);
2976 return NS_OK;
2979 void
2980 ServiceWorkerManager::PropagateSoftUpdate(const OriginAttributes& aOriginAttributes,
2981 const nsAString& aScope)
2983 MOZ_ASSERT(NS_IsMainThread());
2984 mActor->SendPropagateSoftUpdate(aOriginAttributes, nsString(aScope));
2987 NS_IMETHODIMP
2988 ServiceWorkerManager::PropagateUnregister(nsIPrincipal* aPrincipal,
2989 nsIServiceWorkerUnregisterCallback* aCallback,
2990 const nsAString& aScope)
2992 MOZ_ASSERT(NS_IsMainThread());
2993 MOZ_ASSERT(aPrincipal);
2995 PrincipalInfo principalInfo;
2996 if (NS_WARN_IF(NS_FAILED(PrincipalToPrincipalInfo(aPrincipal,
2997 &principalInfo)))) {
2998 return NS_ERROR_FAILURE;
3001 mActor->SendPropagateUnregister(principalInfo, nsString(aScope));
3003 nsresult rv = Unregister(aPrincipal, aCallback, aScope);
3004 if (NS_WARN_IF(NS_FAILED(rv))) {
3005 return rv;
3008 return NS_OK;
3011 void
3012 ServiceWorkerManager::NotifyListenersOnRegister(
3013 nsIServiceWorkerRegistrationInfo* aInfo)
3015 nsTArray<nsCOMPtr<nsIServiceWorkerManagerListener>> listeners(mListeners);
3016 for (size_t index = 0; index < listeners.Length(); ++index) {
3017 listeners[index]->OnRegister(aInfo);
3021 void
3022 ServiceWorkerManager::NotifyListenersOnUnregister(
3023 nsIServiceWorkerRegistrationInfo* aInfo)
3025 nsTArray<nsCOMPtr<nsIServiceWorkerManagerListener>> listeners(mListeners);
3026 for (size_t index = 0; index < listeners.Length(); ++index) {
3027 listeners[index]->OnUnregister(aInfo);
3031 class UpdateTimerCallback final : public nsITimerCallback
3032 , public nsINamed
3034 nsCOMPtr<nsIPrincipal> mPrincipal;
3035 const nsCString mScope;
3037 ~UpdateTimerCallback()
3041 public:
3042 UpdateTimerCallback(nsIPrincipal* aPrincipal, const nsACString& aScope)
3043 : mPrincipal(aPrincipal)
3044 , mScope(aScope)
3046 MOZ_ASSERT(NS_IsMainThread());
3047 MOZ_ASSERT(mPrincipal);
3048 MOZ_ASSERT(!mScope.IsEmpty());
3051 NS_IMETHOD
3052 Notify(nsITimer* aTimer) override
3054 MOZ_ASSERT(NS_IsMainThread());
3056 RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
3057 if (!swm) {
3058 // shutting down, do nothing
3059 return NS_OK;
3062 swm->UpdateTimerFired(mPrincipal, mScope);
3063 return NS_OK;
3066 NS_IMETHOD
3067 GetName(nsACString& aName) override
3069 aName.AssignLiteral("UpdateTimerCallback");
3070 return NS_OK;
3073 NS_DECL_ISUPPORTS
3076 NS_IMPL_ISUPPORTS(UpdateTimerCallback, nsITimerCallback, nsINamed)
3078 bool
3079 ServiceWorkerManager::MayHaveActiveServiceWorkerInstance(ContentParent* aContent,
3080 nsIPrincipal* aPrincipal)
3082 MOZ_ASSERT(NS_IsMainThread());
3083 MOZ_ASSERT(aPrincipal);
3085 if (mShuttingDown) {
3086 return false;
3089 nsAutoCString scopeKey;
3090 nsresult rv = PrincipalToScopeKey(aPrincipal, scopeKey);
3091 if (NS_WARN_IF(NS_FAILED(rv))) {
3092 return false;
3095 RegistrationDataPerPrincipal* data;
3096 if (!mRegistrationInfos.Get(scopeKey, &data)) {
3097 return false;
3100 return true;
3103 void
3104 ServiceWorkerManager::ScheduleUpdateTimer(nsIPrincipal* aPrincipal,
3105 const nsACString& aScope)
3107 MOZ_ASSERT(NS_IsMainThread());
3108 MOZ_ASSERT(aPrincipal);
3109 MOZ_ASSERT(!aScope.IsEmpty());
3111 if (mShuttingDown) {
3112 return;
3115 nsAutoCString scopeKey;
3116 nsresult rv = PrincipalToScopeKey(aPrincipal, scopeKey);
3117 if (NS_WARN_IF(NS_FAILED(rv))) {
3118 return;
3121 RegistrationDataPerPrincipal* data;
3122 if (!mRegistrationInfos.Get(scopeKey, &data)) {
3123 return;
3126 nsCOMPtr<nsITimer>& timer = data->mUpdateTimers.GetOrInsert(aScope);
3127 if (timer) {
3128 // There is already a timer scheduled. In this case just use the original
3129 // schedule time. We don't want to push it out to a later time since that
3130 // could allow updates to be starved forever if events are continuously
3131 // fired.
3132 return;
3135 nsCOMPtr<nsITimerCallback> callback = new UpdateTimerCallback(aPrincipal,
3136 aScope);
3138 const uint32_t UPDATE_DELAY_MS = 1000;
3140 // Label with SystemGroup because UpdateTimerCallback only sends an IPC message
3141 // (PServiceWorkerUpdaterConstructor) without touching any web contents.
3142 rv = NS_NewTimerWithCallback(getter_AddRefs(timer),
3143 callback, UPDATE_DELAY_MS,
3144 nsITimer::TYPE_ONE_SHOT,
3145 SystemGroup::EventTargetFor(TaskCategory::Other));
3147 if (NS_WARN_IF(NS_FAILED(rv))) {
3148 data->mUpdateTimers.Remove(aScope); // another lookup, but very rare
3149 return;
3153 void
3154 ServiceWorkerManager::UpdateTimerFired(nsIPrincipal* aPrincipal,
3155 const nsACString& aScope)
3157 MOZ_ASSERT(NS_IsMainThread());
3158 MOZ_ASSERT(aPrincipal);
3159 MOZ_ASSERT(!aScope.IsEmpty());
3161 if (mShuttingDown) {
3162 return;
3165 // First cleanup the timer.
3166 nsAutoCString scopeKey;
3167 nsresult rv = PrincipalToScopeKey(aPrincipal, scopeKey);
3168 if (NS_WARN_IF(NS_FAILED(rv))) {
3169 return;
3172 RegistrationDataPerPrincipal* data;
3173 if (!mRegistrationInfos.Get(scopeKey, &data)) {
3174 return;
3177 if (auto entry = data->mUpdateTimers.Lookup(aScope)) {
3178 entry.Data()->Cancel();
3179 entry.Remove();
3182 RefPtr<ServiceWorkerRegistrationInfo> registration;
3183 data->mInfos.Get(aScope, getter_AddRefs(registration));
3184 if (!registration) {
3185 return;
3188 if (!registration->CheckAndClearIfUpdateNeeded()) {
3189 return;
3192 OriginAttributes attrs = aPrincipal->OriginAttributesRef();
3194 SoftUpdate(attrs, aScope);
3197 void
3198 ServiceWorkerManager::MaybeSendUnregister(nsIPrincipal* aPrincipal,
3199 const nsACString& aScope)
3201 MOZ_ASSERT(NS_IsMainThread());
3202 MOZ_ASSERT(aPrincipal);
3203 MOZ_ASSERT(!aScope.IsEmpty());
3205 if (!mActor) {
3206 return;
3209 PrincipalInfo principalInfo;
3210 nsresult rv = PrincipalToPrincipalInfo(aPrincipal, &principalInfo);
3211 if (NS_WARN_IF(NS_FAILED(rv))) {
3212 return;
3215 Unused << mActor->SendUnregister(principalInfo, NS_ConvertUTF8toUTF16(aScope));
3218 } // namespace dom
3219 } // namespace mozilla