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