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