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/. */
6 
7 #include "ServiceWorkerRegistrationInfo.h"
8 
9 #include "ServiceWorkerManager.h"
10 #include "ServiceWorkerPrivate.h"
11 #include "ServiceWorkerRegistrationListener.h"
12 
13 #include "mozilla/Preferences.h"
14 #include "mozilla/SchedulerGroup.h"
15 #include "mozilla/StaticPrefs_dom.h"
16 
17 namespace mozilla {
18 namespace dom {
19 
20 namespace {
21 
22 class ContinueActivateRunnable final : public LifeCycleEventCallback {
23   nsMainThreadPtrHandle<ServiceWorkerRegistrationInfo> mRegistration;
24   bool mSuccess;
25 
26  public:
ContinueActivateRunnable(const nsMainThreadPtrHandle<ServiceWorkerRegistrationInfo> & aRegistration)27   explicit ContinueActivateRunnable(
28       const nsMainThreadPtrHandle<ServiceWorkerRegistrationInfo>& aRegistration)
29       : mRegistration(aRegistration), mSuccess(false) {
30     MOZ_ASSERT(NS_IsMainThread());
31   }
32 
SetResult(bool aResult)33   void SetResult(bool aResult) override { mSuccess = aResult; }
34 
35   NS_IMETHOD
Run()36   Run() override {
37     MOZ_ASSERT(NS_IsMainThread());
38     mRegistration->FinishActivate(mSuccess);
39     mRegistration = nullptr;
40     return NS_OK;
41   }
42 };
43 
44 }  // anonymous namespace
45 
ShutdownWorkers()46 void ServiceWorkerRegistrationInfo::ShutdownWorkers() {
47   ForEachWorker([](RefPtr<ServiceWorkerInfo>& aWorker) {
48     aWorker->WorkerPrivate()->NoteDeadServiceWorkerInfo();
49     aWorker = nullptr;
50   });
51 }
52 
Clear()53 void ServiceWorkerRegistrationInfo::Clear() {
54   ForEachWorker([](RefPtr<ServiceWorkerInfo>& aWorker) {
55     aWorker->UpdateState(ServiceWorkerState::Redundant);
56     aWorker->UpdateRedundantTime();
57   });
58 
59   // FIXME: Abort any inflight requests from installing worker.
60 
61   ShutdownWorkers();
62   UpdateRegistrationState();
63   NotifyChromeRegistrationListeners();
64   NotifyCleared();
65 }
66 
ClearAsCorrupt()67 void ServiceWorkerRegistrationInfo::ClearAsCorrupt() {
68   mCorrupt = true;
69   Clear();
70 }
71 
IsCorrupt() const72 bool ServiceWorkerRegistrationInfo::IsCorrupt() const { return mCorrupt; }
73 
ServiceWorkerRegistrationInfo(const nsACString & aScope,nsIPrincipal * aPrincipal,ServiceWorkerUpdateViaCache aUpdateViaCache,IPCNavigationPreloadState && aNavigationPreloadState)74 ServiceWorkerRegistrationInfo::ServiceWorkerRegistrationInfo(
75     const nsACString& aScope, nsIPrincipal* aPrincipal,
76     ServiceWorkerUpdateViaCache aUpdateViaCache,
77     IPCNavigationPreloadState&& aNavigationPreloadState)
78     : mPrincipal(aPrincipal),
79       mDescriptor(GetNextId(), GetNextVersion(), aPrincipal, aScope,
80                   aUpdateViaCache),
81       mControlledClientsCounter(0),
82       mDelayMultiplier(0),
83       mUpdateState(NoUpdate),
84       mCreationTime(PR_Now()),
85       mCreationTimeStamp(TimeStamp::Now()),
86       mLastUpdateTime(0),
87       mUnregistered(false),
88       mCorrupt(false),
89       mNavigationPreloadState(std::move(aNavigationPreloadState)) {
90   MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
91 }
92 
~ServiceWorkerRegistrationInfo()93 ServiceWorkerRegistrationInfo::~ServiceWorkerRegistrationInfo() {
94   MOZ_DIAGNOSTIC_ASSERT(!IsControllingClients());
95 }
96 
AddInstance(ServiceWorkerRegistrationListener * aInstance,const ServiceWorkerRegistrationDescriptor & aDescriptor)97 void ServiceWorkerRegistrationInfo::AddInstance(
98     ServiceWorkerRegistrationListener* aInstance,
99     const ServiceWorkerRegistrationDescriptor& aDescriptor) {
100   MOZ_DIAGNOSTIC_ASSERT(aInstance);
101   MOZ_ASSERT(!mInstanceList.Contains(aInstance));
102   MOZ_DIAGNOSTIC_ASSERT(aDescriptor.Id() == mDescriptor.Id());
103   MOZ_DIAGNOSTIC_ASSERT(aDescriptor.PrincipalInfo() ==
104                         mDescriptor.PrincipalInfo());
105   MOZ_DIAGNOSTIC_ASSERT(aDescriptor.Scope() == mDescriptor.Scope());
106   MOZ_DIAGNOSTIC_ASSERT(aDescriptor.Version() <= mDescriptor.Version());
107   uint64_t lastVersion = aDescriptor.Version();
108   for (auto& entry : mVersionList) {
109     if (lastVersion > entry->mDescriptor.Version()) {
110       continue;
111     }
112     lastVersion = entry->mDescriptor.Version();
113     aInstance->UpdateState(entry->mDescriptor);
114   }
115   // Note, the mDescriptor may be contained in the version list.  Since the
116   // version list is aged out, though, it may also not be in the version list.
117   // So always check for the mDescriptor update here.
118   if (lastVersion < mDescriptor.Version()) {
119     aInstance->UpdateState(mDescriptor);
120   }
121   mInstanceList.AppendElement(aInstance);
122 }
123 
RemoveInstance(ServiceWorkerRegistrationListener * aInstance)124 void ServiceWorkerRegistrationInfo::RemoveInstance(
125     ServiceWorkerRegistrationListener* aInstance) {
126   MOZ_DIAGNOSTIC_ASSERT(aInstance);
127   DebugOnly<bool> removed = mInstanceList.RemoveElement(aInstance);
128   MOZ_ASSERT(removed);
129 }
130 
Scope() const131 const nsCString& ServiceWorkerRegistrationInfo::Scope() const {
132   return mDescriptor.Scope();
133 }
134 
Principal() const135 nsIPrincipal* ServiceWorkerRegistrationInfo::Principal() const {
136   return mPrincipal;
137 }
138 
IsUnregistered() const139 bool ServiceWorkerRegistrationInfo::IsUnregistered() const {
140   return mUnregistered;
141 }
142 
SetUnregistered()143 void ServiceWorkerRegistrationInfo::SetUnregistered() {
144 #ifdef DEBUG
145   MOZ_ASSERT(!mUnregistered);
146 
147   RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
148   MOZ_ASSERT(swm);
149 
150   RefPtr<ServiceWorkerRegistrationInfo> registration =
151       swm->GetRegistration(Principal(), Scope());
152   MOZ_ASSERT(registration != this);
153 #endif
154 
155   mUnregistered = true;
156   NotifyChromeRegistrationListeners();
157 }
158 
NS_IMPL_ISUPPORTS(ServiceWorkerRegistrationInfo,nsIServiceWorkerRegistrationInfo)159 NS_IMPL_ISUPPORTS(ServiceWorkerRegistrationInfo,
160                   nsIServiceWorkerRegistrationInfo)
161 
162 NS_IMETHODIMP
163 ServiceWorkerRegistrationInfo::GetPrincipal(nsIPrincipal** aPrincipal) {
164   MOZ_ASSERT(NS_IsMainThread());
165   NS_ADDREF(*aPrincipal = mPrincipal);
166   return NS_OK;
167 }
168 
GetUnregistered(bool * aUnregistered)169 NS_IMETHODIMP ServiceWorkerRegistrationInfo::GetUnregistered(
170     bool* aUnregistered) {
171   MOZ_ASSERT(NS_IsMainThread());
172   MOZ_ASSERT(aUnregistered);
173   *aUnregistered = mUnregistered;
174   return NS_OK;
175 }
176 
177 NS_IMETHODIMP
GetScope(nsAString & aScope)178 ServiceWorkerRegistrationInfo::GetScope(nsAString& aScope) {
179   MOZ_ASSERT(NS_IsMainThread());
180   CopyUTF8toUTF16(Scope(), aScope);
181   return NS_OK;
182 }
183 
184 NS_IMETHODIMP
GetScriptSpec(nsAString & aScriptSpec)185 ServiceWorkerRegistrationInfo::GetScriptSpec(nsAString& aScriptSpec) {
186   MOZ_ASSERT(NS_IsMainThread());
187   RefPtr<ServiceWorkerInfo> newest = NewestIncludingEvaluating();
188   if (newest) {
189     CopyUTF8toUTF16(newest->ScriptSpec(), aScriptSpec);
190   }
191   return NS_OK;
192 }
193 
194 NS_IMETHODIMP
GetUpdateViaCache(uint16_t * aUpdateViaCache)195 ServiceWorkerRegistrationInfo::GetUpdateViaCache(uint16_t* aUpdateViaCache) {
196   *aUpdateViaCache = static_cast<uint16_t>(GetUpdateViaCache());
197   return NS_OK;
198 }
199 
200 NS_IMETHODIMP
GetLastUpdateTime(PRTime * _retval)201 ServiceWorkerRegistrationInfo::GetLastUpdateTime(PRTime* _retval) {
202   MOZ_ASSERT(NS_IsMainThread());
203   MOZ_ASSERT(_retval);
204   *_retval = mLastUpdateTime;
205   return NS_OK;
206 }
207 
208 NS_IMETHODIMP
GetEvaluatingWorker(nsIServiceWorkerInfo ** aResult)209 ServiceWorkerRegistrationInfo::GetEvaluatingWorker(
210     nsIServiceWorkerInfo** aResult) {
211   MOZ_ASSERT(NS_IsMainThread());
212   RefPtr<ServiceWorkerInfo> info = mEvaluatingWorker;
213   info.forget(aResult);
214   return NS_OK;
215 }
216 
217 NS_IMETHODIMP
GetInstallingWorker(nsIServiceWorkerInfo ** aResult)218 ServiceWorkerRegistrationInfo::GetInstallingWorker(
219     nsIServiceWorkerInfo** aResult) {
220   MOZ_ASSERT(NS_IsMainThread());
221   RefPtr<ServiceWorkerInfo> info = mInstallingWorker;
222   info.forget(aResult);
223   return NS_OK;
224 }
225 
226 NS_IMETHODIMP
GetWaitingWorker(nsIServiceWorkerInfo ** aResult)227 ServiceWorkerRegistrationInfo::GetWaitingWorker(
228     nsIServiceWorkerInfo** aResult) {
229   MOZ_ASSERT(NS_IsMainThread());
230   RefPtr<ServiceWorkerInfo> info = mWaitingWorker;
231   info.forget(aResult);
232   return NS_OK;
233 }
234 
235 NS_IMETHODIMP
GetActiveWorker(nsIServiceWorkerInfo ** aResult)236 ServiceWorkerRegistrationInfo::GetActiveWorker(nsIServiceWorkerInfo** aResult) {
237   MOZ_ASSERT(NS_IsMainThread());
238   RefPtr<ServiceWorkerInfo> info = mActiveWorker;
239   info.forget(aResult);
240   return NS_OK;
241 }
242 
243 NS_IMETHODIMP
GetQuotaUsageCheckCount(int32_t * aQuotaUsageCheckCount)244 ServiceWorkerRegistrationInfo::GetQuotaUsageCheckCount(
245     int32_t* aQuotaUsageCheckCount) {
246   MOZ_ASSERT(NS_IsMainThread());
247   MOZ_ASSERT(aQuotaUsageCheckCount);
248 
249   // This value is actually stored on SWM's internal-only
250   // RegistrationDataPerPrincipal structure, but we expose it here for
251   // simplicity for our consumers, so we have to ask SWM to look it up for us.
252   RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
253   MOZ_ASSERT(swm);
254 
255   *aQuotaUsageCheckCount = swm->GetPrincipalQuotaUsageCheckCount(mPrincipal);
256 
257   return NS_OK;
258 }
259 
260 NS_IMETHODIMP
GetWorkerByID(uint64_t aID,nsIServiceWorkerInfo ** aResult)261 ServiceWorkerRegistrationInfo::GetWorkerByID(uint64_t aID,
262                                              nsIServiceWorkerInfo** aResult) {
263   MOZ_ASSERT(NS_IsMainThread());
264   MOZ_ASSERT(aResult);
265 
266   RefPtr<ServiceWorkerInfo> info = GetServiceWorkerInfoById(aID);
267   // It is ok to return null for a missing service worker info.
268   info.forget(aResult);
269   return NS_OK;
270 }
271 
272 NS_IMETHODIMP
AddListener(nsIServiceWorkerRegistrationInfoListener * aListener)273 ServiceWorkerRegistrationInfo::AddListener(
274     nsIServiceWorkerRegistrationInfoListener* aListener) {
275   MOZ_ASSERT(NS_IsMainThread());
276 
277   if (!aListener || mListeners.Contains(aListener)) {
278     return NS_ERROR_INVALID_ARG;
279   }
280 
281   mListeners.AppendElement(aListener);
282 
283   return NS_OK;
284 }
285 
286 NS_IMETHODIMP
RemoveListener(nsIServiceWorkerRegistrationInfoListener * aListener)287 ServiceWorkerRegistrationInfo::RemoveListener(
288     nsIServiceWorkerRegistrationInfoListener* aListener) {
289   MOZ_ASSERT(NS_IsMainThread());
290 
291   if (!aListener || !mListeners.Contains(aListener)) {
292     return NS_ERROR_INVALID_ARG;
293   }
294 
295   mListeners.RemoveElement(aListener);
296 
297   return NS_OK;
298 }
299 
300 NS_IMETHODIMP
ForceShutdown()301 ServiceWorkerRegistrationInfo::ForceShutdown() {
302   ClearInstalling();
303   ShutdownWorkers();
304   return NS_OK;
305 }
306 
307 already_AddRefed<ServiceWorkerInfo>
GetServiceWorkerInfoById(uint64_t aId)308 ServiceWorkerRegistrationInfo::GetServiceWorkerInfoById(uint64_t aId) {
309   MOZ_ASSERT(NS_IsMainThread());
310 
311   RefPtr<ServiceWorkerInfo> serviceWorker;
312   if (mEvaluatingWorker && mEvaluatingWorker->ID() == aId) {
313     serviceWorker = mEvaluatingWorker;
314   } else if (mInstallingWorker && mInstallingWorker->ID() == aId) {
315     serviceWorker = mInstallingWorker;
316   } else if (mWaitingWorker && mWaitingWorker->ID() == aId) {
317     serviceWorker = mWaitingWorker;
318   } else if (mActiveWorker && mActiveWorker->ID() == aId) {
319     serviceWorker = mActiveWorker;
320   }
321 
322   return serviceWorker.forget();
323 }
324 
TryToActivateAsync(TryToActivateCallback && aCallback)325 void ServiceWorkerRegistrationInfo::TryToActivateAsync(
326     TryToActivateCallback&& aCallback) {
327   MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(
328       NewRunnableMethod<StoreCopyPassByRRef<TryToActivateCallback>>(
329           "ServiceWorkerRegistrationInfo::TryToActivate", this,
330           &ServiceWorkerRegistrationInfo::TryToActivate,
331           std::move(aCallback))));
332 }
333 
334 /*
335  * TryToActivate should not be called directly, use TryToActivateAsync instead.
336  */
TryToActivate(TryToActivateCallback && aCallback)337 void ServiceWorkerRegistrationInfo::TryToActivate(
338     TryToActivateCallback&& aCallback) {
339   MOZ_ASSERT(NS_IsMainThread());
340   bool controlling = IsControllingClients();
341   bool skipWaiting = mWaitingWorker && mWaitingWorker->SkipWaitingFlag();
342   bool idle = IsIdle();
343   if (idle && (!controlling || skipWaiting)) {
344     Activate();
345   }
346 
347   if (aCallback) {
348     aCallback();
349   }
350 }
351 
Activate()352 void ServiceWorkerRegistrationInfo::Activate() {
353   if (!mWaitingWorker) {
354     return;
355   }
356 
357   RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
358   if (!swm) {
359     // browser shutdown began during async activation step
360     return;
361   }
362 
363   TransitionWaitingToActive();
364 
365   // FIXME(nsm): Unlink appcache if there is one.
366 
367   // "Queue a task to fire a simple event named controllerchange..."
368   MOZ_DIAGNOSTIC_ASSERT(mActiveWorker);
369   swm->UpdateClientControllers(this);
370 
371   nsMainThreadPtrHandle<ServiceWorkerRegistrationInfo> handle(
372       new nsMainThreadPtrHolder<ServiceWorkerRegistrationInfo>(
373           "ServiceWorkerRegistrationInfoProxy", this));
374   RefPtr<LifeCycleEventCallback> callback =
375       new ContinueActivateRunnable(handle);
376 
377   ServiceWorkerPrivate* workerPrivate = mActiveWorker->WorkerPrivate();
378   MOZ_ASSERT(workerPrivate);
379   nsresult rv = workerPrivate->SendLifeCycleEvent(u"activate"_ns, callback);
380   if (NS_WARN_IF(NS_FAILED(rv))) {
381     nsCOMPtr<nsIRunnable> failRunnable = NewRunnableMethod<bool>(
382         "dom::ServiceWorkerRegistrationInfo::FinishActivate", this,
383         &ServiceWorkerRegistrationInfo::FinishActivate, false /* success */);
384     MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(failRunnable.forget()));
385     return;
386   }
387 }
388 
FinishActivate(bool aSuccess)389 void ServiceWorkerRegistrationInfo::FinishActivate(bool aSuccess) {
390   if (mUnregistered || !mActiveWorker ||
391       mActiveWorker->State() != ServiceWorkerState::Activating) {
392     return;
393   }
394 
395   // Activation never fails, so aSuccess is ignored.
396   mActiveWorker->UpdateState(ServiceWorkerState::Activated);
397   mActiveWorker->UpdateActivatedTime();
398 
399   UpdateRegistrationState();
400   NotifyChromeRegistrationListeners();
401 
402   RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
403   if (!swm) {
404     // browser shutdown started during async activation completion step
405     return;
406   }
407   swm->StoreRegistration(mPrincipal, this);
408 }
409 
RefreshLastUpdateCheckTime()410 void ServiceWorkerRegistrationInfo::RefreshLastUpdateCheckTime() {
411   MOZ_ASSERT(NS_IsMainThread());
412 
413   mLastUpdateTime =
414       mCreationTime +
415       static_cast<PRTime>(
416           (TimeStamp::Now() - mCreationTimeStamp).ToMicroseconds());
417   NotifyChromeRegistrationListeners();
418 }
419 
IsLastUpdateCheckTimeOverOneDay() const420 bool ServiceWorkerRegistrationInfo::IsLastUpdateCheckTimeOverOneDay() const {
421   MOZ_ASSERT(NS_IsMainThread());
422 
423   // For testing.
424   if (Preferences::GetBool("dom.serviceWorkers.testUpdateOverOneDay")) {
425     return true;
426   }
427 
428   const int64_t kSecondsPerDay = 86400;
429   const int64_t nowMicros =
430       mCreationTime +
431       static_cast<PRTime>(
432           (TimeStamp::Now() - mCreationTimeStamp).ToMicroseconds());
433 
434   // now < mLastUpdateTime if the system time is reset between storing
435   // and loading mLastUpdateTime from ServiceWorkerRegistrar.
436   if (nowMicros < mLastUpdateTime ||
437       (nowMicros - mLastUpdateTime) / PR_USEC_PER_SEC > kSecondsPerDay) {
438     return true;
439   }
440   return false;
441 }
442 
UpdateRegistrationState()443 void ServiceWorkerRegistrationInfo::UpdateRegistrationState() {
444   UpdateRegistrationState(mDescriptor.UpdateViaCache());
445 }
446 
UpdateRegistrationState(ServiceWorkerUpdateViaCache aUpdateViaCache)447 void ServiceWorkerRegistrationInfo::UpdateRegistrationState(
448     ServiceWorkerUpdateViaCache aUpdateViaCache) {
449   MOZ_ASSERT(NS_IsMainThread());
450 
451   TimeStamp oldest = TimeStamp::Now() - TimeDuration::FromSeconds(30);
452   if (!mVersionList.IsEmpty() && mVersionList[0]->mTimeStamp < oldest) {
453     nsTArray<UniquePtr<VersionEntry>> list = std::move(mVersionList);
454     for (auto& entry : list) {
455       if (entry->mTimeStamp >= oldest) {
456         mVersionList.AppendElement(std::move(entry));
457       }
458     }
459   }
460   mVersionList.AppendElement(MakeUnique<VersionEntry>(mDescriptor));
461 
462   // We are going to modify the descriptor, so increase its version number.
463   mDescriptor.SetVersion(GetNextVersion());
464 
465   // Note, this also sets the new version number on the ServiceWorkerInfo
466   // objects before we copy over their updated descriptors.
467   mDescriptor.SetWorkers(mInstallingWorker, mWaitingWorker, mActiveWorker);
468 
469   mDescriptor.SetUpdateViaCache(aUpdateViaCache);
470 
471   for (RefPtr<ServiceWorkerRegistrationListener> pinnedTarget :
472        mInstanceList.ForwardRange()) {
473     pinnedTarget->UpdateState(mDescriptor);
474   }
475 }
476 
NotifyChromeRegistrationListeners()477 void ServiceWorkerRegistrationInfo::NotifyChromeRegistrationListeners() {
478   nsTArray<nsCOMPtr<nsIServiceWorkerRegistrationInfoListener>> listeners(
479       mListeners.Clone());
480   for (size_t index = 0; index < listeners.Length(); ++index) {
481     listeners[index]->OnChange();
482   }
483 }
484 
MaybeScheduleTimeCheckAndUpdate()485 void ServiceWorkerRegistrationInfo::MaybeScheduleTimeCheckAndUpdate() {
486   MOZ_ASSERT(NS_IsMainThread());
487 
488   RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
489   if (!swm) {
490     // shutting down, do nothing
491     return;
492   }
493 
494   if (mUpdateState == NoUpdate) {
495     mUpdateState = NeedTimeCheckAndUpdate;
496   }
497 
498   swm->ScheduleUpdateTimer(mPrincipal, Scope());
499 }
500 
MaybeScheduleUpdate()501 void ServiceWorkerRegistrationInfo::MaybeScheduleUpdate() {
502   MOZ_ASSERT(NS_IsMainThread());
503 
504   RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
505   if (!swm) {
506     // shutting down, do nothing
507     return;
508   }
509 
510   // When reach the navigation fault threshold, calling unregister instead of
511   // scheduling update.
512   if (mActiveWorker) {
513     uint32_t navigationFaultCount;
514     mActiveWorker->GetNavigationFaultCount(&navigationFaultCount);
515     const auto navigationFaultThreshold = StaticPrefs::
516         dom_serviceWorkers_mitigations_navigation_fault_threshold();
517     // Disable unregister mitigation when navigation fault threshold is 0.
518     if (navigationFaultThreshold <= navigationFaultCount &&
519         navigationFaultThreshold != 0) {
520       swm->Unregister(mPrincipal, nullptr, NS_ConvertUTF8toUTF16(Scope()));
521       return;
522     }
523   }
524 
525   mUpdateState = NeedUpdate;
526 
527   swm->ScheduleUpdateTimer(mPrincipal, Scope());
528 }
529 
CheckAndClearIfUpdateNeeded()530 bool ServiceWorkerRegistrationInfo::CheckAndClearIfUpdateNeeded() {
531   MOZ_ASSERT(NS_IsMainThread());
532 
533   bool result =
534       mUpdateState == NeedUpdate || (mUpdateState == NeedTimeCheckAndUpdate &&
535                                      IsLastUpdateCheckTimeOverOneDay());
536 
537   mUpdateState = NoUpdate;
538 
539   return result;
540 }
541 
GetEvaluating() const542 ServiceWorkerInfo* ServiceWorkerRegistrationInfo::GetEvaluating() const {
543   MOZ_ASSERT(NS_IsMainThread());
544   return mEvaluatingWorker;
545 }
546 
GetInstalling() const547 ServiceWorkerInfo* ServiceWorkerRegistrationInfo::GetInstalling() const {
548   MOZ_ASSERT(NS_IsMainThread());
549   return mInstallingWorker;
550 }
551 
GetWaiting() const552 ServiceWorkerInfo* ServiceWorkerRegistrationInfo::GetWaiting() const {
553   MOZ_ASSERT(NS_IsMainThread());
554   return mWaitingWorker;
555 }
556 
GetActive() const557 ServiceWorkerInfo* ServiceWorkerRegistrationInfo::GetActive() const {
558   MOZ_ASSERT(NS_IsMainThread());
559   return mActiveWorker;
560 }
561 
GetByDescriptor(const ServiceWorkerDescriptor & aDescriptor) const562 ServiceWorkerInfo* ServiceWorkerRegistrationInfo::GetByDescriptor(
563     const ServiceWorkerDescriptor& aDescriptor) const {
564   if (mActiveWorker && mActiveWorker->Descriptor().Matches(aDescriptor)) {
565     return mActiveWorker;
566   }
567   if (mWaitingWorker && mWaitingWorker->Descriptor().Matches(aDescriptor)) {
568     return mWaitingWorker;
569   }
570   if (mInstallingWorker &&
571       mInstallingWorker->Descriptor().Matches(aDescriptor)) {
572     return mInstallingWorker;
573   }
574   if (mEvaluatingWorker &&
575       mEvaluatingWorker->Descriptor().Matches(aDescriptor)) {
576     return mEvaluatingWorker;
577   }
578   return nullptr;
579 }
580 
SetEvaluating(ServiceWorkerInfo * aServiceWorker)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);
589 
590   mEvaluatingWorker = aServiceWorker;
591 
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();
596 }
597 
ClearEvaluating()598 void ServiceWorkerRegistrationInfo::ClearEvaluating() {
599   MOZ_ASSERT(NS_IsMainThread());
600 
601   if (!mEvaluatingWorker) {
602     return;
603   }
604 
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;
609 
610   // As for SetEvaluating, UpdateRegistrationState() does not need to be called.
611   NotifyChromeRegistrationListeners();
612 }
613 
ClearInstalling()614 void ServiceWorkerRegistrationInfo::ClearInstalling() {
615   MOZ_ASSERT(NS_IsMainThread());
616 
617   if (!mInstallingWorker) {
618     return;
619   }
620 
621   RefPtr<ServiceWorkerInfo> installing = std::move(mInstallingWorker);
622   installing->UpdateState(ServiceWorkerState::Redundant);
623   installing->UpdateRedundantTime();
624 
625   UpdateRegistrationState();
626   NotifyChromeRegistrationListeners();
627 }
628 
TransitionEvaluatingToInstalling()629 void ServiceWorkerRegistrationInfo::TransitionEvaluatingToInstalling() {
630   MOZ_ASSERT(NS_IsMainThread());
631   MOZ_ASSERT(mEvaluatingWorker);
632   MOZ_ASSERT(!mInstallingWorker);
633 
634   mInstallingWorker = std::move(mEvaluatingWorker);
635   mInstallingWorker->UpdateState(ServiceWorkerState::Installing);
636 
637   UpdateRegistrationState();
638   NotifyChromeRegistrationListeners();
639 }
640 
TransitionInstallingToWaiting()641 void ServiceWorkerRegistrationInfo::TransitionInstallingToWaiting() {
642   MOZ_ASSERT(NS_IsMainThread());
643   MOZ_ASSERT(mInstallingWorker);
644 
645   if (mWaitingWorker) {
646     MOZ_ASSERT(mInstallingWorker->CacheName() != mWaitingWorker->CacheName());
647     mWaitingWorker->UpdateState(ServiceWorkerState::Redundant);
648     mWaitingWorker->UpdateRedundantTime();
649   }
650 
651   mWaitingWorker = std::move(mInstallingWorker);
652   mWaitingWorker->UpdateState(ServiceWorkerState::Installed);
653   mWaitingWorker->UpdateInstalledTime();
654 
655   UpdateRegistrationState();
656   NotifyChromeRegistrationListeners();
657 
658   // TODO: When bug 1426401 is implemented we will need to call
659   //       StoreRegistration() here to persist the waiting worker.
660 }
661 
SetActive(ServiceWorkerInfo * aServiceWorker)662 void ServiceWorkerRegistrationInfo::SetActive(
663     ServiceWorkerInfo* aServiceWorker) {
664   MOZ_ASSERT(NS_IsMainThread());
665   MOZ_ASSERT(aServiceWorker);
666 
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);
674 
675   if (mActiveWorker) {
676     MOZ_ASSERT(aServiceWorker->CacheName() != mActiveWorker->CacheName());
677     mActiveWorker->UpdateState(ServiceWorkerState::Redundant);
678     mActiveWorker->UpdateRedundantTime();
679   }
680 
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);
687 
688   // We don't need to update activated time when we load registration from
689   // registrar.
690   UpdateRegistrationState();
691   NotifyChromeRegistrationListeners();
692 }
693 
TransitionWaitingToActive()694 void ServiceWorkerRegistrationInfo::TransitionWaitingToActive() {
695   MOZ_ASSERT(NS_IsMainThread());
696   MOZ_ASSERT(mWaitingWorker);
697 
698   if (mActiveWorker) {
699     MOZ_ASSERT(mWaitingWorker->CacheName() != mActiveWorker->CacheName());
700     mActiveWorker->UpdateState(ServiceWorkerState::Redundant);
701     mActiveWorker->UpdateRedundantTime();
702   }
703 
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);
708 
709   nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(
710       "ServiceWorkerRegistrationInfo::TransitionWaitingToActive", [] {
711         RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
712         if (swm) {
713           swm->CheckPendingReadyPromises();
714         }
715       });
716   MOZ_ALWAYS_SUCCEEDS(
717       SchedulerGroup::Dispatch(TaskCategory::Other, r.forget()));
718 
719   UpdateRegistrationState();
720   NotifyChromeRegistrationListeners();
721 }
722 
IsIdle() const723 bool ServiceWorkerRegistrationInfo::IsIdle() const {
724   return !mActiveWorker || mActiveWorker->WorkerPrivate()->IsIdle();
725 }
726 
GetUpdateViaCache() const727 ServiceWorkerUpdateViaCache ServiceWorkerRegistrationInfo::GetUpdateViaCache()
728     const {
729   return mDescriptor.UpdateViaCache();
730 }
731 
SetUpdateViaCache(ServiceWorkerUpdateViaCache aUpdateViaCache)732 void ServiceWorkerRegistrationInfo::SetUpdateViaCache(
733     ServiceWorkerUpdateViaCache aUpdateViaCache) {
734   UpdateRegistrationState(aUpdateViaCache);
735 }
736 
GetLastUpdateTime() const737 int64_t ServiceWorkerRegistrationInfo::GetLastUpdateTime() const {
738   return mLastUpdateTime;
739 }
740 
SetLastUpdateTime(const int64_t aTime)741 void ServiceWorkerRegistrationInfo::SetLastUpdateTime(const int64_t aTime) {
742   if (aTime == 0) {
743     return;
744   }
745 
746   mLastUpdateTime = aTime;
747 }
748 
749 const ServiceWorkerRegistrationDescriptor&
Descriptor() const750 ServiceWorkerRegistrationInfo::Descriptor() const {
751   return mDescriptor;
752 }
753 
Id() const754 uint64_t ServiceWorkerRegistrationInfo::Id() const { return mDescriptor.Id(); }
755 
Version() const756 uint64_t ServiceWorkerRegistrationInfo::Version() const {
757   return mDescriptor.Version();
758 }
759 
GetUpdateDelay(const bool aWithMultiplier)760 uint32_t ServiceWorkerRegistrationInfo::GetUpdateDelay(
761     const bool aWithMultiplier) {
762   uint32_t delay = Preferences::GetInt("dom.serviceWorkers.update_delay", 1000);
763 
764   if (!aWithMultiplier) {
765     return delay;
766   }
767 
768   // This can potentially happen if you spam registration->Update(). We don't
769   // want to wrap to a lower value.
770   if (mDelayMultiplier >= INT_MAX / (delay ? delay : 1)) {
771     return INT_MAX;
772   }
773 
774   delay *= mDelayMultiplier;
775 
776   if (!mControlledClientsCounter && mDelayMultiplier < (INT_MAX / 30)) {
777     mDelayMultiplier = (mDelayMultiplier ? mDelayMultiplier : 1) * 30;
778   }
779 
780   return delay;
781 }
782 
FireUpdateFound()783 void ServiceWorkerRegistrationInfo::FireUpdateFound() {
784   for (RefPtr<ServiceWorkerRegistrationListener> pinnedTarget :
785        mInstanceList.ForwardRange()) {
786     pinnedTarget->FireUpdateFound();
787   }
788 }
789 
NotifyCleared()790 void ServiceWorkerRegistrationInfo::NotifyCleared() {
791   for (RefPtr<ServiceWorkerRegistrationListener> pinnedTarget :
792        mInstanceList.ForwardRange()) {
793     pinnedTarget->RegistrationCleared();
794   }
795 }
796 
ClearWhenIdle()797 void ServiceWorkerRegistrationInfo::ClearWhenIdle() {
798   MOZ_ASSERT(NS_IsMainThread());
799   MOZ_ASSERT(IsUnregistered());
800   MOZ_ASSERT(!IsControllingClients());
801   MOZ_ASSERT(!IsIdle(), "Already idle!");
802 
803   RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
804   MOZ_ASSERT(swm);
805 
806   swm->AddOrphanedRegistration(this);
807 
808   /**
809    * Although a Service Worker will transition to idle many times during its
810    * lifetime, the promise is only resolved once `GetIdlePromise` has been
811    * called, populating the `MozPromiseHolder`. Additionally, this is the only
812    * time this method will be called for the given ServiceWorker. This means we
813    * will be notified to the transition we are interested in, and there are no
814    * other callers to get confused.
815    *
816    * Note that because we are using `MozPromise`, our callback will be invoked
817    * as a separate task, so there is a small potential for races in the event
818    * code if things are still holding onto the ServiceWorker binding and using
819    * `postMessage()` or other mechanisms to schedule new events on it, which
820    * would make it non-idle. However, this is a race inherent in the spec which
821    * does not deal with the reality of multiple threads in "Try Clear
822    * Registration".
823    */
824   GetActive()->WorkerPrivate()->GetIdlePromise()->Then(
825       GetCurrentSerialEventTarget(), __func__,
826       [self = RefPtr<ServiceWorkerRegistrationInfo>(this)](
827           const GenericPromise::ResolveOrRejectValue& aResult) {
828         MOZ_ASSERT(aResult.IsResolve());
829         // This registration was already unregistered and not controlling
830         // clients when `ClearWhenIdle` was called, so there should be no way
831         // that more clients were acquired.
832         MOZ_ASSERT(!self->IsControllingClients());
833         MOZ_ASSERT(self->IsIdle());
834         self->Clear();
835 
836         RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
837         if (swm) {
838           swm->RemoveOrphanedRegistration(self);
839         }
840       });
841 }
842 
AgentClusterId() const843 const nsID& ServiceWorkerRegistrationInfo::AgentClusterId() const {
844   return mAgentClusterId;
845 }
846 
SetNavigationPreloadEnabled(const bool & aEnabled)847 void ServiceWorkerRegistrationInfo::SetNavigationPreloadEnabled(
848     const bool& aEnabled) {
849   MOZ_ASSERT(NS_IsMainThread());
850   mNavigationPreloadState.enabled() = aEnabled;
851 }
852 
SetNavigationPreloadHeader(const nsCString & aHeader)853 void ServiceWorkerRegistrationInfo::SetNavigationPreloadHeader(
854     const nsCString& aHeader) {
855   MOZ_ASSERT(NS_IsMainThread());
856   mNavigationPreloadState.headerValue() = aHeader;
857 }
858 
859 IPCNavigationPreloadState
GetNavigationPreloadState() const860 ServiceWorkerRegistrationInfo::GetNavigationPreloadState() const {
861   MOZ_ASSERT(NS_IsMainThread());
862   return mNavigationPreloadState;
863 }
864 
865 // static
GetNextId()866 uint64_t ServiceWorkerRegistrationInfo::GetNextId() {
867   MOZ_ASSERT(NS_IsMainThread());
868   static uint64_t sNextId = 0;
869   return ++sNextId;
870 }
871 
872 // static
GetNextVersion()873 uint64_t ServiceWorkerRegistrationInfo::GetNextVersion() {
874   MOZ_ASSERT(NS_IsMainThread());
875   static uint64_t sNextVersion = 0;
876   return ++sNextVersion;
877 }
878 
ForEachWorker(void (* aFunc)(RefPtr<ServiceWorkerInfo> &))879 void ServiceWorkerRegistrationInfo::ForEachWorker(
880     void (*aFunc)(RefPtr<ServiceWorkerInfo>&)) {
881   if (mEvaluatingWorker) {
882     aFunc(mEvaluatingWorker);
883   }
884 
885   if (mInstallingWorker) {
886     aFunc(mInstallingWorker);
887   }
888 
889   if (mWaitingWorker) {
890     aFunc(mWaitingWorker);
891   }
892 
893   if (mActiveWorker) {
894     aFunc(mActiveWorker);
895   }
896 }
897 
898 }  // namespace dom
899 }  // namespace mozilla
900