no bug - Import translations from android-l10n r=release a=l10n CLOSED TREE
[gecko.git] / dom / serviceworkers / ServiceWorkerRegistrationInfo.cpp
blobd4a99e977ebb9296c06b5883f273240eff19a2fe
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 {
19 namespace {
21 class ContinueActivateRunnable final : public LifeCycleEventCallback {
22 nsMainThreadPtrHandle<ServiceWorkerRegistrationInfo> mRegistration;
23 bool mSuccess;
25 public:
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; }
34 NS_IMETHOD
35 Run() override {
36 MOZ_ASSERT(NS_IsMainThread());
37 mRegistration->FinishActivate(mSuccess);
38 mRegistration = nullptr;
39 return NS_OK;
43 } // anonymous namespace
45 void ServiceWorkerRegistrationInfo::ShutdownWorkers() {
46 ForEachWorker([](RefPtr<ServiceWorkerInfo>& aWorker) {
47 aWorker->WorkerPrivate()->NoteDeadServiceWorkerInfo();
48 aWorker = nullptr;
49 });
52 void ServiceWorkerRegistrationInfo::Clear() {
53 ForEachWorker([](RefPtr<ServiceWorkerInfo>& aWorker) {
54 aWorker->UpdateState(ServiceWorkerState::Redundant);
55 aWorker->UpdateRedundantTime();
56 });
58 // FIXME: Abort any inflight requests from installing worker.
60 ShutdownWorkers();
61 UpdateRegistrationState();
62 NotifyChromeRegistrationListeners();
63 NotifyCleared();
66 void ServiceWorkerRegistrationInfo::ClearAsCorrupt() {
67 mCorrupt = true;
68 Clear();
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,
79 aUpdateViaCache),
80 mControlledClientsCounter(0),
81 mDelayMultiplier(0),
82 mUpdateState(NoUpdate),
83 mCreationTime(PR_Now()),
84 mCreationTimeStamp(TimeStamp::Now()),
85 mLastUpdateTime(0),
86 mUnregistered(false),
87 mCorrupt(false),
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()) {
109 continue;
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);
127 MOZ_ASSERT(removed);
130 const nsCString& ServiceWorkerRegistrationInfo::Scope() const {
131 return mDescriptor.Scope();
134 nsIPrincipal* ServiceWorkerRegistrationInfo::Principal() const {
135 return mPrincipal;
138 bool ServiceWorkerRegistrationInfo::IsUnregistered() const {
139 return mUnregistered;
142 void ServiceWorkerRegistrationInfo::SetUnregistered() {
143 #ifdef DEBUG
144 MOZ_ASSERT(!mUnregistered);
146 RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
147 MOZ_ASSERT(swm);
149 RefPtr<ServiceWorkerRegistrationInfo> registration =
150 swm->GetRegistration(Principal(), Scope());
151 MOZ_ASSERT(registration != this);
152 #endif
154 mUnregistered = true;
155 NotifyChromeRegistrationListeners();
158 NS_IMPL_ISUPPORTS(ServiceWorkerRegistrationInfo,
159 nsIServiceWorkerRegistrationInfo)
161 NS_IMETHODIMP
162 ServiceWorkerRegistrationInfo::GetPrincipal(nsIPrincipal** aPrincipal) {
163 MOZ_ASSERT(NS_IsMainThread());
164 NS_ADDREF(*aPrincipal = mPrincipal);
165 return NS_OK;
168 NS_IMETHODIMP ServiceWorkerRegistrationInfo::GetUnregistered(
169 bool* aUnregistered) {
170 MOZ_ASSERT(NS_IsMainThread());
171 MOZ_ASSERT(aUnregistered);
172 *aUnregistered = mUnregistered;
173 return NS_OK;
176 NS_IMETHODIMP
177 ServiceWorkerRegistrationInfo::GetScope(nsAString& aScope) {
178 MOZ_ASSERT(NS_IsMainThread());
179 CopyUTF8toUTF16(Scope(), aScope);
180 return NS_OK;
183 NS_IMETHODIMP
184 ServiceWorkerRegistrationInfo::GetScriptSpec(nsAString& aScriptSpec) {
185 MOZ_ASSERT(NS_IsMainThread());
186 RefPtr<ServiceWorkerInfo> newest = NewestIncludingEvaluating();
187 if (newest) {
188 CopyUTF8toUTF16(newest->ScriptSpec(), aScriptSpec);
190 return NS_OK;
193 NS_IMETHODIMP
194 ServiceWorkerRegistrationInfo::GetUpdateViaCache(uint16_t* aUpdateViaCache) {
195 *aUpdateViaCache = static_cast<uint16_t>(GetUpdateViaCache());
196 return NS_OK;
199 NS_IMETHODIMP
200 ServiceWorkerRegistrationInfo::GetLastUpdateTime(PRTime* _retval) {
201 MOZ_ASSERT(NS_IsMainThread());
202 MOZ_ASSERT(_retval);
203 *_retval = mLastUpdateTime;
204 return NS_OK;
207 NS_IMETHODIMP
208 ServiceWorkerRegistrationInfo::GetEvaluatingWorker(
209 nsIServiceWorkerInfo** aResult) {
210 MOZ_ASSERT(NS_IsMainThread());
211 RefPtr<ServiceWorkerInfo> info = mEvaluatingWorker;
212 info.forget(aResult);
213 return NS_OK;
216 NS_IMETHODIMP
217 ServiceWorkerRegistrationInfo::GetInstallingWorker(
218 nsIServiceWorkerInfo** aResult) {
219 MOZ_ASSERT(NS_IsMainThread());
220 RefPtr<ServiceWorkerInfo> info = mInstallingWorker;
221 info.forget(aResult);
222 return NS_OK;
225 NS_IMETHODIMP
226 ServiceWorkerRegistrationInfo::GetWaitingWorker(
227 nsIServiceWorkerInfo** aResult) {
228 MOZ_ASSERT(NS_IsMainThread());
229 RefPtr<ServiceWorkerInfo> info = mWaitingWorker;
230 info.forget(aResult);
231 return NS_OK;
234 NS_IMETHODIMP
235 ServiceWorkerRegistrationInfo::GetActiveWorker(nsIServiceWorkerInfo** aResult) {
236 MOZ_ASSERT(NS_IsMainThread());
237 RefPtr<ServiceWorkerInfo> info = mActiveWorker;
238 info.forget(aResult);
239 return NS_OK;
242 NS_IMETHODIMP
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();
252 MOZ_ASSERT(swm);
254 *aQuotaUsageCheckCount = swm->GetPrincipalQuotaUsageCheckCount(mPrincipal);
256 return NS_OK;
259 NS_IMETHODIMP
260 ServiceWorkerRegistrationInfo::GetWorkerByID(uint64_t aID,
261 nsIServiceWorkerInfo** aResult) {
262 MOZ_ASSERT(NS_IsMainThread());
263 MOZ_ASSERT(aResult);
265 RefPtr<ServiceWorkerInfo> info = GetServiceWorkerInfoById(aID);
266 // It is ok to return null for a missing service worker info.
267 info.forget(aResult);
268 return NS_OK;
271 NS_IMETHODIMP
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);
282 return NS_OK;
285 NS_IMETHODIMP
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);
296 return NS_OK;
299 NS_IMETHODIMP
300 ServiceWorkerRegistrationInfo::ForceShutdown() {
301 ClearInstalling();
302 ShutdownWorkers();
303 return NS_OK;
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)) {
343 Activate();
346 if (aCallback) {
347 aCallback();
351 void ServiceWorkerRegistrationInfo::Activate() {
352 if (!mWaitingWorker) {
353 return;
356 RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
357 if (!swm) {
358 // browser shutdown began during async activation step
359 return;
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()));
384 return;
388 void ServiceWorkerRegistrationInfo::FinishActivate(bool aSuccess) {
389 if (mUnregistered || !mActiveWorker ||
390 mActiveWorker->State() != ServiceWorkerState::Activating) {
391 return;
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();
402 if (!swm) {
403 // browser shutdown started during async activation completion step
404 return;
406 swm->StoreRegistration(mPrincipal, this);
409 void ServiceWorkerRegistrationInfo::RefreshLastUpdateCheckTime() {
410 MOZ_ASSERT(NS_IsMainThread());
412 mLastUpdateTime =
413 mCreationTime +
414 static_cast<PRTime>(
415 (TimeStamp::Now() - mCreationTimeStamp).ToMicroseconds());
416 NotifyChromeRegistrationListeners();
419 bool ServiceWorkerRegistrationInfo::IsLastUpdateCheckTimeOverOneDay() const {
420 MOZ_ASSERT(NS_IsMainThread());
422 // For testing.
423 if (Preferences::GetBool("dom.serviceWorkers.testUpdateOverOneDay")) {
424 return true;
427 const int64_t kSecondsPerDay = 86400;
428 const int64_t nowMicros =
429 mCreationTime +
430 static_cast<PRTime>(
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) {
437 return true;
439 return false;
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(
478 mListeners.Clone());
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();
488 if (!swm) {
489 // shutting down, do nothing
490 return;
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();
504 if (!swm) {
505 // shutting down, do nothing
506 return;
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) {
519 CheckQuotaUsage();
520 swm->Unregister(mPrincipal, nullptr, NS_ConvertUTF8toUTF16(Scope()));
521 return;
525 mUpdateState = NeedUpdate;
527 swm->ScheduleUpdateTimer(mPrincipal, Scope());
530 bool ServiceWorkerRegistrationInfo::CheckAndClearIfUpdateNeeded() {
531 MOZ_ASSERT(NS_IsMainThread());
533 bool result =
534 mUpdateState == NeedUpdate || (mUpdateState == NeedTimeCheckAndUpdate &&
535 IsLastUpdateCheckTimeOverOneDay());
537 mUpdateState = NoUpdate;
539 return result;
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;
578 return nullptr;
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) {
602 return;
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) {
618 return;
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
670 // overrides.
671 MOZ_ASSERT(mInstallingWorker != aServiceWorker);
672 MOZ_ASSERT(mWaitingWorker != aServiceWorker);
673 MOZ_ASSERT(mActiveWorker != aServiceWorker);
675 if (mActiveWorker) {
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
683 // Activated state.
684 mActiveWorker = aServiceWorker;
685 mActiveWorker->SetActivateStateUncheckedWithoutEvent(
686 ServiceWorkerState::Activated);
688 // We don't need to update activated time when we load registration from
689 // registrar.
690 UpdateRegistrationState();
691 NotifyChromeRegistrationListeners();
694 void ServiceWorkerRegistrationInfo::TransitionWaitingToActive() {
695 MOZ_ASSERT(NS_IsMainThread());
696 MOZ_ASSERT(mWaitingWorker);
698 if (mActiveWorker) {
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();
712 if (swm) {
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()
726 const {
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) {
740 if (aTime == 0) {
741 return;
744 mLastUpdateTime = aTime;
747 const ServiceWorkerRegistrationDescriptor&
748 ServiceWorkerRegistrationInfo::Descriptor() const {
749 return mDescriptor;
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) {
763 return delay;
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)) {
769 return INT_MAX;
772 delay *= mDelayMultiplier;
774 if (!mControlledClientsCounter && mDelayMultiplier < (INT_MAX / 30)) {
775 mDelayMultiplier = (mDelayMultiplier ? mDelayMultiplier : 1) * 30;
778 return delay;
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();
802 MOZ_ASSERT(swm);
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
820 * Registration".
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());
832 self->Clear();
834 RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
835 if (swm) {
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;
863 // static
864 uint64_t ServiceWorkerRegistrationInfo::GetNextId() {
865 MOZ_ASSERT(NS_IsMainThread());
866 static uint64_t sNextId = 0;
867 return ++sNextId;
870 // static
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);
891 if (mActiveWorker) {
892 aFunc(mActiveWorker);
896 void ServiceWorkerRegistrationInfo::CheckQuotaUsage() {
897 MOZ_ASSERT(NS_IsMainThread());
899 RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
900 MOZ_ASSERT(swm);
902 swm->CheckPrincipalQuotaUsage(mPrincipal, Scope());
905 } // namespace mozilla::dom