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 "ServiceWorkerRegistrationInfo.h"
9 #include "ServiceWorkerManager.h"
10 #include "ServiceWorkerPrivate.h"
11 #include "ServiceWorkerRegistrationListener.h"
13 #include "mozilla/Preferences.h"
14 #include "mozilla/SchedulerGroup.h"
15 #include "mozilla/StaticPrefs_dom.h"
17 namespace mozilla::dom
{
21 class ContinueActivateRunnable final
: public LifeCycleEventCallback
{
22 nsMainThreadPtrHandle
<ServiceWorkerRegistrationInfo
> mRegistration
;
26 explicit ContinueActivateRunnable(
27 const nsMainThreadPtrHandle
<ServiceWorkerRegistrationInfo
>& aRegistration
)
28 : mRegistration(aRegistration
), mSuccess(false) {
29 MOZ_ASSERT(NS_IsMainThread());
32 void SetResult(bool aResult
) override
{ mSuccess
= aResult
; }
36 MOZ_ASSERT(NS_IsMainThread());
37 mRegistration
->FinishActivate(mSuccess
);
38 mRegistration
= nullptr;
43 } // anonymous namespace
45 void ServiceWorkerRegistrationInfo::ShutdownWorkers() {
46 ForEachWorker([](RefPtr
<ServiceWorkerInfo
>& aWorker
) {
47 aWorker
->WorkerPrivate()->NoteDeadServiceWorkerInfo();
52 void ServiceWorkerRegistrationInfo::Clear() {
53 ForEachWorker([](RefPtr
<ServiceWorkerInfo
>& aWorker
) {
54 aWorker
->UpdateState(ServiceWorkerState::Redundant
);
55 aWorker
->UpdateRedundantTime();
58 // FIXME: Abort any inflight requests from installing worker.
61 UpdateRegistrationState();
62 NotifyChromeRegistrationListeners();
66 void ServiceWorkerRegistrationInfo::ClearAsCorrupt() {
71 bool ServiceWorkerRegistrationInfo::IsCorrupt() const { return mCorrupt
; }
73 ServiceWorkerRegistrationInfo::ServiceWorkerRegistrationInfo(
74 const nsACString
& aScope
, nsIPrincipal
* aPrincipal
,
75 ServiceWorkerUpdateViaCache aUpdateViaCache
,
76 IPCNavigationPreloadState
&& aNavigationPreloadState
)
77 : mPrincipal(aPrincipal
),
78 mDescriptor(GetNextId(), GetNextVersion(), aPrincipal
, aScope
,
80 mControlledClientsCounter(0),
82 mUpdateState(NoUpdate
),
83 mCreationTime(PR_Now()),
84 mCreationTimeStamp(TimeStamp::Now()),
88 mNavigationPreloadState(std::move(aNavigationPreloadState
)) {
89 MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default
);
92 ServiceWorkerRegistrationInfo::~ServiceWorkerRegistrationInfo() {
93 MOZ_DIAGNOSTIC_ASSERT(!IsControllingClients());
96 void ServiceWorkerRegistrationInfo::AddInstance(
97 ServiceWorkerRegistrationListener
* aInstance
,
98 const ServiceWorkerRegistrationDescriptor
& aDescriptor
) {
99 MOZ_DIAGNOSTIC_ASSERT(aInstance
);
100 MOZ_ASSERT(!mInstanceList
.Contains(aInstance
));
101 MOZ_DIAGNOSTIC_ASSERT(aDescriptor
.Id() == mDescriptor
.Id());
102 MOZ_DIAGNOSTIC_ASSERT(aDescriptor
.PrincipalInfo() ==
103 mDescriptor
.PrincipalInfo());
104 MOZ_DIAGNOSTIC_ASSERT(aDescriptor
.Scope() == mDescriptor
.Scope());
105 MOZ_DIAGNOSTIC_ASSERT(aDescriptor
.Version() <= mDescriptor
.Version());
106 uint64_t lastVersion
= aDescriptor
.Version();
107 for (auto& entry
: mVersionList
) {
108 if (lastVersion
> entry
->mDescriptor
.Version()) {
111 lastVersion
= entry
->mDescriptor
.Version();
112 aInstance
->UpdateState(entry
->mDescriptor
);
114 // Note, the mDescriptor may be contained in the version list. Since the
115 // version list is aged out, though, it may also not be in the version list.
116 // So always check for the mDescriptor update here.
117 if (lastVersion
< mDescriptor
.Version()) {
118 aInstance
->UpdateState(mDescriptor
);
120 mInstanceList
.AppendElement(aInstance
);
123 void ServiceWorkerRegistrationInfo::RemoveInstance(
124 ServiceWorkerRegistrationListener
* aInstance
) {
125 MOZ_DIAGNOSTIC_ASSERT(aInstance
);
126 DebugOnly
<bool> removed
= mInstanceList
.RemoveElement(aInstance
);
130 const nsCString
& ServiceWorkerRegistrationInfo::Scope() const {
131 return mDescriptor
.Scope();
134 nsIPrincipal
* ServiceWorkerRegistrationInfo::Principal() const {
138 bool ServiceWorkerRegistrationInfo::IsUnregistered() const {
139 return mUnregistered
;
142 void ServiceWorkerRegistrationInfo::SetUnregistered() {
144 MOZ_ASSERT(!mUnregistered
);
146 RefPtr
<ServiceWorkerManager
> swm
= ServiceWorkerManager::GetInstance();
149 RefPtr
<ServiceWorkerRegistrationInfo
> registration
=
150 swm
->GetRegistration(Principal(), Scope());
151 MOZ_ASSERT(registration
!= this);
154 mUnregistered
= true;
155 NotifyChromeRegistrationListeners();
158 NS_IMPL_ISUPPORTS(ServiceWorkerRegistrationInfo
,
159 nsIServiceWorkerRegistrationInfo
)
162 ServiceWorkerRegistrationInfo::GetPrincipal(nsIPrincipal
** aPrincipal
) {
163 MOZ_ASSERT(NS_IsMainThread());
164 NS_ADDREF(*aPrincipal
= mPrincipal
);
168 NS_IMETHODIMP
ServiceWorkerRegistrationInfo::GetUnregistered(
169 bool* aUnregistered
) {
170 MOZ_ASSERT(NS_IsMainThread());
171 MOZ_ASSERT(aUnregistered
);
172 *aUnregistered
= mUnregistered
;
177 ServiceWorkerRegistrationInfo::GetScope(nsAString
& aScope
) {
178 MOZ_ASSERT(NS_IsMainThread());
179 CopyUTF8toUTF16(Scope(), aScope
);
184 ServiceWorkerRegistrationInfo::GetScriptSpec(nsAString
& aScriptSpec
) {
185 MOZ_ASSERT(NS_IsMainThread());
186 RefPtr
<ServiceWorkerInfo
> newest
= NewestIncludingEvaluating();
188 CopyUTF8toUTF16(newest
->ScriptSpec(), aScriptSpec
);
194 ServiceWorkerRegistrationInfo::GetUpdateViaCache(uint16_t* aUpdateViaCache
) {
195 *aUpdateViaCache
= static_cast<uint16_t>(GetUpdateViaCache());
200 ServiceWorkerRegistrationInfo::GetLastUpdateTime(PRTime
* _retval
) {
201 MOZ_ASSERT(NS_IsMainThread());
203 *_retval
= mLastUpdateTime
;
208 ServiceWorkerRegistrationInfo::GetEvaluatingWorker(
209 nsIServiceWorkerInfo
** aResult
) {
210 MOZ_ASSERT(NS_IsMainThread());
211 RefPtr
<ServiceWorkerInfo
> info
= mEvaluatingWorker
;
212 info
.forget(aResult
);
217 ServiceWorkerRegistrationInfo::GetInstallingWorker(
218 nsIServiceWorkerInfo
** aResult
) {
219 MOZ_ASSERT(NS_IsMainThread());
220 RefPtr
<ServiceWorkerInfo
> info
= mInstallingWorker
;
221 info
.forget(aResult
);
226 ServiceWorkerRegistrationInfo::GetWaitingWorker(
227 nsIServiceWorkerInfo
** aResult
) {
228 MOZ_ASSERT(NS_IsMainThread());
229 RefPtr
<ServiceWorkerInfo
> info
= mWaitingWorker
;
230 info
.forget(aResult
);
235 ServiceWorkerRegistrationInfo::GetActiveWorker(nsIServiceWorkerInfo
** aResult
) {
236 MOZ_ASSERT(NS_IsMainThread());
237 RefPtr
<ServiceWorkerInfo
> info
= mActiveWorker
;
238 info
.forget(aResult
);
243 ServiceWorkerRegistrationInfo::GetQuotaUsageCheckCount(
244 int32_t* aQuotaUsageCheckCount
) {
245 MOZ_ASSERT(NS_IsMainThread());
246 MOZ_ASSERT(aQuotaUsageCheckCount
);
248 // This value is actually stored on SWM's internal-only
249 // RegistrationDataPerPrincipal structure, but we expose it here for
250 // simplicity for our consumers, so we have to ask SWM to look it up for us.
251 RefPtr
<ServiceWorkerManager
> swm
= ServiceWorkerManager::GetInstance();
254 *aQuotaUsageCheckCount
= swm
->GetPrincipalQuotaUsageCheckCount(mPrincipal
);
260 ServiceWorkerRegistrationInfo::GetWorkerByID(uint64_t aID
,
261 nsIServiceWorkerInfo
** aResult
) {
262 MOZ_ASSERT(NS_IsMainThread());
265 RefPtr
<ServiceWorkerInfo
> info
= GetServiceWorkerInfoById(aID
);
266 // It is ok to return null for a missing service worker info.
267 info
.forget(aResult
);
272 ServiceWorkerRegistrationInfo::AddListener(
273 nsIServiceWorkerRegistrationInfoListener
* aListener
) {
274 MOZ_ASSERT(NS_IsMainThread());
276 if (!aListener
|| mListeners
.Contains(aListener
)) {
277 return NS_ERROR_INVALID_ARG
;
280 mListeners
.AppendElement(aListener
);
286 ServiceWorkerRegistrationInfo::RemoveListener(
287 nsIServiceWorkerRegistrationInfoListener
* aListener
) {
288 MOZ_ASSERT(NS_IsMainThread());
290 if (!aListener
|| !mListeners
.Contains(aListener
)) {
291 return NS_ERROR_INVALID_ARG
;
294 mListeners
.RemoveElement(aListener
);
300 ServiceWorkerRegistrationInfo::ForceShutdown() {
306 already_AddRefed
<ServiceWorkerInfo
>
307 ServiceWorkerRegistrationInfo::GetServiceWorkerInfoById(uint64_t aId
) {
308 MOZ_ASSERT(NS_IsMainThread());
310 RefPtr
<ServiceWorkerInfo
> serviceWorker
;
311 if (mEvaluatingWorker
&& mEvaluatingWorker
->ID() == aId
) {
312 serviceWorker
= mEvaluatingWorker
;
313 } else if (mInstallingWorker
&& mInstallingWorker
->ID() == aId
) {
314 serviceWorker
= mInstallingWorker
;
315 } else if (mWaitingWorker
&& mWaitingWorker
->ID() == aId
) {
316 serviceWorker
= mWaitingWorker
;
317 } else if (mActiveWorker
&& mActiveWorker
->ID() == aId
) {
318 serviceWorker
= mActiveWorker
;
321 return serviceWorker
.forget();
324 void ServiceWorkerRegistrationInfo::TryToActivateAsync(
325 TryToActivateCallback
&& aCallback
) {
326 MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(
327 NewRunnableMethod
<StoreCopyPassByRRef
<TryToActivateCallback
>>(
328 "ServiceWorkerRegistrationInfo::TryToActivate", this,
329 &ServiceWorkerRegistrationInfo::TryToActivate
,
330 std::move(aCallback
))));
334 * TryToActivate should not be called directly, use TryToActivateAsync instead.
336 void ServiceWorkerRegistrationInfo::TryToActivate(
337 TryToActivateCallback
&& aCallback
) {
338 MOZ_ASSERT(NS_IsMainThread());
339 bool controlling
= IsControllingClients();
340 bool skipWaiting
= mWaitingWorker
&& mWaitingWorker
->SkipWaitingFlag();
341 bool idle
= IsIdle();
342 if (idle
&& (!controlling
|| skipWaiting
)) {
351 void ServiceWorkerRegistrationInfo::Activate() {
352 if (!mWaitingWorker
) {
356 RefPtr
<ServiceWorkerManager
> swm
= ServiceWorkerManager::GetInstance();
358 // browser shutdown began during async activation step
362 TransitionWaitingToActive();
364 // FIXME(nsm): Unlink appcache if there is one.
366 // "Queue a task to fire a simple event named controllerchange..."
367 MOZ_DIAGNOSTIC_ASSERT(mActiveWorker
);
368 swm
->UpdateClientControllers(this);
370 nsMainThreadPtrHandle
<ServiceWorkerRegistrationInfo
> handle(
371 new nsMainThreadPtrHolder
<ServiceWorkerRegistrationInfo
>(
372 "ServiceWorkerRegistrationInfoProxy", this));
373 RefPtr
<LifeCycleEventCallback
> callback
=
374 new ContinueActivateRunnable(handle
);
376 ServiceWorkerPrivate
* workerPrivate
= mActiveWorker
->WorkerPrivate();
377 MOZ_ASSERT(workerPrivate
);
378 nsresult rv
= workerPrivate
->SendLifeCycleEvent(u
"activate"_ns
, callback
);
379 if (NS_WARN_IF(NS_FAILED(rv
))) {
380 nsCOMPtr
<nsIRunnable
> failRunnable
= NewRunnableMethod
<bool>(
381 "dom::ServiceWorkerRegistrationInfo::FinishActivate", this,
382 &ServiceWorkerRegistrationInfo::FinishActivate
, false /* success */);
383 MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(failRunnable
.forget()));
388 void ServiceWorkerRegistrationInfo::FinishActivate(bool aSuccess
) {
389 if (mUnregistered
|| !mActiveWorker
||
390 mActiveWorker
->State() != ServiceWorkerState::Activating
) {
394 // Activation never fails, so aSuccess is ignored.
395 mActiveWorker
->UpdateState(ServiceWorkerState::Activated
);
396 mActiveWorker
->UpdateActivatedTime();
398 UpdateRegistrationState();
399 NotifyChromeRegistrationListeners();
401 RefPtr
<ServiceWorkerManager
> swm
= ServiceWorkerManager::GetInstance();
403 // browser shutdown started during async activation completion step
406 swm
->StoreRegistration(mPrincipal
, this);
409 void ServiceWorkerRegistrationInfo::RefreshLastUpdateCheckTime() {
410 MOZ_ASSERT(NS_IsMainThread());
415 (TimeStamp::Now() - mCreationTimeStamp
).ToMicroseconds());
416 NotifyChromeRegistrationListeners();
419 bool ServiceWorkerRegistrationInfo::IsLastUpdateCheckTimeOverOneDay() const {
420 MOZ_ASSERT(NS_IsMainThread());
423 if (Preferences::GetBool("dom.serviceWorkers.testUpdateOverOneDay")) {
427 const int64_t kSecondsPerDay
= 86400;
428 const int64_t nowMicros
=
431 (TimeStamp::Now() - mCreationTimeStamp
).ToMicroseconds());
433 // now < mLastUpdateTime if the system time is reset between storing
434 // and loading mLastUpdateTime from ServiceWorkerRegistrar.
435 if (nowMicros
< mLastUpdateTime
||
436 (nowMicros
- mLastUpdateTime
) / PR_USEC_PER_SEC
> kSecondsPerDay
) {
442 void ServiceWorkerRegistrationInfo::UpdateRegistrationState() {
443 UpdateRegistrationState(mDescriptor
.UpdateViaCache());
446 void ServiceWorkerRegistrationInfo::UpdateRegistrationState(
447 ServiceWorkerUpdateViaCache aUpdateViaCache
) {
448 MOZ_ASSERT(NS_IsMainThread());
450 TimeStamp oldest
= TimeStamp::Now() - TimeDuration::FromSeconds(30);
451 if (!mVersionList
.IsEmpty() && mVersionList
[0]->mTimeStamp
< oldest
) {
452 nsTArray
<UniquePtr
<VersionEntry
>> list
= std::move(mVersionList
);
453 for (auto& entry
: list
) {
454 if (entry
->mTimeStamp
>= oldest
) {
455 mVersionList
.AppendElement(std::move(entry
));
459 mVersionList
.AppendElement(MakeUnique
<VersionEntry
>(mDescriptor
));
461 // We are going to modify the descriptor, so increase its version number.
462 mDescriptor
.SetVersion(GetNextVersion());
464 // Note, this also sets the new version number on the ServiceWorkerInfo
465 // objects before we copy over their updated descriptors.
466 mDescriptor
.SetWorkers(mInstallingWorker
, mWaitingWorker
, mActiveWorker
);
468 mDescriptor
.SetUpdateViaCache(aUpdateViaCache
);
470 for (RefPtr
<ServiceWorkerRegistrationListener
> pinnedTarget
:
471 mInstanceList
.ForwardRange()) {
472 pinnedTarget
->UpdateState(mDescriptor
);
476 void ServiceWorkerRegistrationInfo::NotifyChromeRegistrationListeners() {
477 nsTArray
<nsCOMPtr
<nsIServiceWorkerRegistrationInfoListener
>> listeners(
479 for (size_t index
= 0; index
< listeners
.Length(); ++index
) {
480 listeners
[index
]->OnChange();
484 void ServiceWorkerRegistrationInfo::MaybeScheduleTimeCheckAndUpdate() {
485 MOZ_ASSERT(NS_IsMainThread());
487 RefPtr
<ServiceWorkerManager
> swm
= ServiceWorkerManager::GetInstance();
489 // shutting down, do nothing
493 if (mUpdateState
== NoUpdate
) {
494 mUpdateState
= NeedTimeCheckAndUpdate
;
497 swm
->ScheduleUpdateTimer(mPrincipal
, Scope());
500 void ServiceWorkerRegistrationInfo::MaybeScheduleUpdate() {
501 MOZ_ASSERT(NS_IsMainThread());
503 RefPtr
<ServiceWorkerManager
> swm
= ServiceWorkerManager::GetInstance();
505 // shutting down, do nothing
509 // When reach the navigation fault threshold, calling unregister instead of
510 // scheduling update.
511 if (mActiveWorker
&& !mUnregistered
) {
512 uint32_t navigationFaultCount
;
513 mActiveWorker
->GetNavigationFaultCount(&navigationFaultCount
);
514 const auto navigationFaultThreshold
= StaticPrefs::
515 dom_serviceWorkers_mitigations_navigation_fault_threshold();
516 // Disable unregister mitigation when navigation fault threshold is 0.
517 if (navigationFaultThreshold
<= navigationFaultCount
&&
518 navigationFaultThreshold
!= 0) {
520 swm
->Unregister(mPrincipal
, nullptr, NS_ConvertUTF8toUTF16(Scope()));
525 mUpdateState
= NeedUpdate
;
527 swm
->ScheduleUpdateTimer(mPrincipal
, Scope());
530 bool ServiceWorkerRegistrationInfo::CheckAndClearIfUpdateNeeded() {
531 MOZ_ASSERT(NS_IsMainThread());
534 mUpdateState
== NeedUpdate
|| (mUpdateState
== NeedTimeCheckAndUpdate
&&
535 IsLastUpdateCheckTimeOverOneDay());
537 mUpdateState
= NoUpdate
;
542 ServiceWorkerInfo
* ServiceWorkerRegistrationInfo::GetEvaluating() const {
543 MOZ_ASSERT(NS_IsMainThread());
544 return mEvaluatingWorker
;
547 ServiceWorkerInfo
* ServiceWorkerRegistrationInfo::GetInstalling() const {
548 MOZ_ASSERT(NS_IsMainThread());
549 return mInstallingWorker
;
552 ServiceWorkerInfo
* ServiceWorkerRegistrationInfo::GetWaiting() const {
553 MOZ_ASSERT(NS_IsMainThread());
554 return mWaitingWorker
;
557 ServiceWorkerInfo
* ServiceWorkerRegistrationInfo::GetActive() const {
558 MOZ_ASSERT(NS_IsMainThread());
559 return mActiveWorker
;
562 ServiceWorkerInfo
* ServiceWorkerRegistrationInfo::GetByDescriptor(
563 const ServiceWorkerDescriptor
& aDescriptor
) const {
564 if (mActiveWorker
&& mActiveWorker
->Descriptor().Matches(aDescriptor
)) {
565 return mActiveWorker
;
567 if (mWaitingWorker
&& mWaitingWorker
->Descriptor().Matches(aDescriptor
)) {
568 return mWaitingWorker
;
570 if (mInstallingWorker
&&
571 mInstallingWorker
->Descriptor().Matches(aDescriptor
)) {
572 return mInstallingWorker
;
574 if (mEvaluatingWorker
&&
575 mEvaluatingWorker
->Descriptor().Matches(aDescriptor
)) {
576 return mEvaluatingWorker
;
581 void ServiceWorkerRegistrationInfo::SetEvaluating(
582 ServiceWorkerInfo
* aServiceWorker
) {
583 MOZ_ASSERT(NS_IsMainThread());
584 MOZ_ASSERT(aServiceWorker
);
585 MOZ_ASSERT(!mEvaluatingWorker
);
586 MOZ_ASSERT(!mInstallingWorker
);
587 MOZ_ASSERT(mWaitingWorker
!= aServiceWorker
);
588 MOZ_ASSERT(mActiveWorker
!= aServiceWorker
);
590 mEvaluatingWorker
= aServiceWorker
;
592 // We don't call UpdateRegistrationState() here because the evaluating worker
593 // is currently not exposed to content on the registration, so calling it here
594 // would produce redundant IPC traffic.
595 NotifyChromeRegistrationListeners();
598 void ServiceWorkerRegistrationInfo::ClearEvaluating() {
599 MOZ_ASSERT(NS_IsMainThread());
601 if (!mEvaluatingWorker
) {
605 mEvaluatingWorker
->UpdateState(ServiceWorkerState::Redundant
);
606 // We don't update the redundant time for the sw here, since we've not expose
607 // evalutingWorker yet.
608 mEvaluatingWorker
= nullptr;
610 // As for SetEvaluating, UpdateRegistrationState() does not need to be called.
611 NotifyChromeRegistrationListeners();
614 void ServiceWorkerRegistrationInfo::ClearInstalling() {
615 MOZ_ASSERT(NS_IsMainThread());
617 if (!mInstallingWorker
) {
621 RefPtr
<ServiceWorkerInfo
> installing
= std::move(mInstallingWorker
);
622 installing
->UpdateState(ServiceWorkerState::Redundant
);
623 installing
->UpdateRedundantTime();
625 UpdateRegistrationState();
626 NotifyChromeRegistrationListeners();
629 void ServiceWorkerRegistrationInfo::TransitionEvaluatingToInstalling() {
630 MOZ_ASSERT(NS_IsMainThread());
631 MOZ_ASSERT(mEvaluatingWorker
);
632 MOZ_ASSERT(!mInstallingWorker
);
634 mInstallingWorker
= std::move(mEvaluatingWorker
);
635 mInstallingWorker
->UpdateState(ServiceWorkerState::Installing
);
637 UpdateRegistrationState();
638 NotifyChromeRegistrationListeners();
641 void ServiceWorkerRegistrationInfo::TransitionInstallingToWaiting() {
642 MOZ_ASSERT(NS_IsMainThread());
643 MOZ_ASSERT(mInstallingWorker
);
645 if (mWaitingWorker
) {
646 MOZ_ASSERT(mInstallingWorker
->CacheName() != mWaitingWorker
->CacheName());
647 mWaitingWorker
->UpdateState(ServiceWorkerState::Redundant
);
648 mWaitingWorker
->UpdateRedundantTime();
651 mWaitingWorker
= std::move(mInstallingWorker
);
652 mWaitingWorker
->UpdateState(ServiceWorkerState::Installed
);
653 mWaitingWorker
->UpdateInstalledTime();
655 UpdateRegistrationState();
656 NotifyChromeRegistrationListeners();
658 // TODO: When bug 1426401 is implemented we will need to call
659 // StoreRegistration() here to persist the waiting worker.
662 void ServiceWorkerRegistrationInfo::SetActive(
663 ServiceWorkerInfo
* aServiceWorker
) {
664 MOZ_ASSERT(NS_IsMainThread());
665 MOZ_ASSERT(aServiceWorker
);
667 // TODO: Assert installing, waiting, and active are nullptr once the SWM
668 // moves to the parent process. After that happens this code will
669 // only run for browser initialization and not for cross-process
671 MOZ_ASSERT(mInstallingWorker
!= aServiceWorker
);
672 MOZ_ASSERT(mWaitingWorker
!= aServiceWorker
);
673 MOZ_ASSERT(mActiveWorker
!= aServiceWorker
);
676 MOZ_ASSERT(aServiceWorker
->CacheName() != mActiveWorker
->CacheName());
677 mActiveWorker
->UpdateState(ServiceWorkerState::Redundant
);
678 mActiveWorker
->UpdateRedundantTime();
681 // The active worker is being overriden due to initial load or
682 // another process activating a worker. Move straight to the
684 mActiveWorker
= aServiceWorker
;
685 mActiveWorker
->SetActivateStateUncheckedWithoutEvent(
686 ServiceWorkerState::Activated
);
688 // We don't need to update activated time when we load registration from
690 UpdateRegistrationState();
691 NotifyChromeRegistrationListeners();
694 void ServiceWorkerRegistrationInfo::TransitionWaitingToActive() {
695 MOZ_ASSERT(NS_IsMainThread());
696 MOZ_ASSERT(mWaitingWorker
);
699 MOZ_ASSERT(mWaitingWorker
->CacheName() != mActiveWorker
->CacheName());
700 mActiveWorker
->UpdateState(ServiceWorkerState::Redundant
);
701 mActiveWorker
->UpdateRedundantTime();
704 // We are transitioning from waiting to active normally, so go to
705 // the activating state.
706 mActiveWorker
= std::move(mWaitingWorker
);
707 mActiveWorker
->UpdateState(ServiceWorkerState::Activating
);
709 nsCOMPtr
<nsIRunnable
> r
= NS_NewRunnableFunction(
710 "ServiceWorkerRegistrationInfo::TransitionWaitingToActive", [] {
711 RefPtr
<ServiceWorkerManager
> swm
= ServiceWorkerManager::GetInstance();
713 swm
->CheckPendingReadyPromises();
716 MOZ_ALWAYS_SUCCEEDS(SchedulerGroup::Dispatch(r
.forget()));
717 UpdateRegistrationState();
718 NotifyChromeRegistrationListeners();
721 bool ServiceWorkerRegistrationInfo::IsIdle() const {
722 return !mActiveWorker
|| mActiveWorker
->WorkerPrivate()->IsIdle();
725 ServiceWorkerUpdateViaCache
ServiceWorkerRegistrationInfo::GetUpdateViaCache()
727 return mDescriptor
.UpdateViaCache();
730 void ServiceWorkerRegistrationInfo::SetUpdateViaCache(
731 ServiceWorkerUpdateViaCache aUpdateViaCache
) {
732 UpdateRegistrationState(aUpdateViaCache
);
735 int64_t ServiceWorkerRegistrationInfo::GetLastUpdateTime() const {
736 return mLastUpdateTime
;
739 void ServiceWorkerRegistrationInfo::SetLastUpdateTime(const int64_t aTime
) {
744 mLastUpdateTime
= aTime
;
747 const ServiceWorkerRegistrationDescriptor
&
748 ServiceWorkerRegistrationInfo::Descriptor() const {
752 uint64_t ServiceWorkerRegistrationInfo::Id() const { return mDescriptor
.Id(); }
754 uint64_t ServiceWorkerRegistrationInfo::Version() const {
755 return mDescriptor
.Version();
758 uint32_t ServiceWorkerRegistrationInfo::GetUpdateDelay(
759 const bool aWithMultiplier
) {
760 uint32_t delay
= Preferences::GetInt("dom.serviceWorkers.update_delay", 1000);
762 if (!aWithMultiplier
) {
766 // This can potentially happen if you spam registration->Update(). We don't
767 // want to wrap to a lower value.
768 if (mDelayMultiplier
>= INT_MAX
/ (delay
? delay
: 1)) {
772 delay
*= mDelayMultiplier
;
774 if (!mControlledClientsCounter
&& mDelayMultiplier
< (INT_MAX
/ 30)) {
775 mDelayMultiplier
= (mDelayMultiplier
? mDelayMultiplier
: 1) * 30;
781 void ServiceWorkerRegistrationInfo::FireUpdateFound() {
782 for (RefPtr
<ServiceWorkerRegistrationListener
> pinnedTarget
:
783 mInstanceList
.ForwardRange()) {
784 pinnedTarget
->FireUpdateFound();
788 void ServiceWorkerRegistrationInfo::NotifyCleared() {
789 for (RefPtr
<ServiceWorkerRegistrationListener
> pinnedTarget
:
790 mInstanceList
.ForwardRange()) {
791 pinnedTarget
->RegistrationCleared();
795 void ServiceWorkerRegistrationInfo::ClearWhenIdle() {
796 MOZ_ASSERT(NS_IsMainThread());
797 MOZ_ASSERT(IsUnregistered());
798 MOZ_ASSERT(!IsControllingClients());
799 MOZ_ASSERT(!IsIdle(), "Already idle!");
801 RefPtr
<ServiceWorkerManager
> swm
= ServiceWorkerManager::GetInstance();
804 swm
->AddOrphanedRegistration(this);
807 * Although a Service Worker will transition to idle many times during its
808 * lifetime, the promise is only resolved once `GetIdlePromise` has been
809 * called, populating the `MozPromiseHolder`. Additionally, this is the only
810 * time this method will be called for the given ServiceWorker. This means we
811 * will be notified to the transition we are interested in, and there are no
812 * other callers to get confused.
814 * Note that because we are using `MozPromise`, our callback will be invoked
815 * as a separate task, so there is a small potential for races in the event
816 * code if things are still holding onto the ServiceWorker binding and using
817 * `postMessage()` or other mechanisms to schedule new events on it, which
818 * would make it non-idle. However, this is a race inherent in the spec which
819 * does not deal with the reality of multiple threads in "Try Clear
822 GetActive()->WorkerPrivate()->GetIdlePromise()->Then(
823 GetCurrentSerialEventTarget(), __func__
,
824 [self
= RefPtr
<ServiceWorkerRegistrationInfo
>(this)](
825 const GenericPromise::ResolveOrRejectValue
& aResult
) {
826 MOZ_ASSERT(aResult
.IsResolve());
827 // This registration was already unregistered and not controlling
828 // clients when `ClearWhenIdle` was called, so there should be no way
829 // that more clients were acquired.
830 MOZ_ASSERT(!self
->IsControllingClients());
831 MOZ_ASSERT(self
->IsIdle());
834 RefPtr
<ServiceWorkerManager
> swm
= ServiceWorkerManager::GetInstance();
836 swm
->RemoveOrphanedRegistration(self
);
841 const nsID
& ServiceWorkerRegistrationInfo::AgentClusterId() const {
842 return mAgentClusterId
;
845 void ServiceWorkerRegistrationInfo::SetNavigationPreloadEnabled(
846 const bool& aEnabled
) {
847 MOZ_ASSERT(NS_IsMainThread());
848 mNavigationPreloadState
.enabled() = aEnabled
;
851 void ServiceWorkerRegistrationInfo::SetNavigationPreloadHeader(
852 const nsCString
& aHeader
) {
853 MOZ_ASSERT(NS_IsMainThread());
854 mNavigationPreloadState
.headerValue() = aHeader
;
857 IPCNavigationPreloadState
858 ServiceWorkerRegistrationInfo::GetNavigationPreloadState() const {
859 MOZ_ASSERT(NS_IsMainThread());
860 return mNavigationPreloadState
;
864 uint64_t ServiceWorkerRegistrationInfo::GetNextId() {
865 MOZ_ASSERT(NS_IsMainThread());
866 static uint64_t sNextId
= 0;
871 uint64_t ServiceWorkerRegistrationInfo::GetNextVersion() {
872 MOZ_ASSERT(NS_IsMainThread());
873 static uint64_t sNextVersion
= 0;
874 return ++sNextVersion
;
877 void ServiceWorkerRegistrationInfo::ForEachWorker(
878 void (*aFunc
)(RefPtr
<ServiceWorkerInfo
>&)) {
879 if (mEvaluatingWorker
) {
880 aFunc(mEvaluatingWorker
);
883 if (mInstallingWorker
) {
884 aFunc(mInstallingWorker
);
887 if (mWaitingWorker
) {
888 aFunc(mWaitingWorker
);
892 aFunc(mActiveWorker
);
896 void ServiceWorkerRegistrationInfo::CheckQuotaUsage() {
897 MOZ_ASSERT(NS_IsMainThread());
899 RefPtr
<ServiceWorkerManager
> swm
= ServiceWorkerManager::GetInstance();
902 swm
->CheckPrincipalQuotaUsage(mPrincipal
, Scope());
905 } // namespace mozilla::dom