no bug - Bumping Firefox l10n changesets r=release a=l10n-bump DONTBUILD CLOSED TREE
[gecko.git] / dom / serviceworkers / ServiceWorkerOp.cpp
blob8811404a0f7577a37d3aa2ecd00ed875dd277d04
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/. */
7 #include "ServiceWorkerOp.h"
9 #include <utility>
11 #include "ServiceWorkerOpPromise.h"
12 #include "js/Exception.h" // JS::ExceptionStack, JS::StealPendingExceptionStack
13 #include "jsapi.h"
15 #include "nsCOMPtr.h"
16 #include "nsContentUtils.h"
17 #include "nsDebug.h"
18 #include "nsError.h"
19 #include "nsINamed.h"
20 #include "nsIPushErrorReporter.h"
21 #include "nsISupportsImpl.h"
22 #include "nsITimer.h"
23 #include "nsIURI.h"
24 #include "nsServiceManagerUtils.h"
25 #include "nsTArray.h"
26 #include "nsThreadUtils.h"
28 #include "ServiceWorkerCloneData.h"
29 #include "ServiceWorkerShutdownState.h"
30 #include "mozilla/Assertions.h"
31 #include "mozilla/CycleCollectedJSContext.h"
32 #include "mozilla/DebugOnly.h"
33 #include "mozilla/ErrorResult.h"
34 #include "mozilla/OwningNonNull.h"
35 #include "mozilla/SchedulerGroup.h"
36 #include "mozilla/ScopeExit.h"
37 #include "mozilla/Telemetry.h"
38 #include "mozilla/Unused.h"
39 #include "mozilla/dom/BindingDeclarations.h"
40 #include "mozilla/dom/Client.h"
41 #include "mozilla/dom/ExtendableMessageEventBinding.h"
42 #include "mozilla/dom/FetchEventBinding.h"
43 #include "mozilla/dom/FetchEventOpProxyChild.h"
44 #include "mozilla/dom/InternalHeaders.h"
45 #include "mozilla/dom/InternalRequest.h"
46 #include "mozilla/dom/InternalResponse.h"
47 #include "mozilla/dom/Notification.h"
48 #include "mozilla/dom/NotificationEvent.h"
49 #include "mozilla/dom/NotificationEventBinding.h"
50 #include "mozilla/dom/PerformanceTiming.h"
51 #include "mozilla/dom/PerformanceStorage.h"
52 #include "mozilla/dom/PushEventBinding.h"
53 #include "mozilla/dom/RemoteWorkerChild.h"
54 #include "mozilla/dom/RemoteWorkerService.h"
55 #include "mozilla/dom/Request.h"
56 #include "mozilla/dom/Response.h"
57 #include "mozilla/dom/RootedDictionary.h"
58 #include "mozilla/dom/SafeRefPtr.h"
59 #include "mozilla/dom/ServiceWorkerBinding.h"
60 #include "mozilla/dom/ServiceWorkerGlobalScopeBinding.h"
61 #include "mozilla/dom/WorkerCommon.h"
62 #include "mozilla/dom/WorkerRef.h"
63 #include "mozilla/dom/WorkerScope.h"
64 #include "mozilla/extensions/ExtensionBrowser.h"
65 #include "mozilla/ipc/IPCStreamUtils.h"
67 namespace mozilla::dom {
69 namespace {
71 class ExtendableEventKeepAliveHandler final
72 : public ExtendableEvent::ExtensionsHandler,
73 public PromiseNativeHandler {
74 public:
75 NS_DECL_ISUPPORTS
77 static RefPtr<ExtendableEventKeepAliveHandler> Create(
78 RefPtr<ExtendableEventCallback> aCallback) {
79 MOZ_ASSERT(IsCurrentThreadRunningWorker());
81 RefPtr<ExtendableEventKeepAliveHandler> self =
82 new ExtendableEventKeepAliveHandler(std::move(aCallback));
84 self->mWorkerRef = StrongWorkerRef::Create(
85 GetCurrentThreadWorkerPrivate(), "ExtendableEventKeepAliveHandler",
86 [self]() { self->Cleanup(); });
88 if (NS_WARN_IF(!self->mWorkerRef)) {
89 return nullptr;
92 return self;
95 /**
96 * ExtendableEvent::ExtensionsHandler interface
98 bool WaitOnPromise(Promise& aPromise) override {
99 if (!mAcceptingPromises) {
100 MOZ_ASSERT(!GetDispatchFlag());
101 MOZ_ASSERT(!mSelfRef, "We shouldn't be holding a self reference!");
102 return false;
105 if (!mSelfRef) {
106 MOZ_ASSERT(!mPendingPromisesCount);
107 mSelfRef = this;
110 ++mPendingPromisesCount;
111 aPromise.AppendNativeHandler(this);
113 return true;
117 * PromiseNativeHandler interface
119 void ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue,
120 ErrorResult& aRv) override {
121 RemovePromise(Resolved);
124 void RejectedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue,
125 ErrorResult& aRv) override {
126 RemovePromise(Rejected);
129 void MaybeDone() {
130 MOZ_ASSERT(IsCurrentThreadRunningWorker());
131 MOZ_ASSERT(!GetDispatchFlag());
133 if (mPendingPromisesCount) {
134 return;
137 if (mCallback) {
138 mCallback->FinishedWithResult(mRejected ? Rejected : Resolved);
139 mCallback = nullptr;
142 Cleanup();
145 private:
147 * This class is useful for the case where pending microtasks will continue
148 * extending the event, which means that the event is not "done." For example:
150 * // `e` is an ExtendableEvent, `p` is a Promise
151 * e.waitUntil(p);
152 * p.then(() => e.waitUntil(otherPromise));
154 class MaybeDoneRunner : public MicroTaskRunnable {
155 public:
156 explicit MaybeDoneRunner(RefPtr<ExtendableEventKeepAliveHandler> aHandler)
157 : mHandler(std::move(aHandler)) {}
159 void Run(AutoSlowOperation& /* unused */) override {
160 mHandler->MaybeDone();
163 private:
164 RefPtr<ExtendableEventKeepAliveHandler> mHandler;
167 explicit ExtendableEventKeepAliveHandler(
168 RefPtr<ExtendableEventCallback> aCallback)
169 : mCallback(std::move(aCallback)) {}
171 ~ExtendableEventKeepAliveHandler() { Cleanup(); }
173 void Cleanup() {
174 MOZ_ASSERT(IsCurrentThreadRunningWorker());
176 if (mCallback) {
177 mCallback->FinishedWithResult(Rejected);
180 mSelfRef = nullptr;
181 mWorkerRef = nullptr;
182 mCallback = nullptr;
183 mAcceptingPromises = false;
186 void RemovePromise(ExtendableEventResult aResult) {
187 MOZ_ASSERT(IsCurrentThreadRunningWorker());
188 MOZ_DIAGNOSTIC_ASSERT(mPendingPromisesCount > 0);
190 // NOTE: mSelfRef can be nullptr here if MaybeCleanup() was just called
191 // before a promise settled. This can happen, for example, if the worker
192 // thread is being terminated for running too long, browser shutdown, etc.
194 mRejected |= (aResult == Rejected);
196 --mPendingPromisesCount;
197 if (mPendingPromisesCount || GetDispatchFlag()) {
198 return;
201 CycleCollectedJSContext* cx = CycleCollectedJSContext::Get();
202 MOZ_ASSERT(cx);
204 RefPtr<MaybeDoneRunner> r = new MaybeDoneRunner(this);
205 cx->DispatchToMicroTask(r.forget());
209 * We start holding a self reference when the first extension promise is
210 * added, and this reference is released when the last promise settles or
211 * when the worker is shutting down.
213 * This is needed in the case that we're waiting indefinitely on a to-be-GC'ed
214 * promise that's no longer reachable and will never be settled.
216 RefPtr<ExtendableEventKeepAliveHandler> mSelfRef;
218 RefPtr<StrongWorkerRef> mWorkerRef;
220 RefPtr<ExtendableEventCallback> mCallback;
222 uint32_t mPendingPromisesCount = 0;
224 bool mRejected = false;
225 bool mAcceptingPromises = true;
228 NS_IMPL_ISUPPORTS0(ExtendableEventKeepAliveHandler)
230 nsresult DispatchExtendableEventOnWorkerScope(
231 JSContext* aCx, WorkerGlobalScope* aWorkerScope, ExtendableEvent* aEvent,
232 RefPtr<ExtendableEventCallback> aCallback) {
233 MOZ_ASSERT(aCx);
234 MOZ_ASSERT(aWorkerScope);
235 MOZ_ASSERT(aEvent);
237 nsCOMPtr<nsIGlobalObject> globalObject = aWorkerScope;
238 WidgetEvent* internalEvent = aEvent->WidgetEventPtr();
240 RefPtr<ExtendableEventKeepAliveHandler> keepAliveHandler =
241 ExtendableEventKeepAliveHandler::Create(std::move(aCallback));
242 if (NS_WARN_IF(!keepAliveHandler)) {
243 return NS_ERROR_FAILURE;
246 // This must be always set *before* dispatching the event, otherwise
247 // waitUntil() calls will fail.
248 aEvent->SetKeepAliveHandler(keepAliveHandler);
250 ErrorResult result;
251 aWorkerScope->DispatchEvent(*aEvent, result);
252 if (NS_WARN_IF(result.Failed())) {
253 result.SuppressException();
254 return NS_ERROR_FAILURE;
257 keepAliveHandler->MaybeDone();
259 // We don't block the event when getting an exception but still report the
260 // error message. NOTE: this will not stop the event.
261 if (internalEvent->mFlags.mExceptionWasRaised) {
262 return NS_ERROR_XPC_JS_THREW_EXCEPTION;
265 return NS_OK;
268 bool DispatchFailed(nsresult aStatus) {
269 return NS_FAILED(aStatus) && aStatus != NS_ERROR_XPC_JS_THREW_EXCEPTION;
272 } // anonymous namespace
274 class ServiceWorkerOp::ServiceWorkerOpRunnable final
275 : public WorkerDebuggeeRunnable {
276 public:
277 NS_DECL_ISUPPORTS_INHERITED
279 ServiceWorkerOpRunnable(RefPtr<ServiceWorkerOp> aOwner,
280 WorkerPrivate* aWorkerPrivate)
281 : WorkerDebuggeeRunnable(aWorkerPrivate, "ServiceWorkerOpRunnable",
282 WorkerThread),
283 mOwner(std::move(aOwner)) {
284 AssertIsOnMainThread();
285 MOZ_ASSERT(mOwner);
286 MOZ_ASSERT(aWorkerPrivate);
289 private:
290 ~ServiceWorkerOpRunnable() = default;
292 bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override {
293 MOZ_ASSERT(aWorkerPrivate);
294 aWorkerPrivate->AssertIsOnWorkerThread();
295 MOZ_ASSERT(aWorkerPrivate->IsServiceWorker());
296 MOZ_ASSERT(mOwner);
298 if (aWorkerPrivate->GlobalScope()->IsDying()) {
299 Unused << Cancel();
300 return true;
303 bool rv = mOwner->Exec(aCx, aWorkerPrivate);
304 Unused << NS_WARN_IF(!rv);
305 mOwner = nullptr;
307 return rv;
310 nsresult Cancel() override {
311 MOZ_ASSERT(mOwner);
313 mOwner->RejectAll(NS_ERROR_DOM_ABORT_ERR);
314 mOwner = nullptr;
316 return NS_OK;
319 RefPtr<ServiceWorkerOp> mOwner;
322 NS_IMPL_ISUPPORTS_INHERITED0(ServiceWorkerOp::ServiceWorkerOpRunnable,
323 WorkerRunnable)
325 bool ServiceWorkerOp::MaybeStart(RemoteWorkerChild* aOwner,
326 RemoteWorkerChild::State& aState) {
327 MOZ_ASSERT(!mStarted);
328 MOZ_ASSERT(aOwner);
329 MOZ_ASSERT(aOwner->GetActorEventTarget()->IsOnCurrentThread());
331 auto launcherData = aOwner->mLauncherData.Access();
333 if (NS_WARN_IF(!aOwner->CanSend())) {
334 RejectAll(NS_ERROR_DOM_ABORT_ERR);
335 mStarted = true;
336 return true;
339 // Allow termination to happen while the Service Worker is initializing.
340 if (aState.is<Pending>() && !IsTerminationOp()) {
341 return false;
344 if (NS_WARN_IF(aState.is<RemoteWorkerChild::Canceled>()) ||
345 NS_WARN_IF(aState.is<RemoteWorkerChild::Killed>())) {
346 RejectAll(NS_ERROR_DOM_INVALID_STATE_ERR);
347 mStarted = true;
348 return true;
351 MOZ_ASSERT(aState.is<RemoteWorkerChild::Running>() || IsTerminationOp());
353 RefPtr<ServiceWorkerOp> self = this;
355 if (IsTerminationOp()) {
356 aOwner->GetTerminationPromise()->Then(
357 GetCurrentSerialEventTarget(), __func__,
358 [self](
359 const GenericNonExclusivePromise::ResolveOrRejectValue& aResult) {
360 MaybeReportServiceWorkerShutdownProgress(self->mArgs, true);
362 MOZ_ASSERT(!self->mPromiseHolder.IsEmpty());
364 if (NS_WARN_IF(aResult.IsReject())) {
365 self->mPromiseHolder.Reject(aResult.RejectValue(), __func__);
366 return;
369 self->mPromiseHolder.Resolve(NS_OK, __func__);
373 // NewRunnableMethod doesn't work here because the template does not appear to
374 // be able to deal with the owner argument having storage as a RefPtr but
375 // with the method taking a RefPtr&.
376 RefPtr<RemoteWorkerChild> owner = aOwner;
377 nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(
378 __func__, [self = std::move(self), owner = std::move(owner)]() mutable {
379 self->StartOnMainThread(owner);
382 mStarted = true;
384 MOZ_ALWAYS_SUCCEEDS(SchedulerGroup::Dispatch(r.forget()));
385 return true;
388 void ServiceWorkerOp::StartOnMainThread(RefPtr<RemoteWorkerChild>& aOwner) {
389 MaybeReportServiceWorkerShutdownProgress(mArgs);
392 auto lock = aOwner->mState.Lock();
394 if (NS_WARN_IF(!lock->is<Running>() && !IsTerminationOp())) {
395 RejectAll(NS_ERROR_DOM_INVALID_STATE_ERR);
396 return;
400 if (IsTerminationOp()) {
401 aOwner->CloseWorkerOnMainThread();
402 } else {
403 auto lock = aOwner->mState.Lock();
404 MOZ_ASSERT(lock->is<Running>());
406 RefPtr<WorkerRunnable> workerRunnable =
407 GetRunnable(lock->as<Running>().mWorkerPrivate);
409 if (NS_WARN_IF(!workerRunnable->Dispatch())) {
410 RejectAll(NS_ERROR_FAILURE);
415 void ServiceWorkerOp::Cancel() { RejectAll(NS_ERROR_DOM_ABORT_ERR); }
417 ServiceWorkerOp::ServiceWorkerOp(
418 ServiceWorkerOpArgs&& aArgs,
419 std::function<void(const ServiceWorkerOpResult&)>&& aCallback)
420 : mArgs(std::move(aArgs)) {
421 MOZ_ASSERT(RemoteWorkerService::Thread()->IsOnCurrentThread());
423 RefPtr<ServiceWorkerOpPromise> promise = mPromiseHolder.Ensure(__func__);
425 promise->Then(
426 GetCurrentSerialEventTarget(), __func__,
427 [callback = std::move(aCallback)](
428 ServiceWorkerOpPromise::ResolveOrRejectValue&& aResult) mutable {
429 if (NS_WARN_IF(aResult.IsReject())) {
430 MOZ_ASSERT(NS_FAILED(aResult.RejectValue()));
431 callback(aResult.RejectValue());
432 return;
435 callback(aResult.ResolveValue());
439 ServiceWorkerOp::~ServiceWorkerOp() {
440 Unused << NS_WARN_IF(!mPromiseHolder.IsEmpty());
441 mPromiseHolder.RejectIfExists(NS_ERROR_DOM_ABORT_ERR, __func__);
444 bool ServiceWorkerOp::Started() const {
445 MOZ_ASSERT(RemoteWorkerService::Thread()->IsOnCurrentThread());
447 return mStarted;
450 bool ServiceWorkerOp::IsTerminationOp() const {
451 return mArgs.type() ==
452 ServiceWorkerOpArgs::TServiceWorkerTerminateWorkerOpArgs;
455 RefPtr<WorkerRunnable> ServiceWorkerOp::GetRunnable(
456 WorkerPrivate* aWorkerPrivate) {
457 AssertIsOnMainThread();
458 MOZ_ASSERT(aWorkerPrivate);
460 return new ServiceWorkerOpRunnable(this, aWorkerPrivate);
463 void ServiceWorkerOp::RejectAll(nsresult aStatus) {
464 MOZ_ASSERT(!mPromiseHolder.IsEmpty());
465 mPromiseHolder.Reject(aStatus, __func__);
468 class CheckScriptEvaluationOp final : public ServiceWorkerOp {
469 using ServiceWorkerOp::ServiceWorkerOp;
471 public:
472 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(CheckScriptEvaluationOp, override)
474 private:
475 ~CheckScriptEvaluationOp() = default;
477 bool Exec(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override {
478 MOZ_ASSERT(aWorkerPrivate);
479 aWorkerPrivate->AssertIsOnWorkerThread();
480 MOZ_ASSERT(aWorkerPrivate->IsServiceWorker());
481 MOZ_ASSERT(!mPromiseHolder.IsEmpty());
483 ServiceWorkerCheckScriptEvaluationOpResult result;
484 result.workerScriptExecutedSuccessfully() =
485 aWorkerPrivate->WorkerScriptExecutedSuccessfully();
486 result.fetchHandlerWasAdded() = aWorkerPrivate->FetchHandlerWasAdded();
488 mPromiseHolder.Resolve(result, __func__);
490 return true;
494 class TerminateServiceWorkerOp final : public ServiceWorkerOp {
495 using ServiceWorkerOp::ServiceWorkerOp;
497 public:
498 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(TerminateServiceWorkerOp, override)
500 private:
501 ~TerminateServiceWorkerOp() = default;
503 bool Exec(JSContext*, WorkerPrivate*) override {
504 MOZ_ASSERT_UNREACHABLE(
505 "Worker termination should be handled in "
506 "`ServiceWorkerOp::MaybeStart()`");
508 return false;
512 class UpdateServiceWorkerStateOp final : public ServiceWorkerOp {
513 using ServiceWorkerOp::ServiceWorkerOp;
515 public:
516 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(UpdateServiceWorkerStateOp, override);
518 private:
519 class UpdateStateOpRunnable final : public MainThreadWorkerControlRunnable {
520 public:
521 NS_DECL_ISUPPORTS_INHERITED
523 UpdateStateOpRunnable(RefPtr<UpdateServiceWorkerStateOp> aOwner,
524 WorkerPrivate* aWorkerPrivate)
525 : MainThreadWorkerControlRunnable(aWorkerPrivate),
526 mOwner(std::move(aOwner)) {
527 AssertIsOnMainThread();
528 MOZ_ASSERT(mOwner);
529 MOZ_ASSERT(aWorkerPrivate);
532 private:
533 ~UpdateStateOpRunnable() = default;
535 bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override {
536 MOZ_ASSERT(aWorkerPrivate);
537 aWorkerPrivate->AssertIsOnWorkerThread();
538 MOZ_ASSERT(aWorkerPrivate->IsServiceWorker());
540 if (mOwner) {
541 Unused << mOwner->Exec(aCx, aWorkerPrivate);
542 mOwner = nullptr;
545 return true;
548 nsresult Cancel() override {
549 MOZ_ASSERT(mOwner);
551 mOwner->RejectAll(NS_ERROR_DOM_ABORT_ERR);
552 mOwner = nullptr;
554 return NS_OK;
557 RefPtr<UpdateServiceWorkerStateOp> mOwner;
560 ~UpdateServiceWorkerStateOp() = default;
562 RefPtr<WorkerRunnable> GetRunnable(WorkerPrivate* aWorkerPrivate) override {
563 AssertIsOnMainThread();
564 MOZ_ASSERT(aWorkerPrivate);
565 MOZ_ASSERT(mArgs.type() ==
566 ServiceWorkerOpArgs::TServiceWorkerUpdateStateOpArgs);
568 return new UpdateStateOpRunnable(this, aWorkerPrivate);
571 bool Exec(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override {
572 MOZ_ASSERT(aWorkerPrivate);
573 aWorkerPrivate->AssertIsOnWorkerThread();
574 MOZ_ASSERT(aWorkerPrivate->IsServiceWorker());
575 MOZ_ASSERT(!mPromiseHolder.IsEmpty());
577 ServiceWorkerState state =
578 mArgs.get_ServiceWorkerUpdateStateOpArgs().state();
579 aWorkerPrivate->UpdateServiceWorkerState(state);
581 mPromiseHolder.Resolve(NS_OK, __func__);
583 return true;
587 NS_IMPL_ISUPPORTS_INHERITED0(UpdateServiceWorkerStateOp::UpdateStateOpRunnable,
588 MainThreadWorkerControlRunnable)
590 void ExtendableEventOp::FinishedWithResult(ExtendableEventResult aResult) {
591 MOZ_ASSERT(IsCurrentThreadRunningWorker());
592 MOZ_ASSERT(!mPromiseHolder.IsEmpty());
594 mPromiseHolder.Resolve(aResult == Resolved ? NS_OK : NS_ERROR_FAILURE,
595 __func__);
598 class LifeCycleEventOp final : public ExtendableEventOp {
599 using ExtendableEventOp::ExtendableEventOp;
601 public:
602 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(LifeCycleEventOp, override)
604 private:
605 ~LifeCycleEventOp() = default;
607 bool Exec(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override {
608 MOZ_ASSERT(aWorkerPrivate);
609 aWorkerPrivate->AssertIsOnWorkerThread();
610 MOZ_ASSERT(aWorkerPrivate->IsServiceWorker());
611 MOZ_ASSERT(!mPromiseHolder.IsEmpty());
613 RefPtr<ExtendableEvent> event;
614 RefPtr<EventTarget> target = aWorkerPrivate->GlobalScope();
616 const nsString& eventName =
617 mArgs.get_ServiceWorkerLifeCycleEventOpArgs().eventName();
619 if (eventName.EqualsASCII("install") || eventName.EqualsASCII("activate")) {
620 ExtendableEventInit init;
621 init.mBubbles = false;
622 init.mCancelable = false;
623 event = ExtendableEvent::Constructor(target, eventName, init);
624 } else {
625 MOZ_CRASH("Unexpected lifecycle event");
628 event->SetTrusted(true);
630 nsresult rv = DispatchExtendableEventOnWorkerScope(
631 aCx, aWorkerPrivate->GlobalScope(), event, this);
633 if (NS_WARN_IF(DispatchFailed(rv))) {
634 RejectAll(rv);
637 return !DispatchFailed(rv);
642 * PushEventOp
644 class PushEventOp final : public ExtendableEventOp {
645 using ExtendableEventOp::ExtendableEventOp;
647 public:
648 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(PushEventOp, override)
650 private:
651 ~PushEventOp() = default;
653 bool Exec(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override {
654 MOZ_ASSERT(aWorkerPrivate);
655 aWorkerPrivate->AssertIsOnWorkerThread();
656 MOZ_ASSERT(aWorkerPrivate->IsServiceWorker());
657 MOZ_ASSERT(!mPromiseHolder.IsEmpty());
659 ErrorResult result;
661 auto scopeExit = MakeScopeExit([&] {
662 MOZ_ASSERT(result.Failed());
664 RejectAll(result.StealNSResult());
665 ReportError(aWorkerPrivate);
668 const ServiceWorkerPushEventOpArgs& args =
669 mArgs.get_ServiceWorkerPushEventOpArgs();
671 RootedDictionary<PushEventInit> pushEventInit(aCx);
673 if (args.data().type() != OptionalPushData::Tvoid_t) {
674 const auto& bytes = args.data().get_ArrayOfuint8_t();
675 JSObject* data = Uint8Array::Create(aCx, bytes, result);
677 if (result.Failed()) {
678 return false;
681 DebugOnly<bool> inited =
682 pushEventInit.mData.Construct().SetAsArrayBufferView().Init(data);
683 MOZ_ASSERT(inited);
686 pushEventInit.mBubbles = false;
687 pushEventInit.mCancelable = false;
689 GlobalObject globalObj(aCx, aWorkerPrivate->GlobalScope()->GetWrapper());
690 RefPtr<PushEvent> pushEvent =
691 PushEvent::Constructor(globalObj, u"push"_ns, pushEventInit, result);
693 if (NS_WARN_IF(result.Failed())) {
694 return false;
697 pushEvent->SetTrusted(true);
699 scopeExit.release();
701 nsresult rv = DispatchExtendableEventOnWorkerScope(
702 aCx, aWorkerPrivate->GlobalScope(), pushEvent, this);
704 if (NS_FAILED(rv)) {
705 if (NS_WARN_IF(DispatchFailed(rv))) {
706 RejectAll(rv);
709 // We don't cancel WorkerPrivate when catching an exception.
710 ReportError(aWorkerPrivate,
711 nsIPushErrorReporter::DELIVERY_UNCAUGHT_EXCEPTION);
714 return !DispatchFailed(rv);
717 void FinishedWithResult(ExtendableEventResult aResult) override {
718 MOZ_ASSERT(IsCurrentThreadRunningWorker());
720 WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
722 if (aResult == Rejected) {
723 ReportError(workerPrivate,
724 nsIPushErrorReporter::DELIVERY_UNHANDLED_REJECTION);
727 ExtendableEventOp::FinishedWithResult(aResult);
730 void ReportError(
731 WorkerPrivate* aWorkerPrivate,
732 uint16_t aError = nsIPushErrorReporter::DELIVERY_INTERNAL_ERROR) {
733 MOZ_ASSERT(aWorkerPrivate);
734 aWorkerPrivate->AssertIsOnWorkerThread();
735 MOZ_ASSERT(aWorkerPrivate->IsServiceWorker());
737 if (NS_WARN_IF(aError > nsIPushErrorReporter::DELIVERY_INTERNAL_ERROR) ||
738 mArgs.get_ServiceWorkerPushEventOpArgs().messageId().IsEmpty()) {
739 return;
742 nsString messageId = mArgs.get_ServiceWorkerPushEventOpArgs().messageId();
743 nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(
744 __func__, [messageId = std::move(messageId), error = aError] {
745 nsCOMPtr<nsIPushErrorReporter> reporter =
746 do_GetService("@mozilla.org/push/Service;1");
748 if (reporter) {
749 nsresult rv = reporter->ReportDeliveryError(messageId, error);
750 Unused << NS_WARN_IF(NS_FAILED(rv));
754 MOZ_ALWAYS_SUCCEEDS(aWorkerPrivate->DispatchToMainThread(r.forget()));
758 class PushSubscriptionChangeEventOp final : public ExtendableEventOp {
759 using ExtendableEventOp::ExtendableEventOp;
761 public:
762 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(PushSubscriptionChangeEventOp, override)
764 private:
765 ~PushSubscriptionChangeEventOp() = default;
767 bool Exec(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override {
768 MOZ_ASSERT(aWorkerPrivate);
769 aWorkerPrivate->AssertIsOnWorkerThread();
770 MOZ_ASSERT(aWorkerPrivate->IsServiceWorker());
771 MOZ_ASSERT(!mPromiseHolder.IsEmpty());
773 RefPtr<EventTarget> target = aWorkerPrivate->GlobalScope();
775 ExtendableEventInit init;
776 init.mBubbles = false;
777 init.mCancelable = false;
779 RefPtr<ExtendableEvent> event = ExtendableEvent::Constructor(
780 target, u"pushsubscriptionchange"_ns, init);
781 event->SetTrusted(true);
783 nsresult rv = DispatchExtendableEventOnWorkerScope(
784 aCx, aWorkerPrivate->GlobalScope(), event, this);
786 if (NS_WARN_IF(DispatchFailed(rv))) {
787 RejectAll(rv);
790 return !DispatchFailed(rv);
794 class NotificationEventOp : public ExtendableEventOp,
795 public nsITimerCallback,
796 public nsINamed {
797 using ExtendableEventOp::ExtendableEventOp;
799 public:
800 NS_DECL_THREADSAFE_ISUPPORTS
802 private:
803 ~NotificationEventOp() {
804 MOZ_DIAGNOSTIC_ASSERT(!mTimer);
805 MOZ_DIAGNOSTIC_ASSERT(!mWorkerRef);
808 void ClearWindowAllowed(WorkerPrivate* aWorkerPrivate) {
809 MOZ_ASSERT(aWorkerPrivate);
810 aWorkerPrivate->AssertIsOnWorkerThread();
811 MOZ_ASSERT(aWorkerPrivate->IsServiceWorker());
813 if (!mTimer) {
814 return;
817 // This might be executed after the global was unrooted, in which case
818 // GlobalScope() will return null. Making the check here just to be safe.
819 WorkerGlobalScope* globalScope = aWorkerPrivate->GlobalScope();
820 if (!globalScope) {
821 return;
824 globalScope->ConsumeWindowInteraction();
825 mTimer->Cancel();
826 mTimer = nullptr;
828 mWorkerRef = nullptr;
831 void StartClearWindowTimer(WorkerPrivate* aWorkerPrivate) {
832 MOZ_ASSERT(aWorkerPrivate);
833 aWorkerPrivate->AssertIsOnWorkerThread();
834 MOZ_ASSERT(!mTimer);
836 nsresult rv;
837 nsCOMPtr<nsITimer> timer =
838 NS_NewTimer(aWorkerPrivate->ControlEventTarget());
839 if (NS_WARN_IF(!timer)) {
840 return;
843 MOZ_ASSERT(!mWorkerRef);
844 RefPtr<NotificationEventOp> self = this;
845 mWorkerRef = StrongWorkerRef::Create(
846 aWorkerPrivate, "NotificationEventOp", [self = std::move(self)] {
847 // We could try to hold the worker alive until the timer fires, but
848 // other APIs are not likely to work in this partially shutdown state.
849 // We might as well let the worker thread exit.
850 self->ClearWindowAllowed(self->mWorkerRef->Private());
853 if (!mWorkerRef) {
854 return;
857 aWorkerPrivate->GlobalScope()->AllowWindowInteraction();
858 timer.swap(mTimer);
860 // We swap first and then initialize the timer so that even if initializing
861 // fails, we still clean the interaction count correctly.
862 uint32_t delay = mArgs.get_ServiceWorkerNotificationEventOpArgs()
863 .disableOpenClickDelay();
864 rv = mTimer->InitWithCallback(this, delay, nsITimer::TYPE_ONE_SHOT);
866 if (NS_WARN_IF(NS_FAILED(rv))) {
867 ClearWindowAllowed(aWorkerPrivate);
868 return;
872 // ExtendableEventOp interface
873 bool Exec(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override {
874 MOZ_ASSERT(aWorkerPrivate);
875 aWorkerPrivate->AssertIsOnWorkerThread();
876 MOZ_ASSERT(aWorkerPrivate->IsServiceWorker());
877 MOZ_ASSERT(!mPromiseHolder.IsEmpty());
879 RefPtr<EventTarget> target = aWorkerPrivate->GlobalScope();
881 ServiceWorkerNotificationEventOpArgs& args =
882 mArgs.get_ServiceWorkerNotificationEventOpArgs();
884 auto result = Notification::ConstructFromFields(
885 aWorkerPrivate->GlobalScope(), args.id(), args.title(), args.dir(),
886 args.lang(), args.body(), args.tag(), args.icon(), args.data(),
887 args.scope());
889 if (NS_WARN_IF(result.isErr())) {
890 return false;
893 NotificationEventInit init;
894 init.mNotification = result.unwrap();
895 init.mBubbles = false;
896 init.mCancelable = false;
898 RefPtr<NotificationEvent> notificationEvent =
899 NotificationEvent::Constructor(target, args.eventName(), init);
901 notificationEvent->SetTrusted(true);
903 if (args.eventName().EqualsLiteral("notificationclick")) {
904 StartClearWindowTimer(aWorkerPrivate);
907 nsresult rv = DispatchExtendableEventOnWorkerScope(
908 aCx, aWorkerPrivate->GlobalScope(), notificationEvent, this);
910 if (NS_WARN_IF(DispatchFailed(rv))) {
911 // This will reject mPromiseHolder.
912 FinishedWithResult(Rejected);
915 return !DispatchFailed(rv);
918 void FinishedWithResult(ExtendableEventResult aResult) override {
919 MOZ_ASSERT(IsCurrentThreadRunningWorker());
921 WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
922 MOZ_ASSERT(workerPrivate);
924 ClearWindowAllowed(workerPrivate);
926 ExtendableEventOp::FinishedWithResult(aResult);
929 // nsITimerCallback interface
930 NS_IMETHOD Notify(nsITimer* aTimer) override {
931 MOZ_DIAGNOSTIC_ASSERT(mTimer == aTimer);
932 WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
933 ClearWindowAllowed(workerPrivate);
934 return NS_OK;
937 // nsINamed interface
938 NS_IMETHOD GetName(nsACString& aName) override {
939 aName.AssignLiteral("NotificationEventOp");
940 return NS_OK;
943 nsCOMPtr<nsITimer> mTimer;
944 RefPtr<StrongWorkerRef> mWorkerRef;
947 NS_IMPL_ISUPPORTS(NotificationEventOp, nsITimerCallback, nsINamed)
949 class MessageEventOp final : public ExtendableEventOp {
950 using ExtendableEventOp::ExtendableEventOp;
952 public:
953 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MessageEventOp, override)
955 MessageEventOp(ServiceWorkerOpArgs&& aArgs,
956 std::function<void(const ServiceWorkerOpResult&)>&& aCallback)
957 : ExtendableEventOp(std::move(aArgs), std::move(aCallback)),
958 mData(new ServiceWorkerCloneData()) {
959 mData->CopyFromClonedMessageData(
960 mArgs.get_ServiceWorkerMessageEventOpArgs().clonedData());
963 private:
964 ~MessageEventOp() = default;
966 bool Exec(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override {
967 MOZ_ASSERT(aWorkerPrivate);
968 aWorkerPrivate->AssertIsOnWorkerThread();
969 MOZ_ASSERT(aWorkerPrivate->IsServiceWorker());
970 MOZ_ASSERT(!mPromiseHolder.IsEmpty());
972 JS::Rooted<JS::Value> messageData(aCx);
973 nsCOMPtr<nsIGlobalObject> sgo = aWorkerPrivate->GlobalScope();
974 ErrorResult rv;
975 if (!mData->IsErrorMessageData()) {
976 mData->Read(aCx, &messageData, rv);
979 // If mData is an error message data, then it means that it failed to
980 // serialize on the caller side because it contains a shared memory object.
981 // If deserialization fails, we will fire a messageerror event.
982 const bool deserializationFailed =
983 rv.Failed() || mData->IsErrorMessageData();
985 Sequence<OwningNonNull<MessagePort>> ports;
986 if (!mData->TakeTransferredPortsAsSequence(ports)) {
987 RejectAll(NS_ERROR_FAILURE);
988 rv.SuppressException();
989 return false;
992 RootedDictionary<ExtendableMessageEventInit> init(aCx);
994 init.mBubbles = false;
995 init.mCancelable = false;
997 // On a messageerror event, we disregard ports:
998 // https://w3c.github.io/ServiceWorker/#service-worker-postmessage
999 if (!deserializationFailed) {
1000 init.mData = messageData;
1001 init.mPorts = std::move(ports);
1004 nsCOMPtr<nsIURI> url;
1005 nsresult result = NS_NewURI(getter_AddRefs(url),
1006 mArgs.get_ServiceWorkerMessageEventOpArgs()
1007 .clientInfoAndState()
1008 .info()
1009 .url());
1010 if (NS_WARN_IF(NS_FAILED(result))) {
1011 RejectAll(result);
1012 rv.SuppressException();
1013 return false;
1016 OriginAttributes attrs;
1017 nsCOMPtr<nsIPrincipal> principal =
1018 BasePrincipal::CreateContentPrincipal(url, attrs);
1019 if (!principal) {
1020 return false;
1023 nsCString origin;
1024 result = principal->GetOriginNoSuffix(origin);
1025 if (NS_WARN_IF(NS_FAILED(result))) {
1026 RejectAll(result);
1027 rv.SuppressException();
1028 return false;
1031 CopyUTF8toUTF16(origin, init.mOrigin);
1033 init.mSource.SetValue().SetAsClient() = new Client(
1034 sgo, mArgs.get_ServiceWorkerMessageEventOpArgs().clientInfoAndState());
1036 rv.SuppressException();
1037 RefPtr<EventTarget> target = aWorkerPrivate->GlobalScope();
1038 RefPtr<ExtendableMessageEvent> extendableEvent =
1039 ExtendableMessageEvent::Constructor(
1040 target, deserializationFailed ? u"messageerror"_ns : u"message"_ns,
1041 init);
1043 extendableEvent->SetTrusted(true);
1045 nsresult rv2 = DispatchExtendableEventOnWorkerScope(
1046 aCx, aWorkerPrivate->GlobalScope(), extendableEvent, this);
1048 if (NS_WARN_IF(DispatchFailed(rv2))) {
1049 RejectAll(rv2);
1052 return !DispatchFailed(rv2);
1055 RefPtr<ServiceWorkerCloneData> mData;
1059 * Used for ScopeExit-style network request cancelation in
1060 * `ResolvedCallback()` (e.g. if `FetchEvent::RespondWith()` is resolved with
1061 * a non-JS object).
1063 class MOZ_STACK_CLASS FetchEventOp::AutoCancel {
1064 public:
1065 explicit AutoCancel(FetchEventOp* aOwner)
1066 : mOwner(aOwner),
1067 mLine(0),
1068 mColumn(0),
1069 mMessageName("InterceptionFailedWithURL"_ns) {
1070 MOZ_ASSERT(IsCurrentThreadRunningWorker());
1071 MOZ_ASSERT(mOwner);
1073 nsAutoString requestURL;
1074 mOwner->GetRequestURL(requestURL);
1075 mParams.AppendElement(requestURL);
1078 ~AutoCancel() {
1079 if (mOwner) {
1080 if (mSourceSpec.IsEmpty()) {
1081 mOwner->AsyncLog(mMessageName, std::move(mParams));
1082 } else {
1083 mOwner->AsyncLog(mSourceSpec, mLine, mColumn, mMessageName,
1084 std::move(mParams));
1087 MOZ_ASSERT(!mOwner->mRespondWithPromiseHolder.IsEmpty());
1088 mOwner->mHandled->MaybeRejectWithNetworkError("AutoCancel"_ns);
1089 mOwner->mRespondWithPromiseHolder.Reject(
1090 CancelInterceptionArgs(
1091 NS_ERROR_INTERCEPTION_FAILED,
1092 FetchEventTimeStamps(mOwner->mFetchHandlerStart,
1093 mOwner->mFetchHandlerFinish)),
1094 __func__);
1098 // This function steals the error message from a ErrorResult.
1099 void SetCancelErrorResult(JSContext* aCx, ErrorResult& aRv) {
1100 MOZ_DIAGNOSTIC_ASSERT(aRv.Failed());
1101 MOZ_DIAGNOSTIC_ASSERT(!JS_IsExceptionPending(aCx));
1103 // Storing the error as exception in the JSContext.
1104 if (!aRv.MaybeSetPendingException(aCx)) {
1105 return;
1108 MOZ_ASSERT(!aRv.Failed());
1110 // Let's take the pending exception.
1111 JS::ExceptionStack exnStack(aCx);
1112 if (!JS::StealPendingExceptionStack(aCx, &exnStack)) {
1113 return;
1116 // Converting the exception in a JS::ErrorReportBuilder.
1117 JS::ErrorReportBuilder report(aCx);
1118 if (!report.init(aCx, exnStack, JS::ErrorReportBuilder::WithSideEffects)) {
1119 JS_ClearPendingException(aCx);
1120 return;
1123 MOZ_ASSERT(mOwner);
1124 MOZ_ASSERT(mMessageName.EqualsLiteral("InterceptionFailedWithURL"));
1125 MOZ_ASSERT(mParams.Length() == 1);
1127 // Let's store the error message here.
1128 mMessageName.Assign(report.toStringResult().c_str());
1129 mParams.Clear();
1132 template <typename... Params>
1133 void SetCancelMessage(const nsACString& aMessageName, Params&&... aParams) {
1134 MOZ_ASSERT(mOwner);
1135 MOZ_ASSERT(mMessageName.EqualsLiteral("InterceptionFailedWithURL"));
1136 MOZ_ASSERT(mParams.Length() == 1);
1137 mMessageName = aMessageName;
1138 mParams.Clear();
1139 StringArrayAppender::Append(mParams, sizeof...(Params),
1140 std::forward<Params>(aParams)...);
1143 template <typename... Params>
1144 void SetCancelMessageAndLocation(const nsACString& aSourceSpec,
1145 uint32_t aLine, uint32_t aColumn,
1146 const nsACString& aMessageName,
1147 Params&&... aParams) {
1148 MOZ_ASSERT(mOwner);
1149 MOZ_ASSERT(mMessageName.EqualsLiteral("InterceptionFailedWithURL"));
1150 MOZ_ASSERT(mParams.Length() == 1);
1152 mSourceSpec = aSourceSpec;
1153 mLine = aLine;
1154 mColumn = aColumn;
1156 mMessageName = aMessageName;
1157 mParams.Clear();
1158 StringArrayAppender::Append(mParams, sizeof...(Params),
1159 std::forward<Params>(aParams)...);
1162 void Reset() { mOwner = nullptr; }
1164 private:
1165 FetchEventOp* MOZ_NON_OWNING_REF mOwner;
1166 nsCString mSourceSpec;
1167 uint32_t mLine;
1168 uint32_t mColumn;
1169 nsCString mMessageName;
1170 nsTArray<nsString> mParams;
1173 NS_IMPL_ISUPPORTS0(FetchEventOp)
1175 void FetchEventOp::SetActor(RefPtr<FetchEventOpProxyChild> aActor) {
1176 MOZ_ASSERT(RemoteWorkerService::Thread()->IsOnCurrentThread());
1177 MOZ_ASSERT(!Started());
1178 MOZ_ASSERT(!mActor);
1180 mActor = std::move(aActor);
1183 void FetchEventOp::RevokeActor(FetchEventOpProxyChild* aActor) {
1184 MOZ_ASSERT(aActor);
1185 MOZ_ASSERT_IF(mActor, mActor == aActor);
1187 mActor = nullptr;
1190 RefPtr<FetchEventRespondWithPromise> FetchEventOp::GetRespondWithPromise() {
1191 MOZ_ASSERT(RemoteWorkerService::Thread()->IsOnCurrentThread());
1192 MOZ_ASSERT(!Started());
1193 MOZ_ASSERT(mRespondWithPromiseHolder.IsEmpty());
1195 return mRespondWithPromiseHolder.Ensure(__func__);
1198 void FetchEventOp::RespondWithCalledAt(const nsCString& aRespondWithScriptSpec,
1199 uint32_t aRespondWithLineNumber,
1200 uint32_t aRespondWithColumnNumber) {
1201 MOZ_ASSERT(IsCurrentThreadRunningWorker());
1202 MOZ_ASSERT(!mRespondWithClosure);
1204 mRespondWithClosure.emplace(aRespondWithScriptSpec, aRespondWithLineNumber,
1205 aRespondWithColumnNumber);
1208 void FetchEventOp::ReportCanceled(const nsCString& aPreventDefaultScriptSpec,
1209 uint32_t aPreventDefaultLineNumber,
1210 uint32_t aPreventDefaultColumnNumber) {
1211 MOZ_ASSERT(IsCurrentThreadRunningWorker());
1212 MOZ_ASSERT(mActor);
1213 MOZ_ASSERT(!mPromiseHolder.IsEmpty());
1215 nsString requestURL;
1216 GetRequestURL(requestURL);
1218 AsyncLog(aPreventDefaultScriptSpec, aPreventDefaultLineNumber,
1219 aPreventDefaultColumnNumber, "InterceptionCanceledWithURL"_ns,
1220 {std::move(requestURL)});
1223 FetchEventOp::~FetchEventOp() {
1224 mRespondWithPromiseHolder.RejectIfExists(
1225 CancelInterceptionArgs(
1226 NS_ERROR_DOM_ABORT_ERR,
1227 FetchEventTimeStamps(mFetchHandlerStart, mFetchHandlerFinish)),
1228 __func__);
1231 void FetchEventOp::RejectAll(nsresult aStatus) {
1232 MOZ_ASSERT(!mRespondWithPromiseHolder.IsEmpty());
1233 MOZ_ASSERT(!mPromiseHolder.IsEmpty());
1235 if (mFetchHandlerStart.IsNull()) {
1236 mFetchHandlerStart = TimeStamp::Now();
1238 if (mFetchHandlerFinish.IsNull()) {
1239 mFetchHandlerFinish = TimeStamp::Now();
1242 mRespondWithPromiseHolder.Reject(
1243 CancelInterceptionArgs(
1244 aStatus,
1245 FetchEventTimeStamps(mFetchHandlerStart, mFetchHandlerFinish)),
1246 __func__);
1247 mPromiseHolder.Reject(aStatus, __func__);
1250 void FetchEventOp::FinishedWithResult(ExtendableEventResult aResult) {
1251 MOZ_ASSERT(IsCurrentThreadRunningWorker());
1252 MOZ_ASSERT(!mPromiseHolder.IsEmpty());
1253 MOZ_ASSERT(!mResult);
1255 mResult.emplace(aResult);
1258 * This should only return early if neither waitUntil() nor respondWith()
1259 * are called. The early return is so that mRespondWithPromiseHolder has a
1260 * chance to settle before mPromiseHolder does.
1262 if (!mPostDispatchChecksDone) {
1263 return;
1266 MaybeFinished();
1269 void FetchEventOp::MaybeFinished() {
1270 MOZ_ASSERT(IsCurrentThreadRunningWorker());
1271 MOZ_ASSERT(!mPromiseHolder.IsEmpty());
1273 if (mResult) {
1274 // It's possible that mRespondWithPromiseHolder wasn't settled. That happens
1275 // if the worker was terminated before the respondWith promise settled.
1277 mHandled = nullptr;
1278 mPreloadResponse = nullptr;
1279 mPreloadResponseAvailablePromiseRequestHolder.DisconnectIfExists();
1280 mPreloadResponseTimingPromiseRequestHolder.DisconnectIfExists();
1281 mPreloadResponseEndPromiseRequestHolder.DisconnectIfExists();
1283 ServiceWorkerFetchEventOpResult result(
1284 mResult.value() == Resolved ? NS_OK : NS_ERROR_FAILURE);
1286 mPromiseHolder.Resolve(result, __func__);
1290 bool FetchEventOp::Exec(JSContext* aCx, WorkerPrivate* aWorkerPrivate) {
1291 aWorkerPrivate->AssertIsOnWorkerThread();
1292 MOZ_ASSERT(aWorkerPrivate->IsServiceWorker());
1293 MOZ_ASSERT(!mRespondWithPromiseHolder.IsEmpty());
1294 MOZ_ASSERT(!mPromiseHolder.IsEmpty());
1296 nsresult rv = DispatchFetchEvent(aCx, aWorkerPrivate);
1298 if (NS_WARN_IF(NS_FAILED(rv))) {
1299 RejectAll(rv);
1302 return NS_SUCCEEDED(rv);
1305 void FetchEventOp::AsyncLog(const nsCString& aMessageName,
1306 nsTArray<nsString> aParams) {
1307 MOZ_ASSERT(mActor);
1308 MOZ_ASSERT(!mPromiseHolder.IsEmpty());
1309 MOZ_ASSERT(mRespondWithClosure);
1311 const FetchEventRespondWithClosure& closure = mRespondWithClosure.ref();
1313 AsyncLog(closure.respondWithScriptSpec(), closure.respondWithLineNumber(),
1314 closure.respondWithColumnNumber(), aMessageName, std::move(aParams));
1317 void FetchEventOp::AsyncLog(const nsCString& aScriptSpec, uint32_t aLineNumber,
1318 uint32_t aColumnNumber,
1319 const nsCString& aMessageName,
1320 nsTArray<nsString> aParams) {
1321 MOZ_ASSERT(mActor);
1322 MOZ_ASSERT(!mPromiseHolder.IsEmpty());
1324 // Capture `this` because FetchEventOpProxyChild (mActor) is not thread
1325 // safe, so an AddRef from RefPtr<FetchEventOpProxyChild>'s constructor will
1326 // assert.
1327 RefPtr<FetchEventOp> self = this;
1329 nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(
1330 __func__, [self = std::move(self), spec = aScriptSpec, line = aLineNumber,
1331 column = aColumnNumber, messageName = aMessageName,
1332 params = std::move(aParams)] {
1333 if (NS_WARN_IF(!self->mActor)) {
1334 return;
1337 Unused << self->mActor->SendAsyncLog(spec, line, column, messageName,
1338 params);
1341 MOZ_ALWAYS_SUCCEEDS(
1342 RemoteWorkerService::Thread()->Dispatch(r.forget(), NS_DISPATCH_NORMAL));
1345 void FetchEventOp::GetRequestURL(nsAString& aOutRequestURL) {
1346 nsTArray<nsCString>& urls =
1347 mArgs.get_ParentToChildServiceWorkerFetchEventOpArgs()
1348 .common()
1349 .internalRequest()
1350 .urlList();
1351 MOZ_ASSERT(!urls.IsEmpty());
1353 CopyUTF8toUTF16(urls.LastElement(), aOutRequestURL);
1356 void FetchEventOp::ResolvedCallback(JSContext* aCx,
1357 JS::Handle<JS::Value> aValue,
1358 ErrorResult& aRv) {
1359 MOZ_ASSERT(IsCurrentThreadRunningWorker());
1360 MOZ_ASSERT(mRespondWithClosure);
1361 MOZ_ASSERT(!mRespondWithPromiseHolder.IsEmpty());
1362 MOZ_ASSERT(!mPromiseHolder.IsEmpty());
1364 mFetchHandlerFinish = TimeStamp::Now();
1366 nsAutoString requestURL;
1367 GetRequestURL(requestURL);
1369 AutoCancel autoCancel(this);
1371 if (!aValue.isObject()) {
1372 NS_WARNING(
1373 "FetchEvent::RespondWith was passed a promise resolved to a "
1374 "non-Object "
1375 "value");
1377 nsCString sourceSpec;
1378 uint32_t line = 0;
1379 uint32_t column = 0;
1380 nsString valueString;
1381 nsContentUtils::ExtractErrorValues(aCx, aValue, sourceSpec, &line, &column,
1382 valueString);
1384 autoCancel.SetCancelMessageAndLocation(sourceSpec, line, column,
1385 "InterceptedNonResponseWithURL"_ns,
1386 requestURL, valueString);
1387 return;
1390 RefPtr<Response> response;
1391 nsresult rv = UNWRAP_OBJECT(Response, &aValue.toObject(), response);
1392 if (NS_FAILED(rv)) {
1393 nsCString sourceSpec;
1394 uint32_t line = 0;
1395 uint32_t column = 0;
1396 nsString valueString;
1397 nsContentUtils::ExtractErrorValues(aCx, aValue, sourceSpec, &line, &column,
1398 valueString);
1400 autoCancel.SetCancelMessageAndLocation(sourceSpec, line, column,
1401 "InterceptedNonResponseWithURL"_ns,
1402 requestURL, valueString);
1403 return;
1406 WorkerPrivate* worker = GetCurrentThreadWorkerPrivate();
1407 MOZ_ASSERT(worker);
1408 worker->AssertIsOnWorkerThread();
1410 // Section "HTTP Fetch", step 3.3:
1411 // If one of the following conditions is true, return a network error:
1412 // * response's type is "error".
1413 // * request's mode is not "no-cors" and response's type is "opaque".
1414 // * request's redirect mode is not "manual" and response's type is
1415 // "opaqueredirect".
1416 // * request's redirect mode is not "follow" and response's url list
1417 // has more than one item.
1419 if (response->Type() == ResponseType::Error) {
1420 autoCancel.SetCancelMessage("InterceptedErrorResponseWithURL"_ns,
1421 requestURL);
1422 return;
1425 const ParentToChildServiceWorkerFetchEventOpArgs& args =
1426 mArgs.get_ParentToChildServiceWorkerFetchEventOpArgs();
1427 const RequestMode requestMode = args.common().internalRequest().requestMode();
1429 if (response->Type() == ResponseType::Opaque &&
1430 requestMode != RequestMode::No_cors) {
1431 NS_ConvertASCIItoUTF16 modeString(
1432 RequestModeValues::GetString(requestMode));
1434 nsAutoString requestURL;
1435 GetRequestURL(requestURL);
1437 autoCancel.SetCancelMessage("BadOpaqueInterceptionRequestModeWithURL"_ns,
1438 requestURL, modeString);
1439 return;
1442 const RequestRedirect requestRedirectMode =
1443 args.common().internalRequest().requestRedirect();
1445 if (requestRedirectMode != RequestRedirect::Manual &&
1446 response->Type() == ResponseType::Opaqueredirect) {
1447 autoCancel.SetCancelMessage("BadOpaqueRedirectInterceptionWithURL"_ns,
1448 requestURL);
1449 return;
1452 if (requestRedirectMode != RequestRedirect::Follow &&
1453 response->Redirected()) {
1454 autoCancel.SetCancelMessage("BadRedirectModeInterceptionWithURL"_ns,
1455 requestURL);
1456 return;
1459 if (NS_WARN_IF(response->BodyUsed())) {
1460 autoCancel.SetCancelMessage("InterceptedUsedResponseWithURL"_ns,
1461 requestURL);
1462 return;
1465 SafeRefPtr<InternalResponse> ir = response->GetInternalResponse();
1466 if (NS_WARN_IF(!ir)) {
1467 return;
1470 // An extra safety check to make sure our invariant that opaque and cors
1471 // responses always have a URL does not break.
1472 if (NS_WARN_IF((response->Type() == ResponseType::Opaque ||
1473 response->Type() == ResponseType::Cors) &&
1474 ir->GetUnfilteredURL().IsEmpty())) {
1475 MOZ_DIAGNOSTIC_ASSERT(false, "Cors or opaque Response without a URL");
1476 return;
1479 if (requestMode == RequestMode::Same_origin &&
1480 response->Type() == ResponseType::Cors) {
1481 Telemetry::ScalarAdd(Telemetry::ScalarID::SW_CORS_RES_FOR_SO_REQ_COUNT, 1);
1483 // XXXtt: Will have a pref to enable the quirk response in bug 1419684.
1484 // The variadic template provided by StringArrayAppender requires exactly
1485 // an nsString.
1486 NS_ConvertUTF8toUTF16 responseURL(ir->GetUnfilteredURL());
1487 autoCancel.SetCancelMessage("CorsResponseForSameOriginRequest"_ns,
1488 requestURL, responseURL);
1489 return;
1492 nsCOMPtr<nsIInputStream> body;
1493 ir->GetUnfilteredBody(getter_AddRefs(body));
1494 // Errors and redirects may not have a body.
1495 if (body) {
1496 ErrorResult error;
1497 response->SetBodyUsed(aCx, error);
1498 error.WouldReportJSException();
1499 if (NS_WARN_IF(error.Failed())) {
1500 autoCancel.SetCancelErrorResult(aCx, error);
1501 return;
1505 if (!ir->GetChannelInfo().IsInitialized()) {
1506 // This is a synthetic response (I think and hope so).
1507 ir->InitChannelInfo(worker->GetChannelInfo());
1510 autoCancel.Reset();
1512 // https://w3c.github.io/ServiceWorker/#on-fetch-request-algorithm Step 26: If
1513 // eventHandled is not null, then resolve eventHandled.
1515 // mRespondWithPromiseHolder will resolve a MozPromise that will resolve on
1516 // the worker owner's thread, so it's fine to resolve the mHandled promise now
1517 // because content will not interfere with respondWith getting the Response to
1518 // where it's going.
1519 mHandled->MaybeResolveWithUndefined();
1520 mRespondWithPromiseHolder.Resolve(
1521 FetchEventRespondWithResult(std::make_tuple(
1522 std::move(ir), mRespondWithClosure.ref(),
1523 FetchEventTimeStamps(mFetchHandlerStart, mFetchHandlerFinish))),
1524 __func__);
1527 void FetchEventOp::RejectedCallback(JSContext* aCx,
1528 JS::Handle<JS::Value> aValue,
1529 ErrorResult& aRv) {
1530 MOZ_ASSERT(IsCurrentThreadRunningWorker());
1531 MOZ_ASSERT(mRespondWithClosure);
1532 MOZ_ASSERT(!mRespondWithPromiseHolder.IsEmpty());
1533 MOZ_ASSERT(!mPromiseHolder.IsEmpty());
1535 mFetchHandlerFinish = TimeStamp::Now();
1537 FetchEventRespondWithClosure& closure = mRespondWithClosure.ref();
1539 nsCString sourceSpec = closure.respondWithScriptSpec();
1540 uint32_t line = closure.respondWithLineNumber();
1541 uint32_t column = closure.respondWithColumnNumber();
1542 nsString valueString;
1544 nsContentUtils::ExtractErrorValues(aCx, aValue, sourceSpec, &line, &column,
1545 valueString);
1547 nsString requestURL;
1548 GetRequestURL(requestURL);
1550 AsyncLog(sourceSpec, line, column, "InterceptionRejectedResponseWithURL"_ns,
1551 {std::move(requestURL), valueString});
1553 // https://w3c.github.io/ServiceWorker/#on-fetch-request-algorithm Step 25.1:
1554 // If eventHandled is not null, then reject eventHandled with a "NetworkError"
1555 // DOMException in workerRealm.
1556 mHandled->MaybeRejectWithNetworkError(
1557 "FetchEvent.respondWith() Promise rejected"_ns);
1558 mRespondWithPromiseHolder.Resolve(
1559 FetchEventRespondWithResult(CancelInterceptionArgs(
1560 NS_ERROR_INTERCEPTION_FAILED,
1561 FetchEventTimeStamps(mFetchHandlerStart, mFetchHandlerFinish))),
1562 __func__);
1565 nsresult FetchEventOp::DispatchFetchEvent(JSContext* aCx,
1566 WorkerPrivate* aWorkerPrivate) {
1567 MOZ_ASSERT(aCx);
1568 MOZ_ASSERT(aWorkerPrivate);
1569 aWorkerPrivate->AssertIsOnWorkerThread();
1570 MOZ_ASSERT(aWorkerPrivate->IsServiceWorker());
1572 ParentToChildServiceWorkerFetchEventOpArgs& args =
1573 mArgs.get_ParentToChildServiceWorkerFetchEventOpArgs();
1576 * Testing: Failure injection.
1578 * There are a number of different ways that this fetch event could have
1579 * failed that would result in cancellation. This injection point helps
1580 * simulate them without worrying about shifting implementation details with
1581 * full fidelity reproductions of current scenarios.
1583 * Broadly speaking, we expect fetch event scenarios to fail because of:
1584 * - Script load failure, which results in the CompileScriptRunnable closing
1585 * the worker and thereby cancelling all pending operations, including this
1586 * fetch. The `ServiceWorkerOp::Cancel` impl just calls
1587 * RejectAll(NS_ERROR_DOM_ABORT_ERR) which we are able to approximate by
1588 * returning the same nsresult here, as our caller also calls RejectAll.
1589 * (And timing-wise, this rejection will happen in the correct sequence.)
1590 * - An exception gets thrown in the processing of the promise that was passed
1591 * to respondWith and it ends up rejecting. The rejection will be converted
1592 * by `FetchEventOp::RejectedCallback` into a cancellation with
1593 * NS_ERROR_INTERCEPTION_FAILED, and by returning that here we approximate
1594 * that failure mode.
1596 if (NS_FAILED(args.common().testingInjectCancellation())) {
1597 return args.common().testingInjectCancellation();
1601 * Step 1: get the InternalRequest. The InternalRequest can't be constructed
1602 * here from mArgs because the IPCStream has to be deserialized on the
1603 * thread receiving the ServiceWorkerFetchEventOpArgs.
1604 * FetchEventOpProxyChild will have already deserialized the stream on the
1605 * correct thread before creating this op, so we can take its saved
1606 * InternalRequest.
1608 SafeRefPtr<InternalRequest> internalRequest =
1609 mActor->ExtractInternalRequest();
1612 * Step 2: get the worker's global object
1614 GlobalObject globalObject(aCx, aWorkerPrivate->GlobalScope()->GetWrapper());
1615 nsCOMPtr<nsIGlobalObject> globalObjectAsSupports =
1616 do_QueryInterface(globalObject.GetAsSupports());
1617 if (NS_WARN_IF(!globalObjectAsSupports)) {
1618 return NS_ERROR_DOM_INVALID_STATE_ERR;
1622 * Step 3: create the public DOM Request object
1623 * TODO: this Request object should be created with an AbortSignal object
1624 * which should be aborted if the loading is aborted. See but 1394102.
1626 RefPtr<Request> request =
1627 new Request(globalObjectAsSupports, internalRequest.clonePtr(), nullptr);
1628 MOZ_ASSERT_IF(internalRequest->IsNavigationRequest(),
1629 request->Redirect() == RequestRedirect::Manual);
1632 * Step 4a: create the FetchEventInit
1634 RootedDictionary<FetchEventInit> fetchEventInit(aCx);
1635 fetchEventInit.mRequest = request;
1636 fetchEventInit.mBubbles = false;
1637 fetchEventInit.mCancelable = true;
1640 * TODO: only expose the FetchEvent.clientId on subresource requests for
1641 * now. Once we implement .targetClientId we can then start exposing
1642 * .clientId on non-subresource requests as well. See bug 1487534.
1644 if (!args.common().clientId().IsEmpty() &&
1645 !internalRequest->IsNavigationRequest()) {
1646 fetchEventInit.mClientId = args.common().clientId();
1650 * https://w3c.github.io/ServiceWorker/#on-fetch-request-algorithm
1652 * "If request is a non-subresource request and request’s
1653 * destination is not "report", initialize e’s resultingClientId attribute
1654 * to reservedClient’s [resultingClient's] id, and to the empty string
1655 * otherwise." (Step 18.8)
1657 if (!args.common().resultingClientId().IsEmpty() &&
1658 args.common().isNonSubresourceRequest() &&
1659 internalRequest->Destination() != RequestDestination::Report) {
1660 fetchEventInit.mResultingClientId = args.common().resultingClientId();
1664 * Step 4b: create the FetchEvent
1666 RefPtr<FetchEvent> fetchEvent =
1667 FetchEvent::Constructor(globalObject, u"fetch"_ns, fetchEventInit);
1668 fetchEvent->SetTrusted(true);
1669 fetchEvent->PostInit(args.common().workerScriptSpec(), this);
1670 mHandled = fetchEvent->Handled();
1671 mPreloadResponse = fetchEvent->PreloadResponse();
1673 if (args.common().preloadNavigation()) {
1674 RefPtr<FetchEventPreloadResponseAvailablePromise> preloadResponsePromise =
1675 mActor->GetPreloadResponseAvailablePromise();
1676 MOZ_ASSERT(preloadResponsePromise);
1678 // If preloadResponsePromise has already settled then this callback will get
1679 // run synchronously here.
1680 RefPtr<FetchEventOp> self = this;
1681 preloadResponsePromise
1682 ->Then(
1683 GetCurrentSerialEventTarget(), __func__,
1684 [self, globalObjectAsSupports](
1685 SafeRefPtr<InternalResponse>&& aPreloadResponse) {
1686 self->mPreloadResponse->MaybeResolve(
1687 MakeRefPtr<Response>(globalObjectAsSupports,
1688 std::move(aPreloadResponse), nullptr));
1689 self->mPreloadResponseAvailablePromiseRequestHolder.Complete();
1691 [self](int) {
1692 self->mPreloadResponseAvailablePromiseRequestHolder.Complete();
1694 ->Track(mPreloadResponseAvailablePromiseRequestHolder);
1696 RefPtr<PerformanceStorage> performanceStorage =
1697 aWorkerPrivate->GetPerformanceStorage();
1699 RefPtr<FetchEventPreloadResponseTimingPromise>
1700 preloadResponseTimingPromise =
1701 mActor->GetPreloadResponseTimingPromise();
1702 MOZ_ASSERT(preloadResponseTimingPromise);
1703 preloadResponseTimingPromise
1704 ->Then(
1705 GetCurrentSerialEventTarget(), __func__,
1706 [self, performanceStorage,
1707 globalObjectAsSupports](ResponseTiming&& aTiming) {
1708 if (performanceStorage && !aTiming.entryName().IsEmpty() &&
1709 aTiming.initiatorType().Equals(u"navigation"_ns)) {
1710 performanceStorage->AddEntry(
1711 aTiming.entryName(), aTiming.initiatorType(),
1712 MakeUnique<PerformanceTimingData>(aTiming.timingData()));
1714 self->mPreloadResponseTimingPromiseRequestHolder.Complete();
1716 [self](int) {
1717 self->mPreloadResponseTimingPromiseRequestHolder.Complete();
1719 ->Track(mPreloadResponseTimingPromiseRequestHolder);
1721 RefPtr<FetchEventPreloadResponseEndPromise> preloadResponseEndPromise =
1722 mActor->GetPreloadResponseEndPromise();
1723 MOZ_ASSERT(preloadResponseEndPromise);
1724 preloadResponseEndPromise
1725 ->Then(
1726 GetCurrentSerialEventTarget(), __func__,
1727 [self, globalObjectAsSupports](ResponseEndArgs&& aArgs) {
1728 if (aArgs.endReason() == FetchDriverObserver::eAborted) {
1729 self->mPreloadResponse->MaybeReject(NS_ERROR_DOM_ABORT_ERR);
1731 self->mPreloadResponseEndPromiseRequestHolder.Complete();
1733 [self](int) {
1734 self->mPreloadResponseEndPromiseRequestHolder.Complete();
1736 ->Track(mPreloadResponseEndPromiseRequestHolder);
1737 } else {
1738 // preload navigation is disabled, resolved preload response promise with
1739 // undefined as default behavior.
1740 mPreloadResponse->MaybeResolveWithUndefined();
1743 mFetchHandlerStart = TimeStamp::Now();
1746 * Step 5: Dispatch the FetchEvent to the worker's global object
1748 nsresult rv = DispatchExtendableEventOnWorkerScope(
1749 aCx, aWorkerPrivate->GlobalScope(), fetchEvent, this);
1750 bool dispatchFailed = NS_FAILED(rv) && rv != NS_ERROR_XPC_JS_THREW_EXCEPTION;
1752 if (NS_WARN_IF(dispatchFailed)) {
1753 mHandled = nullptr;
1754 mPreloadResponse = nullptr;
1755 return rv;
1759 * At this point, there are 4 (legal) scenarios:
1761 * 1) If neither waitUntil() nor respondWith() are called,
1762 * DispatchExtendableEventOnWorkerScope() will have already called
1763 * FinishedWithResult(), but this call will have recorded the result
1764 * (mResult) and returned early so that mRespondWithPromiseHolder can be
1765 * settled first. mRespondWithPromiseHolder will be settled below, followed
1766 * by a call to MaybeFinished() which settles mPromiseHolder.
1768 * 2) If waitUntil() is called at least once, and respondWith() is not
1769 * called, DispatchExtendableEventOnWorkerScope() will NOT have called
1770 * FinishedWithResult(). We'll settle mRespondWithPromiseHolder first, and
1771 * at some point in the future when the last waitUntil() promise settles,
1772 * FinishedWithResult() will be called, settling mPromiseHolder.
1774 * 3) If waitUntil() is not called, and respondWith() is called,
1775 * DispatchExtendableEventOnWorkerScope() will NOT have called
1776 * FinishedWithResult(). We can also guarantee that
1777 * mRespondWithPromiseHolder will be settled before mPromiseHolder, due to
1778 * the Promise::AppendNativeHandler() call ordering in
1779 * FetchEvent::RespondWith().
1781 * 4) If waitUntil() is called at least once, and respondWith() is also
1782 * called, the effect is similar to scenario 3), with the most imporant
1783 * property being mRespondWithPromiseHolder settling before mPromiseHolder.
1785 * Note that if mPromiseHolder is settled before mRespondWithPromiseHolder,
1786 * FetchEventOpChild will cancel the interception.
1788 if (!fetchEvent->WaitToRespond()) {
1789 MOZ_ASSERT(!mRespondWithPromiseHolder.IsEmpty());
1790 MOZ_ASSERT(!aWorkerPrivate->UsesSystemPrincipal(),
1791 "We don't support system-principal serviceworkers");
1793 mFetchHandlerFinish = TimeStamp::Now();
1795 if (fetchEvent->DefaultPrevented(CallerType::NonSystem)) {
1796 // https://w3c.github.io/ServiceWorker/#on-fetch-request-algorithm
1797 // Step 24.1.1: If eventHandled is not null, then reject eventHandled with
1798 // a "NetworkError" DOMException in workerRealm.
1799 mHandled->MaybeRejectWithNetworkError(
1800 "FetchEvent.preventDefault() called"_ns);
1801 mRespondWithPromiseHolder.Resolve(
1802 FetchEventRespondWithResult(CancelInterceptionArgs(
1803 NS_ERROR_INTERCEPTION_FAILED,
1804 FetchEventTimeStamps(mFetchHandlerStart, mFetchHandlerFinish))),
1805 __func__);
1806 } else {
1807 // https://w3c.github.io/ServiceWorker/#on-fetch-request-algorithm
1808 // Step 24.2: If eventHandled is not null, then resolve eventHandled.
1809 mHandled->MaybeResolveWithUndefined();
1810 mRespondWithPromiseHolder.Resolve(
1811 FetchEventRespondWithResult(ResetInterceptionArgs(
1812 FetchEventTimeStamps(mFetchHandlerStart, mFetchHandlerFinish))),
1813 __func__);
1815 } else {
1816 MOZ_ASSERT(mRespondWithClosure);
1819 mPostDispatchChecksDone = true;
1820 MaybeFinished();
1822 return NS_OK;
1825 class ExtensionAPIEventOp final : public ServiceWorkerOp {
1826 using ServiceWorkerOp::ServiceWorkerOp;
1828 public:
1829 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(ExtensionAPIEventOp, override)
1831 private:
1832 ~ExtensionAPIEventOp() = default;
1834 bool Exec(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override {
1835 MOZ_ASSERT(aWorkerPrivate);
1836 aWorkerPrivate->AssertIsOnWorkerThread();
1837 MOZ_ASSERT(aWorkerPrivate->IsServiceWorker());
1838 MOZ_ASSERT(aWorkerPrivate->ExtensionAPIAllowed());
1839 MOZ_ASSERT(!mPromiseHolder.IsEmpty());
1841 ServiceWorkerExtensionAPIEventOpArgs& args =
1842 mArgs.get_ServiceWorkerExtensionAPIEventOpArgs();
1844 ServiceWorkerExtensionAPIEventOpResult result;
1845 result.extensionAPIEventListenerWasAdded() = false;
1847 if (aWorkerPrivate->WorkerScriptExecutedSuccessfully()) {
1848 GlobalObject globalObj(aCx, aWorkerPrivate->GlobalScope()->GetWrapper());
1849 RefPtr<ServiceWorkerGlobalScope> scope;
1850 UNWRAP_OBJECT(ServiceWorkerGlobalScope, globalObj.Get(), scope);
1851 SafeRefPtr<extensions::ExtensionBrowser> extensionAPI =
1852 scope->AcquireExtensionBrowser();
1853 if (!extensionAPI) {
1854 // If the worker script did never access the WebExtension APIs
1855 // then we can return earlier, no event listener could have been added.
1856 mPromiseHolder.Resolve(result, __func__);
1857 return true;
1859 // Check if a listener has been subscribed on the expected WebExtensions
1860 // API event.
1861 bool hasWakeupListener = extensionAPI->HasWakeupEventListener(
1862 args.apiNamespace(), args.apiEventName());
1863 result.extensionAPIEventListenerWasAdded() = hasWakeupListener;
1864 mPromiseHolder.Resolve(result, __func__);
1865 } else {
1866 mPromiseHolder.Resolve(result, __func__);
1869 return true;
1873 /* static */ already_AddRefed<ServiceWorkerOp> ServiceWorkerOp::Create(
1874 ServiceWorkerOpArgs&& aArgs,
1875 std::function<void(const ServiceWorkerOpResult&)>&& aCallback) {
1876 MOZ_ASSERT(RemoteWorkerService::Thread()->IsOnCurrentThread());
1878 RefPtr<ServiceWorkerOp> op;
1880 switch (aArgs.type()) {
1881 case ServiceWorkerOpArgs::TServiceWorkerCheckScriptEvaluationOpArgs:
1882 op = MakeRefPtr<CheckScriptEvaluationOp>(std::move(aArgs),
1883 std::move(aCallback));
1884 break;
1885 case ServiceWorkerOpArgs::TServiceWorkerUpdateStateOpArgs:
1886 op = MakeRefPtr<UpdateServiceWorkerStateOp>(std::move(aArgs),
1887 std::move(aCallback));
1888 break;
1889 case ServiceWorkerOpArgs::TServiceWorkerTerminateWorkerOpArgs:
1890 op = MakeRefPtr<TerminateServiceWorkerOp>(std::move(aArgs),
1891 std::move(aCallback));
1892 break;
1893 case ServiceWorkerOpArgs::TServiceWorkerLifeCycleEventOpArgs:
1894 op = MakeRefPtr<LifeCycleEventOp>(std::move(aArgs), std::move(aCallback));
1895 break;
1896 case ServiceWorkerOpArgs::TServiceWorkerPushEventOpArgs:
1897 op = MakeRefPtr<PushEventOp>(std::move(aArgs), std::move(aCallback));
1898 break;
1899 case ServiceWorkerOpArgs::TServiceWorkerPushSubscriptionChangeEventOpArgs:
1900 op = MakeRefPtr<PushSubscriptionChangeEventOp>(std::move(aArgs),
1901 std::move(aCallback));
1902 break;
1903 case ServiceWorkerOpArgs::TServiceWorkerNotificationEventOpArgs:
1904 op = MakeRefPtr<NotificationEventOp>(std::move(aArgs),
1905 std::move(aCallback));
1906 break;
1907 case ServiceWorkerOpArgs::TServiceWorkerMessageEventOpArgs:
1908 op = MakeRefPtr<MessageEventOp>(std::move(aArgs), std::move(aCallback));
1909 break;
1910 case ServiceWorkerOpArgs::TParentToChildServiceWorkerFetchEventOpArgs:
1911 op = MakeRefPtr<FetchEventOp>(std::move(aArgs), std::move(aCallback));
1912 break;
1913 case ServiceWorkerOpArgs::TServiceWorkerExtensionAPIEventOpArgs:
1914 op = MakeRefPtr<ExtensionAPIEventOp>(std::move(aArgs),
1915 std::move(aCallback));
1916 break;
1917 default:
1918 MOZ_CRASH("Unknown Service Worker operation!");
1919 return nullptr;
1922 return op.forget();
1925 } // namespace mozilla::dom