Source code

Revision control

Other Tools

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