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 "ServiceWorkerRegistrationImpl.h"
8
9
#include "ipc/ErrorIPCUtils.h"
10
#include "mozilla/dom/Promise.h"
11
#include "mozilla/dom/PromiseWorkerProxy.h"
12
#include "mozilla/dom/PushManagerBinding.h"
13
#include "mozilla/dom/PushManager.h"
14
#include "mozilla/dom/ServiceWorkerRegistrationBinding.h"
15
#include "mozilla/dom/WorkerCommon.h"
16
#include "mozilla/dom/WorkerPrivate.h"
17
#include "mozilla/dom/WorkerRef.h"
18
#include "mozilla/dom/WorkerScope.h"
19
#include "mozilla/Services.h"
20
#include "mozilla/Unused.h"
21
#include "nsCycleCollectionParticipant.h"
22
#include "nsNetUtil.h"
23
#include "nsServiceManagerUtils.h"
24
#include "ServiceWorker.h"
25
#include "ServiceWorkerManager.h"
26
#include "ServiceWorkerPrivate.h"
27
#include "ServiceWorkerRegistration.h"
28
#include "ServiceWorkerUnregisterCallback.h"
29
30
#include "mozilla/dom/Document.h"
31
#include "nsIServiceWorkerManager.h"
32
#include "nsPIDOMWindow.h"
33
#include "nsContentUtils.h"
34
35
namespace mozilla {
36
namespace dom {
37
38
////////////////////////////////////////////////////
39
// Main Thread implementation
40
41
ServiceWorkerRegistrationMainThread::ServiceWorkerRegistrationMainThread(
42
const ServiceWorkerRegistrationDescriptor& aDescriptor)
43
: mOuter(nullptr),
44
mDescriptor(aDescriptor),
45
mScope(NS_ConvertUTF8toUTF16(aDescriptor.Scope())),
46
mListeningForEvents(false) {
47
MOZ_ASSERT(NS_IsMainThread());
48
}
49
50
ServiceWorkerRegistrationMainThread::~ServiceWorkerRegistrationMainThread() {
51
MOZ_DIAGNOSTIC_ASSERT(!mListeningForEvents);
52
MOZ_DIAGNOSTIC_ASSERT(!mOuter);
53
}
54
55
// XXXnsm, maybe this can be optimized to only add when a event handler is
56
// registered.
57
void ServiceWorkerRegistrationMainThread::StartListeningForEvents() {
58
MOZ_ASSERT(NS_IsMainThread());
59
MOZ_ASSERT(!mListeningForEvents);
60
MOZ_DIAGNOSTIC_ASSERT(!mInfo);
61
62
RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
63
NS_ENSURE_TRUE_VOID(swm);
64
65
mInfo =
66
swm->GetRegistration(mDescriptor.PrincipalInfo(), mDescriptor.Scope());
67
NS_ENSURE_TRUE_VOID(mInfo);
68
69
mInfo->AddInstance(this, mDescriptor);
70
mListeningForEvents = true;
71
}
72
73
void ServiceWorkerRegistrationMainThread::StopListeningForEvents() {
74
MOZ_ASSERT(NS_IsMainThread());
75
if (!mListeningForEvents) {
76
return;
77
}
78
79
MOZ_DIAGNOSTIC_ASSERT(mInfo);
80
mInfo->RemoveInstance(this);
81
mInfo = nullptr;
82
83
mListeningForEvents = false;
84
}
85
86
void ServiceWorkerRegistrationMainThread::RegistrationClearedInternal() {
87
MOZ_ASSERT(NS_IsMainThread());
88
// Its possible for the binding object to be collected while we the
89
// runnable to call this method is in the event queue. Double check
90
// whether there is still anything to do here.
91
if (mOuter) {
92
mOuter->RegistrationCleared();
93
}
94
StopListeningForEvents();
95
}
96
97
// NB: These functions use NS_ENSURE_TRUE_VOID to be noisy about preconditions
98
// that would otherwise cause things to silently not happen if they were false.
99
void ServiceWorkerRegistrationMainThread::UpdateState(
100
const ServiceWorkerRegistrationDescriptor& aDescriptor) {
101
NS_ENSURE_TRUE_VOID(mOuter);
102
103
nsIGlobalObject* global = mOuter->GetParentObject();
104
NS_ENSURE_TRUE_VOID(global);
105
106
RefPtr<ServiceWorkerRegistrationMainThread> self = this;
107
nsCOMPtr<nsIRunnable> r =
108
NS_NewRunnableFunction("ServiceWorkerRegistrationMainThread::UpdateState",
109
[self, desc = std::move(aDescriptor)]() mutable {
110
self->mDescriptor = std::move(desc);
111
NS_ENSURE_TRUE_VOID(self->mOuter);
112
self->mOuter->UpdateState(self->mDescriptor);
113
});
114
115
Unused << global->EventTargetFor(TaskCategory::Other)
116
->Dispatch(r.forget(), NS_DISPATCH_NORMAL);
117
}
118
119
void ServiceWorkerRegistrationMainThread::FireUpdateFound() {
120
NS_ENSURE_TRUE_VOID(mOuter);
121
122
nsIGlobalObject* global = mOuter->GetParentObject();
123
NS_ENSURE_TRUE_VOID(global);
124
125
RefPtr<ServiceWorkerRegistrationMainThread> self = this;
126
nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(
127
"ServiceWorkerRegistrationMainThread::FireUpdateFound", [self]() mutable {
128
NS_ENSURE_TRUE_VOID(self->mOuter);
129
self->mOuter->MaybeDispatchUpdateFoundRunnable();
130
});
131
132
Unused << global->EventTargetFor(TaskCategory::Other)
133
->Dispatch(r.forget(), NS_DISPATCH_NORMAL);
134
}
135
136
void ServiceWorkerRegistrationMainThread::RegistrationCleared() {
137
NS_ENSURE_TRUE_VOID(mOuter);
138
139
nsIGlobalObject* global = mOuter->GetParentObject();
140
NS_ENSURE_TRUE_VOID(global);
141
142
// Queue a runnable to clean up the registration. This is necessary
143
// because there may be runnables in the event queue already to
144
// update the registration state. We want to let those run
145
// if possible before clearing our mOuter reference.
146
nsCOMPtr<nsIRunnable> r = NewRunnableMethod(
147
"ServiceWorkerRegistrationMainThread::RegistrationCleared", this,
148
&ServiceWorkerRegistrationMainThread::RegistrationClearedInternal);
149
150
Unused << global->EventTargetFor(TaskCategory::Other)
151
->Dispatch(r.forget(), NS_DISPATCH_NORMAL);
152
}
153
154
bool ServiceWorkerRegistrationMainThread::MatchesDescriptor(
155
const ServiceWorkerRegistrationDescriptor& aDescriptor) {
156
return mOuter->MatchesDescriptor(aDescriptor);
157
}
158
159
void ServiceWorkerRegistrationMainThread::SetServiceWorkerRegistration(
160
ServiceWorkerRegistration* aReg) {
161
MOZ_DIAGNOSTIC_ASSERT(aReg);
162
MOZ_DIAGNOSTIC_ASSERT(!mOuter);
163
mOuter = aReg;
164
StartListeningForEvents();
165
}
166
167
void ServiceWorkerRegistrationMainThread::ClearServiceWorkerRegistration(
168
ServiceWorkerRegistration* aReg) {
169
MOZ_ASSERT_IF(mOuter, mOuter == aReg);
170
StopListeningForEvents();
171
mOuter = nullptr;
172
}
173
174
namespace {
175
176
void UpdateInternal(nsIPrincipal* aPrincipal, const nsACString& aScope,
177
ServiceWorkerUpdateFinishCallback* aCallback) {
178
MOZ_ASSERT(NS_IsMainThread());
179
MOZ_ASSERT(aPrincipal);
180
MOZ_ASSERT(aCallback);
181
182
RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
183
if (!swm) {
184
// browser shutdown
185
return;
186
}
187
188
swm->Update(aPrincipal, aScope, aCallback);
189
}
190
191
class MainThreadUpdateCallback final
192
: public ServiceWorkerUpdateFinishCallback {
193
RefPtr<ServiceWorkerRegistrationPromise::Private> mPromise;
194
195
~MainThreadUpdateCallback() {
196
mPromise->Reject(NS_ERROR_DOM_ABORT_ERR, __func__);
197
}
198
199
public:
200
MainThreadUpdateCallback()
201
: mPromise(new ServiceWorkerRegistrationPromise::Private(__func__)) {}
202
203
void UpdateSucceeded(ServiceWorkerRegistrationInfo* aRegistration) override {
204
mPromise->Resolve(aRegistration->Descriptor(), __func__);
205
}
206
207
void UpdateFailed(ErrorResult& aStatus) override {
208
mPromise->Reject(std::move(aStatus), __func__);
209
}
210
211
RefPtr<ServiceWorkerRegistrationPromise> Promise() const { return mPromise; }
212
};
213
214
class WorkerThreadUpdateCallback final
215
: public ServiceWorkerUpdateFinishCallback {
216
RefPtr<ThreadSafeWorkerRef> mWorkerRef;
217
RefPtr<ServiceWorkerRegistrationPromise::Private> mPromise;
218
219
~WorkerThreadUpdateCallback() = default;
220
221
public:
222
WorkerThreadUpdateCallback(
223
RefPtr<ThreadSafeWorkerRef>&& aWorkerRef,
224
ServiceWorkerRegistrationPromise::Private* aPromise)
225
: mWorkerRef(std::move(aWorkerRef)), mPromise(aPromise) {
226
MOZ_ASSERT(NS_IsMainThread());
227
}
228
229
void UpdateSucceeded(ServiceWorkerRegistrationInfo* aRegistration) override {
230
mPromise->Resolve(aRegistration->Descriptor(), __func__);
231
mWorkerRef = nullptr;
232
}
233
234
void UpdateFailed(ErrorResult& aStatus) override {
235
mPromise->Reject(std::move(aStatus), __func__);
236
mWorkerRef = nullptr;
237
}
238
};
239
240
class SWRUpdateRunnable final : public Runnable {
241
class TimerCallback final : public nsITimerCallback {
242
RefPtr<ServiceWorkerPrivate> mPrivate;
243
RefPtr<Runnable> mRunnable;
244
245
public:
246
TimerCallback(ServiceWorkerPrivate* aPrivate, Runnable* aRunnable)
247
: mPrivate(aPrivate), mRunnable(aRunnable) {
248
MOZ_ASSERT(mPrivate);
249
MOZ_ASSERT(aRunnable);
250
}
251
252
NS_IMETHOD
253
Notify(nsITimer* aTimer) override {
254
mRunnable->Run();
255
mPrivate->RemoveISupports(aTimer);
256
257
return NS_OK;
258
}
259
260
NS_DECL_THREADSAFE_ISUPPORTS
261
262
private:
263
~TimerCallback() {}
264
};
265
266
public:
267
SWRUpdateRunnable(StrongWorkerRef* aWorkerRef,
268
ServiceWorkerRegistrationPromise::Private* aPromise,
269
const ServiceWorkerDescriptor& aDescriptor)
270
: Runnable("dom::SWRUpdateRunnable"),
271
mMutex("SWRUpdateRunnable"),
272
mWorkerRef(new ThreadSafeWorkerRef(aWorkerRef)),
273
mPromise(aPromise),
274
mDescriptor(aDescriptor),
275
mDelayed(false) {
276
MOZ_DIAGNOSTIC_ASSERT(mWorkerRef);
277
MOZ_DIAGNOSTIC_ASSERT(mPromise);
278
}
279
280
NS_IMETHOD
281
Run() override {
282
MOZ_ASSERT(NS_IsMainThread());
283
ErrorResult result;
284
285
nsCOMPtr<nsIPrincipal> principal = mDescriptor.GetPrincipal();
286
if (NS_WARN_IF(!principal)) {
287
mPromise->Reject(NS_ERROR_DOM_INVALID_STATE_ERR, __func__);
288
return NS_OK;
289
}
290
291
RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
292
if (NS_WARN_IF(!swm)) {
293
mPromise->Reject(NS_ERROR_DOM_INVALID_STATE_ERR, __func__);
294
return NS_OK;
295
}
296
297
// This will delay update jobs originating from a service worker thread.
298
// We don't currently handle ServiceWorkerRegistration.update() from other
299
// worker types. Also, we assume this registration matches self.registration
300
// on the service worker global. This is ok for now because service worker
301
// globals are the only worker contexts where we expose
302
// ServiceWorkerRegistration.
303
RefPtr<ServiceWorkerRegistrationInfo> registration =
304
swm->GetRegistration(principal, mDescriptor.Scope());
305
if (NS_WARN_IF(!registration)) {
306
return NS_OK;
307
}
308
309
RefPtr<ServiceWorkerInfo> worker =
310
registration->GetByDescriptor(mDescriptor);
311
uint32_t delay = registration->GetUpdateDelay();
312
313
// if we have a timer object, it means we've already been delayed once.
314
if (delay && !mDelayed) {
315
nsCOMPtr<nsITimerCallback> cb =
316
new TimerCallback(worker->WorkerPrivate(), this);
317
Result<nsCOMPtr<nsITimer>, nsresult> result = NS_NewTimerWithCallback(
318
cb, delay, nsITimer::TYPE_ONE_SHOT,
319
SystemGroup::EventTargetFor(TaskCategory::Other));
320
321
nsCOMPtr<nsITimer> timer = result.unwrapOr(nullptr);
322
if (NS_WARN_IF(!timer)) {
323
return NS_OK;
324
}
325
326
mDelayed = true;
327
328
// We're storing the timer object on the calling service worker's private.
329
// ServiceWorkerPrivate will drop the reference if the worker terminates,
330
// which will cancel the timer.
331
if (!worker->WorkerPrivate()->MaybeStoreISupports(timer)) {
332
// The worker thread is already shutting down. Just cancel the timer
333
// and let the update runnable be destroyed.
334
timer->Cancel();
335
return NS_OK;
336
}
337
338
return NS_OK;
339
}
340
341
RefPtr<ServiceWorkerRegistrationPromise::Private> promise;
342
{
343
MutexAutoLock lock(mMutex);
344
promise.swap(mPromise);
345
}
346
347
RefPtr<WorkerThreadUpdateCallback> cb =
348
new WorkerThreadUpdateCallback(std::move(mWorkerRef), promise);
349
UpdateInternal(principal, mDescriptor.Scope(), cb);
350
351
return NS_OK;
352
}
353
354
private:
355
~SWRUpdateRunnable() {
356
MutexAutoLock lock(mMutex);
357
if (mPromise) {
358
mPromise->Reject(NS_ERROR_DOM_INVALID_STATE_ERR, __func__);
359
}
360
}
361
362
// Protects promise access across threads
363
Mutex mMutex;
364
365
RefPtr<ThreadSafeWorkerRef> mWorkerRef;
366
RefPtr<ServiceWorkerRegistrationPromise::Private> mPromise;
367
const ServiceWorkerDescriptor mDescriptor;
368
bool mDelayed;
369
};
370
371
NS_IMPL_ISUPPORTS(SWRUpdateRunnable::TimerCallback, nsITimerCallback)
372
373
class WorkerUnregisterCallback final
374
: public nsIServiceWorkerUnregisterCallback {
375
RefPtr<ThreadSafeWorkerRef> mWorkerRef;
376
RefPtr<GenericPromise::Private> mPromise;
377
378
public:
379
NS_DECL_ISUPPORTS
380
381
WorkerUnregisterCallback(RefPtr<ThreadSafeWorkerRef>&& aWorkerRef,
382
RefPtr<GenericPromise::Private>&& aPromise)
383
: mWorkerRef(std::move(aWorkerRef)), mPromise(std::move(aPromise)) {
384
MOZ_DIAGNOSTIC_ASSERT(mWorkerRef);
385
MOZ_DIAGNOSTIC_ASSERT(mPromise);
386
}
387
388
NS_IMETHOD
389
UnregisterSucceeded(bool aState) override {
390
mPromise->Resolve(aState, __func__);
391
mWorkerRef = nullptr;
392
return NS_OK;
393
}
394
395
NS_IMETHOD
396
UnregisterFailed() override {
397
mPromise->Reject(NS_ERROR_DOM_SECURITY_ERR, __func__);
398
mWorkerRef = nullptr;
399
return NS_OK;
400
}
401
402
private:
403
~WorkerUnregisterCallback() = default;
404
};
405
406
NS_IMPL_ISUPPORTS(WorkerUnregisterCallback, nsIServiceWorkerUnregisterCallback);
407
408
/*
409
* If the worker goes away, we still continue to unregister, but we don't try to
410
* resolve the worker Promise (which doesn't exist by that point).
411
*/
412
class StartUnregisterRunnable final : public Runnable {
413
// The promise is protected by the mutex.
414
Mutex mMutex;
415
416
RefPtr<ThreadSafeWorkerRef> mWorkerRef;
417
RefPtr<GenericPromise::Private> mPromise;
418
const ServiceWorkerRegistrationDescriptor mDescriptor;
419
420
~StartUnregisterRunnable() {
421
MutexAutoLock lock(mMutex);
422
if (mPromise) {
423
mPromise->Reject(NS_ERROR_DOM_INVALID_STATE_ERR, __func__);
424
}
425
}
426
427
public:
428
StartUnregisterRunnable(
429
StrongWorkerRef* aWorkerRef, GenericPromise::Private* aPromise,
430
const ServiceWorkerRegistrationDescriptor& aDescriptor)
431
: Runnable("dom::StartUnregisterRunnable"),
432
mMutex("StartUnregisterRunnable"),
433
mWorkerRef(new ThreadSafeWorkerRef(aWorkerRef)),
434
mPromise(aPromise),
435
mDescriptor(aDescriptor) {
436
MOZ_DIAGNOSTIC_ASSERT(mWorkerRef);
437
MOZ_DIAGNOSTIC_ASSERT(mPromise);
438
}
439
440
NS_IMETHOD
441
Run() override {
442
MOZ_ASSERT(NS_IsMainThread());
443
444
nsCOMPtr<nsIPrincipal> principal = mDescriptor.GetPrincipal();
445
if (!principal) {
446
mPromise->Reject(NS_ERROR_DOM_INVALID_STATE_ERR, __func__);
447
return NS_OK;
448
}
449
450
nsCOMPtr<nsIServiceWorkerManager> swm =
451
mozilla::services::GetServiceWorkerManager();
452
if (!swm) {
453
mPromise->Reject(NS_ERROR_DOM_INVALID_STATE_ERR, __func__);
454
return NS_OK;
455
}
456
457
RefPtr<GenericPromise::Private> promise;
458
{
459
MutexAutoLock lock(mMutex);
460
promise = mPromise.forget();
461
}
462
463
RefPtr<WorkerUnregisterCallback> cb =
464
new WorkerUnregisterCallback(std::move(mWorkerRef), std::move(promise));
465
466
nsresult rv = swm->Unregister(principal, cb,
467
NS_ConvertUTF8toUTF16(mDescriptor.Scope()));
468
if (NS_WARN_IF(NS_FAILED(rv))) {
469
mPromise->Reject(rv, __func__);
470
return NS_OK;
471
}
472
473
return NS_OK;
474
}
475
};
476
477
} // namespace
478
479
void ServiceWorkerRegistrationMainThread::Update(
480
ServiceWorkerRegistrationCallback&& aSuccessCB,
481
ServiceWorkerFailureCallback&& aFailureCB) {
482
MOZ_ASSERT(NS_IsMainThread());
483
MOZ_DIAGNOSTIC_ASSERT(mOuter);
484
485
nsIGlobalObject* global = mOuter->GetParentObject();
486
if (!global) {
487
aFailureCB(CopyableErrorResult(NS_ERROR_DOM_INVALID_STATE_ERR));
488
return;
489
}
490
491
nsCOMPtr<nsIPrincipal> principal = mDescriptor.GetPrincipal();
492
if (!principal) {
493
aFailureCB(CopyableErrorResult(NS_ERROR_DOM_INVALID_STATE_ERR));
494
return;
495
}
496
497
RefPtr<MainThreadUpdateCallback> cb = new MainThreadUpdateCallback();
498
UpdateInternal(principal, NS_ConvertUTF16toUTF8(mScope), cb);
499
500
auto holder =
501
MakeRefPtr<DOMMozPromiseRequestHolder<ServiceWorkerRegistrationPromise>>(
502
global);
503
504
cb->Promise()
505
->Then(
506
global->EventTargetFor(TaskCategory::Other), __func__,
507
[successCB = std::move(aSuccessCB),
508
holder](const ServiceWorkerRegistrationDescriptor& aDescriptor) {
509
holder->Complete();
510
successCB(aDescriptor);
511
},
512
[failureCB = std::move(aFailureCB),
513
holder](const CopyableErrorResult& aRv) {
514
holder->Complete();
515
failureCB(CopyableErrorResult(aRv));
516
})
517
->Track(*holder);
518
}
519
520
void ServiceWorkerRegistrationMainThread::Unregister(
521
ServiceWorkerBoolCallback&& aSuccessCB,
522
ServiceWorkerFailureCallback&& aFailureCB) {
523
MOZ_ASSERT(NS_IsMainThread());
524
MOZ_DIAGNOSTIC_ASSERT(mOuter);
525
526
nsIGlobalObject* global = mOuter->GetParentObject();
527
if (!global) {
528
aFailureCB(CopyableErrorResult(NS_ERROR_DOM_INVALID_STATE_ERR));
529
return;
530
}
531
532
nsCOMPtr<nsIServiceWorkerManager> swm =
533
mozilla::services::GetServiceWorkerManager();
534
if (!swm) {
535
aFailureCB(CopyableErrorResult(NS_ERROR_DOM_INVALID_STATE_ERR));
536
return;
537
}
538
539
nsCOMPtr<nsIPrincipal> principal = mDescriptor.GetPrincipal();
540
if (!principal) {
541
aFailureCB(CopyableErrorResult(NS_ERROR_DOM_INVALID_STATE_ERR));
542
return;
543
}
544
545
RefPtr<UnregisterCallback> cb = new UnregisterCallback();
546
547
nsresult rv = swm->Unregister(principal, cb,
548
NS_ConvertUTF8toUTF16(mDescriptor.Scope()));
549
if (NS_FAILED(rv)) {
550
aFailureCB(CopyableErrorResult(rv));
551
return;
552
}
553
554
auto holder = MakeRefPtr<DOMMozPromiseRequestHolder<GenericPromise>>(global);
555
556
cb->Promise()
557
->Then(
558
global->EventTargetFor(TaskCategory::Other), __func__,
559
[successCB = std::move(aSuccessCB), holder](bool aResult) {
560
holder->Complete();
561
successCB(aResult);
562
},
563
[failureCB = std::move(aFailureCB), holder](nsresult aRv) {
564
holder->Complete();
565
failureCB(CopyableErrorResult(aRv));
566
})
567
->Track(*holder);
568
}
569
570
////////////////////////////////////////////////////
571
// Worker Thread implementation
572
573
class WorkerListener final : public ServiceWorkerRegistrationListener {
574
ServiceWorkerRegistrationDescriptor mDescriptor;
575
nsMainThreadPtrHandle<ServiceWorkerRegistrationInfo> mInfo;
576
nsCOMPtr<nsISerialEventTarget> mEventTarget;
577
bool mListeningForEvents;
578
579
// Set and unset on worker thread, used on main-thread and protected by mutex.
580
ServiceWorkerRegistrationWorkerThread* mRegistration;
581
582
Mutex mMutex;
583
584
public:
585
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(WorkerListener, override)
586
587
WorkerListener(ServiceWorkerRegistrationWorkerThread* aReg,
588
const ServiceWorkerRegistrationDescriptor& aDescriptor,
589
nsISerialEventTarget* aEventTarget)
590
: mDescriptor(aDescriptor),
591
mEventTarget(aEventTarget),
592
mListeningForEvents(false),
593
mRegistration(aReg),
594
mMutex("WorkerListener::mMutex") {
595
MOZ_ASSERT(IsCurrentThreadRunningWorker());
596
MOZ_ASSERT(mEventTarget);
597
MOZ_ASSERT(mRegistration);
598
}
599
600
void StartListeningForEvents() {
601
MOZ_ASSERT(NS_IsMainThread());
602
MOZ_DIAGNOSTIC_ASSERT(!mListeningForEvents);
603
MOZ_DIAGNOSTIC_ASSERT(!mInfo);
604
605
RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
606
NS_ENSURE_TRUE_VOID(swm);
607
608
RefPtr<ServiceWorkerRegistrationInfo> info =
609
swm->GetRegistration(mDescriptor.PrincipalInfo(), mDescriptor.Scope());
610
NS_ENSURE_TRUE_VOID(info);
611
612
mInfo = new nsMainThreadPtrHolder<ServiceWorkerRegistrationInfo>(
613
"WorkerListener::mInfo", info);
614
615
mInfo->AddInstance(this, mDescriptor);
616
mListeningForEvents = true;
617
}
618
619
void StopListeningForEvents() {
620
MOZ_ASSERT(NS_IsMainThread());
621
622
if (!mListeningForEvents) {
623
return;
624
}
625
626
MOZ_DIAGNOSTIC_ASSERT(mInfo);
627
mInfo->RemoveInstance(this);
628
mListeningForEvents = false;
629
}
630
631
// ServiceWorkerRegistrationListener
632
void UpdateState(
633
const ServiceWorkerRegistrationDescriptor& aDescriptor) override {
634
MOZ_ASSERT(NS_IsMainThread());
635
636
mDescriptor = aDescriptor;
637
638
nsCOMPtr<nsIRunnable> r =
639
NewCancelableRunnableMethod<ServiceWorkerRegistrationDescriptor>(
640
"WorkerListener::UpdateState", this,
641
&WorkerListener::UpdateStateOnWorkerThread, aDescriptor);
642
643
Unused << mEventTarget->Dispatch(r.forget(), NS_DISPATCH_NORMAL);
644
}
645
646
void FireUpdateFound() override {
647
MOZ_ASSERT(NS_IsMainThread());
648
649
nsCOMPtr<nsIRunnable> r = NewCancelableRunnableMethod(
650
"WorkerListener::FireUpdateFound", this,
651
&WorkerListener::FireUpdateFoundOnWorkerThread);
652
653
Unused << mEventTarget->Dispatch(r.forget(), NS_DISPATCH_NORMAL);
654
}
655
656
void UpdateStateOnWorkerThread(
657
const ServiceWorkerRegistrationDescriptor& aDescriptor) {
658
MOZ_ASSERT(IsCurrentThreadRunningWorker());
659
if (mRegistration) {
660
mRegistration->UpdateState(aDescriptor);
661
}
662
}
663
664
void FireUpdateFoundOnWorkerThread() {
665
MOZ_ASSERT(IsCurrentThreadRunningWorker());
666
if (mRegistration) {
667
mRegistration->FireUpdateFound();
668
}
669
}
670
671
void RegistrationCleared() override;
672
673
void GetScope(nsAString& aScope) const override {
674
CopyUTF8toUTF16(mDescriptor.Scope(), aScope);
675
}
676
677
bool MatchesDescriptor(
678
const ServiceWorkerRegistrationDescriptor& aDescriptor) override {
679
// TODO: Not implemented
680
return false;
681
}
682
683
void ClearRegistration() {
684
MOZ_ASSERT(IsCurrentThreadRunningWorker());
685
MutexAutoLock lock(mMutex);
686
mRegistration = nullptr;
687
}
688
689
private:
690
~WorkerListener() { MOZ_ASSERT(!mListeningForEvents); }
691
};
692
693
ServiceWorkerRegistrationWorkerThread::ServiceWorkerRegistrationWorkerThread(
694
const ServiceWorkerRegistrationDescriptor& aDescriptor)
695
: mOuter(nullptr),
696
mDescriptor(aDescriptor),
697
mScope(NS_ConvertUTF8toUTF16(aDescriptor.Scope())) {}
698
699
ServiceWorkerRegistrationWorkerThread::
700
~ServiceWorkerRegistrationWorkerThread() {
701
MOZ_DIAGNOSTIC_ASSERT(!mListener);
702
MOZ_DIAGNOSTIC_ASSERT(!mOuter);
703
}
704
705
void ServiceWorkerRegistrationWorkerThread::RegistrationCleared() {
706
// The SWM notifying us that the registration was removed on the MT may
707
// race with ClearServiceWorkerRegistration() on the worker thread. So
708
// double-check that mOuter is still valid.
709
if (mOuter) {
710
mOuter->RegistrationCleared();
711
}
712
}
713
714
void ServiceWorkerRegistrationWorkerThread::SetServiceWorkerRegistration(
715
ServiceWorkerRegistration* aReg) {
716
MOZ_DIAGNOSTIC_ASSERT(aReg);
717
MOZ_DIAGNOSTIC_ASSERT(!mOuter);
718
mOuter = aReg;
719
InitListener();
720
}
721
722
void ServiceWorkerRegistrationWorkerThread::ClearServiceWorkerRegistration(
723
ServiceWorkerRegistration* aReg) {
724
MOZ_ASSERT_IF(mOuter, mOuter == aReg);
725
ReleaseListener();
726
mOuter = nullptr;
727
}
728
729
void ServiceWorkerRegistrationWorkerThread::Update(
730
ServiceWorkerRegistrationCallback&& aSuccessCB,
731
ServiceWorkerFailureCallback&& aFailureCB) {
732
if (NS_WARN_IF(!mWorkerRef->GetPrivate())) {
733
aFailureCB(CopyableErrorResult(NS_ERROR_DOM_INVALID_STATE_ERR));
734
return;
735
}
736
737
RefPtr<StrongWorkerRef> workerRef = StrongWorkerRef::Create(
738
mWorkerRef->GetPrivate(), "ServiceWorkerRegistration::Update");
739
if (NS_WARN_IF(!workerRef)) {
740
aFailureCB(CopyableErrorResult(NS_ERROR_DOM_INVALID_STATE_ERR));
741
return;
742
}
743
744
nsIGlobalObject* global = workerRef->Private()->GlobalScope();
745
if (NS_WARN_IF(!global)) {
746
aFailureCB(CopyableErrorResult(NS_ERROR_DOM_INVALID_STATE_ERR));
747
return;
748
}
749
750
// Eventually we need to support all workers, but for right now this
751
// code assumes we're on a service worker global as self.registration.
752
if (NS_WARN_IF(!workerRef->Private()->IsServiceWorker())) {
753
aFailureCB(CopyableErrorResult(NS_ERROR_DOM_INVALID_STATE_ERR));
754
return;
755
}
756
757
// Avoid infinite update loops by ignoring update() calls during top
758
// level script evaluation. See:
760
if (workerRef->Private()->IsLoadingWorkerScript()) {
761
aSuccessCB(mDescriptor);
762
return;
763
}
764
765
auto promise =
766
MakeRefPtr<ServiceWorkerRegistrationPromise::Private>(__func__);
767
auto holder =
768
MakeRefPtr<DOMMozPromiseRequestHolder<ServiceWorkerRegistrationPromise>>(
769
global);
770
771
promise
772
->Then(
773
global->EventTargetFor(TaskCategory::Other), __func__,
774
[successCB = std::move(aSuccessCB),
775
holder](const ServiceWorkerRegistrationDescriptor& aDescriptor) {
776
holder->Complete();
777
successCB(aDescriptor);
778
},
779
[failureCB = std::move(aFailureCB),
780
holder](const CopyableErrorResult& aRv) {
781
holder->Complete();
782
failureCB(CopyableErrorResult(aRv));
783
})
784
->Track(*holder);
785
786
RefPtr<SWRUpdateRunnable> r = new SWRUpdateRunnable(
787
workerRef, promise, workerRef->Private()->GetServiceWorkerDescriptor());
788
789
nsresult rv = workerRef->Private()->DispatchToMainThread(r.forget());
790
if (NS_FAILED(rv)) {
791
promise->Reject(NS_ERROR_DOM_INVALID_STATE_ERR, __func__);
792
return;
793
}
794
}
795
796
void ServiceWorkerRegistrationWorkerThread::Unregister(
797
ServiceWorkerBoolCallback&& aSuccessCB,
798
ServiceWorkerFailureCallback&& aFailureCB) {
799
if (NS_WARN_IF(!mWorkerRef->GetPrivate())) {
800
aFailureCB(CopyableErrorResult(NS_ERROR_DOM_INVALID_STATE_ERR));
801
return;
802
}
803
804
RefPtr<StrongWorkerRef> workerRef =
805
StrongWorkerRef::Create(mWorkerRef->GetPrivate(), __func__);
806
if (NS_WARN_IF(!workerRef)) {
807
aFailureCB(CopyableErrorResult(NS_ERROR_DOM_INVALID_STATE_ERR));
808
return;
809
}
810
811
// Eventually we need to support all workers, but for right now this
812
// code assumes we're on a service worker global as self.registration.
813
if (NS_WARN_IF(!workerRef->Private()->IsServiceWorker())) {
814
aFailureCB(CopyableErrorResult(NS_ERROR_DOM_INVALID_STATE_ERR));
815
return;
816
}
817
818
nsIGlobalObject* global = workerRef->Private()->GlobalScope();
819
if (!global) {
820
aFailureCB(CopyableErrorResult(NS_ERROR_DOM_INVALID_STATE_ERR));
821
return;
822
}
823
824
auto promise = MakeRefPtr<GenericPromise::Private>(__func__);
825
auto holder = MakeRefPtr<DOMMozPromiseRequestHolder<GenericPromise>>(global);
826
827
promise
828
->Then(
829
global->EventTargetFor(TaskCategory::Other), __func__,
830
[successCB = std::move(aSuccessCB), holder](bool aResult) {
831
holder->Complete();
832
successCB(aResult);
833
},
834
[failureCB = std::move(aFailureCB), holder](nsresult aRv) {
835
holder->Complete();
836
failureCB(CopyableErrorResult(aRv));
837
})
838
->Track(*holder);
839
840
RefPtr<StartUnregisterRunnable> r =
841
new StartUnregisterRunnable(workerRef, promise, mDescriptor);
842
843
nsresult rv = workerRef->Private()->DispatchToMainThread(r);
844
if (NS_FAILED(rv)) {
845
promise->Reject(NS_ERROR_DOM_INVALID_STATE_ERR, __func__);
846
return;
847
}
848
}
849
850
void ServiceWorkerRegistrationWorkerThread::InitListener() {
851
MOZ_ASSERT(!mListener);
852
WorkerPrivate* worker = GetCurrentThreadWorkerPrivate();
853
MOZ_ASSERT(worker);
854
worker->AssertIsOnWorkerThread();
855
856
RefPtr<ServiceWorkerRegistrationWorkerThread> self = this;
857
mWorkerRef = WeakWorkerRef::Create(worker, [self]() {
858
self->ReleaseListener();
859
860
// Break the ref-cycle immediately when the worker thread starts to
861
// teardown. We must make sure its GC'd before the worker RuntimeService is
862
// destroyed. The WorkerListener may not be able to post a runnable
863
// clearing this value after shutdown begins and thus delaying cleanup too
864
// late.
865
self->mOuter = nullptr;
866
});
867
868
if (NS_WARN_IF(!mWorkerRef)) {
869
return;
870
}
871
872
mListener =
873
new WorkerListener(this, mDescriptor, worker->HybridEventTarget());
874
875
nsCOMPtr<nsIRunnable> r =
876
NewRunnableMethod("dom::WorkerListener::StartListeningForEvents",
877
mListener, &WorkerListener::StartListeningForEvents);
878
MOZ_ALWAYS_SUCCEEDS(worker->DispatchToMainThread(r.forget()));
879
}
880
881
void ServiceWorkerRegistrationWorkerThread::ReleaseListener() {
882
if (!mListener) {
883
return;
884
}
885
886
MOZ_ASSERT(IsCurrentThreadRunningWorker());
887
888
mListener->ClearRegistration();
889
890
nsCOMPtr<nsIRunnable> r = NewCancelableRunnableMethod(
891
"dom::WorkerListener::StopListeningForEvents", mListener,
892
&WorkerListener::StopListeningForEvents);
893
// Calling GetPrivate() is safe because this method is called when the
894
// WorkerRef is notified.
895
MOZ_ALWAYS_SUCCEEDS(
896
mWorkerRef->GetPrivate()->DispatchToMainThread(r.forget()));
897
898
mListener = nullptr;
899
mWorkerRef = nullptr;
900
}
901
902
void ServiceWorkerRegistrationWorkerThread::UpdateState(
903
const ServiceWorkerRegistrationDescriptor& aDescriptor) {
904
if (mOuter) {
905
mOuter->UpdateState(aDescriptor);
906
}
907
}
908
909
void ServiceWorkerRegistrationWorkerThread::FireUpdateFound() {
910
if (mOuter) {
911
mOuter->MaybeDispatchUpdateFoundRunnable();
912
}
913
}
914
915
class RegistrationClearedWorkerRunnable final : public WorkerRunnable {
916
RefPtr<WorkerListener> mListener;
917
918
public:
919
RegistrationClearedWorkerRunnable(WorkerPrivate* aWorkerPrivate,
920
WorkerListener* aListener)
921
: WorkerRunnable(aWorkerPrivate), mListener(aListener) {
922
// Need this assertion for now since runnables which modify busy count can
923
// only be dispatched from parent thread to worker thread and we don't deal
924
// with nested workers. SW threads can't be nested.
925
MOZ_ASSERT(aWorkerPrivate->IsServiceWorker());
926
}
927
928
bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override {
929
MOZ_ASSERT(aWorkerPrivate);
930
aWorkerPrivate->AssertIsOnWorkerThread();
931
mListener->RegistrationCleared();
932
return true;
933
}
934
};
935
936
void WorkerListener::RegistrationCleared() {
937
MutexAutoLock lock(mMutex);
938
if (!mRegistration) {
939
return;
940
}
941
942
if (NS_IsMainThread()) {
943
RefPtr<WorkerRunnable> r = new RegistrationClearedWorkerRunnable(
944
mRegistration->GetWorkerPrivate(lock), this);
945
Unused << r->Dispatch();
946
947
StopListeningForEvents();
948
return;
949
}
950
951
mRegistration->RegistrationCleared();
952
}
953
954
WorkerPrivate* ServiceWorkerRegistrationWorkerThread::GetWorkerPrivate(
955
const MutexAutoLock& aProofOfLock) {
956
// In this case, calling GetUnsafePrivate() is ok because we have a proof of
957
// mutex lock.
958
MOZ_ASSERT(mWorkerRef && mWorkerRef->GetUnsafePrivate());
959
return mWorkerRef->GetUnsafePrivate();
960
}
961
962
} // namespace dom
963
} // namespace mozilla