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 "ServiceWorkerOp.h"
8 
9 #include <utility>
10 
11 #include "js/Exception.h"  // JS::ExceptionStack, JS::StealPendingExceptionStack
12 #include "jsapi.h"
13 
14 #include "nsCOMPtr.h"
15 #include "nsContentUtils.h"
16 #include "nsDebug.h"
17 #include "nsError.h"
18 #include "nsINamed.h"
19 #include "nsIPushErrorReporter.h"
20 #include "nsISupportsImpl.h"
21 #include "nsITimer.h"
22 #include "nsProxyRelease.h"
23 #include "nsServiceManagerUtils.h"
24 #include "nsTArray.h"
25 #include "nsThreadUtils.h"
26 
27 #include "ServiceWorkerCloneData.h"
28 #include "ServiceWorkerShutdownState.h"
29 #include "mozilla/Assertions.h"
30 #include "mozilla/CycleCollectedJSContext.h"
31 #include "mozilla/DebugOnly.h"
32 #include "mozilla/ErrorResult.h"
33 #include "mozilla/OwningNonNull.h"
34 #include "mozilla/SchedulerGroup.h"
35 #include "mozilla/ScopeExit.h"
36 #include "mozilla/Telemetry.h"
37 #include "mozilla/Unused.h"
38 #include "mozilla/dom/BindingDeclarations.h"
39 #include "mozilla/dom/Client.h"
40 #include "mozilla/dom/ExtendableMessageEventBinding.h"
41 #include "mozilla/dom/FetchEventBinding.h"
42 #include "mozilla/dom/FetchEventOpProxyChild.h"
43 #include "mozilla/dom/InternalHeaders.h"
44 #include "mozilla/dom/InternalRequest.h"
45 #include "mozilla/dom/InternalResponse.h"
46 #include "mozilla/dom/Notification.h"
47 #include "mozilla/dom/NotificationEvent.h"
48 #include "mozilla/dom/NotificationEventBinding.h"
49 #include "mozilla/dom/PushEventBinding.h"
50 #include "mozilla/dom/RemoteWorkerChild.h"
51 #include "mozilla/dom/RemoteWorkerService.h"
52 #include "mozilla/dom/Request.h"
53 #include "mozilla/dom/Response.h"
54 #include "mozilla/dom/RootedDictionary.h"
55 #include "mozilla/dom/ServiceWorkerBinding.h"
56 #include "mozilla/dom/WorkerCommon.h"
57 #include "mozilla/dom/WorkerPrivate.h"
58 #include "mozilla/dom/WorkerRef.h"
59 #include "mozilla/dom/WorkerScope.h"
60 #include "mozilla/ipc/IPCStreamUtils.h"
61 #include "mozilla/net/MozURL.h"
62 
63 namespace mozilla {
64 namespace dom {
65 
66 namespace {
67 
68 class ExtendableEventKeepAliveHandler final
69     : public ExtendableEvent::ExtensionsHandler,
70       public PromiseNativeHandler {
71  public:
72   NS_DECL_ISUPPORTS
73 
Create(RefPtr<ExtendableEventCallback> aCallback)74   static RefPtr<ExtendableEventKeepAliveHandler> Create(
75       RefPtr<ExtendableEventCallback> aCallback) {
76     MOZ_ASSERT(IsCurrentThreadRunningWorker());
77 
78     RefPtr<ExtendableEventKeepAliveHandler> self =
79         new ExtendableEventKeepAliveHandler(std::move(aCallback));
80 
81     self->mWorkerRef = StrongWorkerRef::Create(
82         GetCurrentThreadWorkerPrivate(), "ExtendableEventKeepAliveHandler",
83         [self]() { self->Cleanup(); });
84 
85     if (NS_WARN_IF(!self->mWorkerRef)) {
86       return nullptr;
87     }
88 
89     return self;
90   }
91 
92   /**
93    * ExtendableEvent::ExtensionsHandler interface
94    */
WaitOnPromise(Promise & aPromise)95   bool WaitOnPromise(Promise& aPromise) override {
96     if (!mAcceptingPromises) {
97       MOZ_ASSERT(!GetDispatchFlag());
98       MOZ_ASSERT(!mSelfRef, "We shouldn't be holding a self reference!");
99       return false;
100     }
101 
102     if (!mSelfRef) {
103       MOZ_ASSERT(!mPendingPromisesCount);
104       mSelfRef = this;
105     }
106 
107     ++mPendingPromisesCount;
108     aPromise.AppendNativeHandler(this);
109 
110     return true;
111   }
112 
113   /**
114    * PromiseNativeHandler interface
115    */
ResolvedCallback(JSContext * aCx,JS::Handle<JS::Value> aValue)116   void ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override {
117     RemovePromise(Resolved);
118   }
119 
RejectedCallback(JSContext * aCx,JS::Handle<JS::Value> aValue)120   void RejectedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override {
121     RemovePromise(Rejected);
122   }
123 
MaybeDone()124   void MaybeDone() {
125     MOZ_ASSERT(IsCurrentThreadRunningWorker());
126     MOZ_ASSERT(!GetDispatchFlag());
127 
128     if (mPendingPromisesCount) {
129       return;
130     }
131 
132     if (mCallback) {
133       mCallback->FinishedWithResult(mRejected ? Rejected : Resolved);
134       mCallback = nullptr;
135     }
136 
137     Cleanup();
138   }
139 
140  private:
141   /**
142    * This class is useful for the case where pending microtasks will continue
143    * extending the event, which means that the event is not "done." For example:
144    *
145    * // `e` is an ExtendableEvent, `p` is a Promise
146    * e.waitUntil(p);
147    * p.then(() => e.waitUntil(otherPromise));
148    */
149   class MaybeDoneRunner : public MicroTaskRunnable {
150    public:
MaybeDoneRunner(RefPtr<ExtendableEventKeepAliveHandler> aHandler)151     explicit MaybeDoneRunner(RefPtr<ExtendableEventKeepAliveHandler> aHandler)
152         : mHandler(std::move(aHandler)) {}
153 
Run(AutoSlowOperation &)154     void Run(AutoSlowOperation& /* unused */) override {
155       mHandler->MaybeDone();
156     }
157 
158    private:
159     RefPtr<ExtendableEventKeepAliveHandler> mHandler;
160   };
161 
ExtendableEventKeepAliveHandler(RefPtr<ExtendableEventCallback> aCallback)162   explicit ExtendableEventKeepAliveHandler(
163       RefPtr<ExtendableEventCallback> aCallback)
164       : mCallback(std::move(aCallback)) {}
165 
~ExtendableEventKeepAliveHandler()166   ~ExtendableEventKeepAliveHandler() { Cleanup(); }
167 
Cleanup()168   void Cleanup() {
169     MOZ_ASSERT(IsCurrentThreadRunningWorker());
170 
171     if (mCallback) {
172       mCallback->FinishedWithResult(Rejected);
173     }
174 
175     mSelfRef = nullptr;
176     mWorkerRef = nullptr;
177     mCallback = nullptr;
178     mAcceptingPromises = false;
179   }
180 
RemovePromise(ExtendableEventResult aResult)181   void RemovePromise(ExtendableEventResult aResult) {
182     MOZ_ASSERT(IsCurrentThreadRunningWorker());
183     MOZ_DIAGNOSTIC_ASSERT(mPendingPromisesCount > 0);
184 
185     // NOTE: mSelfRef can be nullptr here if MaybeCleanup() was just called
186     // before a promise settled. This can happen, for example, if the worker
187     // thread is being terminated for running too long, browser shutdown, etc.
188 
189     mRejected |= (aResult == Rejected);
190 
191     --mPendingPromisesCount;
192     if (mPendingPromisesCount || GetDispatchFlag()) {
193       return;
194     }
195 
196     CycleCollectedJSContext* cx = CycleCollectedJSContext::Get();
197     MOZ_ASSERT(cx);
198 
199     RefPtr<MaybeDoneRunner> r = new MaybeDoneRunner(this);
200     cx->DispatchToMicroTask(r.forget());
201   }
202 
203   /**
204    * We start holding a self reference when the first extension promise is
205    * added, and this reference is released when the last promise settles or
206    * when the worker is shutting down.
207    *
208    * This is needed in the case that we're waiting indefinitely on a to-be-GC'ed
209    * promise that's no longer reachable and will never be settled.
210    */
211   RefPtr<ExtendableEventKeepAliveHandler> mSelfRef;
212 
213   RefPtr<StrongWorkerRef> mWorkerRef;
214 
215   RefPtr<ExtendableEventCallback> mCallback;
216 
217   uint32_t mPendingPromisesCount = 0;
218 
219   bool mRejected = false;
220   bool mAcceptingPromises = true;
221 };
222 
NS_IMPL_ISUPPORTS0(ExtendableEventKeepAliveHandler)223 NS_IMPL_ISUPPORTS0(ExtendableEventKeepAliveHandler)
224 
225 nsresult DispatchExtendableEventOnWorkerScope(
226     JSContext* aCx, WorkerGlobalScope* aWorkerScope, ExtendableEvent* aEvent,
227     RefPtr<ExtendableEventCallback> aCallback) {
228   MOZ_ASSERT(aCx);
229   MOZ_ASSERT(aWorkerScope);
230   MOZ_ASSERT(aEvent);
231 
232   nsCOMPtr<nsIGlobalObject> globalObject = aWorkerScope;
233   WidgetEvent* internalEvent = aEvent->WidgetEventPtr();
234 
235   RefPtr<ExtendableEventKeepAliveHandler> keepAliveHandler =
236       ExtendableEventKeepAliveHandler::Create(std::move(aCallback));
237   if (NS_WARN_IF(!keepAliveHandler)) {
238     return NS_ERROR_FAILURE;
239   }
240 
241   // This must be always set *before* dispatching the event, otherwise
242   // waitUntil() calls will fail.
243   aEvent->SetKeepAliveHandler(keepAliveHandler);
244 
245   ErrorResult result;
246   aWorkerScope->DispatchEvent(*aEvent, result);
247   if (NS_WARN_IF(result.Failed())) {
248     result.SuppressException();
249     return NS_ERROR_FAILURE;
250   }
251 
252   keepAliveHandler->MaybeDone();
253 
254   // We don't block the event when getting an exception but still report the
255   // error message. NOTE: this will not stop the event.
256   if (internalEvent->mFlags.mExceptionWasRaised) {
257     return NS_ERROR_XPC_JS_THREW_EXCEPTION;
258   }
259 
260   return NS_OK;
261 }
262 
DispatchFailed(nsresult aStatus)263 bool DispatchFailed(nsresult aStatus) {
264   return NS_FAILED(aStatus) && aStatus != NS_ERROR_XPC_JS_THREW_EXCEPTION;
265 }
266 
267 }  // anonymous namespace
268 
269 class ServiceWorkerOp::ServiceWorkerOpRunnable : public WorkerDebuggeeRunnable {
270  public:
271   NS_DECL_ISUPPORTS_INHERITED
272 
ServiceWorkerOpRunnable(RefPtr<ServiceWorkerOp> aOwner,WorkerPrivate * aWorkerPrivate)273   ServiceWorkerOpRunnable(RefPtr<ServiceWorkerOp> aOwner,
274                           WorkerPrivate* aWorkerPrivate)
275       : WorkerDebuggeeRunnable(aWorkerPrivate, WorkerThreadModifyBusyCount),
276         mOwner(std::move(aOwner)) {
277     AssertIsOnMainThread();
278     MOZ_ASSERT(mOwner);
279     MOZ_ASSERT(aWorkerPrivate);
280   }
281 
282  private:
283   ~ServiceWorkerOpRunnable() = default;
284 
WorkerRun(JSContext * aCx,WorkerPrivate * aWorkerPrivate)285   bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override {
286     MOZ_ASSERT(aWorkerPrivate);
287     aWorkerPrivate->AssertIsOnWorkerThread();
288     MOZ_ASSERT(aWorkerPrivate->IsServiceWorker());
289     MOZ_ASSERT(mOwner);
290 
291     bool rv = mOwner->Exec(aCx, aWorkerPrivate);
292     Unused << NS_WARN_IF(!rv);
293     mOwner = nullptr;
294 
295     return rv;
296   }
297 
Cancel()298   nsresult Cancel() override {
299     MOZ_ASSERT(mOwner);
300 
301     mOwner->RejectAll(NS_ERROR_DOM_ABORT_ERR);
302     mOwner = nullptr;
303 
304     return WorkerRunnable::Cancel();
305   }
306 
307   RefPtr<ServiceWorkerOp> mOwner;
308 };
309 
NS_IMPL_ISUPPORTS_INHERITED0(ServiceWorkerOp::ServiceWorkerOpRunnable,WorkerRunnable)310 NS_IMPL_ISUPPORTS_INHERITED0(ServiceWorkerOp::ServiceWorkerOpRunnable,
311                              WorkerRunnable)
312 
313 bool ServiceWorkerOp::MaybeStart(RemoteWorkerChild* aOwner,
314                                  RemoteWorkerChild::State& aState) {
315   MOZ_ASSERT(!mStarted);
316   MOZ_ASSERT(aOwner);
317   MOZ_ASSERT(aOwner->GetOwningEventTarget()->IsOnCurrentThread());
318 
319   auto launcherData = aOwner->mLauncherData.Access();
320 
321   if (NS_WARN_IF(!launcherData->mIPCActive)) {
322     RejectAll(NS_ERROR_DOM_ABORT_ERR);
323     mStarted = true;
324     return true;
325   }
326 
327   // Allow termination to happen while the Service Worker is initializing.
328   if (aState.is<Pending>() && !IsTerminationOp()) {
329     return false;
330   }
331 
332   if (NS_WARN_IF(aState.is<RemoteWorkerChild::PendingTerminated>()) ||
333       NS_WARN_IF(aState.is<RemoteWorkerChild::Terminated>())) {
334     RejectAll(NS_ERROR_DOM_INVALID_STATE_ERR);
335     mStarted = true;
336     return true;
337   }
338 
339   MOZ_ASSERT(aState.is<RemoteWorkerChild::Running>() || IsTerminationOp());
340 
341   RefPtr<ServiceWorkerOp> self = this;
342 
343   if (IsTerminationOp()) {
344     aOwner->GetTerminationPromise()->Then(
345         GetCurrentSerialEventTarget(), __func__,
346         [self](
347             const GenericNonExclusivePromise::ResolveOrRejectValue& aResult) {
348           MaybeReportServiceWorkerShutdownProgress(self->mArgs, true);
349 
350           MOZ_ASSERT(!self->mPromiseHolder.IsEmpty());
351 
352           if (NS_WARN_IF(aResult.IsReject())) {
353             self->mPromiseHolder.Reject(aResult.RejectValue(), __func__);
354             return;
355           }
356 
357           self->mPromiseHolder.Resolve(NS_OK, __func__);
358         });
359   }
360 
361   RefPtr<RemoteWorkerChild> owner = aOwner;
362 
363   nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(
364       __func__, [self = std::move(self), owner = std::move(owner)]() mutable {
365         MaybeReportServiceWorkerShutdownProgress(self->mArgs);
366 
367         auto lock = owner->mState.Lock();
368         auto& state = lock.ref();
369 
370         if (NS_WARN_IF(!state.is<Running>() && !self->IsTerminationOp())) {
371           self->RejectAll(NS_ERROR_DOM_INVALID_STATE_ERR);
372           return;
373         }
374 
375         if (self->IsTerminationOp()) {
376           owner->CloseWorkerOnMainThread(state);
377         } else {
378           MOZ_ASSERT(state.is<Running>());
379 
380           RefPtr<WorkerRunnable> workerRunnable =
381               self->GetRunnable(state.as<Running>().mWorkerPrivate);
382 
383           if (NS_WARN_IF(!workerRunnable->Dispatch())) {
384             self->RejectAll(NS_ERROR_FAILURE);
385           }
386         }
387 
388         nsCOMPtr<nsIEventTarget> target = owner->GetOwningEventTarget();
389         NS_ProxyRelease(__func__, target, owner.forget());
390       });
391 
392   mStarted = true;
393 
394   MOZ_ALWAYS_SUCCEEDS(
395       SchedulerGroup::Dispatch(TaskCategory::Other, r.forget()));
396 
397   return true;
398 }
399 
Cancel()400 void ServiceWorkerOp::Cancel() { RejectAll(NS_ERROR_DOM_ABORT_ERR); }
401 
ServiceWorkerOp(ServiceWorkerOpArgs && aArgs,std::function<void (const ServiceWorkerOpResult &)> && aCallback)402 ServiceWorkerOp::ServiceWorkerOp(
403     ServiceWorkerOpArgs&& aArgs,
404     std::function<void(const ServiceWorkerOpResult&)>&& aCallback)
405     : mArgs(std::move(aArgs)) {
406   MOZ_ASSERT(RemoteWorkerService::Thread()->IsOnCurrentThread());
407 
408   RefPtr<ServiceWorkerOpPromise> promise = mPromiseHolder.Ensure(__func__);
409 
410   promise->Then(
411       GetCurrentSerialEventTarget(), __func__,
412       [callback = std::move(aCallback)](
413           ServiceWorkerOpPromise::ResolveOrRejectValue&& aResult) mutable {
414         if (NS_WARN_IF(aResult.IsReject())) {
415           MOZ_ASSERT(NS_FAILED(aResult.RejectValue()));
416           callback(aResult.RejectValue());
417           return;
418         }
419 
420         callback(aResult.ResolveValue());
421       });
422 }
423 
~ServiceWorkerOp()424 ServiceWorkerOp::~ServiceWorkerOp() {
425   Unused << NS_WARN_IF(!mPromiseHolder.IsEmpty());
426   mPromiseHolder.RejectIfExists(NS_ERROR_DOM_ABORT_ERR, __func__);
427 }
428 
Started() const429 bool ServiceWorkerOp::Started() const {
430   MOZ_ASSERT(RemoteWorkerService::Thread()->IsOnCurrentThread());
431 
432   return mStarted;
433 }
434 
IsTerminationOp() const435 bool ServiceWorkerOp::IsTerminationOp() const {
436   return mArgs.type() ==
437          ServiceWorkerOpArgs::TServiceWorkerTerminateWorkerOpArgs;
438 }
439 
GetRunnable(WorkerPrivate * aWorkerPrivate)440 RefPtr<WorkerRunnable> ServiceWorkerOp::GetRunnable(
441     WorkerPrivate* aWorkerPrivate) {
442   AssertIsOnMainThread();
443   MOZ_ASSERT(aWorkerPrivate);
444 
445   return new ServiceWorkerOpRunnable(this, aWorkerPrivate);
446 }
447 
RejectAll(nsresult aStatus)448 void ServiceWorkerOp::RejectAll(nsresult aStatus) {
449   MOZ_ASSERT(!mPromiseHolder.IsEmpty());
450   mPromiseHolder.Reject(aStatus, __func__);
451 }
452 
453 class CheckScriptEvaluationOp final : public ServiceWorkerOp {
454   using ServiceWorkerOp::ServiceWorkerOp;
455 
456  public:
457   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(CheckScriptEvaluationOp, override)
458 
459  private:
460   ~CheckScriptEvaluationOp() = default;
461 
Exec(JSContext * aCx,WorkerPrivate * aWorkerPrivate)462   bool Exec(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override {
463     MOZ_ASSERT(aWorkerPrivate);
464     aWorkerPrivate->AssertIsOnWorkerThread();
465     MOZ_ASSERT(aWorkerPrivate->IsServiceWorker());
466     MOZ_ASSERT(!mPromiseHolder.IsEmpty());
467 
468     ServiceWorkerCheckScriptEvaluationOpResult result;
469     result.workerScriptExecutedSuccessfully() =
470         aWorkerPrivate->WorkerScriptExecutedSuccessfully();
471     result.fetchHandlerWasAdded() = aWorkerPrivate->FetchHandlerWasAdded();
472 
473     mPromiseHolder.Resolve(result, __func__);
474 
475     return true;
476   }
477 };
478 
479 class TerminateServiceWorkerOp final : public ServiceWorkerOp {
480   using ServiceWorkerOp::ServiceWorkerOp;
481 
482  public:
483   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(TerminateServiceWorkerOp, override)
484 
485  private:
486   ~TerminateServiceWorkerOp() = default;
487 
Exec(JSContext *,WorkerPrivate *)488   bool Exec(JSContext*, WorkerPrivate*) override {
489     MOZ_ASSERT_UNREACHABLE(
490         "Worker termination should be handled in "
491         "`ServiceWorkerOp::MaybeStart()`");
492 
493     return false;
494   }
495 };
496 
497 class UpdateServiceWorkerStateOp final : public ServiceWorkerOp {
498   using ServiceWorkerOp::ServiceWorkerOp;
499 
500  public:
501   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(UpdateServiceWorkerStateOp, override);
502 
503  private:
504   class UpdateStateOpRunnable final : public MainThreadWorkerControlRunnable {
505    public:
506     NS_DECL_ISUPPORTS_INHERITED
507 
UpdateStateOpRunnable(RefPtr<UpdateServiceWorkerStateOp> aOwner,WorkerPrivate * aWorkerPrivate)508     UpdateStateOpRunnable(RefPtr<UpdateServiceWorkerStateOp> aOwner,
509                           WorkerPrivate* aWorkerPrivate)
510         : MainThreadWorkerControlRunnable(aWorkerPrivate),
511           mOwner(std::move(aOwner)) {
512       AssertIsOnMainThread();
513       MOZ_ASSERT(mOwner);
514       MOZ_ASSERT(aWorkerPrivate);
515     }
516 
517    private:
518     ~UpdateStateOpRunnable() = default;
519 
WorkerRun(JSContext * aCx,WorkerPrivate * aWorkerPrivate)520     bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override {
521       MOZ_ASSERT(aWorkerPrivate);
522       aWorkerPrivate->AssertIsOnWorkerThread();
523       MOZ_ASSERT(aWorkerPrivate->IsServiceWorker());
524 
525       if (mOwner) {
526         Unused << mOwner->Exec(aCx, aWorkerPrivate);
527         mOwner = nullptr;
528       }
529 
530       return true;
531     }
532 
Cancel()533     nsresult Cancel() override {
534       MOZ_ASSERT(mOwner);
535 
536       mOwner->RejectAll(NS_ERROR_DOM_ABORT_ERR);
537       mOwner = nullptr;
538 
539       return MainThreadWorkerControlRunnable::Cancel();
540     }
541 
542     RefPtr<UpdateServiceWorkerStateOp> mOwner;
543   };
544 
545   ~UpdateServiceWorkerStateOp() = default;
546 
GetRunnable(WorkerPrivate * aWorkerPrivate)547   RefPtr<WorkerRunnable> GetRunnable(WorkerPrivate* aWorkerPrivate) override {
548     AssertIsOnMainThread();
549     MOZ_ASSERT(aWorkerPrivate);
550     MOZ_ASSERT(mArgs.type() ==
551                ServiceWorkerOpArgs::TServiceWorkerUpdateStateOpArgs);
552 
553     return new UpdateStateOpRunnable(this, aWorkerPrivate);
554   }
555 
Exec(JSContext * aCx,WorkerPrivate * aWorkerPrivate)556   bool Exec(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override {
557     MOZ_ASSERT(aWorkerPrivate);
558     aWorkerPrivate->AssertIsOnWorkerThread();
559     MOZ_ASSERT(aWorkerPrivate->IsServiceWorker());
560     MOZ_ASSERT(!mPromiseHolder.IsEmpty());
561 
562     ServiceWorkerState state =
563         mArgs.get_ServiceWorkerUpdateStateOpArgs().state();
564     aWorkerPrivate->UpdateServiceWorkerState(state);
565 
566     mPromiseHolder.Resolve(NS_OK, __func__);
567 
568     return true;
569   }
570 };
571 
NS_IMPL_ISUPPORTS_INHERITED0(UpdateServiceWorkerStateOp::UpdateStateOpRunnable,MainThreadWorkerControlRunnable)572 NS_IMPL_ISUPPORTS_INHERITED0(UpdateServiceWorkerStateOp::UpdateStateOpRunnable,
573                              MainThreadWorkerControlRunnable)
574 
575 void ExtendableEventOp::FinishedWithResult(ExtendableEventResult aResult) {
576   MOZ_ASSERT(IsCurrentThreadRunningWorker());
577   MOZ_ASSERT(!mPromiseHolder.IsEmpty());
578 
579   mPromiseHolder.Resolve(aResult == Resolved ? NS_OK : NS_ERROR_FAILURE,
580                          __func__);
581 }
582 
583 class LifeCycleEventOp final : public ExtendableEventOp {
584   using ExtendableEventOp::ExtendableEventOp;
585 
586  public:
587   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(LifeCycleEventOp, override)
588 
589  private:
590   ~LifeCycleEventOp() = default;
591 
Exec(JSContext * aCx,WorkerPrivate * aWorkerPrivate)592   bool Exec(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override {
593     MOZ_ASSERT(aWorkerPrivate);
594     aWorkerPrivate->AssertIsOnWorkerThread();
595     MOZ_ASSERT(aWorkerPrivate->IsServiceWorker());
596     MOZ_ASSERT(!mPromiseHolder.IsEmpty());
597 
598     RefPtr<ExtendableEvent> event;
599     RefPtr<EventTarget> target = aWorkerPrivate->GlobalScope();
600 
601     const nsString& eventName =
602         mArgs.get_ServiceWorkerLifeCycleEventOpArgs().eventName();
603 
604     if (eventName.EqualsASCII("install") || eventName.EqualsASCII("activate")) {
605       ExtendableEventInit init;
606       init.mBubbles = false;
607       init.mCancelable = false;
608       event = ExtendableEvent::Constructor(target, eventName, init);
609     } else {
610       MOZ_CRASH("Unexpected lifecycle event");
611     }
612 
613     event->SetTrusted(true);
614 
615     nsresult rv = DispatchExtendableEventOnWorkerScope(
616         aCx, aWorkerPrivate->GlobalScope(), event, this);
617 
618     if (NS_WARN_IF(DispatchFailed(rv))) {
619       RejectAll(rv);
620     }
621 
622     return !DispatchFailed(rv);
623   }
624 };
625 
626 /**
627  * PushEventOp
628  */
629 class PushEventOp final : public ExtendableEventOp {
630   using ExtendableEventOp::ExtendableEventOp;
631 
632  public:
633   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(PushEventOp, override)
634 
635  private:
636   ~PushEventOp() = default;
637 
Exec(JSContext * aCx,WorkerPrivate * aWorkerPrivate)638   bool Exec(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override {
639     MOZ_ASSERT(aWorkerPrivate);
640     aWorkerPrivate->AssertIsOnWorkerThread();
641     MOZ_ASSERT(aWorkerPrivate->IsServiceWorker());
642     MOZ_ASSERT(!mPromiseHolder.IsEmpty());
643 
644     ErrorResult result;
645 
646     auto scopeExit = MakeScopeExit([&] {
647       MOZ_ASSERT(result.Failed());
648 
649       RejectAll(result.StealNSResult());
650       ReportError(aWorkerPrivate);
651     });
652 
653     const ServiceWorkerPushEventOpArgs& args =
654         mArgs.get_ServiceWorkerPushEventOpArgs();
655 
656     RootedDictionary<PushEventInit> pushEventInit(aCx);
657 
658     if (args.data().type() != OptionalPushData::Tvoid_t) {
659       auto& bytes = args.data().get_ArrayOfuint8_t();
660       JSObject* data =
661           Uint8Array::Create(aCx, bytes.Length(), bytes.Elements());
662 
663       if (!data) {
664         result = ErrorResult(NS_ERROR_FAILURE);
665         return false;
666       }
667 
668       DebugOnly<bool> inited =
669           pushEventInit.mData.Construct().SetAsArrayBufferView().Init(data);
670       MOZ_ASSERT(inited);
671     }
672 
673     pushEventInit.mBubbles = false;
674     pushEventInit.mCancelable = false;
675 
676     GlobalObject globalObj(aCx, aWorkerPrivate->GlobalScope()->GetWrapper());
677     RefPtr<PushEvent> pushEvent =
678         PushEvent::Constructor(globalObj, u"push"_ns, pushEventInit, result);
679 
680     if (NS_WARN_IF(result.Failed())) {
681       return false;
682     }
683 
684     pushEvent->SetTrusted(true);
685 
686     scopeExit.release();
687 
688     nsresult rv = DispatchExtendableEventOnWorkerScope(
689         aCx, aWorkerPrivate->GlobalScope(), pushEvent, this);
690 
691     if (NS_FAILED(rv)) {
692       if (NS_WARN_IF(DispatchFailed(rv))) {
693         RejectAll(rv);
694       }
695 
696       // We don't cancel WorkerPrivate when catching an exception.
697       ReportError(aWorkerPrivate,
698                   nsIPushErrorReporter::DELIVERY_UNCAUGHT_EXCEPTION);
699     }
700 
701     return !DispatchFailed(rv);
702   }
703 
FinishedWithResult(ExtendableEventResult aResult)704   void FinishedWithResult(ExtendableEventResult aResult) override {
705     MOZ_ASSERT(IsCurrentThreadRunningWorker());
706 
707     WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
708 
709     if (aResult == Rejected) {
710       ReportError(workerPrivate,
711                   nsIPushErrorReporter::DELIVERY_UNHANDLED_REJECTION);
712     }
713 
714     ExtendableEventOp::FinishedWithResult(aResult);
715   }
716 
ReportError(WorkerPrivate * aWorkerPrivate,uint16_t aError=nsIPushErrorReporter::DELIVERY_INTERNAL_ERROR)717   void ReportError(
718       WorkerPrivate* aWorkerPrivate,
719       uint16_t aError = nsIPushErrorReporter::DELIVERY_INTERNAL_ERROR) {
720     MOZ_ASSERT(aWorkerPrivate);
721     aWorkerPrivate->AssertIsOnWorkerThread();
722     MOZ_ASSERT(aWorkerPrivate->IsServiceWorker());
723 
724     if (NS_WARN_IF(aError > nsIPushErrorReporter::DELIVERY_INTERNAL_ERROR) ||
725         mArgs.get_ServiceWorkerPushEventOpArgs().messageId().IsEmpty()) {
726       return;
727     }
728 
729     nsString messageId = mArgs.get_ServiceWorkerPushEventOpArgs().messageId();
730     nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(
731         __func__, [messageId = std::move(messageId), error = aError] {
732           nsCOMPtr<nsIPushErrorReporter> reporter =
733               do_GetService("@mozilla.org/push/Service;1");
734 
735           if (reporter) {
736             nsresult rv = reporter->ReportDeliveryError(messageId, error);
737             Unused << NS_WARN_IF(NS_FAILED(rv));
738           }
739         });
740 
741     MOZ_ALWAYS_SUCCEEDS(aWorkerPrivate->DispatchToMainThread(r.forget()));
742   }
743 };
744 
745 class PushSubscriptionChangeEventOp final : public ExtendableEventOp {
746   using ExtendableEventOp::ExtendableEventOp;
747 
748  public:
749   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(PushSubscriptionChangeEventOp, override)
750 
751  private:
752   ~PushSubscriptionChangeEventOp() = default;
753 
Exec(JSContext * aCx,WorkerPrivate * aWorkerPrivate)754   bool Exec(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override {
755     MOZ_ASSERT(aWorkerPrivate);
756     aWorkerPrivate->AssertIsOnWorkerThread();
757     MOZ_ASSERT(aWorkerPrivate->IsServiceWorker());
758     MOZ_ASSERT(!mPromiseHolder.IsEmpty());
759 
760     RefPtr<EventTarget> target = aWorkerPrivate->GlobalScope();
761 
762     ExtendableEventInit init;
763     init.mBubbles = false;
764     init.mCancelable = false;
765 
766     RefPtr<ExtendableEvent> event = ExtendableEvent::Constructor(
767         target, u"pushsubscriptionchange"_ns, init);
768     event->SetTrusted(true);
769 
770     nsresult rv = DispatchExtendableEventOnWorkerScope(
771         aCx, aWorkerPrivate->GlobalScope(), event, this);
772 
773     if (NS_WARN_IF(DispatchFailed(rv))) {
774       RejectAll(rv);
775     }
776 
777     return !DispatchFailed(rv);
778   }
779 };
780 
781 class NotificationEventOp : public ExtendableEventOp,
782                             public nsITimerCallback,
783                             public nsINamed {
784   using ExtendableEventOp::ExtendableEventOp;
785 
786  public:
787   NS_DECL_THREADSAFE_ISUPPORTS
788 
789  private:
~NotificationEventOp()790   ~NotificationEventOp() {
791     MOZ_DIAGNOSTIC_ASSERT(!mTimer);
792     MOZ_DIAGNOSTIC_ASSERT(!mWorkerRef);
793   }
794 
ClearWindowAllowed(WorkerPrivate * aWorkerPrivate)795   void ClearWindowAllowed(WorkerPrivate* aWorkerPrivate) {
796     MOZ_ASSERT(aWorkerPrivate);
797     aWorkerPrivate->AssertIsOnWorkerThread();
798     MOZ_ASSERT(aWorkerPrivate->IsServiceWorker());
799 
800     if (!mTimer) {
801       return;
802     }
803 
804     // This might be executed after the global was unrooted, in which case
805     // GlobalScope() will return null. Making the check here just to be safe.
806     WorkerGlobalScope* globalScope = aWorkerPrivate->GlobalScope();
807     if (!globalScope) {
808       return;
809     }
810 
811     globalScope->ConsumeWindowInteraction();
812     mTimer->Cancel();
813     mTimer = nullptr;
814 
815     mWorkerRef = nullptr;
816   }
817 
StartClearWindowTimer(WorkerPrivate * aWorkerPrivate)818   void StartClearWindowTimer(WorkerPrivate* aWorkerPrivate) {
819     MOZ_ASSERT(aWorkerPrivate);
820     aWorkerPrivate->AssertIsOnWorkerThread();
821     MOZ_ASSERT(!mTimer);
822 
823     nsresult rv;
824     nsCOMPtr<nsITimer> timer =
825         NS_NewTimer(aWorkerPrivate->ControlEventTarget());
826     if (NS_WARN_IF(!timer)) {
827       return;
828     }
829 
830     MOZ_ASSERT(!mWorkerRef);
831     RefPtr<NotificationEventOp> self = this;
832     mWorkerRef = StrongWorkerRef::Create(
833         aWorkerPrivate, "NotificationEventOp", [self = std::move(self)] {
834           // We could try to hold the worker alive until the timer fires, but
835           // other APIs are not likely to work in this partially shutdown state.
836           // We might as well let the worker thread exit.
837           self->ClearWindowAllowed(self->mWorkerRef->Private());
838         });
839 
840     if (!mWorkerRef) {
841       return;
842     }
843 
844     aWorkerPrivate->GlobalScope()->AllowWindowInteraction();
845     timer.swap(mTimer);
846 
847     // We swap first and then initialize the timer so that even if initializing
848     // fails, we still clean the busy count and interaction count correctly.
849     // The timer can't be initialized before modyfing the busy count since the
850     // timer thread could run and call the timeout but the worker may
851     // already be terminating and modifying the busy count could fail.
852     uint32_t delay = mArgs.get_ServiceWorkerNotificationEventOpArgs()
853                          .disableOpenClickDelay();
854     rv = mTimer->InitWithCallback(this, delay, nsITimer::TYPE_ONE_SHOT);
855 
856     if (NS_WARN_IF(NS_FAILED(rv))) {
857       ClearWindowAllowed(aWorkerPrivate);
858       return;
859     }
860   }
861 
862   // ExtendableEventOp interface
Exec(JSContext * aCx,WorkerPrivate * aWorkerPrivate)863   bool Exec(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override {
864     MOZ_ASSERT(aWorkerPrivate);
865     aWorkerPrivate->AssertIsOnWorkerThread();
866     MOZ_ASSERT(aWorkerPrivate->IsServiceWorker());
867     MOZ_ASSERT(!mPromiseHolder.IsEmpty());
868 
869     RefPtr<EventTarget> target = aWorkerPrivate->GlobalScope();
870 
871     ServiceWorkerNotificationEventOpArgs& args =
872         mArgs.get_ServiceWorkerNotificationEventOpArgs();
873 
874     ErrorResult result;
875     RefPtr<Notification> notification = Notification::ConstructFromFields(
876         aWorkerPrivate->GlobalScope(), args.id(), args.title(), args.dir(),
877         args.lang(), args.body(), args.tag(), args.icon(), args.data(),
878         args.scope(), result);
879 
880     if (NS_WARN_IF(result.Failed())) {
881       return false;
882     }
883 
884     NotificationEventInit init;
885     init.mNotification = notification;
886     init.mBubbles = false;
887     init.mCancelable = false;
888 
889     RefPtr<NotificationEvent> notificationEvent =
890         NotificationEvent::Constructor(target, args.eventName(), init);
891 
892     notificationEvent->SetTrusted(true);
893 
894     if (args.eventName().EqualsLiteral("notificationclick")) {
895       StartClearWindowTimer(aWorkerPrivate);
896     }
897 
898     nsresult rv = DispatchExtendableEventOnWorkerScope(
899         aCx, aWorkerPrivate->GlobalScope(), notificationEvent, this);
900 
901     if (NS_WARN_IF(DispatchFailed(rv))) {
902       // This will reject mPromiseHolder.
903       FinishedWithResult(Rejected);
904     }
905 
906     return !DispatchFailed(rv);
907   }
908 
FinishedWithResult(ExtendableEventResult aResult)909   void FinishedWithResult(ExtendableEventResult aResult) override {
910     MOZ_ASSERT(IsCurrentThreadRunningWorker());
911 
912     WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
913     MOZ_ASSERT(workerPrivate);
914 
915     ClearWindowAllowed(workerPrivate);
916 
917     ExtendableEventOp::FinishedWithResult(aResult);
918   }
919 
920   // nsITimerCallback interface
Notify(nsITimer * aTimer)921   NS_IMETHOD Notify(nsITimer* aTimer) override {
922     MOZ_DIAGNOSTIC_ASSERT(mTimer == aTimer);
923     WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
924     ClearWindowAllowed(workerPrivate);
925     return NS_OK;
926   }
927 
928   // nsINamed interface
GetName(nsACString & aName)929   NS_IMETHOD GetName(nsACString& aName) override {
930     aName.AssignLiteral("NotificationEventOp");
931     return NS_OK;
932   }
933 
934   nsCOMPtr<nsITimer> mTimer;
935   RefPtr<StrongWorkerRef> mWorkerRef;
936 };
937 
938 NS_IMPL_ISUPPORTS(NotificationEventOp, nsITimerCallback, nsINamed)
939 
940 class MessageEventOp final : public ExtendableEventOp {
941   using ExtendableEventOp::ExtendableEventOp;
942 
943  public:
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MessageEventOp,override)944   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MessageEventOp, override)
945 
946   MessageEventOp(ServiceWorkerOpArgs&& aArgs,
947                  std::function<void(const ServiceWorkerOpResult&)>&& aCallback)
948       : ExtendableEventOp(std::move(aArgs), std::move(aCallback)),
949         mData(new ServiceWorkerCloneData()) {
950     mData->CopyFromClonedMessageDataForBackgroundChild(
951         mArgs.get_ServiceWorkerMessageEventOpArgs().clonedData());
952   }
953 
954  private:
955   ~MessageEventOp() = default;
956 
Exec(JSContext * aCx,WorkerPrivate * aWorkerPrivate)957   bool Exec(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override {
958     MOZ_ASSERT(aWorkerPrivate);
959     aWorkerPrivate->AssertIsOnWorkerThread();
960     MOZ_ASSERT(aWorkerPrivate->IsServiceWorker());
961     MOZ_ASSERT(!mPromiseHolder.IsEmpty());
962 
963     JS::Rooted<JS::Value> messageData(aCx);
964     nsCOMPtr<nsIGlobalObject> sgo = aWorkerPrivate->GlobalScope();
965     ErrorResult rv;
966     if (!mData->IsErrorMessageData()) {
967       mData->Read(aCx, &messageData, rv);
968     }
969 
970     // If mData is an error message data, then it means that it failed to
971     // serialize on the caller side because it contains a shared memory object.
972     // If deserialization fails, we will fire a messageerror event.
973     const bool deserializationFailed =
974         rv.Failed() || mData->IsErrorMessageData();
975 
976     Sequence<OwningNonNull<MessagePort>> ports;
977     if (!mData->TakeTransferredPortsAsSequence(ports)) {
978       RejectAll(NS_ERROR_FAILURE);
979       rv.SuppressException();
980       return false;
981     }
982 
983     RootedDictionary<ExtendableMessageEventInit> init(aCx);
984 
985     init.mBubbles = false;
986     init.mCancelable = false;
987 
988     // On a messageerror event, we disregard ports:
989     // https://w3c.github.io/ServiceWorker/#service-worker-postmessage
990     if (!deserializationFailed) {
991       init.mData = messageData;
992       init.mPorts = std::move(ports);
993     }
994 
995     RefPtr<net::MozURL> mozUrl;
996     nsresult result = net::MozURL::Init(
997         getter_AddRefs(mozUrl), mArgs.get_ServiceWorkerMessageEventOpArgs()
998                                     .clientInfoAndState()
999                                     .info()
1000                                     .url());
1001     if (NS_WARN_IF(NS_FAILED(result))) {
1002       RejectAll(result);
1003       rv.SuppressException();
1004       return false;
1005     }
1006 
1007     nsCString origin;
1008     mozUrl->Origin(origin);
1009 
1010     CopyUTF8toUTF16(origin, init.mOrigin);
1011 
1012     init.mSource.SetValue().SetAsClient() = new Client(
1013         sgo, mArgs.get_ServiceWorkerMessageEventOpArgs().clientInfoAndState());
1014 
1015     rv.SuppressException();
1016     RefPtr<EventTarget> target = aWorkerPrivate->GlobalScope();
1017     RefPtr<ExtendableMessageEvent> extendableEvent =
1018         ExtendableMessageEvent::Constructor(
1019             target, deserializationFailed ? u"messageerror"_ns : u"message"_ns,
1020             init);
1021 
1022     extendableEvent->SetTrusted(true);
1023 
1024     nsresult rv2 = DispatchExtendableEventOnWorkerScope(
1025         aCx, aWorkerPrivate->GlobalScope(), extendableEvent, this);
1026 
1027     if (NS_WARN_IF(DispatchFailed(rv2))) {
1028       RejectAll(rv2);
1029     }
1030 
1031     return !DispatchFailed(rv2);
1032   }
1033 
1034   RefPtr<ServiceWorkerCloneData> mData;
1035 };
1036 
1037 /**
1038  * Used for ScopeExit-style network request cancelation in
1039  * `ResolvedCallback()` (e.g. if `FetchEvent::RespondWith()` is resolved with
1040  * a non-JS object).
1041  */
1042 class MOZ_STACK_CLASS FetchEventOp::AutoCancel {
1043  public:
AutoCancel(FetchEventOp * aOwner)1044   explicit AutoCancel(FetchEventOp* aOwner)
1045       : mOwner(aOwner),
1046         mLine(0),
1047         mColumn(0),
1048         mMessageName("InterceptionFailedWithURL"_ns) {
1049     MOZ_ASSERT(IsCurrentThreadRunningWorker());
1050     MOZ_ASSERT(mOwner);
1051 
1052     nsAutoString requestURL;
1053     mOwner->GetRequestURL(requestURL);
1054     mParams.AppendElement(requestURL);
1055   }
1056 
~AutoCancel()1057   ~AutoCancel() {
1058     if (mOwner) {
1059       if (mSourceSpec.IsEmpty()) {
1060         mOwner->AsyncLog(mMessageName, std::move(mParams));
1061       } else {
1062         mOwner->AsyncLog(mSourceSpec, mLine, mColumn, mMessageName,
1063                          std::move(mParams));
1064       }
1065 
1066       MOZ_ASSERT(!mOwner->mRespondWithPromiseHolder.IsEmpty());
1067       mOwner->mHandled->MaybeRejectWithNetworkError("AutoCancel"_ns);
1068       mOwner->mRespondWithPromiseHolder.Reject(NS_ERROR_INTERCEPTION_FAILED,
1069                                                __func__);
1070     }
1071   }
1072 
1073   // This function steals the error message from a ErrorResult.
SetCancelErrorResult(JSContext * aCx,ErrorResult & aRv)1074   void SetCancelErrorResult(JSContext* aCx, ErrorResult& aRv) {
1075     MOZ_DIAGNOSTIC_ASSERT(aRv.Failed());
1076     MOZ_DIAGNOSTIC_ASSERT(!JS_IsExceptionPending(aCx));
1077 
1078     // Storing the error as exception in the JSContext.
1079     if (!aRv.MaybeSetPendingException(aCx)) {
1080       return;
1081     }
1082 
1083     MOZ_ASSERT(!aRv.Failed());
1084 
1085     // Let's take the pending exception.
1086     JS::ExceptionStack exnStack(aCx);
1087     if (!JS::StealPendingExceptionStack(aCx, &exnStack)) {
1088       return;
1089     }
1090 
1091     // Converting the exception in a JS::ErrorReportBuilder.
1092     JS::ErrorReportBuilder report(aCx);
1093     if (!report.init(aCx, exnStack, JS::ErrorReportBuilder::WithSideEffects)) {
1094       JS_ClearPendingException(aCx);
1095       return;
1096     }
1097 
1098     MOZ_ASSERT(mOwner);
1099     MOZ_ASSERT(mMessageName.EqualsLiteral("InterceptionFailedWithURL"));
1100     MOZ_ASSERT(mParams.Length() == 1);
1101 
1102     // Let's store the error message here.
1103     mMessageName.Assign(report.toStringResult().c_str());
1104     mParams.Clear();
1105   }
1106 
1107   template <typename... Params>
SetCancelMessage(const nsACString & aMessageName,Params &&...aParams)1108   void SetCancelMessage(const nsACString& aMessageName, Params&&... aParams) {
1109     MOZ_ASSERT(mOwner);
1110     MOZ_ASSERT(mMessageName.EqualsLiteral("InterceptionFailedWithURL"));
1111     MOZ_ASSERT(mParams.Length() == 1);
1112     mMessageName = aMessageName;
1113     mParams.Clear();
1114     StringArrayAppender::Append(mParams, sizeof...(Params),
1115                                 std::forward<Params>(aParams)...);
1116   }
1117 
1118   template <typename... Params>
SetCancelMessageAndLocation(const nsACString & aSourceSpec,uint32_t aLine,uint32_t aColumn,const nsACString & aMessageName,Params &&...aParams)1119   void SetCancelMessageAndLocation(const nsACString& aSourceSpec,
1120                                    uint32_t aLine, uint32_t aColumn,
1121                                    const nsACString& aMessageName,
1122                                    Params&&... aParams) {
1123     MOZ_ASSERT(mOwner);
1124     MOZ_ASSERT(mMessageName.EqualsLiteral("InterceptionFailedWithURL"));
1125     MOZ_ASSERT(mParams.Length() == 1);
1126 
1127     mSourceSpec = aSourceSpec;
1128     mLine = aLine;
1129     mColumn = aColumn;
1130 
1131     mMessageName = aMessageName;
1132     mParams.Clear();
1133     StringArrayAppender::Append(mParams, sizeof...(Params),
1134                                 std::forward<Params>(aParams)...);
1135   }
1136 
Reset()1137   void Reset() { mOwner = nullptr; }
1138 
1139  private:
1140   FetchEventOp* MOZ_NON_OWNING_REF mOwner;
1141   nsCString mSourceSpec;
1142   uint32_t mLine;
1143   uint32_t mColumn;
1144   nsCString mMessageName;
1145   nsTArray<nsString> mParams;
1146 };
1147 
NS_IMPL_ISUPPORTS0(FetchEventOp)1148 NS_IMPL_ISUPPORTS0(FetchEventOp)
1149 
1150 void FetchEventOp::SetActor(RefPtr<FetchEventOpProxyChild> aActor) {
1151   MOZ_ASSERT(RemoteWorkerService::Thread()->IsOnCurrentThread());
1152   MOZ_ASSERT(!Started());
1153   MOZ_ASSERT(!mActor);
1154 
1155   mActor = std::move(aActor);
1156 }
1157 
RevokeActor(FetchEventOpProxyChild * aActor)1158 void FetchEventOp::RevokeActor(FetchEventOpProxyChild* aActor) {
1159   MOZ_ASSERT(aActor);
1160   MOZ_ASSERT_IF(mActor, mActor == aActor);
1161 
1162   mActor = nullptr;
1163 }
1164 
GetRespondWithPromise()1165 RefPtr<FetchEventRespondWithPromise> FetchEventOp::GetRespondWithPromise() {
1166   MOZ_ASSERT(RemoteWorkerService::Thread()->IsOnCurrentThread());
1167   MOZ_ASSERT(!Started());
1168   MOZ_ASSERT(mRespondWithPromiseHolder.IsEmpty());
1169 
1170   return mRespondWithPromiseHolder.Ensure(__func__);
1171 }
1172 
RespondWithCalledAt(const nsCString & aRespondWithScriptSpec,uint32_t aRespondWithLineNumber,uint32_t aRespondWithColumnNumber)1173 void FetchEventOp::RespondWithCalledAt(const nsCString& aRespondWithScriptSpec,
1174                                        uint32_t aRespondWithLineNumber,
1175                                        uint32_t aRespondWithColumnNumber) {
1176   MOZ_ASSERT(IsCurrentThreadRunningWorker());
1177   MOZ_ASSERT(!mRespondWithClosure);
1178 
1179   mRespondWithClosure.emplace(aRespondWithScriptSpec, aRespondWithLineNumber,
1180                               aRespondWithColumnNumber);
1181 }
1182 
ReportCanceled(const nsCString & aPreventDefaultScriptSpec,uint32_t aPreventDefaultLineNumber,uint32_t aPreventDefaultColumnNumber)1183 void FetchEventOp::ReportCanceled(const nsCString& aPreventDefaultScriptSpec,
1184                                   uint32_t aPreventDefaultLineNumber,
1185                                   uint32_t aPreventDefaultColumnNumber) {
1186   MOZ_ASSERT(IsCurrentThreadRunningWorker());
1187   MOZ_ASSERT(mActor);
1188   MOZ_ASSERT(!mPromiseHolder.IsEmpty());
1189 
1190   nsString requestURL;
1191   GetRequestURL(requestURL);
1192 
1193   AsyncLog(aPreventDefaultScriptSpec, aPreventDefaultLineNumber,
1194            aPreventDefaultColumnNumber, "InterceptionCanceledWithURL"_ns,
1195            {std::move(requestURL)});
1196 }
1197 
~FetchEventOp()1198 FetchEventOp::~FetchEventOp() {
1199   mRespondWithPromiseHolder.RejectIfExists(NS_ERROR_DOM_ABORT_ERR, __func__);
1200 
1201   if (mActor) {
1202     NS_ProxyRelease("FetchEventOp::mActor", RemoteWorkerService::Thread(),
1203                     mActor.forget());
1204   }
1205 }
1206 
RejectAll(nsresult aStatus)1207 void FetchEventOp::RejectAll(nsresult aStatus) {
1208   MOZ_ASSERT(!mRespondWithPromiseHolder.IsEmpty());
1209   MOZ_ASSERT(!mPromiseHolder.IsEmpty());
1210 
1211   mRespondWithPromiseHolder.Reject(aStatus, __func__);
1212   mPromiseHolder.Reject(aStatus, __func__);
1213 }
1214 
FinishedWithResult(ExtendableEventResult aResult)1215 void FetchEventOp::FinishedWithResult(ExtendableEventResult aResult) {
1216   MOZ_ASSERT(IsCurrentThreadRunningWorker());
1217   MOZ_ASSERT(!mPromiseHolder.IsEmpty());
1218   MOZ_ASSERT(!mResult);
1219 
1220   mResult.emplace(aResult);
1221 
1222   /**
1223    * This should only return early if neither waitUntil() nor respondWith()
1224    * are called. The early return is so that mRespondWithPromiseHolder has a
1225    * chance to settle before mPromiseHolder does.
1226    */
1227   if (!mPostDispatchChecksDone) {
1228     return;
1229   }
1230 
1231   MaybeFinished();
1232 }
1233 
MaybeFinished()1234 void FetchEventOp::MaybeFinished() {
1235   MOZ_ASSERT(IsCurrentThreadRunningWorker());
1236   MOZ_ASSERT(!mPromiseHolder.IsEmpty());
1237 
1238   if (mResult) {
1239     // It's possible that mRespondWithPromiseHolder wasn't settled. That happens
1240     // if the worker was terminated before the respondWith promise settled.
1241 
1242     mHandled = nullptr;
1243 
1244     ServiceWorkerFetchEventOpResult result(
1245         mResult.value() == Resolved ? NS_OK : NS_ERROR_FAILURE);
1246 
1247     mPromiseHolder.Resolve(result, __func__);
1248   }
1249 }
1250 
Exec(JSContext * aCx,WorkerPrivate * aWorkerPrivate)1251 bool FetchEventOp::Exec(JSContext* aCx, WorkerPrivate* aWorkerPrivate) {
1252   aWorkerPrivate->AssertIsOnWorkerThread();
1253   MOZ_ASSERT(aWorkerPrivate->IsServiceWorker());
1254   MOZ_ASSERT(!mRespondWithPromiseHolder.IsEmpty());
1255   MOZ_ASSERT(!mPromiseHolder.IsEmpty());
1256 
1257   nsresult rv = DispatchFetchEvent(aCx, aWorkerPrivate);
1258 
1259   if (NS_WARN_IF(NS_FAILED(rv))) {
1260     RejectAll(rv);
1261   }
1262 
1263   return NS_SUCCEEDED(rv);
1264 }
1265 
AsyncLog(const nsCString & aMessageName,nsTArray<nsString> aParams)1266 void FetchEventOp::AsyncLog(const nsCString& aMessageName,
1267                             nsTArray<nsString> aParams) {
1268   MOZ_ASSERT(mActor);
1269   MOZ_ASSERT(!mPromiseHolder.IsEmpty());
1270   MOZ_ASSERT(mRespondWithClosure);
1271 
1272   const FetchEventRespondWithClosure& closure = mRespondWithClosure.ref();
1273 
1274   AsyncLog(closure.respondWithScriptSpec(), closure.respondWithLineNumber(),
1275            closure.respondWithColumnNumber(), aMessageName, std::move(aParams));
1276 }
1277 
AsyncLog(const nsCString & aScriptSpec,uint32_t aLineNumber,uint32_t aColumnNumber,const nsCString & aMessageName,nsTArray<nsString> aParams)1278 void FetchEventOp::AsyncLog(const nsCString& aScriptSpec, uint32_t aLineNumber,
1279                             uint32_t aColumnNumber,
1280                             const nsCString& aMessageName,
1281                             nsTArray<nsString> aParams) {
1282   MOZ_ASSERT(mActor);
1283   MOZ_ASSERT(!mPromiseHolder.IsEmpty());
1284 
1285   // Capture `this` because FetchEventOpProxyChild (mActor) is not thread
1286   // safe, so an AddRef from RefPtr<FetchEventOpProxyChild>'s constructor will
1287   // assert.
1288   RefPtr<FetchEventOp> self = this;
1289 
1290   nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(
1291       __func__, [self = std::move(self), spec = aScriptSpec, line = aLineNumber,
1292                  column = aColumnNumber, messageName = aMessageName,
1293                  params = std::move(aParams)] {
1294         if (NS_WARN_IF(!self->mActor)) {
1295           return;
1296         }
1297 
1298         Unused << self->mActor->SendAsyncLog(spec, line, column, messageName,
1299                                              params);
1300       });
1301 
1302   MOZ_ALWAYS_SUCCEEDS(
1303       RemoteWorkerService::Thread()->Dispatch(r.forget(), NS_DISPATCH_NORMAL));
1304 }
1305 
GetRequestURL(nsAString & aOutRequestURL)1306 void FetchEventOp::GetRequestURL(nsAString& aOutRequestURL) {
1307   nsTArray<nsCString>& urls =
1308       mArgs.get_ServiceWorkerFetchEventOpArgs().internalRequest().urlList();
1309   MOZ_ASSERT(!urls.IsEmpty());
1310 
1311   CopyUTF8toUTF16(urls.LastElement(), aOutRequestURL);
1312 }
1313 
ResolvedCallback(JSContext * aCx,JS::Handle<JS::Value> aValue)1314 void FetchEventOp::ResolvedCallback(JSContext* aCx,
1315                                     JS::Handle<JS::Value> aValue) {
1316   MOZ_ASSERT(IsCurrentThreadRunningWorker());
1317   MOZ_ASSERT(mRespondWithClosure);
1318   MOZ_ASSERT(!mRespondWithPromiseHolder.IsEmpty());
1319   MOZ_ASSERT(!mPromiseHolder.IsEmpty());
1320 
1321   nsAutoString requestURL;
1322   GetRequestURL(requestURL);
1323 
1324   AutoCancel autoCancel(this);
1325 
1326   if (!aValue.isObject()) {
1327     NS_WARNING(
1328         "FetchEvent::RespondWith was passed a promise resolved to a "
1329         "non-Object "
1330         "value");
1331 
1332     nsCString sourceSpec;
1333     uint32_t line = 0;
1334     uint32_t column = 0;
1335     nsString valueString;
1336     nsContentUtils::ExtractErrorValues(aCx, aValue, sourceSpec, &line, &column,
1337                                        valueString);
1338 
1339     autoCancel.SetCancelMessageAndLocation(sourceSpec, line, column,
1340                                            "InterceptedNonResponseWithURL"_ns,
1341                                            requestURL, valueString);
1342     return;
1343   }
1344 
1345   RefPtr<Response> response;
1346   nsresult rv = UNWRAP_OBJECT(Response, &aValue.toObject(), response);
1347   if (NS_FAILED(rv)) {
1348     nsCString sourceSpec;
1349     uint32_t line = 0;
1350     uint32_t column = 0;
1351     nsString valueString;
1352     nsContentUtils::ExtractErrorValues(aCx, aValue, sourceSpec, &line, &column,
1353                                        valueString);
1354 
1355     autoCancel.SetCancelMessageAndLocation(sourceSpec, line, column,
1356                                            "InterceptedNonResponseWithURL"_ns,
1357                                            requestURL, valueString);
1358     return;
1359   }
1360 
1361   WorkerPrivate* worker = GetCurrentThreadWorkerPrivate();
1362   MOZ_ASSERT(worker);
1363   worker->AssertIsOnWorkerThread();
1364 
1365   // Section "HTTP Fetch", step 3.3:
1366   //  If one of the following conditions is true, return a network error:
1367   //    * response's type is "error".
1368   //    * request's mode is not "no-cors" and response's type is "opaque".
1369   //    * request's redirect mode is not "manual" and response's type is
1370   //      "opaqueredirect".
1371   //    * request's redirect mode is not "follow" and response's url list
1372   //      has more than one item.
1373 
1374   if (response->Type() == ResponseType::Error) {
1375     autoCancel.SetCancelMessage("InterceptedErrorResponseWithURL"_ns,
1376                                 requestURL);
1377     return;
1378   }
1379 
1380   const ServiceWorkerFetchEventOpArgs& args =
1381       mArgs.get_ServiceWorkerFetchEventOpArgs();
1382   const RequestMode requestMode = args.internalRequest().requestMode();
1383 
1384   if (response->Type() == ResponseType::Opaque &&
1385       requestMode != RequestMode::No_cors) {
1386     NS_ConvertASCIItoUTF16 modeString(
1387         RequestModeValues::GetString(requestMode));
1388 
1389     nsAutoString requestURL;
1390     GetRequestURL(requestURL);
1391 
1392     autoCancel.SetCancelMessage("BadOpaqueInterceptionRequestModeWithURL"_ns,
1393                                 requestURL, modeString);
1394     return;
1395   }
1396 
1397   const RequestRedirect requestRedirectMode =
1398       args.internalRequest().requestRedirect();
1399 
1400   if (requestRedirectMode != RequestRedirect::Manual &&
1401       response->Type() == ResponseType::Opaqueredirect) {
1402     autoCancel.SetCancelMessage("BadOpaqueRedirectInterceptionWithURL"_ns,
1403                                 requestURL);
1404     return;
1405   }
1406 
1407   if (requestRedirectMode != RequestRedirect::Follow &&
1408       response->Redirected()) {
1409     autoCancel.SetCancelMessage("BadRedirectModeInterceptionWithURL"_ns,
1410                                 requestURL);
1411     return;
1412   }
1413 
1414   {
1415     ErrorResult error;
1416     bool bodyUsed = response->GetBodyUsed(error);
1417     error.WouldReportJSException();
1418     if (NS_WARN_IF(error.Failed())) {
1419       autoCancel.SetCancelErrorResult(aCx, error);
1420       return;
1421     }
1422     if (NS_WARN_IF(bodyUsed)) {
1423       autoCancel.SetCancelMessage("InterceptedUsedResponseWithURL"_ns,
1424                                   requestURL);
1425       return;
1426     }
1427   }
1428 
1429   RefPtr<InternalResponse> ir = response->GetInternalResponse();
1430   if (NS_WARN_IF(!ir)) {
1431     return;
1432   }
1433 
1434   // An extra safety check to make sure our invariant that opaque and cors
1435   // responses always have a URL does not break.
1436   if (NS_WARN_IF((response->Type() == ResponseType::Opaque ||
1437                   response->Type() == ResponseType::Cors) &&
1438                  ir->GetUnfilteredURL().IsEmpty())) {
1439     MOZ_DIAGNOSTIC_ASSERT(false, "Cors or opaque Response without a URL");
1440     return;
1441   }
1442 
1443   Telemetry::ScalarAdd(Telemetry::ScalarID::SW_SYNTHESIZED_RES_COUNT, 1);
1444 
1445   if (requestMode == RequestMode::Same_origin &&
1446       response->Type() == ResponseType::Cors) {
1447     Telemetry::ScalarAdd(Telemetry::ScalarID::SW_CORS_RES_FOR_SO_REQ_COUNT, 1);
1448 
1449     // XXXtt: Will have a pref to enable the quirk response in bug 1419684.
1450     // The variadic template provided by StringArrayAppender requires exactly
1451     // an nsString.
1452     NS_ConvertUTF8toUTF16 responseURL(ir->GetUnfilteredURL());
1453     autoCancel.SetCancelMessage("CorsResponseForSameOriginRequest"_ns,
1454                                 requestURL, responseURL);
1455     return;
1456   }
1457 
1458   nsCOMPtr<nsIInputStream> body;
1459   ir->GetUnfilteredBody(getter_AddRefs(body));
1460   // Errors and redirects may not have a body.
1461   if (body) {
1462     ErrorResult error;
1463     response->SetBodyUsed(aCx, error);
1464     error.WouldReportJSException();
1465     if (NS_WARN_IF(error.Failed())) {
1466       autoCancel.SetCancelErrorResult(aCx, error);
1467       return;
1468     }
1469   }
1470 
1471   if (!ir->GetChannelInfo().IsInitialized()) {
1472     // This is a synthetic response (I think and hope so).
1473     ir->InitChannelInfo(worker->GetChannelInfo());
1474   }
1475 
1476   autoCancel.Reset();
1477 
1478   // https://w3c.github.io/ServiceWorker/#on-fetch-request-algorithm Step 26: If
1479   // eventHandled is not null, then resolve eventHandled.
1480   //
1481   // mRespondWithPromiseHolder will resolve a MozPromise that will resolve on
1482   // the worker owner's thread, so it's fine to resolve the mHandled promise now
1483   // because content will not interfere with respondWith getting the Response to
1484   // where it's going.
1485   mHandled->MaybeResolveWithUndefined();
1486   mRespondWithPromiseHolder.Resolve(
1487       FetchEventRespondWithResult(
1488           SynthesizeResponseArgs(ir, mRespondWithClosure.ref())),
1489       __func__);
1490 }
1491 
RejectedCallback(JSContext * aCx,JS::Handle<JS::Value> aValue)1492 void FetchEventOp::RejectedCallback(JSContext* aCx,
1493                                     JS::Handle<JS::Value> aValue) {
1494   MOZ_ASSERT(IsCurrentThreadRunningWorker());
1495   MOZ_ASSERT(mRespondWithClosure);
1496   MOZ_ASSERT(!mRespondWithPromiseHolder.IsEmpty());
1497   MOZ_ASSERT(!mPromiseHolder.IsEmpty());
1498 
1499   FetchEventRespondWithClosure& closure = mRespondWithClosure.ref();
1500 
1501   nsCString sourceSpec = closure.respondWithScriptSpec();
1502   uint32_t line = closure.respondWithLineNumber();
1503   uint32_t column = closure.respondWithColumnNumber();
1504   nsString valueString;
1505 
1506   nsContentUtils::ExtractErrorValues(aCx, aValue, sourceSpec, &line, &column,
1507                                      valueString);
1508 
1509   nsString requestURL;
1510   GetRequestURL(requestURL);
1511 
1512   AsyncLog(sourceSpec, line, column, "InterceptionRejectedResponseWithURL"_ns,
1513            {std::move(requestURL), valueString});
1514 
1515   // https://w3c.github.io/ServiceWorker/#on-fetch-request-algorithm Step 25.1:
1516   // If eventHandled is not null, then reject eventHandled with a "NetworkError"
1517   // DOMException in workerRealm.
1518   mHandled->MaybeRejectWithNetworkError(
1519       "FetchEvent.respondWith() Promise rejected"_ns);
1520   mRespondWithPromiseHolder.Resolve(
1521       FetchEventRespondWithResult(
1522           CancelInterceptionArgs(NS_ERROR_INTERCEPTION_FAILED)),
1523       __func__);
1524 }
1525 
DispatchFetchEvent(JSContext * aCx,WorkerPrivate * aWorkerPrivate)1526 nsresult FetchEventOp::DispatchFetchEvent(JSContext* aCx,
1527                                           WorkerPrivate* aWorkerPrivate) {
1528   MOZ_ASSERT(aCx);
1529   MOZ_ASSERT(aWorkerPrivate);
1530   aWorkerPrivate->AssertIsOnWorkerThread();
1531   MOZ_ASSERT(aWorkerPrivate->IsServiceWorker());
1532 
1533   ServiceWorkerFetchEventOpArgs& args =
1534       mArgs.get_ServiceWorkerFetchEventOpArgs();
1535 
1536   /**
1537    * Testing: Failure injection.
1538    *
1539    * There are a number of different ways that this fetch event could have
1540    * failed that would result in cancellation.  This injection point helps
1541    * simulate them without worrying about shifting implementation details with
1542    * full fidelity reproductions of current scenarios.
1543    *
1544    * Broadly speaking, we expect fetch event scenarios to fail because of:
1545    * - Script load failure, which results in the CompileScriptRunnable closing
1546    *   the worker and thereby cancelling all pending operations, including this
1547    *   fetch.  The `ServiceWorkerOp::Cancel` impl just calls
1548    *   RejectAll(NS_ERROR_DOM_ABORT_ERR) which we are able to approximate by
1549    *   returning the same nsresult here, as our caller also calls RejectAll.
1550    *   (And timing-wise, this rejection will happen in the correct sequence.)
1551    * - An exception gets thrown in the processing of the promise that was passed
1552    *   to respondWith and it ends up rejecting.  The rejection will be converted
1553    *   by `FetchEventOp::RejectedCallback` into a cancellation with
1554    *   NS_ERROR_INTERCEPTION_FAILED, and by returning that here we approximate
1555    *   that failure mode.
1556    */
1557   if (NS_FAILED(args.testingInjectCancellation())) {
1558     return args.testingInjectCancellation();
1559   }
1560 
1561   /**
1562    * Step 1: get the InternalRequest. The InternalRequest can't be constructed
1563    * here from mArgs because the IPCStream has to be deserialized on the
1564    * thread receiving the ServiceWorkerFetchEventOpArgs.
1565    * FetchEventOpProxyChild will have already deserialized the stream on the
1566    * correct thread before creating this op, so we can take its saved
1567    * InternalRequest.
1568    */
1569   SafeRefPtr<InternalRequest> internalRequest =
1570       mActor->ExtractInternalRequest();
1571 
1572   /**
1573    * Step 2: get the worker's global object
1574    */
1575   GlobalObject globalObject(aCx, aWorkerPrivate->GlobalScope()->GetWrapper());
1576   nsCOMPtr<nsIGlobalObject> globalObjectAsSupports =
1577       do_QueryInterface(globalObject.GetAsSupports());
1578   if (NS_WARN_IF(!globalObjectAsSupports)) {
1579     return NS_ERROR_DOM_INVALID_STATE_ERR;
1580   }
1581 
1582   /**
1583    * Step 3: create the public DOM Request object
1584    * TODO: this Request object should be created with an AbortSignal object
1585    * which should be aborted if the loading is aborted. See but 1394102.
1586    */
1587   RefPtr<Request> request =
1588       new Request(globalObjectAsSupports, internalRequest.clonePtr(), nullptr);
1589   MOZ_ASSERT_IF(internalRequest->IsNavigationRequest(),
1590                 request->Redirect() == RequestRedirect::Manual);
1591 
1592   /**
1593    * Step 4a: create the FetchEventInit
1594    */
1595   RootedDictionary<FetchEventInit> fetchEventInit(aCx);
1596   fetchEventInit.mRequest = request;
1597   fetchEventInit.mBubbles = false;
1598   fetchEventInit.mCancelable = true;
1599 
1600   /**
1601    * TODO: only expose the FetchEvent.clientId on subresource requests for
1602    * now. Once we implement .targetClientId we can then start exposing
1603    * .clientId on non-subresource requests as well.  See bug 1487534.
1604    */
1605   if (!args.clientId().IsEmpty() && !internalRequest->IsNavigationRequest()) {
1606     fetchEventInit.mClientId = args.clientId();
1607   }
1608 
1609   /*
1610    * https://w3c.github.io/ServiceWorker/#on-fetch-request-algorithm
1611    *
1612    * "If request is a non-subresource request and request’s
1613    * destination is not "report", initialize e’s resultingClientId attribute
1614    * to reservedClient’s [resultingClient's] id, and to the empty string
1615    * otherwise." (Step 18.8)
1616    */
1617   if (!args.resultingClientId().IsEmpty() && args.isNonSubresourceRequest() &&
1618       internalRequest->Destination() != RequestDestination::Report) {
1619     fetchEventInit.mResultingClientId = args.resultingClientId();
1620   }
1621 
1622   /**
1623    * Step 4b: create the FetchEvent
1624    */
1625   RefPtr<FetchEvent> fetchEvent =
1626       FetchEvent::Constructor(globalObject, u"fetch"_ns, fetchEventInit);
1627   fetchEvent->SetTrusted(true);
1628   fetchEvent->PostInit(args.workerScriptSpec(), this);
1629   mHandled = fetchEvent->Handled();
1630 
1631   /**
1632    * Step 5: Dispatch the FetchEvent to the worker's global object
1633    */
1634   nsresult rv = DispatchExtendableEventOnWorkerScope(
1635       aCx, aWorkerPrivate->GlobalScope(), fetchEvent, this);
1636   bool dispatchFailed = NS_FAILED(rv) && rv != NS_ERROR_XPC_JS_THREW_EXCEPTION;
1637 
1638   if (NS_WARN_IF(dispatchFailed)) {
1639     mHandled = nullptr;
1640     return rv;
1641   }
1642 
1643   /**
1644    * At this point, there are 4 (legal) scenarios:
1645    *
1646    * 1) If neither waitUntil() nor respondWith() are called,
1647    * DispatchExtendableEventOnWorkerScope() will have already called
1648    * FinishedWithResult(), but this call will have recorded the result
1649    * (mResult) and returned early so that mRespondWithPromiseHolder can be
1650    * settled first. mRespondWithPromiseHolder will be settled below, followed
1651    * by a call to MaybeFinished() which settles mPromiseHolder.
1652    *
1653    * 2) If waitUntil() is called at least once, and respondWith() is not
1654    * called, DispatchExtendableEventOnWorkerScope() will NOT have called
1655    * FinishedWithResult(). We'll settle mRespondWithPromiseHolder first, and
1656    * at some point in the future when the last waitUntil() promise settles,
1657    * FinishedWithResult() will be called, settling mPromiseHolder.
1658    *
1659    * 3) If waitUntil() is not called, and respondWith() is called,
1660    * DispatchExtendableEventOnWorkerScope() will NOT have called
1661    * FinishedWithResult(). We can also guarantee that
1662    * mRespondWithPromiseHolder will be settled before mPromiseHolder, due to
1663    * the Promise::AppendNativeHandler() call ordering in
1664    * FetchEvent::RespondWith().
1665    *
1666    * 4) If waitUntil() is called at least once, and respondWith() is also
1667    * called, the effect is similar to scenario 3), with the most imporant
1668    * property being mRespondWithPromiseHolder settling before mPromiseHolder.
1669    *
1670    * Note that if mPromiseHolder is settled before mRespondWithPromiseHolder,
1671    * FetchEventOpChild will cancel the interception.
1672    */
1673   if (!fetchEvent->WaitToRespond()) {
1674     MOZ_ASSERT(!mRespondWithPromiseHolder.IsEmpty());
1675     MOZ_ASSERT(!aWorkerPrivate->UsesSystemPrincipal(),
1676                "We don't support system-principal serviceworkers");
1677 
1678     if (fetchEvent->DefaultPrevented(CallerType::NonSystem)) {
1679       // https://w3c.github.io/ServiceWorker/#on-fetch-request-algorithm
1680       // Step 24.1.1: If eventHandled is not null, then reject eventHandled with
1681       // a "NetworkError" DOMException in workerRealm.
1682       mHandled->MaybeRejectWithNetworkError(
1683           "FetchEvent.preventDefault() called"_ns);
1684       mRespondWithPromiseHolder.Resolve(
1685           FetchEventRespondWithResult(
1686               CancelInterceptionArgs(NS_ERROR_INTERCEPTION_FAILED)),
1687           __func__);
1688     } else {
1689       // https://w3c.github.io/ServiceWorker/#on-fetch-request-algorithm
1690       // Step 24.2: If eventHandled is not null, then resolve eventHandled.
1691       mHandled->MaybeResolveWithUndefined();
1692       mRespondWithPromiseHolder.Resolve(
1693           FetchEventRespondWithResult(ResetInterceptionArgs()), __func__);
1694     }
1695   } else {
1696     MOZ_ASSERT(mRespondWithClosure);
1697   }
1698 
1699   mPostDispatchChecksDone = true;
1700   MaybeFinished();
1701 
1702   return NS_OK;
1703 }
1704 
Create(ServiceWorkerOpArgs && aArgs,std::function<void (const ServiceWorkerOpResult &)> && aCallback)1705 /* static */ already_AddRefed<ServiceWorkerOp> ServiceWorkerOp::Create(
1706     ServiceWorkerOpArgs&& aArgs,
1707     std::function<void(const ServiceWorkerOpResult&)>&& aCallback) {
1708   MOZ_ASSERT(RemoteWorkerService::Thread()->IsOnCurrentThread());
1709 
1710   RefPtr<ServiceWorkerOp> op;
1711 
1712   switch (aArgs.type()) {
1713     case ServiceWorkerOpArgs::TServiceWorkerCheckScriptEvaluationOpArgs:
1714       op = MakeRefPtr<CheckScriptEvaluationOp>(std::move(aArgs),
1715                                                std::move(aCallback));
1716       break;
1717     case ServiceWorkerOpArgs::TServiceWorkerUpdateStateOpArgs:
1718       op = MakeRefPtr<UpdateServiceWorkerStateOp>(std::move(aArgs),
1719                                                   std::move(aCallback));
1720       break;
1721     case ServiceWorkerOpArgs::TServiceWorkerTerminateWorkerOpArgs:
1722       op = MakeRefPtr<TerminateServiceWorkerOp>(std::move(aArgs),
1723                                                 std::move(aCallback));
1724       break;
1725     case ServiceWorkerOpArgs::TServiceWorkerLifeCycleEventOpArgs:
1726       op = MakeRefPtr<LifeCycleEventOp>(std::move(aArgs), std::move(aCallback));
1727       break;
1728     case ServiceWorkerOpArgs::TServiceWorkerPushEventOpArgs:
1729       op = MakeRefPtr<PushEventOp>(std::move(aArgs), std::move(aCallback));
1730       break;
1731     case ServiceWorkerOpArgs::TServiceWorkerPushSubscriptionChangeEventOpArgs:
1732       op = MakeRefPtr<PushSubscriptionChangeEventOp>(std::move(aArgs),
1733                                                      std::move(aCallback));
1734       break;
1735     case ServiceWorkerOpArgs::TServiceWorkerNotificationEventOpArgs:
1736       op = MakeRefPtr<NotificationEventOp>(std::move(aArgs),
1737                                            std::move(aCallback));
1738       break;
1739     case ServiceWorkerOpArgs::TServiceWorkerMessageEventOpArgs:
1740       op = MakeRefPtr<MessageEventOp>(std::move(aArgs), std::move(aCallback));
1741       break;
1742     case ServiceWorkerOpArgs::TServiceWorkerFetchEventOpArgs:
1743       op = MakeRefPtr<FetchEventOp>(std::move(aArgs), std::move(aCallback));
1744       break;
1745     default:
1746       MOZ_CRASH("Unknown Service Worker operation!");
1747       return nullptr;
1748   }
1749 
1750   return op.forget();
1751 }
1752 
1753 }  // namespace dom
1754 }  // namespace mozilla
1755