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