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"
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"
18 #include "nsINetworkInterceptController.h"
19 #include "nsIMutableArray.h"
20 #include "nsIScriptError.h"
21 #include "nsISimpleEnumerator.h"
23 #include "nsIUploadChannel2.h"
24 #include "nsServiceManagerUtils.h"
26 #include "nsISupportsPrimitives.h"
27 #include "nsIPermissionManager.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"
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"
90 using namespace mozilla
;
91 using namespace mozilla::dom
;
92 using namespace mozilla::ipc
;
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
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
;
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
))) {
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();
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
));
217 class TeardownRunnable final
: public Runnable
220 explicit TeardownRunnable(ServiceWorkerManagerChild
* aActor
)
221 : Runnable("dom::ServiceWorkerManager::TeardownRunnable")
227 NS_IMETHOD
Run() override
230 mActor
->SendShutdown();
235 ~TeardownRunnable() {}
237 RefPtr
<ServiceWorkerManagerChild
> mActor
;
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
)
255 ServiceWorkerManager::ServiceWorkerManager()
257 , mShuttingDown(false)
261 ServiceWorkerManager::~ServiceWorkerManager()
263 // The map will assert if it is not empty when destroyed.
264 mRegistrationInfos
.Clear();
269 ServiceWorkerManager::Init(ServiceWorkerRegistrar
* aRegistrar
)
271 nsCOMPtr
<nsIObserverService
> obs
= mozilla::services::GetObserverService();
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
);
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();
298 PServiceWorkerManagerChild
* actor
=
299 actorChild
->SendPServiceWorkerManagerConstructor();
301 MaybeStartShutdown();
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());
323 RefPtr
<ServiceWorkerRegistrationInfo
> old
=
324 entry
.Data()->mRegistrationInfo
.forget();
326 if (aControlClientHandle
) {
327 ref
= entry
.Data()->mClientHandle
->Control(active
);
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.
344 SystemGroup::EventTargetFor(TaskCategory::Other
), __func__
,
346 // do nothing on success
347 }, [self
, aClientInfo
] (nsresult aRv
) {
348 // failed to control, forget about this client
349 self
->StopControllingClient(aClientInfo
);
355 RefPtr
<ClientHandle
> clientHandle
=
356 ClientManager::CreateHandle(aClientInfo
,
357 SystemGroup::EventTargetFor(TaskCategory::Other
));
359 if (aControlClientHandle
) {
360 ref
= clientHandle
->Control(active
);
362 ref
= GenericPromise::CreateAndResolve(false, __func__
);
365 aRegistrationInfo
->StartControllingClient();
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.
382 SystemGroup::EventTargetFor(TaskCategory::Other
), __func__
,
384 // do nothing on success
385 }, [self
, aClientInfo
] (nsresult aRv
) {
386 // failed to control, forget about this client
387 self
->StopControllingClient(aClientInfo
);
394 ServiceWorkerManager::StopControllingClient(const ClientInfo
& aClientInfo
)
396 auto entry
= mControlledClients
.Lookup(aClientInfo
.Id());
401 RefPtr
<ServiceWorkerRegistrationInfo
> reg
=
402 entry
.Data()->mRegistrationInfo
.forget();
406 StopControllingRegistration(reg
);
410 ServiceWorkerManager::MaybeStartShutdown()
412 MOZ_ASSERT(NS_IsMainThread());
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();
425 it1
.UserData()->mUpdateTimers
.Clear();
427 for (auto it2
= it1
.UserData()->mJobQueues
.Iter(); !it2
.Done(); it2
.Next()) {
428 RefPtr
<ServiceWorkerJobQueue
> queue
= it2
.UserData();
431 it1
.UserData()->mJobQueues
.Clear();
434 nsCOMPtr
<nsIObserverService
> obs
= mozilla::services::GetObserverService();
436 obs
->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID
);
438 if (XRE_IsParentProcess()) {
439 obs
->RemoveObserver(this, CLEAR_ORIGIN_DATA
);
447 mActor
->ManagerShuttingDown();
449 RefPtr
<TeardownRunnable
> runnable
= new TeardownRunnable(mActor
);
450 nsresult rv
= NS_DispatchToMainThread(runnable
);
451 Unused
<< NS_WARN_IF(NS_FAILED(rv
));
455 class ServiceWorkerResolveWindowPromiseOnRegisterCallback final
: public ServiceWorkerJob::Callback
457 RefPtr
<ServiceWorkerRegistrationPromise::Private
> mPromise
;
459 ~ServiceWorkerResolveWindowPromiseOnRegisterCallback()
463 JobFinished(ServiceWorkerJob
* aJob
, ErrorResult
& aStatus
) override
465 MOZ_ASSERT(NS_IsMainThread());
468 if (aStatus
.Failed()) {
469 mPromise
->Reject(std::move(aStatus
), __func__
);
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__
);
482 ServiceWorkerResolveWindowPromiseOnRegisterCallback()
483 : mPromise(new ServiceWorkerRegistrationPromise::Private(__func__
))
486 RefPtr
<ServiceWorkerRegistrationPromise
>
492 NS_INLINE_DECL_REFCOUNTING(ServiceWorkerResolveWindowPromiseOnRegisterCallback
, override
)
497 class PropagateSoftUpdateRunnable final
: public Runnable
500 PropagateSoftUpdateRunnable(const OriginAttributes
& aOriginAttributes
,
501 const nsAString
& aScope
)
502 : Runnable("dom::ServiceWorkerManager::PropagateSoftUpdateRunnable")
503 , mOriginAttributes(aOriginAttributes
)
507 NS_IMETHOD
Run() override
509 MOZ_ASSERT(NS_IsMainThread());
511 RefPtr
<ServiceWorkerManager
> swm
= ServiceWorkerManager::GetInstance();
513 swm
->PropagateSoftUpdate(mOriginAttributes
, mScope
);
520 ~PropagateSoftUpdateRunnable()
523 const OriginAttributes mOriginAttributes
;
524 const nsString mScope
;
527 class PromiseResolverCallback final
: public ServiceWorkerUpdateFinishCallback
530 PromiseResolverCallback(ServiceWorkerUpdateFinishCallback
* aCallback
,
531 GenericPromise::Private
* aPromise
)
532 : mCallback(aCallback
)
535 MOZ_DIAGNOSTIC_ASSERT(mPromise
);
538 void UpdateSucceeded(ServiceWorkerRegistrationInfo
* aInfo
) override
540 MOZ_DIAGNOSTIC_ASSERT(mPromise
);
543 mCallback
->UpdateSucceeded(aInfo
);
549 void UpdateFailed(ErrorResult
& aStatus
) override
551 MOZ_DIAGNOSTIC_ASSERT(mPromise
);
554 mCallback
->UpdateFailed(aStatus
);
561 ~PromiseResolverCallback()
570 mPromise
->Resolve(true, __func__
);
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
587 SoftUpdateRunnable(const OriginAttributes
& aOriginAttributes
,
588 const nsACString
& aScope
,
589 bool aInternalMethod
,
590 GenericPromise::Private
* aPromise
)
591 : CancelableRunnable("dom::ServiceWorkerManager::SoftUpdateRunnable")
592 , mAttrs(aOriginAttributes
)
594 , mInternalMethod(aInternalMethod
)
598 NS_IMETHOD
Run() override
600 MOZ_ASSERT(NS_IsMainThread());
602 RefPtr
<ServiceWorkerManager
> swm
= ServiceWorkerManager::GetInstance();
604 return NS_ERROR_FAILURE
;
607 if (mInternalMethod
) {
608 RefPtr
<PromiseResolverCallback
> callback
=
609 new PromiseResolverCallback(nullptr, mPromise
);
612 swm
->SoftUpdateInternal(mAttrs
, mScope
, callback
);
614 swm
->SoftUpdate(mAttrs
, mScope
);
628 ~SoftUpdateRunnable()
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
657 UpdateRunnable(nsIPrincipal
* aPrincipal
,
658 const nsACString
& aScope
,
659 ServiceWorkerUpdateFinishCallback
* aCallback
,
661 GenericPromise::Private
* aPromise
)
662 : CancelableRunnable("dom::ServiceWorkerManager::UpdateRunnable")
663 , mPrincipal(aPrincipal
)
665 , mCallback(aCallback
)
670 NS_IMETHOD
Run() override
672 MOZ_ASSERT(NS_IsMainThread());
674 RefPtr
<ServiceWorkerManager
> swm
= ServiceWorkerManager::GetInstance();
676 return NS_ERROR_FAILURE
;
679 if (mType
== ePostpone
) {
680 swm
->Update(mPrincipal
, mScope
, mCallback
);
684 MOZ_ASSERT(mPromise
);
686 RefPtr
<PromiseResolverCallback
> callback
=
687 new PromiseResolverCallback(mCallback
, mPromise
);
690 if (mType
== eSuccess
) {
691 swm
->UpdateInternal(mPrincipal
, mScope
, callback
);
695 ErrorResult
error(NS_ERROR_DOM_ABORT_ERR
);
696 callback
->UpdateFailed(error
);
711 mPromise
->Resolve(true, __func__
);
715 nsCOMPtr
<nsIPrincipal
> mPrincipal
;
716 const nsCString mScope
;
717 RefPtr
<ServiceWorkerUpdateFinishCallback
> mCallback
;
720 RefPtr
<GenericPromise::Private
> mPromise
;
723 class ResolvePromiseRunnable final
: public CancelableRunnable
726 explicit ResolvePromiseRunnable(GenericPromise::Private
* aPromise
)
727 : CancelableRunnable("dom::ServiceWorkerManager::ResolvePromiseRunnable")
746 ~ResolvePromiseRunnable()
755 mPromise
->Resolve(true, __func__
);
760 RefPtr
<GenericPromise::Private
> mPromise
;
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);
774 return ServiceWorkerRegistrationPromise::CreateAndReject(rv
, __func__
);
777 nsCOMPtr
<nsIURI
> scriptURI
;
778 rv
= NS_NewURI(getter_AddRefs(scriptURI
), aScriptURL
, nullptr, nullptr);
780 return ServiceWorkerRegistrationPromise::CreateAndReject(rv
, __func__
);
783 rv
= ServiceWorkerScopeAndScriptAreValid(aClientInfo
, scopeURI
, scriptURI
);
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
,
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
;
826 explicit GetRegistrationsRunnable(const ClientInfo
& aClientInfo
)
827 : Runnable("dom::ServiceWorkerManager::GetRegistrationsRunnable")
828 , mClientInfo(aClientInfo
)
829 , mPromise(new ServiceWorkerRegistrationListPromise::Private(__func__
))
832 RefPtr
<ServiceWorkerRegistrationListPromise
>
841 auto scopeExit
= MakeScopeExit([&] {
842 mPromise
->Reject(NS_ERROR_DOM_INVALID_STATE_ERR
, __func__
);
845 RefPtr
<ServiceWorkerManager
> swm
= ServiceWorkerManager::GetInstance();
850 nsCOMPtr
<nsIPrincipal
> principal
= mClientInfo
.GetPrincipal();
855 nsTArray
<ServiceWorkerRegistrationDescriptor
> array
;
857 if (NS_WARN_IF(!BasePrincipal::Cast(principal
)->IsCodebasePrincipal())) {
861 nsAutoCString scopeKey
;
862 nsresult rv
= swm
->PrincipalToScopeKey(principal
, scopeKey
);
863 if (NS_WARN_IF(NS_FAILED(rv
))) {
867 ServiceWorkerManager::RegistrationDataPerPrincipal
* data
;
868 if (!swm
->mRegistrationInfos
.Get(scopeKey
, &data
)) {
870 mPromise
->Resolve(array
, __func__
);
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()) {
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
))) {
889 rv
= principal
->CheckMayLoad(scopeURI
, true /* report */,
890 false /* allowIfInheritsPrincipal */);
891 if (NS_WARN_IF(NS_FAILED(rv
))) {
895 array
.AppendElement(info
->Descriptor());
899 mPromise
->Resolve(array
, __func__
);
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
;
924 GetRegistrationRunnable(const ClientInfo
& aClientInfo
,
925 const nsACString
& aURL
)
926 : Runnable("dom::ServiceWorkerManager::GetRegistrationRunnable")
927 , mClientInfo(aClientInfo
)
928 , mPromise(new ServiceWorkerRegistrationPromise::Private(__func__
))
932 RefPtr
<ServiceWorkerRegistrationPromise
>
941 RefPtr
<ServiceWorkerManager
> swm
= ServiceWorkerManager::GetInstance();
943 mPromise
->Reject(NS_ERROR_DOM_INVALID_STATE_ERR
, __func__
);
947 nsCOMPtr
<nsIPrincipal
> principal
= mClientInfo
.GetPrincipal();
949 mPromise
->Reject(NS_ERROR_DOM_INVALID_STATE_ERR
, __func__
);
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__
);
960 rv
= principal
->CheckMayLoad(uri
, true /* report */,
961 false /* allowIfInheritsPrinciple */);
963 mPromise
->Reject(NS_ERROR_DOM_SECURITY_ERR
, __func__
);
967 RefPtr
<ServiceWorkerRegistrationInfo
> registration
=
968 swm
->GetServiceWorkerRegistrationInfo(principal
, uri
);
971 // Reject with NS_OK means "not found".
972 mPromise
->Reject(NS_OK
, __func__
);
976 mPromise
->Resolve(registration
->Descriptor(), __func__
);
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();
996 ServiceWorkerManager::SendPushEvent(const nsACString
& aOriginAttributes
,
997 const nsACString
& aScope
,
998 uint32_t aDataLength
,
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());
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
,
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
);
1048 return NS_ERROR_FAILURE
;
1050 return info
->WorkerPrivate()->SendPushSubscriptionChangeEvent();
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
);
1074 return NS_ERROR_FAILURE
;
1077 ServiceWorkerPrivate
* workerPrivate
= info
->WorkerPrivate();
1078 return workerPrivate
->SendNotificationEvent(aEventName
, aID
, aTitle
, aDir
,
1080 aIcon
, aData
, aBehavior
,
1081 NS_ConvertUTF8toUTF16(aScope
));
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
);
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(),
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
;
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__
);
1168 mPendingReadyList
.AppendElement(std::move(prd
));
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__
);
1185 mPendingReadyList
.AppendElement(std::move(prd
));
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
;
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 */);
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
)) {
1224 nsCOMPtr
<nsIPrincipal
> principal
=
1225 BasePrincipal::CreateCodebasePrincipal(scopeURI
, aOriginAttributes
);
1226 RefPtr
<ServiceWorkerRegistrationInfo
> registration
=
1227 GetServiceWorkerRegistrationInfo(principal
, scopeURI
);
1228 if (!registration
) {
1232 return registration
->GetActive();
1237 class UnregisterJobCallback final
: public ServiceWorkerJob::Callback
1239 nsCOMPtr
<nsIServiceWorkerUnregisterCallback
> mCallback
;
1241 ~UnregisterJobCallback()
1246 explicit UnregisterJobCallback(nsIServiceWorkerUnregisterCallback
* aCallback
)
1247 : mCallback(aCallback
)
1249 MOZ_ASSERT(NS_IsMainThread());
1250 MOZ_ASSERT(mCallback
);
1254 JobFinished(ServiceWorkerJob
* aJob
, ErrorResult
& aStatus
) override
1256 MOZ_ASSERT(NS_IsMainThread());
1259 if (aStatus
.Failed()) {
1260 mCallback
->UnregisterFailed();
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
1276 ServiceWorkerManager::Unregister(nsIPrincipal
* aPrincipal
,
1277 nsIServiceWorkerUnregisterCallback
* aCallback
,
1278 const nsAString
& aScope
)
1280 MOZ_ASSERT(NS_IsMainThread());
1283 return NS_ERROR_FAILURE
;
1288 // This is not accessible by content, and callers should always ensure scope is
1289 // a correct URI, so this is wrapped in 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
;
1298 nsAutoCString scopeKey
;
1299 rv
= PrincipalToScopeKey(aPrincipal
, scopeKey
);
1300 if (NS_WARN_IF(NS_FAILED(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 */);
1311 RefPtr
<UnregisterJobCallback
> cb
= new UnregisterJobCallback(aCallback
);
1312 job
->AppendResultCallback(cb
);
1315 queue
->ScheduleJob(job
);
1320 ServiceWorkerManager::NotifyUnregister(nsIPrincipal
* aPrincipal
,
1321 const nsAString
& aScope
)
1323 MOZ_ASSERT(NS_IsMainThread());
1324 MOZ_ASSERT(aPrincipal
);
1328 // This is not accessible by content, and callers should always ensure scope is
1329 // a correct URI, so this is wrapped in DEBUG
1331 nsCOMPtr
<nsIURI
> scopeURI
;
1332 rv
= NS_NewURI(getter_AddRefs(scopeURI
), aScope
, nullptr, nullptr);
1333 if (NS_WARN_IF(NS_FAILED(rv
))) {
1338 nsAutoCString scopeKey
;
1339 rv
= PrincipalToScopeKey(aPrincipal
, scopeKey
);
1340 if (NS_WARN_IF(NS_FAILED(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
);
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());
1367 if (reg
->GetActive() != aWorker
) {
1371 if (!reg
->IsControllingClients() && reg
->IsPendingUninstall()) {
1372 RemoveRegistration(reg
);
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();
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;
1407 RefPtr
<ServiceWorkerRegistrar
> swr
;
1409 // Don't create the ServiceWorkerManager until the ServiceWorkerRegistrar is
1411 if (XRE_IsParentProcess()) {
1412 swr
= ServiceWorkerRegistrar::Get();
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();
1431 ServiceWorkerManager::FinishFetch(ServiceWorkerRegistrationInfo
* aRegistration
)
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
,
1444 ConsoleUtils::ReportForServiceWorkerScope(NS_ConvertUTF8toUTF16(aScope
),
1449 ConsoleUtils::eError
);
1454 ServiceWorkerManager::LocalizeAndReportToAllClients(
1455 const nsCString
& aScope
,
1456 const char* aStringKey
,
1457 const nsTArray
<nsString
>& aParamArray
,
1459 const nsString
& aFilename
,
1460 const nsString
& aLine
,
1461 uint32_t aLineNumber
,
1462 uint32_t aColumnNumber
)
1464 RefPtr
<ServiceWorkerManager
> swm
= ServiceWorkerManager::GetInstance();
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
,
1478 NS_WARNING("Failed to format and therefore report localized error.");
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
,
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
))) {
1504 ServiceWorkerManager::RegistrationDataPerPrincipal
* data
;
1505 if (NS_WARN_IF(!mRegistrationInfos
.Get(scopeKey
, &data
))) {
1509 // Always report any uncaught exceptions or errors to the console of
1511 ReportToAllClients(aScope
, aMessage
, aFilename
, aLine
, aLineNumber
,
1512 aColumnNumber
, aFlags
);
1516 ServiceWorkerManager::LoadRegistration(
1517 const ServiceWorkerRegistrationData
& aRegistration
)
1519 MOZ_ASSERT(NS_IsMainThread());
1521 nsCOMPtr
<nsIPrincipal
> principal
=
1522 PrincipalInfoToPrincipal(aRegistration
.principal());
1527 RefPtr
<ServiceWorkerRegistrationInfo
> registration
=
1528 GetRegistration(principal
, aRegistration
.scope());
1529 if (!registration
) {
1531 CreateNewRegistration(
1532 aRegistration
.scope(),
1534 static_cast<ServiceWorkerUpdateViaCache
>(aRegistration
.updateViaCache())
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.
1549 registration
->SetLastUpdateTime(aRegistration
.lastUpdateTime());
1551 nsLoadFlags importsLoadFlags
= nsIChannel::LOAD_BYPASS_SERVICE_WORKER
;
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(),
1563 registration
->Version(),
1565 aRegistration
.cacheName(),
1567 registration
->GetActive()->SetHandlesFetch(aRegistration
.currentWorkerHandlesFetch());
1568 registration
->GetActive()->SetInstalledTime(aRegistration
.currentWorkerInstalledTime());
1569 registration
->GetActive()->SetActivatedTime(aRegistration
.currentWorkerActivatedTime());
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
]);
1585 ServiceWorkerManager::StoreRegistration(
1586 nsIPrincipal
* aPrincipal
,
1587 ServiceWorkerRegistrationInfo
* aRegistration
)
1589 MOZ_ASSERT(aPrincipal
);
1590 MOZ_ASSERT(aRegistration
);
1592 if (mShuttingDown
) {
1596 ServiceWorkerRegistrationData data
;
1597 nsresult rv
= PopulateRegistrationData(aPrincipal
, aRegistration
, data
);
1598 if (NS_WARN_IF(NS_FAILED(rv
))) {
1602 PrincipalInfo principalInfo
;
1603 if (NS_WARN_IF(NS_FAILED(PrincipalToPrincipalInfo(aPrincipal
,
1604 &principalInfo
)))) {
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(),
1620 NS_ENSURE_SUCCESS(rv
, nullptr);
1622 return GetServiceWorkerRegistrationInfo(principal
, uri
);
1625 already_AddRefed
<ServiceWorkerRegistrationInfo
>
1626 ServiceWorkerManager::GetServiceWorkerRegistrationInfo(nsIPrincipal
* aPrincipal
,
1629 MOZ_ASSERT(aPrincipal
);
1632 //XXXnsm Temporary fix until Bug 1171432 is fixed.
1633 if (NS_WARN_IF(BasePrincipal::Cast(aPrincipal
)->AppId() == nsIScriptSecurityManager::UNKNOWN_APP_ID
)) {
1637 nsAutoCString scopeKey
;
1638 nsresult rv
= PrincipalToScopeKey(aPrincipal
, scopeKey
);
1639 if (NS_FAILED(rv
)) {
1643 return GetServiceWorkerRegistrationInfo(scopeKey
, aURI
);
1646 already_AddRefed
<ServiceWorkerRegistrationInfo
>
1647 ServiceWorkerManager::GetServiceWorkerRegistrationInfo(const nsACString
& aScopeKey
,
1653 nsresult rv
= aURI
->GetSpec(spec
);
1654 if (NS_WARN_IF(NS_FAILED(rv
))) {
1658 nsAutoCString scope
;
1659 RegistrationDataPerPrincipal
* data
;
1660 if (!FindScopeForPath(aScopeKey
, spec
, &data
, scope
)) {
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
);
1672 nsAutoCString origin
;
1673 rv
= registration
->Principal()->GetOrigin(origin
);
1674 MOZ_ASSERT(NS_SUCCEEDED(rv
));
1675 MOZ_ASSERT(origin
.Equals(aScopeKey
));
1678 if (registration
->IsPendingUninstall()) {
1681 return registration
.forget();
1684 /* static */ nsresult
1685 ServiceWorkerManager::PrincipalToScopeKey(nsIPrincipal
* aPrincipal
,
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
))) {
1702 /* static */ nsresult
1703 ServiceWorkerManager::PrincipalInfoToScopeKey(const PrincipalInfo
& aPrincipalInfo
,
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
);
1722 ServiceWorkerManager::AddScopeAndRegistration(const nsACString
& aScope
,
1723 ServiceWorkerRegistrationInfo
* aInfo
)
1726 MOZ_ASSERT(aInfo
->Principal());
1728 RefPtr
<ServiceWorkerManager
> swm
= ServiceWorkerManager::GetInstance();
1734 nsAutoCString scopeKey
;
1735 nsresult rv
= swm
->PrincipalToScopeKey(aInfo
->Principal(), scopeKey
);
1736 if (NS_WARN_IF(NS_FAILED(rv
))) {
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
];
1750 if (aScope
.Equals(current
)) {
1751 data
->mInfos
.Put(aScope
, aInfo
);
1752 swm
->NotifyListenersOnRegister(aInfo
);
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
);
1767 data
->mOrderedScopes
.AppendElement(aScope
);
1768 data
->mInfos
.Put(aScope
, aInfo
);
1769 swm
->NotifyListenersOnRegister(aInfo
);
1773 ServiceWorkerManager::FindScopeForPath(const nsACString
& aScopeKey
,
1774 const nsACString
& aPath
,
1775 RegistrationDataPerPrincipal
** aData
,
1780 RefPtr
<ServiceWorkerManager
> swm
= ServiceWorkerManager::GetInstance();
1782 if (!swm
|| !swm
->mRegistrationInfos
.Get(aScopeKey
, aData
)) {
1786 for (uint32_t i
= 0; i
< (*aData
)->mOrderedScopes
.Length(); ++i
) {
1787 const nsCString
& current
= (*aData
)->mOrderedScopes
[i
];
1788 if (StringBeginsWith(aPath
, current
)) {
1798 ServiceWorkerManager::HasScope(nsIPrincipal
* aPrincipal
,
1799 const nsACString
& aScope
)
1801 RefPtr
<ServiceWorkerManager
> swm
= ServiceWorkerManager::GetInstance();
1806 nsAutoCString scopeKey
;
1807 nsresult rv
= PrincipalToScopeKey(aPrincipal
, scopeKey
);
1808 if (NS_WARN_IF(NS_FAILED(rv
))) {
1812 RegistrationDataPerPrincipal
* data
;
1813 if (!swm
->mRegistrationInfos
.Get(scopeKey
, &data
)) {
1817 return data
->mOrderedScopes
.Contains(aScope
);
1821 ServiceWorkerManager::RemoveScopeAndRegistration(ServiceWorkerRegistrationInfo
* aRegistration
)
1823 RefPtr
<ServiceWorkerManager
> swm
= ServiceWorkerManager::GetInstance();
1828 nsAutoCString scopeKey
;
1829 nsresult rv
= swm
->PrincipalToScopeKey(aRegistration
->Principal(), scopeKey
);
1830 if (NS_WARN_IF(NS_FAILED(rv
))) {
1834 RegistrationDataPerPrincipal
* data
;
1835 if (!swm
->mRegistrationInfos
.Get(scopeKey
, &data
)) {
1839 if (auto entry
= data
->mUpdateTimers
.Lookup(aRegistration
->Scope())) {
1840 entry
.Data()->Cancel();
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");
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();
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) {
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
;
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
);
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
1914 ControlledClientData
* data
= mControlledClients
.Get(aClientInfo
.Id());
1915 if (data
&& data
->mRegistrationInfo
) {
1916 data
->mRegistrationInfo
->MaybeScheduleUpdate();
1921 ServiceWorkerManager::StopControllingRegistration(ServiceWorkerRegistrationInfo
* aRegistration
)
1923 aRegistration
->StopControllingClient();
1924 if (aRegistration
->IsControllingClients() || !aRegistration
->IsIdle()) {
1928 if (aRegistration
->IsPendingUninstall()) {
1929 RemoveRegistration(aRegistration
);
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();
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
);
1955 return NS_ERROR_FAILURE
;
1958 aScope
= NS_ConvertUTF8toUTF16(r
->Scope());
1964 class ContinueDispatchFetchEventRunnable
: public Runnable
1966 RefPtr
<ServiceWorkerPrivate
> mServiceWorkerPrivate
;
1967 nsCOMPtr
<nsIInterceptedChannel
> mChannel
;
1968 nsCOMPtr
<nsILoadGroup
> mLoadGroup
;
1971 ContinueDispatchFetchEventRunnable(
1972 ServiceWorkerPrivate
* aServiceWorkerPrivate
,
1973 nsIInterceptedChannel
* aChannel
,
1974 nsILoadGroup
* aLoadGroup
,
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
);
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
);
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
))) {
2010 // The channel might have encountered an unexpected error while ensuring
2011 // the upload stream is cloneable. Check here and reset the interception
2014 rv
= channel
->GetStatus(&status
);
2015 if (NS_WARN_IF(NS_FAILED(rv
) || NS_FAILED(status
))) {
2021 nsCOMPtr
<nsILoadInfo
> loadInfo
= channel
->GetLoadInfo();
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
,
2036 if (NS_WARN_IF(NS_FAILED(rv
))) {
2044 } // anonymous namespace
2047 ServiceWorkerManager::DispatchFetchEvent(nsIInterceptedChannel
* aChannel
,
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())) {
2059 nsCOMPtr
<nsILoadGroup
> loadGroup
;
2060 aRv
= internalChannel
->GetLoadGroup(getter_AddRefs(loadGroup
));
2061 if (NS_WARN_IF(aRv
.Failed())) {
2065 nsCOMPtr
<nsILoadInfo
> loadInfo
= internalChannel
->GetLoadInfo();
2066 if (NS_WARN_IF(!loadInfo
)) {
2067 aRv
.Throw(NS_ERROR_UNEXPECTED
);
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
);
2080 RefPtr
<ServiceWorkerRegistrationInfo
> registration
=
2081 GetRegistration(controller
.ref().PrincipalInfo(), controller
.ref().Scope());
2082 if (NS_WARN_IF(!registration
)) {
2083 aRv
.Throw(NS_ERROR_FAILURE
);
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
);
2094 nsCOMPtr
<nsIURI
> uri
;
2095 aRv
= aChannel
->GetSecureUpgradedChannelURI(getter_AddRefs(uri
));
2096 if (NS_WARN_IF(aRv
.Failed())) {
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
);
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
);
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
,
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(),
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());
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
);
2233 ServiceWorkerManager::IsAvailable(nsIPrincipal
* aPrincipal
,
2236 MOZ_ASSERT(aPrincipal
);
2239 RefPtr
<ServiceWorkerRegistrationInfo
> registration
=
2240 GetServiceWorkerRegistrationInfo(aPrincipal
, aURI
);
2241 return registration
&& registration
->GetActive();
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
);
2264 ServiceWorkerManager::SoftUpdate(const OriginAttributes
& aOriginAttributes
,
2265 const nsACString
& aScope
)
2267 MOZ_ASSERT(NS_IsMainThread());
2269 if (mShuttingDown
) {
2273 if (ServiceWorkerParentInterceptEnabled()) {
2274 SoftUpdateInternal(aOriginAttributes
, aScope
, nullptr);
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
,
2296 class UpdateJobCallback final
: public ServiceWorkerJob::Callback
2298 RefPtr
<ServiceWorkerUpdateFinishCallback
> mCallback
;
2300 ~UpdateJobCallback() = default;
2303 explicit UpdateJobCallback(ServiceWorkerUpdateFinishCallback
* aCallback
)
2304 : mCallback(aCallback
)
2306 MOZ_ASSERT(NS_IsMainThread());
2307 MOZ_ASSERT(mCallback
);
2311 JobFinished(ServiceWorkerJob
* aJob
, ErrorResult
& aStatus
) override
2313 MOZ_ASSERT(NS_IsMainThread());
2316 if (aStatus
.Failed()) {
2317 mCallback
->UpdateFailed(aStatus
);
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
2334 ServiceWorkerManager::SoftUpdateInternal(const OriginAttributes
& aOriginAttributes
,
2335 const nsACString
& aScope
,
2336 ServiceWorkerUpdateFinishCallback
* aCallback
)
2338 MOZ_ASSERT(NS_IsMainThread());
2340 if (mShuttingDown
) {
2344 nsCOMPtr
<nsIURI
> scopeURI
;
2345 nsresult rv
= NS_NewURI(getter_AddRefs(scopeURI
), aScope
);
2346 if (NS_WARN_IF(NS_FAILED(rv
))) {
2350 nsCOMPtr
<nsIPrincipal
> principal
=
2351 BasePrincipal::CreateCodebasePrincipal(scopeURI
, aOriginAttributes
);
2352 if (NS_WARN_IF(!principal
)) {
2356 nsAutoCString scopeKey
;
2357 rv
= PrincipalToScopeKey(principal
, scopeKey
);
2358 if (NS_WARN_IF(NS_FAILED(rv
))) {
2362 RefPtr
<ServiceWorkerRegistrationInfo
> registration
=
2363 GetRegistration(scopeKey
, aScope
);
2364 if (NS_WARN_IF(!registration
)) {
2368 // "If registration's uninstalling flag is set, abort these steps."
2369 if (registration
->IsPendingUninstall()) {
2373 // "If registration's installing worker is not null, abort these steps."
2374 if (registration
->GetInstalling()) {
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();
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
,
2393 RefPtr
<ServiceWorkerUpdateJob
> job
=
2394 new ServiceWorkerUpdateJob(principal
, registration
->Scope(),
2395 newest
->ScriptSpec(), nullptr,
2396 registration
->GetUpdateViaCache());
2399 RefPtr
<UpdateJobCallback
> cb
= new UpdateJobCallback(aCallback
);
2400 job
->AppendResultCallback(cb
);
2403 queue
->ScheduleJob(job
);
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
);
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(),
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();
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
))) {
2465 RefPtr
<ServiceWorkerRegistrationInfo
> registration
=
2466 GetRegistration(scopeKey
, aScope
);
2467 if (NS_WARN_IF(!registration
)) {
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();
2476 RejectUpdateWithInvalidStateError(*aCallback
);
2480 if (newest
->State() == ServiceWorkerState::Installing
) {
2481 RejectUpdateWithInvalidStateError(*aCallback
);
2485 RefPtr
<ServiceWorkerJobQueue
> queue
= GetOrCreateJobQueue(scopeKey
, aScope
);
2487 // "Invoke Update algorithm, or its equivalent, with client, registration as
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
,
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();
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();
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
)) {
2579 RefPtr
<ServiceWorkerInfo
> worker
=
2580 registration
->GetServiceWorkerInfoById(aServiceWorkerID
);
2582 if (NS_WARN_IF(!worker
)) {
2586 worker
->SetSkipWaitingFlag();
2588 if (worker
->State() == ServiceWorkerState::Installed
) {
2589 registration
->TryToActivateAsync();
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
) {
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.
2620 SystemGroup::EventTargetFor(TaskCategory::Other
), __func__
,
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
))) {
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
))) {
2655 return GetRegistration(scopeKey
, aScope
);
2659 ServiceWorkerManager::GetRegistrationByPrincipal(nsIPrincipal
* aPrincipal
,
2660 const nsAString
& aScope
,
2661 nsIServiceWorkerRegistrationInfo
** aInfo
)
2663 MOZ_ASSERT(aPrincipal
);
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
);
2675 return NS_ERROR_FAILURE
;
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
)
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
);
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();
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
);
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).
2748 RefPtr
<ServiceWorkerInfo
> newest
= aRegistration
->Newest();
2749 MOZ_ASSERT(aRegistration
->IsPendingUninstall() || !newest
);
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
);
2762 ServiceWorkerManager::GetAllRegistrations(nsIArray
** aResult
)
2764 MOZ_ASSERT(NS_IsMainThread());
2766 nsCOMPtr
<nsIMutableArray
> array(do_CreateInstance(NS_ARRAY_CONTRACTID
));
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();
2776 if (reg
->IsPendingUninstall()) {
2780 array
->AppendElement(reg
);
2784 array
.forget(aResult
);
2788 // MUST ONLY BE CALLED FROM Remove(), RemoveAll() and RemoveAllRegistrations()!
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
));
2802 if (auto entry
= aRegistrationData
->mUpdateTimers
.Lookup(aRegistration
->Scope())) {
2803 entry
.Data()->Cancel();
2807 // Since Unregister is async, it is ok to call it in an enumeration.
2808 Unregister(aRegistration
->Principal(), nullptr, NS_ConvertUTF8toUTF16(aRegistration
->Scope()));
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
)) {
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(),
2829 if (NS_WARN_IF(NS_FAILED(rv
))) {
2834 rv
= scopeURI
->GetHost(host
);
2835 if (NS_WARN_IF(NS_FAILED(rv
))) {
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
))) {
2846 if (hasRootDomain
) {
2847 ForceUnregister(data
, reg
);
2854 ServiceWorkerManager::PropagateRemove(const nsACString
& aHost
)
2856 MOZ_ASSERT(NS_IsMainThread());
2857 mActor
->SendPropagateRemove(nsCString(aHost
));
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
);
2875 ServiceWorkerManager::PropagateRemoveAll()
2877 MOZ_ASSERT(NS_IsMainThread());
2878 MOZ_ASSERT(XRE_IsParentProcess());
2879 mActor
->SendPropagateRemoveAll();
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();
2899 MOZ_ASSERT(reg
->Principal());
2902 aPattern
->Matches(reg
->Principal()->OriginAttributesRef());
2907 ForceUnregister(data
, reg
);
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
);
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
);
2941 ServiceWorkerManager::Observe(nsISupports
* aSubject
,
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
);
2954 if (strcmp(aTopic
, NS_XPCOM_SHUTDOWN_OBSERVER_ID
) == 0) {
2955 MaybeStartShutdown();
2959 MOZ_CRASH("Received message we aren't supposed to be registered for!");
2964 ServiceWorkerManager::PropagateSoftUpdate(JS::Handle
<JS::Value
> aOriginAttributes
,
2965 const nsAString
& aScope
,
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
);
2980 ServiceWorkerManager::PropagateSoftUpdate(const OriginAttributes
& aOriginAttributes
,
2981 const nsAString
& aScope
)
2983 MOZ_ASSERT(NS_IsMainThread());
2984 mActor
->SendPropagateSoftUpdate(aOriginAttributes
, nsString(aScope
));
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
))) {
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
);
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
3034 nsCOMPtr
<nsIPrincipal
> mPrincipal
;
3035 const nsCString mScope
;
3037 ~UpdateTimerCallback()
3042 UpdateTimerCallback(nsIPrincipal
* aPrincipal
, const nsACString
& aScope
)
3043 : mPrincipal(aPrincipal
)
3046 MOZ_ASSERT(NS_IsMainThread());
3047 MOZ_ASSERT(mPrincipal
);
3048 MOZ_ASSERT(!mScope
.IsEmpty());
3052 Notify(nsITimer
* aTimer
) override
3054 MOZ_ASSERT(NS_IsMainThread());
3056 RefPtr
<ServiceWorkerManager
> swm
= ServiceWorkerManager::GetInstance();
3058 // shutting down, do nothing
3062 swm
->UpdateTimerFired(mPrincipal
, mScope
);
3067 GetName(nsACString
& aName
) override
3069 aName
.AssignLiteral("UpdateTimerCallback");
3076 NS_IMPL_ISUPPORTS(UpdateTimerCallback
, nsITimerCallback
, nsINamed
)
3079 ServiceWorkerManager::MayHaveActiveServiceWorkerInstance(ContentParent
* aContent
,
3080 nsIPrincipal
* aPrincipal
)
3082 MOZ_ASSERT(NS_IsMainThread());
3083 MOZ_ASSERT(aPrincipal
);
3085 if (mShuttingDown
) {
3089 nsAutoCString scopeKey
;
3090 nsresult rv
= PrincipalToScopeKey(aPrincipal
, scopeKey
);
3091 if (NS_WARN_IF(NS_FAILED(rv
))) {
3095 RegistrationDataPerPrincipal
* data
;
3096 if (!mRegistrationInfos
.Get(scopeKey
, &data
)) {
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
) {
3115 nsAutoCString scopeKey
;
3116 nsresult rv
= PrincipalToScopeKey(aPrincipal
, scopeKey
);
3117 if (NS_WARN_IF(NS_FAILED(rv
))) {
3121 RegistrationDataPerPrincipal
* data
;
3122 if (!mRegistrationInfos
.Get(scopeKey
, &data
)) {
3126 nsCOMPtr
<nsITimer
>& timer
= data
->mUpdateTimers
.GetOrInsert(aScope
);
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
3135 nsCOMPtr
<nsITimerCallback
> callback
= new UpdateTimerCallback(aPrincipal
,
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
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
) {
3165 // First cleanup the timer.
3166 nsAutoCString scopeKey
;
3167 nsresult rv
= PrincipalToScopeKey(aPrincipal
, scopeKey
);
3168 if (NS_WARN_IF(NS_FAILED(rv
))) {
3172 RegistrationDataPerPrincipal
* data
;
3173 if (!mRegistrationInfos
.Get(scopeKey
, &data
)) {
3177 if (auto entry
= data
->mUpdateTimers
.Lookup(aScope
)) {
3178 entry
.Data()->Cancel();
3182 RefPtr
<ServiceWorkerRegistrationInfo
> registration
;
3183 data
->mInfos
.Get(aScope
, getter_AddRefs(registration
));
3184 if (!registration
) {
3188 if (!registration
->CheckAndClearIfUpdateNeeded()) {
3192 OriginAttributes attrs
= aPrincipal
->OriginAttributesRef();
3194 SoftUpdate(attrs
, aScope
);
3198 ServiceWorkerManager::MaybeSendUnregister(nsIPrincipal
* aPrincipal
,
3199 const nsACString
& aScope
)
3201 MOZ_ASSERT(NS_IsMainThread());
3202 MOZ_ASSERT(aPrincipal
);
3203 MOZ_ASSERT(!aScope
.IsEmpty());
3209 PrincipalInfo principalInfo
;
3210 nsresult rv
= PrincipalToPrincipalInfo(aPrincipal
, &principalInfo
);
3211 if (NS_WARN_IF(NS_FAILED(rv
))) {
3215 Unused
<< mActor
->SendUnregister(principalInfo
, NS_ConvertUTF8toUTF16(aScope
));
3219 } // namespace mozilla