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 "ServiceWorkerPrivate.h"
8 
9 #include <utility>
10 
11 #include "ServiceWorkerCloneData.h"
12 #include "ServiceWorkerManager.h"
13 #include "ServiceWorkerPrivateImpl.h"
14 #include "ServiceWorkerUtils.h"
15 #include "nsContentUtils.h"
16 #include "nsICacheInfoChannel.h"
17 #include "nsIHttpChannel.h"
18 #include "nsIHttpChannelInternal.h"
19 #include "nsIHttpHeaderVisitor.h"
20 #include "nsINamed.h"
21 #include "nsINetworkInterceptController.h"
22 #include "nsIPushErrorReporter.h"
23 #include "nsISupportsImpl.h"
24 #include "nsIUploadChannel2.h"
25 #include "nsNetUtil.h"
26 #include "nsProxyRelease.h"
27 #include "nsQueryObject.h"
28 #include "nsStreamUtils.h"
29 #include "nsStringStream.h"
30 #include "mozilla/Assertions.h"
31 #include "mozilla/CycleCollectedJSContext.h"  // for MicroTaskRunnable
32 #include "mozilla/JSObjectHolder.h"
33 #include "mozilla/Preferences.h"
34 #include "mozilla/dom/Client.h"
35 #include "mozilla/dom/ClientIPCTypes.h"
36 #include "mozilla/dom/FetchUtil.h"
37 #include "mozilla/dom/IndexedDatabaseManager.h"
38 #include "mozilla/dom/InternalHeaders.h"
39 #include "mozilla/dom/NotificationEvent.h"
40 #include "mozilla/dom/PromiseNativeHandler.h"
41 #include "mozilla/dom/PushEventBinding.h"
42 #include "mozilla/dom/RequestBinding.h"
43 #include "mozilla/dom/WorkerDebugger.h"
44 #include "mozilla/dom/WorkerRef.h"
45 #include "mozilla/dom/WorkerRunnable.h"
46 #include "mozilla/dom/WorkerScope.h"
47 #include "mozilla/dom/ipc/StructuredCloneData.h"
48 #include "mozilla/ipc/BackgroundUtils.h"
49 #include "mozilla/net/CookieJarSettings.h"
50 #include "mozilla/net/NeckoChannelParams.h"
51 #include "mozilla/Services.h"
52 #include "mozilla/StoragePrincipalHelper.h"
53 #include "mozilla/Telemetry.h"
54 #include "mozilla/DebugOnly.h"
55 #include "mozilla/StaticPrefs_dom.h"
56 #include "mozilla/StaticPrefs_privacy.h"
57 #include "mozilla/Unused.h"
58 #include "nsIReferrerInfo.h"
59 
60 using namespace mozilla;
61 using namespace mozilla::dom;
62 
63 namespace mozilla {
64 namespace dom {
65 
66 using mozilla::ipc::PrincipalInfo;
67 
68 NS_IMPL_CYCLE_COLLECTING_NATIVE_ADDREF(ServiceWorkerPrivate)
69 NS_IMPL_CYCLE_COLLECTING_NATIVE_RELEASE(ServiceWorkerPrivate)
70 NS_IMPL_CYCLE_COLLECTION(ServiceWorkerPrivate, mSupportsArray)
71 NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(ServiceWorkerPrivate, AddRef)
72 NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(ServiceWorkerPrivate, Release)
73 
74 // Tracks the "dom.serviceWorkers.disable_open_click_delay" preference. Modified
75 // on main thread, read on worker threads.
76 // It is updated every time a "notificationclick" event is dispatched. While
77 // this is done without synchronization, at the worst, the thread will just get
78 // an older value within which a popup is allowed to be displayed, which will
79 // still be a valid value since it was set prior to dispatching the runnable.
80 Atomic<uint32_t> gDOMDisableOpenClickDelay(0);
81 
KeepAliveToken(ServiceWorkerPrivate * aPrivate)82 KeepAliveToken::KeepAliveToken(ServiceWorkerPrivate* aPrivate)
83     : mPrivate(aPrivate) {
84   MOZ_ASSERT(NS_IsMainThread());
85   MOZ_ASSERT(aPrivate);
86   mPrivate->AddToken();
87 }
88 
~KeepAliveToken()89 KeepAliveToken::~KeepAliveToken() {
90   MOZ_ASSERT(NS_IsMainThread());
91   mPrivate->ReleaseToken();
92 }
93 
NS_IMPL_ISUPPORTS0(KeepAliveToken)94 NS_IMPL_ISUPPORTS0(KeepAliveToken)
95 
96 ServiceWorkerPrivate::ServiceWorkerPrivate(ServiceWorkerInfo* aInfo)
97     : mInfo(aInfo), mDebuggerCount(0), mTokenCount(0) {
98   MOZ_ASSERT(NS_IsMainThread());
99   MOZ_ASSERT(aInfo);
100 
101   mIdleWorkerTimer = NS_NewTimer();
102   MOZ_ASSERT(mIdleWorkerTimer);
103 
104   RefPtr<ServiceWorkerPrivateImpl> inner = new ServiceWorkerPrivateImpl(this);
105 
106   // Assert in all debug builds as well as non-debug Nightly and Dev Edition.
107 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
108   MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(inner->Initialize()));
109 #else
110   MOZ_ALWAYS_SUCCEEDS(inner->Initialize());
111 #endif
112 
113   mInner = std::move(inner);
114 }
115 
~ServiceWorkerPrivate()116 ServiceWorkerPrivate::~ServiceWorkerPrivate() {
117   MOZ_ASSERT(!mWorkerPrivate);
118   MOZ_ASSERT(!mTokenCount);
119   MOZ_ASSERT(!mInner);
120   MOZ_ASSERT(!mInfo);
121   MOZ_ASSERT(mSupportsArray.IsEmpty());
122   MOZ_ASSERT(mIdlePromiseHolder.IsEmpty());
123 
124   mIdleWorkerTimer->Cancel();
125 }
126 
127 namespace {
128 
129 class CheckScriptEvaluationWithCallback final : public WorkerDebuggeeRunnable {
130   nsMainThreadPtrHandle<ServiceWorkerPrivate> mServiceWorkerPrivate;
131   nsMainThreadPtrHandle<KeepAliveToken> mKeepAliveToken;
132 
133   // The script evaluation result must be reported even if the runnable
134   // is cancelled.
135   RefPtr<LifeCycleEventCallback> mScriptEvaluationCallback;
136 
137 #ifdef DEBUG
138   bool mDone;
139 #endif
140 
141  public:
CheckScriptEvaluationWithCallback(WorkerPrivate * aWorkerPrivate,ServiceWorkerPrivate * aServiceWorkerPrivate,KeepAliveToken * aKeepAliveToken,LifeCycleEventCallback * aScriptEvaluationCallback)142   CheckScriptEvaluationWithCallback(
143       WorkerPrivate* aWorkerPrivate,
144       ServiceWorkerPrivate* aServiceWorkerPrivate,
145       KeepAliveToken* aKeepAliveToken,
146       LifeCycleEventCallback* aScriptEvaluationCallback)
147       : WorkerDebuggeeRunnable(aWorkerPrivate, WorkerThreadModifyBusyCount),
148         mServiceWorkerPrivate(new nsMainThreadPtrHolder<ServiceWorkerPrivate>(
149             "CheckScriptEvaluationWithCallback::mServiceWorkerPrivate",
150             aServiceWorkerPrivate)),
151         mKeepAliveToken(new nsMainThreadPtrHolder<KeepAliveToken>(
152             "CheckScriptEvaluationWithCallback::mKeepAliveToken",
153             aKeepAliveToken)),
154         mScriptEvaluationCallback(aScriptEvaluationCallback)
155 #ifdef DEBUG
156         ,
157         mDone(false)
158 #endif
159   {
160     MOZ_ASSERT(NS_IsMainThread());
161   }
162 
~CheckScriptEvaluationWithCallback()163   ~CheckScriptEvaluationWithCallback() { MOZ_ASSERT(mDone); }
164 
WorkerRun(JSContext * aCx,WorkerPrivate * aWorkerPrivate)165   bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override {
166     aWorkerPrivate->AssertIsOnWorkerThread();
167 
168     bool fetchHandlerWasAdded = aWorkerPrivate->FetchHandlerWasAdded();
169     nsCOMPtr<nsIRunnable> runnable = NewRunnableMethod<bool>(
170         "dom::CheckScriptEvaluationWithCallback::ReportFetchFlag", this,
171         &CheckScriptEvaluationWithCallback::ReportFetchFlag,
172         fetchHandlerWasAdded);
173     aWorkerPrivate->DispatchToMainThread(runnable.forget());
174 
175     ReportScriptEvaluationResult(
176         aWorkerPrivate->WorkerScriptExecutedSuccessfully());
177 
178     return true;
179   }
180 
ReportFetchFlag(bool aFetchHandlerWasAdded)181   void ReportFetchFlag(bool aFetchHandlerWasAdded) {
182     MOZ_ASSERT(NS_IsMainThread());
183     mServiceWorkerPrivate->SetHandlesFetch(aFetchHandlerWasAdded);
184   }
185 
Cancel()186   nsresult Cancel() override {
187     // We need to check first if cancel is permitted
188     nsresult rv = WorkerRunnable::Cancel();
189     NS_ENSURE_SUCCESS(rv, rv);
190 
191     ReportScriptEvaluationResult(false);
192     return NS_OK;
193   }
194 
195  private:
ReportScriptEvaluationResult(bool aScriptEvaluationResult)196   void ReportScriptEvaluationResult(bool aScriptEvaluationResult) {
197 #ifdef DEBUG
198     mDone = true;
199 #endif
200     mScriptEvaluationCallback->SetResult(aScriptEvaluationResult);
201     MOZ_ALWAYS_SUCCEEDS(
202         mWorkerPrivate->DispatchToMainThread(mScriptEvaluationCallback));
203   }
204 };
205 
206 }  // anonymous namespace
207 
CheckScriptEvaluation(LifeCycleEventCallback * aScriptEvaluationCallback)208 nsresult ServiceWorkerPrivate::CheckScriptEvaluation(
209     LifeCycleEventCallback* aScriptEvaluationCallback) {
210   MOZ_ASSERT(NS_IsMainThread());
211 
212   if (mInner) {
213     return mInner->CheckScriptEvaluation(aScriptEvaluationCallback);
214   }
215 
216   nsresult rv = SpawnWorkerIfNeeded(LifeCycleEvent);
217   NS_ENSURE_SUCCESS(rv, rv);
218 
219   RefPtr<KeepAliveToken> token = CreateEventKeepAliveToken();
220   RefPtr<WorkerRunnable> r = new CheckScriptEvaluationWithCallback(
221       mWorkerPrivate, this, token, aScriptEvaluationCallback);
222   if (NS_WARN_IF(!r->Dispatch())) {
223     return NS_ERROR_FAILURE;
224   }
225 
226   return NS_OK;
227 }
228 
229 namespace {
230 
231 class KeepAliveHandler final : public ExtendableEvent::ExtensionsHandler,
232                                public PromiseNativeHandler {
233   // This class manages lifetime extensions added by calling WaitUntil()
234   // or RespondWith(). We allow new extensions as long as we still hold
235   // |mKeepAliveToken|. Once the last promise was settled, we queue a microtask
236   // which releases the token and prevents further extensions. By doing this,
237   // we give other pending microtasks a chance to continue adding extensions.
238 
239   RefPtr<StrongWorkerRef> mWorkerRef;
240   nsMainThreadPtrHandle<KeepAliveToken> mKeepAliveToken;
241 
242   // We start holding a self reference when the first extension promise is
243   // added. As far as I can tell, the only case where this is useful is when
244   // we're waiting indefinitely on a promise that's no longer reachable
245   // and will never be settled.
246   // The cycle is broken when the last promise was settled or when the
247   // worker is shutting down.
248   RefPtr<KeepAliveHandler> mSelfRef;
249 
250   // Called when the last promise was settled.
251   RefPtr<ExtendableEventCallback> mCallback;
252 
253   uint32_t mPendingPromisesCount;
254 
255   // We don't actually care what values the promises resolve to, only whether
256   // any of them were rejected.
257   bool mRejected;
258 
259  public:
260   NS_DECL_ISUPPORTS
261 
KeepAliveHandler(const nsMainThreadPtrHandle<KeepAliveToken> & aKeepAliveToken,ExtendableEventCallback * aCallback)262   explicit KeepAliveHandler(
263       const nsMainThreadPtrHandle<KeepAliveToken>& aKeepAliveToken,
264       ExtendableEventCallback* aCallback)
265       : mKeepAliveToken(aKeepAliveToken),
266         mCallback(aCallback),
267         mPendingPromisesCount(0),
268         mRejected(false) {
269     MOZ_ASSERT(mKeepAliveToken);
270   }
271 
Init()272   bool Init() {
273     MOZ_ASSERT(IsCurrentThreadRunningWorker());
274 
275     RefPtr<KeepAliveHandler> self = this;
276     mWorkerRef = StrongWorkerRef::Create(GetCurrentThreadWorkerPrivate(),
277                                          "KeepAliveHandler",
278                                          [self]() { self->MaybeCleanup(); });
279 
280     if (NS_WARN_IF(!mWorkerRef)) {
281       return false;
282     }
283 
284     return true;
285   }
286 
WaitOnPromise(Promise & aPromise)287   bool WaitOnPromise(Promise& aPromise) override {
288     if (!mKeepAliveToken) {
289       MOZ_ASSERT(!GetDispatchFlag());
290       MOZ_ASSERT(!mSelfRef, "We shouldn't be holding a self reference!");
291       return false;
292     }
293     if (!mSelfRef) {
294       MOZ_ASSERT(!mPendingPromisesCount);
295       mSelfRef = this;
296     }
297 
298     ++mPendingPromisesCount;
299     aPromise.AppendNativeHandler(this);
300 
301     return true;
302   }
303 
ResolvedCallback(JSContext * aCx,JS::Handle<JS::Value> aValue,ErrorResult & aRv)304   void ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue,
305                         ErrorResult& aRv) override {
306     RemovePromise(Resolved);
307   }
308 
RejectedCallback(JSContext * aCx,JS::Handle<JS::Value> aValue,ErrorResult & aRv)309   void RejectedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue,
310                         ErrorResult& aRv) override {
311     RemovePromise(Rejected);
312   }
313 
MaybeDone()314   void MaybeDone() {
315     MOZ_ASSERT(IsCurrentThreadRunningWorker());
316     MOZ_ASSERT(!GetDispatchFlag());
317 
318     if (mPendingPromisesCount || !mKeepAliveToken) {
319       return;
320     }
321     if (mCallback) {
322       mCallback->FinishedWithResult(mRejected ? Rejected : Resolved);
323     }
324 
325     MaybeCleanup();
326   }
327 
328  private:
~KeepAliveHandler()329   ~KeepAliveHandler() { MaybeCleanup(); }
330 
MaybeCleanup()331   void MaybeCleanup() {
332     MOZ_ASSERT(IsCurrentThreadRunningWorker());
333 
334     if (!mKeepAliveToken) {
335       return;
336     }
337 
338     mWorkerRef = nullptr;
339     mKeepAliveToken = nullptr;
340     mSelfRef = nullptr;
341   }
342 
343   class MaybeDoneRunner : public MicroTaskRunnable {
344    public:
MaybeDoneRunner(KeepAliveHandler * aHandler)345     explicit MaybeDoneRunner(KeepAliveHandler* aHandler) : mHandler(aHandler) {}
Run(AutoSlowOperation & aAso)346     virtual void Run(AutoSlowOperation& aAso) override {
347       mHandler->MaybeDone();
348     }
349 
350     RefPtr<KeepAliveHandler> mHandler;
351   };
352 
RemovePromise(ExtendableEventResult aResult)353   void RemovePromise(ExtendableEventResult aResult) {
354     MOZ_ASSERT(IsCurrentThreadRunningWorker());
355     MOZ_DIAGNOSTIC_ASSERT(mPendingPromisesCount > 0);
356 
357     // Note: mSelfRef and mKeepAliveToken can be nullptr here
358     //       if MaybeCleanup() was called just before a promise
359     //       settled.  This can happen, for example, if the
360     //       worker thread is being terminated for running too
361     //       long, browser shutdown, etc.
362 
363     mRejected |= (aResult == Rejected);
364 
365     --mPendingPromisesCount;
366     if (mPendingPromisesCount || GetDispatchFlag()) {
367       return;
368     }
369 
370     CycleCollectedJSContext* cx = CycleCollectedJSContext::Get();
371     MOZ_ASSERT(cx);
372 
373     RefPtr<MaybeDoneRunner> r = new MaybeDoneRunner(this);
374     cx->DispatchToMicroTask(r.forget());
375   }
376 };
377 
378 NS_IMPL_ISUPPORTS0(KeepAliveHandler)
379 
380 class RegistrationUpdateRunnable : public Runnable {
381   nsMainThreadPtrHandle<ServiceWorkerRegistrationInfo> mRegistration;
382   const bool mNeedTimeCheck;
383 
384  public:
RegistrationUpdateRunnable(nsMainThreadPtrHandle<ServiceWorkerRegistrationInfo> & aRegistration,bool aNeedTimeCheck)385   RegistrationUpdateRunnable(
386       nsMainThreadPtrHandle<ServiceWorkerRegistrationInfo>& aRegistration,
387       bool aNeedTimeCheck)
388       : Runnable("dom::RegistrationUpdateRunnable"),
389         mRegistration(aRegistration),
390         mNeedTimeCheck(aNeedTimeCheck) {
391     MOZ_DIAGNOSTIC_ASSERT(mRegistration);
392   }
393 
394   NS_IMETHOD
Run()395   Run() override {
396     if (mNeedTimeCheck) {
397       mRegistration->MaybeScheduleTimeCheckAndUpdate();
398     } else {
399       mRegistration->MaybeScheduleUpdate();
400     }
401     return NS_OK;
402   }
403 };
404 
405 class ExtendableEventWorkerRunnable : public WorkerRunnable {
406  protected:
407   nsMainThreadPtrHandle<KeepAliveToken> mKeepAliveToken;
408 
409  public:
ExtendableEventWorkerRunnable(WorkerPrivate * aWorkerPrivate,KeepAliveToken * aKeepAliveToken)410   ExtendableEventWorkerRunnable(WorkerPrivate* aWorkerPrivate,
411                                 KeepAliveToken* aKeepAliveToken)
412       : WorkerRunnable(aWorkerPrivate) {
413     MOZ_ASSERT(NS_IsMainThread());
414     MOZ_ASSERT(aWorkerPrivate);
415     MOZ_ASSERT(aKeepAliveToken);
416 
417     mKeepAliveToken = new nsMainThreadPtrHolder<KeepAliveToken>(
418         "ExtendableEventWorkerRunnable::mKeepAliveToken", aKeepAliveToken);
419   }
420 
DispatchExtendableEventOnWorkerScope(JSContext * aCx,WorkerGlobalScope * aWorkerScope,ExtendableEvent * aEvent,ExtendableEventCallback * aCallback)421   nsresult DispatchExtendableEventOnWorkerScope(
422       JSContext* aCx, WorkerGlobalScope* aWorkerScope, ExtendableEvent* aEvent,
423       ExtendableEventCallback* aCallback) {
424     MOZ_ASSERT(aWorkerScope);
425     MOZ_ASSERT(aEvent);
426     nsCOMPtr<nsIGlobalObject> sgo = aWorkerScope;
427     WidgetEvent* internalEvent = aEvent->WidgetEventPtr();
428 
429     RefPtr<KeepAliveHandler> keepAliveHandler =
430         new KeepAliveHandler(mKeepAliveToken, aCallback);
431     if (NS_WARN_IF(!keepAliveHandler->Init())) {
432       return NS_ERROR_FAILURE;
433     }
434 
435     // This must always be set *before* dispatching the event, otherwise
436     // waitUntil calls will fail.
437     aEvent->SetKeepAliveHandler(keepAliveHandler);
438 
439     ErrorResult result;
440     aWorkerScope->DispatchEvent(*aEvent, result);
441     if (NS_WARN_IF(result.Failed())) {
442       result.SuppressException();
443       return NS_ERROR_FAILURE;
444     }
445 
446     // [[ If e’s extend lifetime promises is empty, unset e’s extensions allowed
447     //    flag and abort these steps. ]]
448     keepAliveHandler->MaybeDone();
449 
450     // We don't block the event when getting an exception but still report the
451     // error message.
452     // Report exception message. Note: This will not stop the event.
453     if (internalEvent->mFlags.mExceptionWasRaised) {
454       result.SuppressException();
455       return NS_ERROR_XPC_JS_THREW_EXCEPTION;
456     }
457 
458     return NS_OK;
459   }
460 };
461 
462 class SendMessageEventRunnable final : public ExtendableEventWorkerRunnable {
463   const ClientInfoAndState mClientInfoAndState;
464   RefPtr<ServiceWorkerCloneData> mData;
465 
466  public:
SendMessageEventRunnable(WorkerPrivate * aWorkerPrivate,KeepAliveToken * aKeepAliveToken,const ClientInfoAndState & aClientInfoAndState,RefPtr<ServiceWorkerCloneData> && aData)467   SendMessageEventRunnable(WorkerPrivate* aWorkerPrivate,
468                            KeepAliveToken* aKeepAliveToken,
469                            const ClientInfoAndState& aClientInfoAndState,
470                            RefPtr<ServiceWorkerCloneData>&& aData)
471       : ExtendableEventWorkerRunnable(aWorkerPrivate, aKeepAliveToken),
472         mClientInfoAndState(aClientInfoAndState),
473         mData(std::move(aData)) {
474     MOZ_ASSERT(NS_IsMainThread());
475     MOZ_DIAGNOSTIC_ASSERT(mData);
476   }
477 
WorkerRun(JSContext * aCx,WorkerPrivate * aWorkerPrivate)478   bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override {
479     JS::Rooted<JS::Value> messageData(aCx);
480     nsCOMPtr<nsIGlobalObject> sgo = aWorkerPrivate->GlobalScope();
481     ErrorResult rv;
482     mData->Read(aCx, &messageData, rv);
483 
484     // If deserialization fails, we will fire a messageerror event
485     bool deserializationFailed = rv.Failed();
486 
487     if (!deserializationFailed && NS_WARN_IF(rv.Failed())) {
488       rv.SuppressException();
489       return true;
490     }
491 
492     Sequence<OwningNonNull<MessagePort>> ports;
493     if (!mData->TakeTransferredPortsAsSequence(ports)) {
494       return true;
495     }
496 
497     RootedDictionary<ExtendableMessageEventInit> init(aCx);
498 
499     init.mBubbles = false;
500     init.mCancelable = false;
501 
502     // On a messageerror event, we disregard ports:
503     // https://w3c.github.io/ServiceWorker/#service-worker-postmessage
504     if (!deserializationFailed) {
505       init.mData = messageData;
506       init.mPorts = std::move(ports);
507     }
508 
509     init.mSource.SetValue().SetAsClient() =
510         new Client(sgo, mClientInfoAndState);
511 
512     rv.SuppressException();
513     RefPtr<EventTarget> target = aWorkerPrivate->GlobalScope();
514     RefPtr<ExtendableMessageEvent> extendableEvent =
515         ExtendableMessageEvent::Constructor(
516             target, deserializationFailed ? u"messageerror"_ns : u"message"_ns,
517             init);
518 
519     extendableEvent->SetTrusted(true);
520 
521     return NS_SUCCEEDED(DispatchExtendableEventOnWorkerScope(
522         aCx, aWorkerPrivate->GlobalScope(), extendableEvent, nullptr));
523   }
524 };
525 
526 }  // anonymous namespace
527 
SendMessageEvent(RefPtr<ServiceWorkerCloneData> && aData,const ClientInfoAndState & aClientInfoAndState)528 nsresult ServiceWorkerPrivate::SendMessageEvent(
529     RefPtr<ServiceWorkerCloneData>&& aData,
530     const ClientInfoAndState& aClientInfoAndState) {
531   MOZ_ASSERT(NS_IsMainThread());
532 
533   if (mInner) {
534     return mInner->SendMessageEvent(std::move(aData), aClientInfoAndState);
535   }
536 
537   nsresult rv = SpawnWorkerIfNeeded(MessageEvent);
538   NS_ENSURE_SUCCESS(rv, rv);
539 
540   RefPtr<KeepAliveToken> token = CreateEventKeepAliveToken();
541   RefPtr<SendMessageEventRunnable> runnable = new SendMessageEventRunnable(
542       mWorkerPrivate, token, aClientInfoAndState, std::move(aData));
543 
544   if (!runnable->Dispatch()) {
545     return NS_ERROR_FAILURE;
546   }
547 
548   return NS_OK;
549 }
550 
551 namespace {
552 
553 // Handle functional event
554 // 9.9.7 If the time difference in seconds calculated by the current time minus
555 // registration's last update check time is greater than 86400, invoke Soft
556 // Update algorithm.
557 class ExtendableFunctionalEventWorkerRunnable
558     : public ExtendableEventWorkerRunnable {
559  protected:
560   nsMainThreadPtrHandle<ServiceWorkerRegistrationInfo> mRegistration;
561 
562  public:
ExtendableFunctionalEventWorkerRunnable(WorkerPrivate * aWorkerPrivate,KeepAliveToken * aKeepAliveToken,nsMainThreadPtrHandle<ServiceWorkerRegistrationInfo> & aRegistration)563   ExtendableFunctionalEventWorkerRunnable(
564       WorkerPrivate* aWorkerPrivate, KeepAliveToken* aKeepAliveToken,
565       nsMainThreadPtrHandle<ServiceWorkerRegistrationInfo>& aRegistration)
566       : ExtendableEventWorkerRunnable(aWorkerPrivate, aKeepAliveToken),
567         mRegistration(aRegistration) {
568     MOZ_DIAGNOSTIC_ASSERT(aRegistration);
569   }
570 
PostRun(JSContext * aCx,WorkerPrivate * aWorkerPrivate,bool aRunResult)571   void PostRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
572                bool aRunResult) override {
573     // Sub-class PreRun() or WorkerRun() methods could clear our mRegistration.
574     if (mRegistration) {
575       nsCOMPtr<nsIRunnable> runnable =
576           new RegistrationUpdateRunnable(mRegistration, true /* time check */);
577       aWorkerPrivate->DispatchToMainThread(runnable.forget());
578     }
579 
580     ExtendableEventWorkerRunnable::PostRun(aCx, aWorkerPrivate, aRunResult);
581   }
582 };
583 
584 /*
585  * Fires 'install' event on the ServiceWorkerGlobalScope. Modifies busy count
586  * since it fires the event. This is ok since there can't be nested
587  * ServiceWorkers, so the parent thread -> worker thread requirement for
588  * runnables is satisfied.
589  */
590 class LifecycleEventWorkerRunnable : public ExtendableEventWorkerRunnable {
591   nsString mEventName;
592   RefPtr<LifeCycleEventCallback> mCallback;
593 
594  public:
LifecycleEventWorkerRunnable(WorkerPrivate * aWorkerPrivate,KeepAliveToken * aToken,const nsAString & aEventName,LifeCycleEventCallback * aCallback)595   LifecycleEventWorkerRunnable(WorkerPrivate* aWorkerPrivate,
596                                KeepAliveToken* aToken,
597                                const nsAString& aEventName,
598                                LifeCycleEventCallback* aCallback)
599       : ExtendableEventWorkerRunnable(aWorkerPrivate, aToken),
600         mEventName(aEventName),
601         mCallback(aCallback) {
602     MOZ_ASSERT(NS_IsMainThread());
603   }
604 
WorkerRun(JSContext * aCx,WorkerPrivate * aWorkerPrivate)605   bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override {
606     MOZ_ASSERT(aWorkerPrivate);
607     return DispatchLifecycleEvent(aCx, aWorkerPrivate);
608   }
609 
Cancel()610   nsresult Cancel() override {
611     // We need to check first if cancel is permitted
612     nsresult rv = WorkerRunnable::Cancel();
613     NS_ENSURE_SUCCESS(rv, rv);
614 
615     mCallback->SetResult(false);
616     MOZ_ALWAYS_SUCCEEDS(mWorkerPrivate->DispatchToMainThread(mCallback));
617 
618     return NS_OK;
619   }
620 
621  private:
622   bool DispatchLifecycleEvent(JSContext* aCx, WorkerPrivate* aWorkerPrivate);
623 };
624 
625 /*
626  * Used to handle ExtendableEvent::waitUntil() and catch abnormal worker
627  * termination during the execution of life cycle events. It is responsible
628  * with advancing the job queue for install/activate tasks.
629  */
630 class LifeCycleEventWatcher final : public ExtendableEventCallback {
631   RefPtr<StrongWorkerRef> mWorkerRef;
632   RefPtr<LifeCycleEventCallback> mCallback;
633 
~LifeCycleEventWatcher()634   ~LifeCycleEventWatcher() {
635     // XXXcatalinb: If all the promises passed to waitUntil go out of scope,
636     // the resulting Promise.all will be cycle collected and it will drop its
637     // native handlers (including this object). Instead of waiting for a timeout
638     // we report the failure now.
639     ReportResult(false);
640   }
641 
642  public:
NS_INLINE_DECL_REFCOUNTING(LifeCycleEventWatcher,override)643   NS_INLINE_DECL_REFCOUNTING(LifeCycleEventWatcher, override)
644 
645   explicit LifeCycleEventWatcher(LifeCycleEventCallback* aCallback)
646       : mCallback(aCallback) {
647     MOZ_ASSERT(IsCurrentThreadRunningWorker());
648   }
649 
Init()650   bool Init() {
651     WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
652     MOZ_ASSERT(workerPrivate);
653 
654     // We need to listen for worker termination in case the event handler
655     // never completes or never resolves the waitUntil promise. There are
656     // two possible scenarios:
657     // 1. The keepAlive token expires and the worker is terminated, in which
658     //    case the registration/update promise will be rejected
659     // 2. A new service worker is registered which will terminate the current
660     //    installing worker.
661     RefPtr<LifeCycleEventWatcher> self = this;
662     mWorkerRef =
663         StrongWorkerRef::Create(workerPrivate, "LifeCycleEventWatcher",
664                                 [self]() { self->ReportResult(false); });
665     if (NS_WARN_IF(!mWorkerRef)) {
666       mCallback->SetResult(false);
667       // Using DispatchToMainThreadForMessaging so that state update on
668       // the main thread doesn't happen too soon.
669       nsresult rv = workerPrivate->DispatchToMainThreadForMessaging(mCallback);
670       Unused << NS_WARN_IF(NS_FAILED(rv));
671       return false;
672     }
673 
674     return true;
675   }
676 
ReportResult(bool aResult)677   void ReportResult(bool aResult) {
678     MOZ_ASSERT(IsCurrentThreadRunningWorker());
679 
680     if (!mWorkerRef) {
681       return;
682     }
683 
684     mCallback->SetResult(aResult);
685     // Using DispatchToMainThreadForMessaging so that state update on
686     // the main thread doesn't happen too soon.
687     nsresult rv =
688         mWorkerRef->Private()->DispatchToMainThreadForMessaging(mCallback);
689     if (NS_WARN_IF(NS_FAILED(rv))) {
690       MOZ_CRASH("Failed to dispatch life cycle event handler.");
691     }
692 
693     mWorkerRef = nullptr;
694   }
695 
FinishedWithResult(ExtendableEventResult aResult)696   void FinishedWithResult(ExtendableEventResult aResult) override {
697     MOZ_ASSERT(IsCurrentThreadRunningWorker());
698     ReportResult(aResult == Resolved);
699 
700     // Note, all WaitUntil() rejections are reported to client consoles
701     // by the WaitUntilHandler in ServiceWorkerEvents.  This ensures that
702     // errors in non-lifecycle events like FetchEvent and PushEvent are
703     // reported properly.
704   }
705 };
706 
DispatchLifecycleEvent(JSContext * aCx,WorkerPrivate * aWorkerPrivate)707 bool LifecycleEventWorkerRunnable::DispatchLifecycleEvent(
708     JSContext* aCx, WorkerPrivate* aWorkerPrivate) {
709   aWorkerPrivate->AssertIsOnWorkerThread();
710   MOZ_ASSERT(aWorkerPrivate->IsServiceWorker());
711 
712   RefPtr<ExtendableEvent> event;
713   RefPtr<EventTarget> target = aWorkerPrivate->GlobalScope();
714 
715   if (mEventName.EqualsASCII("install") || mEventName.EqualsASCII("activate")) {
716     ExtendableEventInit init;
717     init.mBubbles = false;
718     init.mCancelable = false;
719     event = ExtendableEvent::Constructor(target, mEventName, init);
720   } else {
721     MOZ_CRASH("Unexpected lifecycle event");
722   }
723 
724   event->SetTrusted(true);
725 
726   // It is important to initialize the watcher before actually dispatching
727   // the event in order to catch worker termination while the event handler
728   // is still executing. This can happen with infinite loops, for example.
729   RefPtr<LifeCycleEventWatcher> watcher = new LifeCycleEventWatcher(mCallback);
730 
731   if (!watcher->Init()) {
732     return true;
733   }
734 
735   nsresult rv = DispatchExtendableEventOnWorkerScope(
736       aCx, aWorkerPrivate->GlobalScope(), event, watcher);
737   // Do not fail event processing when an exception is thrown.
738   if (NS_FAILED(rv) && rv != NS_ERROR_XPC_JS_THREW_EXCEPTION) {
739     watcher->ReportResult(false);
740   }
741 
742   return true;
743 }
744 
745 }  // anonymous namespace
746 
SendLifeCycleEvent(const nsAString & aEventType,LifeCycleEventCallback * aCallback)747 nsresult ServiceWorkerPrivate::SendLifeCycleEvent(
748     const nsAString& aEventType, LifeCycleEventCallback* aCallback) {
749   MOZ_ASSERT(NS_IsMainThread());
750 
751   if (mInner) {
752     return mInner->SendLifeCycleEvent(aEventType, aCallback);
753   }
754 
755   nsresult rv = SpawnWorkerIfNeeded(LifeCycleEvent);
756   NS_ENSURE_SUCCESS(rv, rv);
757 
758   RefPtr<KeepAliveToken> token = CreateEventKeepAliveToken();
759   RefPtr<WorkerRunnable> r = new LifecycleEventWorkerRunnable(
760       mWorkerPrivate, token, aEventType, aCallback);
761   if (NS_WARN_IF(!r->Dispatch())) {
762     return NS_ERROR_FAILURE;
763   }
764 
765   return NS_OK;
766 }
767 
768 namespace {
769 
770 class PushErrorReporter final : public ExtendableEventCallback {
771   WorkerPrivate* mWorkerPrivate;
772   nsString mMessageId;
773 
774   ~PushErrorReporter() = default;
775 
776  public:
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(PushErrorReporter,override)777   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(PushErrorReporter, override)
778 
779   PushErrorReporter(WorkerPrivate* aWorkerPrivate, const nsAString& aMessageId)
780       : mWorkerPrivate(aWorkerPrivate), mMessageId(aMessageId) {
781     mWorkerPrivate->AssertIsOnWorkerThread();
782   }
783 
FinishedWithResult(ExtendableEventResult aResult)784   void FinishedWithResult(ExtendableEventResult aResult) override {
785     if (aResult == Rejected) {
786       Report(nsIPushErrorReporter::DELIVERY_UNHANDLED_REJECTION);
787     }
788   }
789 
Report(uint16_t aReason=nsIPushErrorReporter::DELIVERY_INTERNAL_ERROR)790   void Report(
791       uint16_t aReason = nsIPushErrorReporter::DELIVERY_INTERNAL_ERROR) {
792     WorkerPrivate* workerPrivate = mWorkerPrivate;
793     mWorkerPrivate->AssertIsOnWorkerThread();
794 
795     if (NS_WARN_IF(aReason > nsIPushErrorReporter::DELIVERY_INTERNAL_ERROR) ||
796         mMessageId.IsEmpty()) {
797       return;
798     }
799     nsCOMPtr<nsIRunnable> runnable = NewRunnableMethod<uint16_t>(
800         "dom::PushErrorReporter::ReportOnMainThread", this,
801         &PushErrorReporter::ReportOnMainThread, aReason);
802     MOZ_ALWAYS_TRUE(
803         NS_SUCCEEDED(workerPrivate->DispatchToMainThread(runnable.forget())));
804   }
805 
ReportOnMainThread(uint16_t aReason)806   void ReportOnMainThread(uint16_t aReason) {
807     MOZ_ASSERT(NS_IsMainThread());
808     nsCOMPtr<nsIPushErrorReporter> reporter =
809         do_GetService("@mozilla.org/push/Service;1");
810     if (reporter) {
811       nsresult rv = reporter->ReportDeliveryError(mMessageId, aReason);
812       Unused << NS_WARN_IF(NS_FAILED(rv));
813     }
814   }
815 };
816 
817 class SendPushEventRunnable final
818     : public ExtendableFunctionalEventWorkerRunnable {
819   nsString mMessageId;
820   Maybe<nsTArray<uint8_t>> mData;
821 
822  public:
SendPushEventRunnable(WorkerPrivate * aWorkerPrivate,KeepAliveToken * aKeepAliveToken,const nsAString & aMessageId,const Maybe<nsTArray<uint8_t>> & aData,nsMainThreadPtrHandle<ServiceWorkerRegistrationInfo> aRegistration)823   SendPushEventRunnable(
824       WorkerPrivate* aWorkerPrivate, KeepAliveToken* aKeepAliveToken,
825       const nsAString& aMessageId, const Maybe<nsTArray<uint8_t>>& aData,
826       nsMainThreadPtrHandle<ServiceWorkerRegistrationInfo> aRegistration)
827       : ExtendableFunctionalEventWorkerRunnable(aWorkerPrivate, aKeepAliveToken,
828                                                 aRegistration),
829         mMessageId(aMessageId),
830         mData(aData ? Some(aData->Clone()) : Nothing()) {
831     MOZ_ASSERT(NS_IsMainThread());
832     MOZ_ASSERT(aWorkerPrivate);
833     MOZ_ASSERT(aWorkerPrivate->IsServiceWorker());
834   }
835 
WorkerRun(JSContext * aCx,WorkerPrivate * aWorkerPrivate)836   bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override {
837     MOZ_ASSERT(aWorkerPrivate);
838     GlobalObject globalObj(aCx, aWorkerPrivate->GlobalScope()->GetWrapper());
839 
840     RefPtr<PushErrorReporter> errorReporter =
841         new PushErrorReporter(aWorkerPrivate, mMessageId);
842 
843     RootedDictionary<PushEventInit> pei(aCx);
844     if (mData) {
845       const nsTArray<uint8_t>& bytes = mData.ref();
846       JSObject* data =
847           Uint8Array::Create(aCx, bytes.Length(), bytes.Elements());
848       if (!data) {
849         errorReporter->Report();
850         return false;
851       }
852       pei.mData.Construct().SetAsArrayBufferView().Init(data);
853     }
854     pei.mBubbles = false;
855     pei.mCancelable = false;
856 
857     ErrorResult result;
858     RefPtr<PushEvent> event =
859         PushEvent::Constructor(globalObj, u"push"_ns, pei, result);
860     if (NS_WARN_IF(result.Failed())) {
861       result.SuppressException();
862       errorReporter->Report();
863       return false;
864     }
865     event->SetTrusted(true);
866 
867     nsresult rv = DispatchExtendableEventOnWorkerScope(
868         aCx, aWorkerPrivate->GlobalScope(), event, errorReporter);
869     if (NS_FAILED(rv)) {
870       // We don't cancel WorkerPrivate when catching an excetpion.
871       errorReporter->Report(nsIPushErrorReporter::DELIVERY_UNCAUGHT_EXCEPTION);
872     }
873 
874     return true;
875   }
876 };
877 
878 class SendPushSubscriptionChangeEventRunnable final
879     : public ExtendableEventWorkerRunnable {
880  public:
SendPushSubscriptionChangeEventRunnable(WorkerPrivate * aWorkerPrivate,KeepAliveToken * aKeepAliveToken)881   explicit SendPushSubscriptionChangeEventRunnable(
882       WorkerPrivate* aWorkerPrivate, KeepAliveToken* aKeepAliveToken)
883       : ExtendableEventWorkerRunnable(aWorkerPrivate, aKeepAliveToken) {
884     MOZ_ASSERT(NS_IsMainThread());
885     MOZ_ASSERT(aWorkerPrivate);
886     MOZ_ASSERT(aWorkerPrivate->IsServiceWorker());
887   }
888 
WorkerRun(JSContext * aCx,WorkerPrivate * aWorkerPrivate)889   bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override {
890     MOZ_ASSERT(aWorkerPrivate);
891 
892     RefPtr<EventTarget> target = aWorkerPrivate->GlobalScope();
893 
894     ExtendableEventInit init;
895     init.mBubbles = false;
896     init.mCancelable = false;
897 
898     RefPtr<ExtendableEvent> event = ExtendableEvent::Constructor(
899         target, u"pushsubscriptionchange"_ns, init);
900 
901     event->SetTrusted(true);
902 
903     DispatchExtendableEventOnWorkerScope(aCx, aWorkerPrivate->GlobalScope(),
904                                          event, nullptr);
905 
906     return true;
907   }
908 };
909 
910 }  // anonymous namespace
911 
SendPushEvent(const nsAString & aMessageId,const Maybe<nsTArray<uint8_t>> & aData,ServiceWorkerRegistrationInfo * aRegistration)912 nsresult ServiceWorkerPrivate::SendPushEvent(
913     const nsAString& aMessageId, const Maybe<nsTArray<uint8_t>>& aData,
914     ServiceWorkerRegistrationInfo* aRegistration) {
915   MOZ_ASSERT(NS_IsMainThread());
916 
917   if (mInner) {
918     return mInner->SendPushEvent(aRegistration, aMessageId, aData);
919   }
920 
921   nsresult rv = SpawnWorkerIfNeeded(PushEvent);
922   NS_ENSURE_SUCCESS(rv, rv);
923 
924   RefPtr<KeepAliveToken> token = CreateEventKeepAliveToken();
925 
926   nsMainThreadPtrHandle<ServiceWorkerRegistrationInfo> regInfo(
927       new nsMainThreadPtrHolder<ServiceWorkerRegistrationInfo>(
928           "ServiceWorkerRegistrationInfoProxy", aRegistration, false));
929 
930   RefPtr<WorkerRunnable> r = new SendPushEventRunnable(
931       mWorkerPrivate, token, aMessageId, aData, regInfo);
932 
933   if (mInfo->State() == ServiceWorkerState::Activating) {
934     mPendingFunctionalEvents.AppendElement(r.forget());
935     return NS_OK;
936   }
937 
938   MOZ_ASSERT(mInfo->State() == ServiceWorkerState::Activated);
939 
940   if (NS_WARN_IF(!r->Dispatch())) {
941     return NS_ERROR_FAILURE;
942   }
943 
944   return NS_OK;
945 }
946 
SendPushSubscriptionChangeEvent()947 nsresult ServiceWorkerPrivate::SendPushSubscriptionChangeEvent() {
948   MOZ_ASSERT(NS_IsMainThread());
949 
950   if (mInner) {
951     return mInner->SendPushSubscriptionChangeEvent();
952   }
953 
954   nsresult rv = SpawnWorkerIfNeeded(PushSubscriptionChangeEvent);
955   NS_ENSURE_SUCCESS(rv, rv);
956 
957   RefPtr<KeepAliveToken> token = CreateEventKeepAliveToken();
958   RefPtr<WorkerRunnable> r =
959       new SendPushSubscriptionChangeEventRunnable(mWorkerPrivate, token);
960   if (NS_WARN_IF(!r->Dispatch())) {
961     return NS_ERROR_FAILURE;
962   }
963 
964   return NS_OK;
965 }
966 
967 namespace {
968 
969 class AllowWindowInteractionHandler final : public ExtendableEventCallback,
970                                             public nsITimerCallback,
971                                             public nsINamed {
972   nsCOMPtr<nsITimer> mTimer;
973   RefPtr<StrongWorkerRef> mWorkerRef;
974 
~AllowWindowInteractionHandler()975   ~AllowWindowInteractionHandler() {
976     // We must either fail to initialize or call ClearWindowAllowed.
977     MOZ_DIAGNOSTIC_ASSERT(!mTimer);
978     MOZ_DIAGNOSTIC_ASSERT(!mWorkerRef);
979   }
980 
ClearWindowAllowed(WorkerPrivate * aWorkerPrivate)981   void ClearWindowAllowed(WorkerPrivate* aWorkerPrivate) {
982     MOZ_ASSERT(aWorkerPrivate);
983     aWorkerPrivate->AssertIsOnWorkerThread();
984 
985     if (!mTimer) {
986       return;
987     }
988 
989     // XXXcatalinb: This *might* be executed after the global was unrooted, in
990     // which case GlobalScope() will return null. Making the check here just
991     // to be safe.
992     WorkerGlobalScope* globalScope = aWorkerPrivate->GlobalScope();
993     if (!globalScope) {
994       return;
995     }
996 
997     globalScope->ConsumeWindowInteraction();
998     mTimer->Cancel();
999     mTimer = nullptr;
1000 
1001     mWorkerRef = nullptr;
1002   }
1003 
StartClearWindowTimer(WorkerPrivate * aWorkerPrivate)1004   void StartClearWindowTimer(WorkerPrivate* aWorkerPrivate) {
1005     MOZ_ASSERT(aWorkerPrivate);
1006     aWorkerPrivate->AssertIsOnWorkerThread();
1007     MOZ_ASSERT(!mTimer);
1008 
1009     nsresult rv;
1010     nsCOMPtr<nsITimer> timer =
1011         NS_NewTimer(aWorkerPrivate->ControlEventTarget());
1012     if (NS_WARN_IF(!timer)) {
1013       return;
1014     }
1015 
1016     MOZ_ASSERT(!mWorkerRef);
1017     RefPtr<AllowWindowInteractionHandler> self = this;
1018     mWorkerRef = StrongWorkerRef::Create(
1019         aWorkerPrivate, "AllowWindowInteractionHandler", [self]() {
1020           // We could try to hold the worker alive until the timer fires, but
1021           // other APIs are not likely to work in this partially shutdown state.
1022           // We might as well let the worker thread exit.
1023           self->ClearWindowAllowed(self->mWorkerRef->Private());
1024         });
1025 
1026     if (!mWorkerRef) {
1027       return;
1028     }
1029 
1030     aWorkerPrivate->GlobalScope()->AllowWindowInteraction();
1031     timer.swap(mTimer);
1032 
1033     // We swap first and then initialize the timer so that even if initializing
1034     // fails, we still clean the busy count and interaction count correctly.
1035     // The timer can't be initialized before modifying the busy count since the
1036     // timer thread could run and call the timeout but the worker may
1037     // already be terminating and modifying the busy count could fail.
1038     rv = mTimer->InitWithCallback(this, gDOMDisableOpenClickDelay,
1039                                   nsITimer::TYPE_ONE_SHOT);
1040     if (NS_WARN_IF(NS_FAILED(rv))) {
1041       ClearWindowAllowed(aWorkerPrivate);
1042       return;
1043     }
1044   }
1045 
1046   // nsITimerCallback virtual methods
1047   NS_IMETHOD
Notify(nsITimer * aTimer)1048   Notify(nsITimer* aTimer) override {
1049     MOZ_DIAGNOSTIC_ASSERT(mTimer == aTimer);
1050     WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
1051     ClearWindowAllowed(workerPrivate);
1052     return NS_OK;
1053   }
1054 
1055   // nsINamed virtual methods
1056   NS_IMETHOD
GetName(nsACString & aName)1057   GetName(nsACString& aName) override {
1058     aName.AssignLiteral("AllowWindowInteractionHandler");
1059     return NS_OK;
1060   }
1061 
1062  public:
1063   NS_DECL_THREADSAFE_ISUPPORTS
1064 
AllowWindowInteractionHandler(WorkerPrivate * aWorkerPrivate)1065   explicit AllowWindowInteractionHandler(WorkerPrivate* aWorkerPrivate) {
1066     StartClearWindowTimer(aWorkerPrivate);
1067   }
1068 
FinishedWithResult(ExtendableEventResult)1069   void FinishedWithResult(ExtendableEventResult /* aResult */) override {
1070     WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
1071     ClearWindowAllowed(workerPrivate);
1072   }
1073 };
1074 
1075 NS_IMPL_ISUPPORTS(AllowWindowInteractionHandler, nsITimerCallback, nsINamed)
1076 
1077 class SendNotificationEventRunnable final
1078     : public ExtendableEventWorkerRunnable {
1079   const nsString mEventName;
1080   const nsString mID;
1081   const nsString mTitle;
1082   const nsString mDir;
1083   const nsString mLang;
1084   const nsString mBody;
1085   const nsString mTag;
1086   const nsString mIcon;
1087   const nsString mData;
1088   const nsString mBehavior;
1089   const nsString mScope;
1090 
1091  public:
SendNotificationEventRunnable(WorkerPrivate * aWorkerPrivate,KeepAliveToken * aKeepAliveToken,const nsAString & aEventName,const nsAString & aID,const nsAString & aTitle,const nsAString & aDir,const nsAString & aLang,const nsAString & aBody,const nsAString & aTag,const nsAString & aIcon,const nsAString & aData,const nsAString & aBehavior,const nsAString & aScope)1092   SendNotificationEventRunnable(WorkerPrivate* aWorkerPrivate,
1093                                 KeepAliveToken* aKeepAliveToken,
1094                                 const nsAString& aEventName,
1095                                 const nsAString& aID, const nsAString& aTitle,
1096                                 const nsAString& aDir, const nsAString& aLang,
1097                                 const nsAString& aBody, const nsAString& aTag,
1098                                 const nsAString& aIcon, const nsAString& aData,
1099                                 const nsAString& aBehavior,
1100                                 const nsAString& aScope)
1101       : ExtendableEventWorkerRunnable(aWorkerPrivate, aKeepAliveToken),
1102         mEventName(aEventName),
1103         mID(aID),
1104         mTitle(aTitle),
1105         mDir(aDir),
1106         mLang(aLang),
1107         mBody(aBody),
1108         mTag(aTag),
1109         mIcon(aIcon),
1110         mData(aData),
1111         mBehavior(aBehavior),
1112         mScope(aScope) {
1113     MOZ_ASSERT(NS_IsMainThread());
1114     MOZ_ASSERT(aWorkerPrivate);
1115     MOZ_ASSERT(aWorkerPrivate->IsServiceWorker());
1116   }
1117 
WorkerRun(JSContext * aCx,WorkerPrivate * aWorkerPrivate)1118   bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override {
1119     MOZ_ASSERT(aWorkerPrivate);
1120 
1121     RefPtr<EventTarget> target = do_QueryObject(aWorkerPrivate->GlobalScope());
1122 
1123     ErrorResult result;
1124     RefPtr<Notification> notification = Notification::ConstructFromFields(
1125         aWorkerPrivate->GlobalScope(), mID, mTitle, mDir, mLang, mBody, mTag,
1126         mIcon, mData, mScope, result);
1127     if (NS_WARN_IF(result.Failed())) {
1128       return false;
1129     }
1130 
1131     NotificationEventInit nei;
1132     nei.mNotification = notification;
1133     nei.mBubbles = false;
1134     nei.mCancelable = false;
1135 
1136     RefPtr<NotificationEvent> event =
1137         NotificationEvent::Constructor(target, mEventName, nei);
1138 
1139     event->SetTrusted(true);
1140 
1141     RefPtr<AllowWindowInteractionHandler> allowWindowInteraction;
1142     if (mEventName.EqualsLiteral(NOTIFICATION_CLICK_EVENT_NAME)) {
1143       allowWindowInteraction =
1144           new AllowWindowInteractionHandler(aWorkerPrivate);
1145     }
1146 
1147     nsresult rv = DispatchExtendableEventOnWorkerScope(
1148         aCx, aWorkerPrivate->GlobalScope(), event, allowWindowInteraction);
1149     // Don't reject when catching an exception
1150     if (NS_FAILED(rv) && rv != NS_ERROR_XPC_JS_THREW_EXCEPTION &&
1151         allowWindowInteraction) {
1152       allowWindowInteraction->FinishedWithResult(Rejected);
1153     }
1154 
1155     return true;
1156   }
1157 };
1158 
1159 }  // namespace
1160 
SendNotificationEvent(const nsAString & aEventName,const nsAString & aID,const nsAString & aTitle,const nsAString & aDir,const nsAString & aLang,const nsAString & aBody,const nsAString & aTag,const nsAString & aIcon,const nsAString & aData,const nsAString & aBehavior,const nsAString & aScope)1161 nsresult ServiceWorkerPrivate::SendNotificationEvent(
1162     const nsAString& aEventName, const nsAString& aID, const nsAString& aTitle,
1163     const nsAString& aDir, const nsAString& aLang, const nsAString& aBody,
1164     const nsAString& aTag, const nsAString& aIcon, const nsAString& aData,
1165     const nsAString& aBehavior, const nsAString& aScope) {
1166   MOZ_ASSERT(NS_IsMainThread());
1167 
1168   WakeUpReason why;
1169   if (aEventName.EqualsLiteral(NOTIFICATION_CLICK_EVENT_NAME)) {
1170     why = NotificationClickEvent;
1171     gDOMDisableOpenClickDelay =
1172         Preferences::GetInt("dom.serviceWorkers.disable_open_click_delay");
1173   } else if (aEventName.EqualsLiteral(NOTIFICATION_CLOSE_EVENT_NAME)) {
1174     why = NotificationCloseEvent;
1175   } else {
1176     MOZ_ASSERT_UNREACHABLE("Invalid notification event name");
1177     return NS_ERROR_FAILURE;
1178   }
1179 
1180   if (mInner) {
1181     return mInner->SendNotificationEvent(aEventName, aID, aTitle, aDir, aLang,
1182                                          aBody, aTag, aIcon, aData, aBehavior,
1183                                          aScope, gDOMDisableOpenClickDelay);
1184   }
1185 
1186   nsresult rv = SpawnWorkerIfNeeded(why);
1187   NS_ENSURE_SUCCESS(rv, rv);
1188 
1189   RefPtr<KeepAliveToken> token = CreateEventKeepAliveToken();
1190 
1191   RefPtr<WorkerRunnable> r = new SendNotificationEventRunnable(
1192       mWorkerPrivate, token, aEventName, aID, aTitle, aDir, aLang, aBody, aTag,
1193       aIcon, aData, aBehavior, aScope);
1194   if (NS_WARN_IF(!r->Dispatch())) {
1195     return NS_ERROR_FAILURE;
1196   }
1197 
1198   return NS_OK;
1199 }
1200 
1201 namespace {
1202 
1203 // Inheriting ExtendableEventWorkerRunnable so that the worker is not terminated
1204 // while handling the fetch event, though that's very unlikely.
1205 class FetchEventRunnable : public ExtendableFunctionalEventWorkerRunnable,
1206                            public nsIHttpHeaderVisitor {
1207   nsMainThreadPtrHandle<nsIInterceptedChannel> mInterceptedChannel;
1208   const nsCString mScriptSpec;
1209   nsTArray<nsCString> mHeaderNames;
1210   nsTArray<nsCString> mHeaderValues;
1211   nsCString mSpec;
1212   nsCString mFragment;
1213   nsCString mMethod;
1214   nsString mClientId;
1215   nsString mResultingClientId;
1216   RequestCache mCacheMode;
1217   RequestMode mRequestMode;
1218   RequestRedirect mRequestRedirect;
1219   RequestCredentials mRequestCredentials;
1220   nsContentPolicyType mContentPolicyType;
1221   nsCOMPtr<nsIInputStream> mUploadStream;
1222   int64_t mUploadStreamContentLength;
1223   nsString mReferrer;
1224   ReferrerPolicy mReferrerPolicy;
1225   nsString mIntegrity;
1226   const bool mIsNonSubresourceRequest;
1227 
1228  public:
FetchEventRunnable(WorkerPrivate * aWorkerPrivate,KeepAliveToken * aKeepAliveToken,nsMainThreadPtrHandle<nsIInterceptedChannel> & aChannel,const nsACString & aScriptSpec,nsMainThreadPtrHandle<ServiceWorkerRegistrationInfo> & aRegistration,const nsAString & aClientId,const nsAString & aResultingClientId,bool aMarkLaunchServiceWorkerEnd,bool aIsNonSubresourceRequest)1229   FetchEventRunnable(
1230       WorkerPrivate* aWorkerPrivate, KeepAliveToken* aKeepAliveToken,
1231       nsMainThreadPtrHandle<nsIInterceptedChannel>& aChannel,
1232       // CSP checks might require the worker script spec
1233       // later on.
1234       const nsACString& aScriptSpec,
1235       nsMainThreadPtrHandle<ServiceWorkerRegistrationInfo>& aRegistration,
1236       const nsAString& aClientId, const nsAString& aResultingClientId,
1237       bool aMarkLaunchServiceWorkerEnd, bool aIsNonSubresourceRequest)
1238       : ExtendableFunctionalEventWorkerRunnable(aWorkerPrivate, aKeepAliveToken,
1239                                                 aRegistration),
1240         mInterceptedChannel(aChannel),
1241         mScriptSpec(aScriptSpec),
1242         mClientId(aClientId),
1243         mResultingClientId(aResultingClientId),
1244         mCacheMode(RequestCache::Default),
1245         mRequestMode(RequestMode::No_cors),
1246         mRequestRedirect(RequestRedirect::Follow)
1247         // By default we set it to same-origin since normal HTTP fetches always
1248         // send credentials to same-origin websites unless explicitly forbidden.
1249         ,
1250         mRequestCredentials(RequestCredentials::Same_origin),
1251         mContentPolicyType(nsIContentPolicy::TYPE_INVALID),
1252         mUploadStreamContentLength(-1),
1253         mReferrer(NS_LITERAL_STRING_FROM_CSTRING(kFETCH_CLIENT_REFERRER_STR)),
1254         mReferrerPolicy(ReferrerPolicy::_empty),
1255         mIsNonSubresourceRequest(aIsNonSubresourceRequest) {
1256     MOZ_ASSERT(aWorkerPrivate);
1257   }
1258 
1259   NS_DECL_ISUPPORTS_INHERITED
1260 
1261   NS_IMETHOD
VisitHeader(const nsACString & aHeader,const nsACString & aValue)1262   VisitHeader(const nsACString& aHeader, const nsACString& aValue) override {
1263     mHeaderNames.AppendElement(aHeader);
1264     mHeaderValues.AppendElement(aValue);
1265     return NS_OK;
1266   }
1267 
Init()1268   nsresult Init() {
1269     MOZ_ASSERT(NS_IsMainThread());
1270     nsCOMPtr<nsIChannel> channel;
1271     nsresult rv = mInterceptedChannel->GetChannel(getter_AddRefs(channel));
1272     NS_ENSURE_SUCCESS(rv, rv);
1273 
1274     nsCOMPtr<nsIURI> uri;
1275     rv = mInterceptedChannel->GetSecureUpgradedChannelURI(getter_AddRefs(uri));
1276     NS_ENSURE_SUCCESS(rv, rv);
1277 
1278     // Normally we rely on the Request constructor to strip the fragment, but
1279     // when creating the FetchEvent we bypass the constructor.  So strip the
1280     // fragment manually here instead.  We can't do it later when we create
1281     // the Request because that code executes off the main thread.
1282     nsCOMPtr<nsIURI> uriNoFragment;
1283     rv = NS_GetURIWithoutRef(uri, getter_AddRefs(uriNoFragment));
1284     NS_ENSURE_SUCCESS(rv, rv);
1285     rv = uriNoFragment->GetSpec(mSpec);
1286     NS_ENSURE_SUCCESS(rv, rv);
1287     rv = uri->GetRef(mFragment);
1288     NS_ENSURE_SUCCESS(rv, rv);
1289 
1290     uint32_t loadFlags;
1291     rv = channel->GetLoadFlags(&loadFlags);
1292     NS_ENSURE_SUCCESS(rv, rv);
1293     nsCOMPtr<nsILoadInfo> loadInfo = channel->LoadInfo();
1294     mContentPolicyType = loadInfo->InternalContentPolicyType();
1295 
1296     nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(channel);
1297     MOZ_ASSERT(httpChannel, "How come we don't have an HTTP channel?");
1298 
1299     mReferrerPolicy = ReferrerPolicy::_empty;
1300     mReferrer.Truncate();
1301     nsCOMPtr<nsIReferrerInfo> referrerInfo = httpChannel->GetReferrerInfo();
1302     if (referrerInfo) {
1303       mReferrerPolicy = referrerInfo->ReferrerPolicy();
1304       Unused << referrerInfo->GetComputedReferrerSpec(mReferrer);
1305     }
1306 
1307     rv = httpChannel->GetRequestMethod(mMethod);
1308     NS_ENSURE_SUCCESS(rv, rv);
1309 
1310     nsCOMPtr<nsIHttpChannelInternal> internalChannel =
1311         do_QueryInterface(httpChannel);
1312     NS_ENSURE_TRUE(internalChannel, NS_ERROR_NOT_AVAILABLE);
1313 
1314     mRequestMode = InternalRequest::MapChannelToRequestMode(channel);
1315 
1316     // This is safe due to static_asserts in ServiceWorkerManager.cpp.
1317     uint32_t redirectMode;
1318     rv = internalChannel->GetRedirectMode(&redirectMode);
1319     MOZ_ASSERT(NS_SUCCEEDED(rv));
1320     mRequestRedirect = static_cast<RequestRedirect>(redirectMode);
1321 
1322     // This is safe due to static_asserts in ServiceWorkerManager.cpp.
1323     uint32_t cacheMode;
1324     rv = internalChannel->GetFetchCacheMode(&cacheMode);
1325     MOZ_ASSERT(NS_SUCCEEDED(rv));
1326     mCacheMode = static_cast<RequestCache>(cacheMode);
1327 
1328     rv = internalChannel->GetIntegrityMetadata(mIntegrity);
1329     MOZ_ASSERT(NS_SUCCEEDED(rv));
1330 
1331     mRequestCredentials =
1332         InternalRequest::MapChannelToRequestCredentials(channel);
1333 
1334     rv = httpChannel->VisitNonDefaultRequestHeaders(this);
1335     NS_ENSURE_SUCCESS(rv, rv);
1336 
1337     nsCOMPtr<nsIUploadChannel2> uploadChannel = do_QueryInterface(httpChannel);
1338     if (uploadChannel) {
1339       MOZ_ASSERT(!mUploadStream);
1340       nsCOMPtr<nsIInputStream> uploadStream;
1341       rv = uploadChannel->CloneUploadStream(&mUploadStreamContentLength,
1342                                             getter_AddRefs(uploadStream));
1343       NS_ENSURE_SUCCESS(rv, rv);
1344       mUploadStream = uploadStream;
1345     }
1346 
1347     return NS_OK;
1348   }
1349 
WorkerRun(JSContext * aCx,WorkerPrivate * aWorkerPrivate)1350   bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override {
1351     MOZ_ASSERT(aWorkerPrivate);
1352 
1353     return DispatchFetchEvent(aCx, aWorkerPrivate);
1354   }
1355 
Cancel()1356   nsresult Cancel() override {
1357     // We need to check first if cancel is permitted
1358     nsresult rv = WorkerRunnable::Cancel();
1359     NS_ENSURE_SUCCESS(rv, rv);
1360 
1361     nsCOMPtr<nsIRunnable> runnable = new ResumeRequest(mInterceptedChannel);
1362     if (NS_FAILED(mWorkerPrivate->DispatchToMainThread(runnable))) {
1363       NS_WARNING("Failed to resume channel on FetchEventRunnable::Cancel()!\n");
1364     }
1365     return NS_OK;
1366   }
1367 
1368  private:
1369   ~FetchEventRunnable() = default;
1370 
1371   class ResumeRequest final : public Runnable {
1372     nsMainThreadPtrHandle<nsIInterceptedChannel> mChannel;
1373 
1374    public:
ResumeRequest(nsMainThreadPtrHandle<nsIInterceptedChannel> & aChannel)1375     explicit ResumeRequest(
1376         nsMainThreadPtrHandle<nsIInterceptedChannel>& aChannel)
1377         : Runnable("dom::FetchEventRunnable::ResumeRequest"),
1378           mChannel(aChannel) {}
1379 
Run()1380     NS_IMETHOD Run() override {
1381       MOZ_ASSERT(NS_IsMainThread());
1382 
1383       nsresult rv = mChannel->ResetInterception(false);
1384       if (NS_FAILED(rv)) {
1385         NS_WARNING("Failed to resume intercepted network request");
1386         mChannel->CancelInterception(rv);
1387       }
1388       return rv;
1389     }
1390   };
1391 
DispatchFetchEvent(JSContext * aCx,WorkerPrivate * aWorkerPrivate)1392   bool DispatchFetchEvent(JSContext* aCx, WorkerPrivate* aWorkerPrivate) {
1393     MOZ_ASSERT(aCx);
1394     MOZ_ASSERT(aWorkerPrivate);
1395     MOZ_ASSERT(aWorkerPrivate->IsServiceWorker());
1396     GlobalObject globalObj(aCx, aWorkerPrivate->GlobalScope()->GetWrapper());
1397 
1398     RefPtr<InternalHeaders> internalHeaders =
1399         new InternalHeaders(HeadersGuardEnum::Request);
1400     MOZ_ASSERT(mHeaderNames.Length() == mHeaderValues.Length());
1401     for (uint32_t i = 0; i < mHeaderNames.Length(); i++) {
1402       ErrorResult result;
1403       internalHeaders->Set(mHeaderNames[i], mHeaderValues[i], result);
1404       if (NS_WARN_IF(result.Failed())) {
1405         result.SuppressException();
1406         return false;
1407       }
1408     }
1409 
1410     ErrorResult result;
1411     internalHeaders->SetGuard(HeadersGuardEnum::Immutable, result);
1412     if (NS_WARN_IF(result.Failed())) {
1413       result.SuppressException();
1414       return false;
1415     }
1416     auto internalReq = MakeSafeRefPtr<InternalRequest>(
1417         mSpec, mFragment, mMethod, internalHeaders.forget(), mCacheMode,
1418         mRequestMode, mRequestRedirect, mRequestCredentials, mReferrer,
1419         mReferrerPolicy, mContentPolicyType, mIntegrity);
1420     internalReq->SetBody(mUploadStream, mUploadStreamContentLength);
1421 
1422     nsCOMPtr<nsIChannel> channel;
1423     nsresult rv = mInterceptedChannel->GetChannel(getter_AddRefs(channel));
1424     NS_ENSURE_SUCCESS(rv, false);
1425 
1426     nsCOMPtr<nsICacheInfoChannel> cic = do_QueryInterface(channel);
1427     if (cic && !cic->PreferredAlternativeDataTypes().IsEmpty()) {
1428       // TODO: the internal request probably needs all the preferred types.
1429       nsAutoCString alternativeDataType;
1430       alternativeDataType.Assign(
1431           cic->PreferredAlternativeDataTypes()[0].type());
1432       internalReq->SetPreferredAlternativeDataType(alternativeDataType);
1433     }
1434 
1435     nsCOMPtr<nsIGlobalObject> global =
1436         do_QueryInterface(globalObj.GetAsSupports());
1437     if (NS_WARN_IF(!global)) {
1438       return false;
1439     }
1440 
1441     // TODO This request object should be created with a AbortSignal object
1442     // which should be aborted if the loading is aborted. See bug 1394102.
1443     RefPtr<Request> request =
1444         new Request(global, internalReq.clonePtr(), nullptr);
1445 
1446     MOZ_ASSERT_IF(internalReq->IsNavigationRequest(),
1447                   request->Redirect() == RequestRedirect::Manual);
1448 
1449     RootedDictionary<FetchEventInit> init(aCx);
1450     init.mRequest = request;
1451     init.mBubbles = false;
1452     init.mCancelable = true;
1453     // Only expose the FetchEvent.clientId on subresource requests for now.
1454     // Once we implement .targetClientId we can then start exposing .clientId
1455     // on non-subresource requests as well.  See bug 1487534.
1456     if (!mClientId.IsEmpty() && !internalReq->IsNavigationRequest()) {
1457       init.mClientId = mClientId;
1458     }
1459 
1460     /*
1461      * https://w3c.github.io/ServiceWorker/#on-fetch-request-algorithm
1462      *
1463      * "If request is a non-subresource request and request’s
1464      * destination is not "report", initialize e’s resultingClientId attribute
1465      * to reservedClient’s [resultingClient's] id, and to the empty string
1466      * otherwise." (Step 18.8)
1467      */
1468     if (!mResultingClientId.IsEmpty() && mIsNonSubresourceRequest &&
1469         internalReq->Destination() != RequestDestination::Report) {
1470       init.mResultingClientId = mResultingClientId;
1471     }
1472 
1473     RefPtr<FetchEvent> event =
1474         FetchEvent::Constructor(globalObj, u"fetch"_ns, init);
1475 
1476     event->PostInit(mInterceptedChannel, mRegistration, mScriptSpec);
1477     event->SetTrusted(true);
1478 
1479     nsresult rv2 = DispatchExtendableEventOnWorkerScope(
1480         aCx, aWorkerPrivate->GlobalScope(), event, nullptr);
1481     if ((NS_WARN_IF(NS_FAILED(rv2)) &&
1482          rv2 != NS_ERROR_XPC_JS_THREW_EXCEPTION) ||
1483         !event->WaitToRespond()) {
1484       nsCOMPtr<nsIRunnable> runnable;
1485       MOZ_ASSERT(!aWorkerPrivate->UsesSystemPrincipal(),
1486                  "We don't support system-principal serviceworkers");
1487       if (event->DefaultPrevented(CallerType::NonSystem)) {
1488         runnable = new CancelChannelRunnable(mInterceptedChannel, mRegistration,
1489                                              NS_ERROR_INTERCEPTION_FAILED);
1490       } else {
1491         runnable = new ResumeRequest(mInterceptedChannel);
1492       }
1493 
1494       MOZ_ALWAYS_SUCCEEDS(
1495           mWorkerPrivate->DispatchToMainThread(runnable.forget()));
1496     }
1497 
1498     return true;
1499   }
1500 };
1501 
1502 NS_IMPL_ISUPPORTS_INHERITED(FetchEventRunnable, WorkerRunnable,
1503                             nsIHttpHeaderVisitor)
1504 
1505 }  // anonymous namespace
1506 
SendFetchEvent(nsIInterceptedChannel * aChannel,nsILoadGroup * aLoadGroup,const nsAString & aClientId,const nsAString & aResultingClientId)1507 nsresult ServiceWorkerPrivate::SendFetchEvent(
1508     nsIInterceptedChannel* aChannel, nsILoadGroup* aLoadGroup,
1509     const nsAString& aClientId, const nsAString& aResultingClientId) {
1510   MOZ_ASSERT(NS_IsMainThread());
1511 
1512   RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
1513   if (NS_WARN_IF(!mInfo || !swm)) {
1514     return NS_ERROR_FAILURE;
1515   }
1516 
1517   nsCOMPtr<nsIChannel> channel;
1518   nsresult rv = aChannel->GetChannel(getter_AddRefs(channel));
1519   NS_ENSURE_SUCCESS(rv, rv);
1520   bool isNonSubresourceRequest =
1521       nsContentUtils::IsNonSubresourceRequest(channel);
1522 
1523   RefPtr<ServiceWorkerRegistrationInfo> registration;
1524   if (isNonSubresourceRequest) {
1525     registration = swm->GetRegistration(mInfo->Principal(), mInfo->Scope());
1526   } else {
1527     nsCOMPtr<nsILoadInfo> loadInfo = channel->LoadInfo();
1528 
1529     // We'll check for a null registration below rather than an error code here.
1530     Unused << swm->GetClientRegistration(loadInfo->GetClientInfo().ref(),
1531                                          getter_AddRefs(registration));
1532   }
1533 
1534   // Its possible the registration is removed between starting the interception
1535   // and actually dispatching the fetch event.  In these cases we simply
1536   // want to restart the original network request.  Since this is a normal
1537   // condition we handle the reset here instead of returning an error which
1538   // would in turn trigger a console report.
1539   if (!registration) {
1540     nsresult rv = aChannel->ResetInterception(false);
1541     if (NS_FAILED(rv)) {
1542       NS_WARNING("Failed to resume intercepted network request");
1543       aChannel->CancelInterception(rv);
1544     }
1545     return NS_OK;
1546   }
1547 
1548   // Handle Fetch algorithm - step 16. If the service worker didn't register
1549   // any fetch event handlers, then abort the interception and maybe trigger
1550   // the soft update algorithm.
1551   if (!mInfo->HandlesFetch()) {
1552     nsresult rv = aChannel->ResetInterception(false);
1553     if (NS_FAILED(rv)) {
1554       NS_WARNING("Failed to resume intercepted network request");
1555       aChannel->CancelInterception(rv);
1556     }
1557 
1558     // Trigger soft updates if necessary.
1559     registration->MaybeScheduleTimeCheckAndUpdate();
1560 
1561     return NS_OK;
1562   }
1563 
1564   if (mInner) {
1565     return mInner->SendFetchEvent(std::move(registration), aChannel, aClientId,
1566                                   aResultingClientId);
1567   }
1568 
1569   bool newWorkerCreated = false;
1570   rv = SpawnWorkerIfNeeded(FetchEvent, &newWorkerCreated, aLoadGroup);
1571   NS_ENSURE_SUCCESS(rv, rv);
1572 
1573   nsMainThreadPtrHandle<nsIInterceptedChannel> handle(
1574       new nsMainThreadPtrHolder<nsIInterceptedChannel>("nsIInterceptedChannel",
1575                                                        aChannel, false));
1576 
1577   nsMainThreadPtrHandle<ServiceWorkerRegistrationInfo> regInfo(
1578       new nsMainThreadPtrHolder<ServiceWorkerRegistrationInfo>(
1579           "ServiceWorkerRegistrationInfoProxy", registration, false));
1580 
1581   RefPtr<KeepAliveToken> token = CreateEventKeepAliveToken();
1582 
1583   RefPtr<FetchEventRunnable> r = new FetchEventRunnable(
1584       mWorkerPrivate, token, handle, mInfo->ScriptSpec(), regInfo, aClientId,
1585       aResultingClientId, newWorkerCreated, isNonSubresourceRequest);
1586   rv = r->Init();
1587   if (NS_WARN_IF(NS_FAILED(rv))) {
1588     return rv;
1589   }
1590 
1591   if (mInfo->State() == ServiceWorkerState::Activating) {
1592     mPendingFunctionalEvents.AppendElement(r.forget());
1593     return NS_OK;
1594   }
1595 
1596   MOZ_ASSERT(mInfo->State() == ServiceWorkerState::Activated);
1597 
1598   if (NS_WARN_IF(!r->Dispatch())) {
1599     return NS_ERROR_FAILURE;
1600   }
1601 
1602   return NS_OK;
1603 }
1604 
1605 Result<RefPtr<ServiceWorkerPrivate::PromiseExtensionWorkerHasListener>,
1606        nsresult>
WakeForExtensionAPIEvent(const nsAString & aExtensionAPINamespace,const nsAString & aExtensionAPIEventName)1607 ServiceWorkerPrivate::WakeForExtensionAPIEvent(
1608     const nsAString& aExtensionAPINamespace,
1609     const nsAString& aExtensionAPIEventName) {
1610   MOZ_ASSERT(NS_IsMainThread());
1611 
1612   if (mInner) {
1613     return mInner->WakeForExtensionAPIEvent(aExtensionAPINamespace,
1614                                             aExtensionAPIEventName);
1615   }
1616 
1617   MOZ_ASSERT_UNREACHABLE(
1618       "WakeForExtensionAPIEvent unsupported in parent intercept mode.");
1619 
1620   return Err(NS_ERROR_NOT_IMPLEMENTED);
1621 }
1622 
SpawnWorkerIfNeeded(WakeUpReason aWhy,bool * aNewWorkerCreated,nsILoadGroup * aLoadGroup)1623 nsresult ServiceWorkerPrivate::SpawnWorkerIfNeeded(WakeUpReason aWhy,
1624                                                    bool* aNewWorkerCreated,
1625                                                    nsILoadGroup* aLoadGroup) {
1626   MOZ_ASSERT(NS_IsMainThread());
1627   MOZ_ASSERT(!mInner);
1628 
1629   // Defaults to no new worker created, but if there is one, we'll set the value
1630   // to true at the end of this function.
1631   if (aNewWorkerCreated) {
1632     *aNewWorkerCreated = false;
1633   }
1634 
1635   // If the worker started shutting down on itself we may have a stale
1636   // reference here.  Invoke our termination code to clean it out.
1637   if (mWorkerPrivate && mWorkerPrivate->ParentStatusProtected() > Running) {
1638     TerminateWorker();
1639     MOZ_DIAGNOSTIC_ASSERT(!mWorkerPrivate);
1640   }
1641 
1642   if (mWorkerPrivate) {
1643     // If we have a load group here then use it to update the service worker
1644     // load group.  This was added when we needed the load group's tab child
1645     // to pass some security checks.  Those security checks are gone, though,
1646     // and we could possibly remove this now.  For now we just do it
1647     // opportunistically.  When the service worker is running in a separate
1648     // process from the client that initiated the intercepted channel, then
1649     // the load group will be nullptr.  UpdateOverrideLoadGroup ignores nullptr
1650     // load groups.
1651     mWorkerPrivate->UpdateOverridenLoadGroup(aLoadGroup);
1652     RenewKeepAliveToken(aWhy);
1653 
1654     return NS_OK;
1655   }
1656 
1657   // Sanity check: mSupportsArray should be empty if we're about to
1658   // spin up a new worker.
1659   MOZ_ASSERT(mSupportsArray.IsEmpty());
1660 
1661   if (NS_WARN_IF(!mInfo)) {
1662     NS_WARNING("Trying to wake up a dead service worker.");
1663     return NS_ERROR_FAILURE;
1664   }
1665 
1666   RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
1667   NS_ENSURE_TRUE(swm, NS_ERROR_FAILURE);
1668 
1669   RefPtr<ServiceWorkerRegistrationInfo> reg =
1670       swm->GetRegistration(mInfo->Principal(), mInfo->Scope());
1671   NS_ENSURE_TRUE(reg, NS_ERROR_FAILURE);
1672 
1673   // TODO(catalinb): Bug 1192138 - Add telemetry for service worker wake-ups.
1674 
1675   // Ensure that the IndexedDatabaseManager is initialized
1676   Unused << NS_WARN_IF(!IndexedDatabaseManager::GetOrCreate());
1677 
1678   WorkerLoadInfo info;
1679   nsresult rv = NS_NewURI(getter_AddRefs(info.mBaseURI), mInfo->ScriptSpec());
1680 
1681   if (NS_WARN_IF(NS_FAILED(rv))) {
1682     return rv;
1683   }
1684 
1685   info.mResolvedScriptURI = info.mBaseURI;
1686   MOZ_ASSERT(!mInfo->CacheName().IsEmpty());
1687   info.mServiceWorkerCacheName = mInfo->CacheName();
1688 
1689   info.mServiceWorkerDescriptor.emplace(mInfo->Descriptor());
1690   info.mServiceWorkerRegistrationDescriptor.emplace(reg->Descriptor());
1691 
1692   info.mLoadGroup = aLoadGroup;
1693 
1694   // If we are loading a script for a ServiceWorker then we must not
1695   // try to intercept it.  If the interception matches the current
1696   // ServiceWorker's scope then we could deadlock the load.
1697   info.mLoadFlags =
1698       mInfo->GetImportsLoadFlags() | nsIChannel::LOAD_BYPASS_SERVICE_WORKER;
1699 
1700   rv = info.mBaseURI->GetHost(info.mDomain);
1701   if (NS_WARN_IF(NS_FAILED(rv))) {
1702     return rv;
1703   }
1704 
1705   info.mPrincipal = mInfo->Principal();
1706   info.mLoadingPrincipal = info.mPrincipal;
1707 
1708   info.mCookieJarSettings =
1709       mozilla::net::CookieJarSettings::Create(info.mPrincipal);
1710 
1711   MOZ_ASSERT(info.mCookieJarSettings);
1712 
1713   // We can get the partitionKey from the principal of the ServiceWorker because
1714   // it's a foreign partitioned principal. In other words, the principal will
1715   // have the partitionKey if it's in a third-party context. Otherwise, we can
1716   // set the partitionKey via the script URI because it's in the first-party
1717   // context.
1718   if (!info.mPrincipal->OriginAttributesRef().mPartitionKey.IsEmpty()) {
1719     net::CookieJarSettings::Cast(info.mCookieJarSettings)
1720         ->SetPartitionKey(info.mPrincipal->OriginAttributesRef().mPartitionKey);
1721   } else {
1722     net::CookieJarSettings::Cast(info.mCookieJarSettings)
1723         ->SetPartitionKey(info.mResolvedScriptURI);
1724   }
1725 
1726   if (StaticPrefs::privacy_partition_serviceWorkers()) {
1727     nsCOMPtr<nsIPrincipal> partitionedPrincipal;
1728     StoragePrincipalHelper::CreatePartitionedPrincipalForServiceWorker(
1729         info.mPrincipal, info.mCookieJarSettings,
1730         getter_AddRefs(partitionedPrincipal));
1731 
1732     info.mPartitionedPrincipal = partitionedPrincipal;
1733   } else {
1734     // The partitioned principal will be the same as the mPrincipal if
1735     // partitioned service worker is disabled.
1736     info.mPartitionedPrincipal = info.mPrincipal;
1737   }
1738 
1739   info.mStorageAccess =
1740       StorageAllowedForServiceWorker(info.mPrincipal, info.mCookieJarSettings);
1741 
1742   info.mOriginAttributes = mInfo->GetOriginAttributes();
1743 
1744   // We don't have a good way to know if a service worker is regsitered under a
1745   // third-party context. The only information we can rely on is if the service
1746   // worker is running under a partitioned principal.
1747   info.mIsThirdPartyContextToTopWindow =
1748       !mInfo->Principal()->OriginAttributesRef().mPartitionKey.IsEmpty();
1749 
1750   // Verify that we don't have any CSP on pristine client.
1751 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
1752   nsCOMPtr<nsIContentSecurityPolicy> csp;
1753   if (info.mChannel) {
1754     nsCOMPtr<nsILoadInfo> loadinfo = info.mChannel->LoadInfo();
1755     csp = loadinfo->GetCsp();
1756   }
1757   MOZ_DIAGNOSTIC_ASSERT(!csp);
1758 #endif
1759 
1760   // Default CSP permissions for now.  These will be overrided if necessary
1761   // based on the script CSP headers during load in ScriptLoader.
1762   info.mEvalAllowed = true;
1763   info.mReportCSPViolations = false;
1764 
1765   WorkerPrivate::OverrideLoadInfoLoadGroup(info, info.mPrincipal);
1766 
1767   rv = info.SetPrincipalsAndCSPOnMainThread(
1768       info.mPrincipal, info.mPartitionedPrincipal, info.mLoadGroup, nullptr);
1769   if (NS_WARN_IF(NS_FAILED(rv))) {
1770     return rv;
1771   }
1772 
1773   info.mAgentClusterId = reg->AgentClusterId();
1774 
1775   AutoJSAPI jsapi;
1776   jsapi.Init();
1777   ErrorResult error;
1778   NS_ConvertUTF8toUTF16 scriptSpec(mInfo->ScriptSpec());
1779 
1780   mWorkerPrivate = WorkerPrivate::Constructor(jsapi.cx(), scriptSpec, false,
1781                                               WorkerKindService, VoidString(),
1782                                               ""_ns, &info, error);
1783   if (NS_WARN_IF(error.Failed())) {
1784     return error.StealNSResult();
1785   }
1786 
1787   RenewKeepAliveToken(aWhy);
1788 
1789   if (aNewWorkerCreated) {
1790     *aNewWorkerCreated = true;
1791   }
1792 
1793   return NS_OK;
1794 }
1795 
MaybeStoreISupports(nsISupports * aSupports)1796 bool ServiceWorkerPrivate::MaybeStoreISupports(nsISupports* aSupports) {
1797   MOZ_ASSERT(NS_IsMainThread());
1798 
1799   if (!mWorkerPrivate) {
1800     MOZ_DIAGNOSTIC_ASSERT(mSupportsArray.IsEmpty());
1801     return false;
1802   }
1803 
1804   MOZ_ASSERT(!mSupportsArray.Contains(aSupports));
1805   mSupportsArray.AppendElement(aSupports);
1806   return true;
1807 }
1808 
RemoveISupports(nsISupports * aSupports)1809 void ServiceWorkerPrivate::RemoveISupports(nsISupports* aSupports) {
1810   MOZ_ASSERT(NS_IsMainThread());
1811   mSupportsArray.RemoveElement(aSupports);
1812 }
1813 
TerminateWorker()1814 void ServiceWorkerPrivate::TerminateWorker() {
1815   MOZ_ASSERT(NS_IsMainThread());
1816 
1817   if (mInner) {
1818     return mInner->TerminateWorker();
1819   }
1820 
1821   mIdleWorkerTimer->Cancel();
1822   mIdleKeepAliveToken = nullptr;
1823   if (mWorkerPrivate) {
1824     if (StaticPrefs::dom_serviceWorkers_testing_enabled()) {
1825       nsCOMPtr<nsIObserverService> os = services::GetObserverService();
1826       if (os) {
1827         os->NotifyObservers(nullptr, "service-worker-shutdown", nullptr);
1828       }
1829     }
1830 
1831     Unused << NS_WARN_IF(!mWorkerPrivate->Cancel());
1832     RefPtr<WorkerPrivate> workerPrivate = std::move(mWorkerPrivate);
1833     mozilla::Unused << workerPrivate;
1834     mSupportsArray.Clear();
1835 
1836     // Any pending events are never going to fire on this worker.  Cancel
1837     // them so that intercepted channels can be reset and other resources
1838     // cleaned up.
1839     nsTArray<RefPtr<WorkerRunnable>> pendingEvents =
1840         std::move(mPendingFunctionalEvents);
1841     for (uint32_t i = 0; i < pendingEvents.Length(); ++i) {
1842       pendingEvents[i]->Cancel();
1843     }
1844   }
1845 }
1846 
NoteDeadServiceWorkerInfo()1847 void ServiceWorkerPrivate::NoteDeadServiceWorkerInfo() {
1848   MOZ_ASSERT(NS_IsMainThread());
1849 
1850   if (mInner) {
1851     mInner->NoteDeadOuter();
1852     mInner = nullptr;
1853   } else {
1854     TerminateWorker();
1855   }
1856 
1857   mInfo = nullptr;
1858 }
1859 
1860 namespace {
1861 
1862 class UpdateStateControlRunnable final
1863     : public MainThreadWorkerControlRunnable {
1864   const ServiceWorkerState mState;
1865 
WorkerRun(JSContext * aCx,WorkerPrivate * aWorkerPrivate)1866   bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override {
1867     MOZ_DIAGNOSTIC_ASSERT(aWorkerPrivate);
1868     aWorkerPrivate->UpdateServiceWorkerState(mState);
1869     return true;
1870   }
1871 
1872  public:
UpdateStateControlRunnable(WorkerPrivate * aWorkerPrivate,ServiceWorkerState aState)1873   UpdateStateControlRunnable(WorkerPrivate* aWorkerPrivate,
1874                              ServiceWorkerState aState)
1875       : MainThreadWorkerControlRunnable(aWorkerPrivate), mState(aState) {}
1876 };
1877 
1878 }  // anonymous namespace
1879 
UpdateState(ServiceWorkerState aState)1880 void ServiceWorkerPrivate::UpdateState(ServiceWorkerState aState) {
1881   MOZ_ASSERT(NS_IsMainThread());
1882 
1883   if (mInner) {
1884     return mInner->UpdateState(aState);
1885   }
1886 
1887   if (!mWorkerPrivate) {
1888     MOZ_DIAGNOSTIC_ASSERT(mPendingFunctionalEvents.IsEmpty());
1889     return;
1890   }
1891 
1892   RefPtr<WorkerRunnable> r =
1893       new UpdateStateControlRunnable(mWorkerPrivate, aState);
1894   Unused << r->Dispatch();
1895 
1896   if (aState != ServiceWorkerState::Activated) {
1897     return;
1898   }
1899 
1900   nsTArray<RefPtr<WorkerRunnable>> pendingEvents =
1901       std::move(mPendingFunctionalEvents);
1902 
1903   for (uint32_t i = 0; i < pendingEvents.Length(); ++i) {
1904     RefPtr<WorkerRunnable> r = std::move(pendingEvents[i]);
1905     if (NS_WARN_IF(!r->Dispatch())) {
1906       NS_WARNING("Failed to dispatch pending functional event!");
1907     }
1908   }
1909 }
1910 
GetDebugger(nsIWorkerDebugger ** aResult)1911 nsresult ServiceWorkerPrivate::GetDebugger(nsIWorkerDebugger** aResult) {
1912   MOZ_ASSERT(NS_IsMainThread());
1913   MOZ_ASSERT(aResult);
1914 
1915   if (mInner) {
1916     *aResult = nullptr;
1917     return NS_ERROR_NOT_IMPLEMENTED;
1918   }
1919 
1920   if (!mDebuggerCount) {
1921     return NS_OK;
1922   }
1923 
1924   MOZ_ASSERT(mWorkerPrivate);
1925 
1926   nsCOMPtr<nsIWorkerDebugger> debugger = mWorkerPrivate->Debugger();
1927   debugger.forget(aResult);
1928 
1929   return NS_OK;
1930 }
1931 
AttachDebugger()1932 nsresult ServiceWorkerPrivate::AttachDebugger() {
1933   MOZ_ASSERT(NS_IsMainThread());
1934 
1935   // When the first debugger attaches to a worker, we spawn a worker if needed,
1936   // and cancel the idle timeout. The idle timeout should not be reset until
1937   // the last debugger detached from the worker.
1938   if (!mDebuggerCount) {
1939     nsresult rv = mInner ? mInner->SpawnWorkerIfNeeded()
1940                          : SpawnWorkerIfNeeded(AttachEvent);
1941     NS_ENSURE_SUCCESS(rv, rv);
1942 
1943     /**
1944      * Under parent-intercept mode (i.e. non-null `mInner`), renewing the idle
1945      * KeepAliveToken for spawning workers happens asynchronously, rather than
1946      * synchronously without parent-intercept (see
1947      * `ServiceWorkerPrivate::SpawnWorkerIfNeeded`). The asynchronous renewal is
1948      * because the actual spawning of workers under parent-intercept occurs in a
1949      * content process, so we will only renew once notified that the worker has
1950      * been successfully created
1951      * (see `ServiceWorkerPrivateImpl::CreationSucceeded`).
1952      *
1953      * This means that the DevTools way of starting up a worker by calling
1954      * `AttachDebugger` immediately followed by `DetachDebugger` will spawn and
1955      * immediately terminate a worker (because `mTokenCount` is possibly 0
1956      * due to the idle KeepAliveToken being created asynchronously). So, just
1957      * renew the KeepAliveToken right now.
1958      */
1959     if (mInner) {
1960       RenewKeepAliveToken(AttachEvent);
1961     }
1962 
1963     mIdleWorkerTimer->Cancel();
1964   }
1965 
1966   ++mDebuggerCount;
1967 
1968   return NS_OK;
1969 }
1970 
DetachDebugger()1971 nsresult ServiceWorkerPrivate::DetachDebugger() {
1972   MOZ_ASSERT(NS_IsMainThread());
1973 
1974   if (!mDebuggerCount) {
1975     return NS_ERROR_UNEXPECTED;
1976   }
1977 
1978   --mDebuggerCount;
1979 
1980   // When the last debugger detaches from a worker, we either reset the idle
1981   // timeout, or terminate the worker if there are no more active tokens.
1982   if (!mDebuggerCount) {
1983     if (mTokenCount) {
1984       ResetIdleTimeout();
1985     } else {
1986       TerminateWorker();
1987     }
1988   }
1989 
1990   return NS_OK;
1991 }
1992 
IsIdle() const1993 bool ServiceWorkerPrivate::IsIdle() const {
1994   MOZ_ASSERT(NS_IsMainThread());
1995   return mTokenCount == 0 || (mTokenCount == 1 && mIdleKeepAliveToken);
1996 }
1997 
GetIdlePromise()1998 RefPtr<GenericPromise> ServiceWorkerPrivate::GetIdlePromise() {
1999 #ifdef DEBUG
2000   MOZ_ASSERT(NS_IsMainThread());
2001   MOZ_ASSERT(!IsIdle());
2002   MOZ_ASSERT(!mIdlePromiseObtained, "Idle promise may only be obtained once!");
2003   mIdlePromiseObtained = true;
2004 #endif
2005 
2006   return mIdlePromiseHolder.Ensure(__func__);
2007 }
2008 
2009 namespace {
2010 
2011 class ServiceWorkerPrivateTimerCallback final : public nsITimerCallback,
2012                                                 public nsINamed {
2013  public:
2014   using Method = void (ServiceWorkerPrivate::*)(nsITimer*);
2015 
ServiceWorkerPrivateTimerCallback(ServiceWorkerPrivate * aServiceWorkerPrivate,Method aMethod)2016   ServiceWorkerPrivateTimerCallback(ServiceWorkerPrivate* aServiceWorkerPrivate,
2017                                     Method aMethod)
2018       : mServiceWorkerPrivate(aServiceWorkerPrivate), mMethod(aMethod) {}
2019 
2020   NS_IMETHOD
Notify(nsITimer * aTimer)2021   Notify(nsITimer* aTimer) override {
2022     (mServiceWorkerPrivate->*mMethod)(aTimer);
2023     mServiceWorkerPrivate = nullptr;
2024     return NS_OK;
2025   }
2026 
2027   NS_IMETHOD
GetName(nsACString & aName)2028   GetName(nsACString& aName) override {
2029     aName.AssignLiteral("ServiceWorkerPrivateTimerCallback");
2030     return NS_OK;
2031   }
2032 
2033  private:
2034   ~ServiceWorkerPrivateTimerCallback() = default;
2035 
2036   RefPtr<ServiceWorkerPrivate> mServiceWorkerPrivate;
2037   Method mMethod;
2038 
2039   NS_DECL_THREADSAFE_ISUPPORTS
2040 };
2041 
2042 NS_IMPL_ISUPPORTS(ServiceWorkerPrivateTimerCallback, nsITimerCallback,
2043                   nsINamed);
2044 
2045 }  // anonymous namespace
2046 
NoteIdleWorkerCallback(nsITimer * aTimer)2047 void ServiceWorkerPrivate::NoteIdleWorkerCallback(nsITimer* aTimer) {
2048   MOZ_ASSERT(NS_IsMainThread());
2049 
2050   MOZ_ASSERT(aTimer == mIdleWorkerTimer, "Invalid timer!");
2051 
2052   // Release ServiceWorkerPrivate's token, since the grace period has ended.
2053   mIdleKeepAliveToken = nullptr;
2054 
2055   if (mWorkerPrivate || (mInner && !mInner->WorkerIsDead())) {
2056     // There sould only be EITHER mWorkerPrivate or mInner (but not both).
2057     MOZ_ASSERT(!(mWorkerPrivate && mInner));
2058 
2059     // If we still have a living worker at this point it means that either there
2060     // are pending waitUntil promises or the worker is doing some long-running
2061     // computation. Wait a bit more until we forcibly terminate the worker.
2062     uint32_t timeout =
2063         Preferences::GetInt("dom.serviceWorkers.idle_extended_timeout");
2064     nsCOMPtr<nsITimerCallback> cb = new ServiceWorkerPrivateTimerCallback(
2065         this, &ServiceWorkerPrivate::TerminateWorkerCallback);
2066     DebugOnly<nsresult> rv = mIdleWorkerTimer->InitWithCallback(
2067         cb, timeout, nsITimer::TYPE_ONE_SHOT);
2068     MOZ_ASSERT(NS_SUCCEEDED(rv));
2069   }
2070 }
2071 
TerminateWorkerCallback(nsITimer * aTimer)2072 void ServiceWorkerPrivate::TerminateWorkerCallback(nsITimer* aTimer) {
2073   MOZ_ASSERT(NS_IsMainThread());
2074 
2075   MOZ_ASSERT(aTimer == this->mIdleWorkerTimer, "Invalid timer!");
2076 
2077   // mInfo must be non-null at this point because NoteDeadServiceWorkerInfo
2078   // which zeroes it calls TerminateWorker which cancels our timer which will
2079   // ensure we don't get invoked even if the nsTimerEvent is in the event queue.
2080   ServiceWorkerManager::LocalizeAndReportToAllClients(
2081       mInfo->Scope(), "ServiceWorkerGraceTimeoutTermination",
2082       nsTArray<nsString>{NS_ConvertUTF8toUTF16(mInfo->Scope())});
2083 
2084   TerminateWorker();
2085 }
2086 
RenewKeepAliveToken(WakeUpReason aWhy)2087 void ServiceWorkerPrivate::RenewKeepAliveToken(WakeUpReason aWhy) {
2088   // We should have an active worker if we're renewing the keep alive token.
2089   MOZ_ASSERT(mWorkerPrivate || (mInner && !mInner->WorkerIsDead()));
2090 
2091   // If there is at least one debugger attached to the worker, the idle worker
2092   // timeout was canceled when the first debugger attached to the worker. It
2093   // should not be reset until the last debugger detaches from the worker.
2094   if (!mDebuggerCount) {
2095     ResetIdleTimeout();
2096   }
2097 
2098   if (!mIdleKeepAliveToken) {
2099     mIdleKeepAliveToken = new KeepAliveToken(this);
2100   }
2101 }
2102 
ResetIdleTimeout()2103 void ServiceWorkerPrivate::ResetIdleTimeout() {
2104   uint32_t timeout = Preferences::GetInt("dom.serviceWorkers.idle_timeout");
2105   nsCOMPtr<nsITimerCallback> cb = new ServiceWorkerPrivateTimerCallback(
2106       this, &ServiceWorkerPrivate::NoteIdleWorkerCallback);
2107   DebugOnly<nsresult> rv =
2108       mIdleWorkerTimer->InitWithCallback(cb, timeout, nsITimer::TYPE_ONE_SHOT);
2109   MOZ_ASSERT(NS_SUCCEEDED(rv));
2110 }
2111 
AddToken()2112 void ServiceWorkerPrivate::AddToken() {
2113   MOZ_ASSERT(NS_IsMainThread());
2114   ++mTokenCount;
2115 }
2116 
ReleaseToken()2117 void ServiceWorkerPrivate::ReleaseToken() {
2118   MOZ_ASSERT(NS_IsMainThread());
2119 
2120   MOZ_ASSERT(mTokenCount > 0);
2121   --mTokenCount;
2122 
2123   if (IsIdle()) {
2124     mIdlePromiseHolder.ResolveIfExists(true, __func__);
2125 
2126     if (!mTokenCount) {
2127       TerminateWorker();
2128     }
2129 
2130     // mInfo can be nullptr here if NoteDeadServiceWorkerInfo() is called while
2131     // the KeepAliveToken is being proxy released as a runnable.
2132     else if (mInfo) {
2133       RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
2134       if (swm) {
2135         swm->WorkerIsIdle(mInfo);
2136       }
2137     }
2138   }
2139 }
2140 
2141 already_AddRefed<KeepAliveToken>
CreateEventKeepAliveToken()2142 ServiceWorkerPrivate::CreateEventKeepAliveToken() {
2143   MOZ_ASSERT(NS_IsMainThread());
2144 
2145   // When the WorkerPrivate is in a separate process, we first hold a normal
2146   // KeepAliveToken. Then, after we're notified that the worker is alive, we
2147   // create the idle KeepAliveToken.
2148   MOZ_ASSERT(mWorkerPrivate || (mInner && !mInner->WorkerIsDead()));
2149   MOZ_ASSERT(mIdleKeepAliveToken || (mInner && !mInner->WorkerIsDead()));
2150 
2151   RefPtr<KeepAliveToken> ref = new KeepAliveToken(this);
2152   return ref.forget();
2153 }
2154 
SetHandlesFetch(bool aValue)2155 void ServiceWorkerPrivate::SetHandlesFetch(bool aValue) {
2156   MOZ_ASSERT(NS_IsMainThread());
2157 
2158   if (NS_WARN_IF(!mInfo)) {
2159     return;
2160   }
2161 
2162   mInfo->SetHandlesFetch(aValue);
2163 }
2164 
2165 }  // namespace dom
2166 }  // namespace mozilla
2167