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