Bug 1945643 - Update to mozilla-nimbus-schemas 2025.1.1 r=chumphreys
[gecko.git] / dom / serviceworkers / ServiceWorkerOp.cpp
blobfc59651d671f2d374071d4bb57c4fb9baf667198
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/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/PerformanceTiming.h"
50 #include "mozilla/dom/PerformanceStorage.h"
51 #include "mozilla/dom/PushEventBinding.h"
52 #include "mozilla/dom/RemoteWorkerChild.h"
53 #include "mozilla/dom/RemoteWorkerNonLifeCycleOpControllerChild.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/ServiceWorker.h"
60 #include "mozilla/dom/ServiceWorkerBinding.h"
61 #include "mozilla/dom/ServiceWorkerGlobalScopeBinding.h"
62 #include "mozilla/dom/WorkerCommon.h"
63 #include "mozilla/dom/WorkerRef.h"
64 #include "mozilla/dom/WorkerScope.h"
65 #include "mozilla/extensions/ExtensionBrowser.h"
66 #include "mozilla/ipc/IPCStreamUtils.h"
68 namespace mozilla::dom {
70 using remoteworker::Canceled;
71 using remoteworker::Killed;
72 using remoteworker::Pending;
73 using remoteworker::Running;
75 namespace {
77 class ExtendableEventKeepAliveHandler final
78 : public ExtendableEvent::ExtensionsHandler,
79 public PromiseNativeHandler {
80 public:
81 NS_DECL_ISUPPORTS
83 static RefPtr<ExtendableEventKeepAliveHandler> Create(
84 RefPtr<ExtendableEventCallback> aCallback) {
85 MOZ_ASSERT(IsCurrentThreadRunningWorker());
87 RefPtr<ExtendableEventKeepAliveHandler> self =
88 new ExtendableEventKeepAliveHandler(std::move(aCallback));
90 self->mWorkerRef = StrongWorkerRef::Create(
91 GetCurrentThreadWorkerPrivate(), "ExtendableEventKeepAliveHandler",
92 [self]() { self->Cleanup(); });
94 if (NS_WARN_IF(!self->mWorkerRef)) {
95 return nullptr;
98 return self;
102 * ExtendableEvent::ExtensionsHandler interface
104 bool WaitOnPromise(Promise& aPromise) override {
105 if (!mAcceptingPromises) {
106 MOZ_ASSERT(!GetDispatchFlag());
107 MOZ_ASSERT(!mSelfRef, "We shouldn't be holding a self reference!");
108 return false;
111 if (!mSelfRef) {
112 MOZ_ASSERT(!mPendingPromisesCount);
113 mSelfRef = this;
116 ++mPendingPromisesCount;
117 aPromise.AppendNativeHandler(this);
119 return true;
123 * PromiseNativeHandler interface
125 void ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue,
126 ErrorResult& aRv) override {
127 RemovePromise(Resolved);
130 void RejectedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue,
131 ErrorResult& aRv) override {
132 RemovePromise(Rejected);
135 void MaybeDone() {
136 MOZ_ASSERT(IsCurrentThreadRunningWorker());
137 MOZ_ASSERT(!GetDispatchFlag());
139 if (mPendingPromisesCount) {
140 return;
143 if (mCallback) {
144 mCallback->FinishedWithResult(mRejected ? Rejected : Resolved);
145 mCallback = nullptr;
148 Cleanup();
151 private:
153 * This class is useful for the case where pending microtasks will continue
154 * extending the event, which means that the event is not "done." For example:
156 * // `e` is an ExtendableEvent, `p` is a Promise
157 * e.waitUntil(p);
158 * p.then(() => e.waitUntil(otherPromise));
160 class MaybeDoneRunner : public MicroTaskRunnable {
161 public:
162 explicit MaybeDoneRunner(RefPtr<ExtendableEventKeepAliveHandler> aHandler)
163 : mHandler(std::move(aHandler)) {}
165 void Run(AutoSlowOperation& /* unused */) override {
166 mHandler->MaybeDone();
169 private:
170 RefPtr<ExtendableEventKeepAliveHandler> mHandler;
173 explicit ExtendableEventKeepAliveHandler(
174 RefPtr<ExtendableEventCallback> aCallback)
175 : mCallback(std::move(aCallback)) {}
177 ~ExtendableEventKeepAliveHandler() { Cleanup(); }
179 void Cleanup() {
180 MOZ_ASSERT(IsCurrentThreadRunningWorker());
182 if (mCallback) {
183 mCallback->FinishedWithResult(Rejected);
186 mSelfRef = nullptr;
187 mWorkerRef = nullptr;
188 mCallback = nullptr;
189 mAcceptingPromises = false;
192 void RemovePromise(ExtendableEventResult aResult) {
193 MOZ_ASSERT(IsCurrentThreadRunningWorker());
194 MOZ_DIAGNOSTIC_ASSERT(mPendingPromisesCount > 0);
196 // NOTE: mSelfRef can be nullptr here if MaybeCleanup() was just called
197 // before a promise settled. This can happen, for example, if the worker
198 // thread is being terminated for running too long, browser shutdown, etc.
200 mRejected |= (aResult == Rejected);
202 --mPendingPromisesCount;
203 if (mPendingPromisesCount || GetDispatchFlag()) {
204 return;
207 CycleCollectedJSContext* cx = CycleCollectedJSContext::Get();
208 MOZ_ASSERT(cx);
210 RefPtr<MaybeDoneRunner> r = new MaybeDoneRunner(this);
211 cx->DispatchToMicroTask(r.forget());
215 * We start holding a self reference when the first extension promise is
216 * added, and this reference is released when the last promise settles or
217 * when the worker is shutting down.
219 * This is needed in the case that we're waiting indefinitely on a to-be-GC'ed
220 * promise that's no longer reachable and will never be settled.
222 RefPtr<ExtendableEventKeepAliveHandler> mSelfRef;
224 RefPtr<StrongWorkerRef> mWorkerRef;
226 RefPtr<ExtendableEventCallback> mCallback;
228 uint32_t mPendingPromisesCount = 0;
230 bool mRejected = false;
231 bool mAcceptingPromises = true;
234 NS_IMPL_ISUPPORTS0(ExtendableEventKeepAliveHandler)
236 nsresult DispatchExtendableEventOnWorkerScope(
237 JSContext* aCx, WorkerGlobalScope* aWorkerScope, ExtendableEvent* aEvent,
238 RefPtr<ExtendableEventCallback> aCallback) {
239 MOZ_ASSERT(aCx);
240 MOZ_ASSERT(aWorkerScope);
241 MOZ_ASSERT(aEvent);
243 nsCOMPtr<nsIGlobalObject> globalObject = aWorkerScope;
244 WidgetEvent* internalEvent = aEvent->WidgetEventPtr();
246 RefPtr<ExtendableEventKeepAliveHandler> keepAliveHandler =
247 ExtendableEventKeepAliveHandler::Create(std::move(aCallback));
248 if (NS_WARN_IF(!keepAliveHandler)) {
249 return NS_ERROR_FAILURE;
252 // This must be always set *before* dispatching the event, otherwise
253 // waitUntil() calls will fail.
254 aEvent->SetKeepAliveHandler(keepAliveHandler);
256 ErrorResult result;
257 aWorkerScope->DispatchEvent(*aEvent, result);
258 if (NS_WARN_IF(result.Failed())) {
259 result.SuppressException();
260 return NS_ERROR_FAILURE;
263 keepAliveHandler->MaybeDone();
265 // We don't block the event when getting an exception but still report the
266 // error message. NOTE: this will not stop the event.
267 if (internalEvent->mFlags.mExceptionWasRaised) {
268 return NS_ERROR_XPC_JS_THREW_EXCEPTION;
271 return NS_OK;
274 bool DispatchFailed(nsresult aStatus) {
275 return NS_FAILED(aStatus) && aStatus != NS_ERROR_XPC_JS_THREW_EXCEPTION;
278 } // anonymous namespace
280 class ServiceWorkerOp::ServiceWorkerOpRunnable final
281 : public WorkerDebuggeeRunnable {
282 public:
283 NS_DECL_ISUPPORTS_INHERITED
285 ServiceWorkerOpRunnable(RefPtr<ServiceWorkerOp> aOwner,
286 WorkerPrivate* aWorkerPrivate)
287 : WorkerDebuggeeRunnable("ServiceWorkerOpRunnable"),
288 mOwner(std::move(aOwner)) {
289 MOZ_ASSERT(mOwner);
290 MOZ_ASSERT(aWorkerPrivate);
293 private:
294 ~ServiceWorkerOpRunnable() = default;
296 bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override {
297 MOZ_ASSERT(aWorkerPrivate);
298 aWorkerPrivate->AssertIsOnWorkerThread();
299 MOZ_ASSERT(aWorkerPrivate->IsServiceWorker());
300 MOZ_ASSERT(mOwner);
302 if (aWorkerPrivate->GlobalScope()->IsDying()) {
303 Unused << Cancel();
304 return true;
307 bool rv = mOwner->Exec(aCx, aWorkerPrivate);
308 Unused << NS_WARN_IF(!rv);
309 mOwner = nullptr;
311 return rv;
314 // Silent PreDispatch and PostDispatch, since ServiceWorkerOpRunnable can be
315 // from the main thread or from the worker thread.
316 bool PreDispatch(WorkerPrivate* WorkerPrivate) override { return true; }
317 void PostDispatch(WorkerPrivate* WorkerPrivate,
318 bool aDispatchResult) override {}
320 nsresult Cancel() override {
321 MOZ_ASSERT(mOwner);
323 mOwner->RejectAll(NS_ERROR_DOM_ABORT_ERR);
324 mOwner = nullptr;
326 return NS_OK;
329 RefPtr<ServiceWorkerOp> mOwner;
332 NS_IMPL_ISUPPORTS_INHERITED0(ServiceWorkerOp::ServiceWorkerOpRunnable,
333 WorkerThreadRunnable)
335 bool ServiceWorkerOp::MaybeStart(RemoteWorkerChild* aOwner,
336 RemoteWorkerState& aState) {
337 MOZ_ASSERT(!mStarted);
338 MOZ_ASSERT(aOwner);
339 MOZ_ASSERT(aOwner->GetActorEventTarget()->IsOnCurrentThread());
341 auto launcherData = aOwner->mLauncherData.Access();
343 if (NS_WARN_IF(!aOwner->CanSend())) {
344 RejectAll(NS_ERROR_DOM_ABORT_ERR);
345 mStarted = true;
346 return true;
349 // Allow termination to happen while the Service Worker is initializing.
350 if (aState.is<Pending>() && !IsTerminationOp()) {
351 return false;
354 if (NS_WARN_IF(aState.is<Canceled>()) || NS_WARN_IF(aState.is<Killed>())) {
355 RejectAll(NS_ERROR_DOM_INVALID_STATE_ERR);
356 mStarted = true;
357 return true;
360 MOZ_ASSERT(aState.is<Running>() || IsTerminationOp());
362 RefPtr<ServiceWorkerOp> self = this;
364 if (IsTerminationOp()) {
365 aOwner->GetTerminationPromise()->Then(
366 GetCurrentSerialEventTarget(), __func__,
367 [self](
368 const GenericNonExclusivePromise::ResolveOrRejectValue& aResult) {
369 MaybeReportServiceWorkerShutdownProgress(self->mArgs, true);
371 MOZ_ASSERT(!self->mPromiseHolder.IsEmpty());
373 if (NS_WARN_IF(aResult.IsReject())) {
374 self->mPromiseHolder.Reject(aResult.RejectValue(), __func__);
375 return;
378 self->mPromiseHolder.Resolve(NS_OK, __func__);
382 // NewRunnableMethod doesn't work here because the template does not appear to
383 // be able to deal with the owner argument having storage as a RefPtr but
384 // with the method taking a RefPtr&.
385 RefPtr<RemoteWorkerChild> owner = aOwner;
386 nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(
387 __func__, [self = std::move(self), owner = std::move(owner)]() mutable {
388 self->StartOnMainThread(owner);
391 mStarted = true;
393 MOZ_ALWAYS_SUCCEEDS(SchedulerGroup::Dispatch(r.forget()));
394 return true;
397 void ServiceWorkerOp::StartOnMainThread(RefPtr<RemoteWorkerChild>& aOwner) {
398 AssertIsOnMainThread();
399 MaybeReportServiceWorkerShutdownProgress(mArgs);
402 auto lock = aOwner->mState.Lock();
404 if (NS_WARN_IF(!lock->is<Running>() && !IsTerminationOp())) {
405 RejectAll(NS_ERROR_DOM_INVALID_STATE_ERR);
406 return;
410 if (IsTerminationOp()) {
411 aOwner->CloseWorkerOnMainThread();
412 } else {
413 auto lock = aOwner->mState.Lock();
414 MOZ_ASSERT(lock->is<Running>());
416 RefPtr<WorkerThreadRunnable> workerRunnable =
417 GetRunnable(lock->as<Running>().mWorkerPrivate);
419 if (NS_WARN_IF(
420 !workerRunnable->Dispatch(lock->as<Running>().mWorkerPrivate))) {
421 RejectAll(NS_ERROR_FAILURE);
426 void ServiceWorkerOp::Start(RemoteWorkerNonLifeCycleOpControllerChild* aOwner,
427 RemoteWorkerState& aState) {
428 MOZ_ASSERT(!mStarted);
429 MOZ_ASSERT(aOwner);
431 if (NS_WARN_IF(!aOwner->CanSend())) {
432 RejectAll(NS_ERROR_DOM_ABORT_ERR);
433 mStarted = true;
434 return;
437 // NonLifeCycle related operations would never start at Pending state.
438 MOZ_ASSERT(!aState.is<Pending>());
440 if (NS_WARN_IF(aState.is<Canceled>()) || NS_WARN_IF(aState.is<Killed>())) {
441 RejectAll(NS_ERROR_DOM_INVALID_STATE_ERR);
442 mStarted = true;
443 return;
446 MOZ_ASSERT(aState.is<Running>());
448 WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
450 MOZ_ASSERT_DEBUG_OR_FUZZING(workerPrivate);
452 RefPtr<WorkerThreadRunnable> workerRunnable = GetRunnable(workerPrivate);
454 if (NS_WARN_IF(!workerRunnable->Dispatch(workerPrivate))) {
455 RejectAll(NS_ERROR_FAILURE);
458 mStarted = true;
461 void ServiceWorkerOp::Cancel() { RejectAll(NS_ERROR_DOM_ABORT_ERR); }
463 ServiceWorkerOp::ServiceWorkerOp(
464 ServiceWorkerOpArgs&& aArgs,
465 std::function<void(const ServiceWorkerOpResult&)>&& aCallback)
466 : mArgs(std::move(aArgs)) {
467 // Can be on the Worker Launcher thread for Terminate operations or on the
468 // Worker thread for other operations
469 RefPtr<ServiceWorkerOpPromise> promise = mPromiseHolder.Ensure(__func__);
471 promise->Then(
472 GetCurrentSerialEventTarget(), __func__,
473 [callback = std::move(aCallback)](
474 ServiceWorkerOpPromise::ResolveOrRejectValue&& aResult) mutable {
475 if (NS_WARN_IF(aResult.IsReject())) {
476 MOZ_ASSERT(NS_FAILED(aResult.RejectValue()));
477 callback(aResult.RejectValue());
478 return;
481 callback(aResult.ResolveValue());
485 ServiceWorkerOp::~ServiceWorkerOp() {
486 Unused << NS_WARN_IF(!mPromiseHolder.IsEmpty());
487 mPromiseHolder.RejectIfExists(NS_ERROR_DOM_ABORT_ERR, __func__);
490 bool ServiceWorkerOp::Started() const {
491 MOZ_ASSERT(RemoteWorkerService::Thread()->IsOnCurrentThread());
492 return mStarted;
495 bool ServiceWorkerOp::IsTerminationOp() const {
496 return mArgs.type() ==
497 ServiceWorkerOpArgs::TServiceWorkerTerminateWorkerOpArgs;
500 RefPtr<WorkerThreadRunnable> ServiceWorkerOp::GetRunnable(
501 WorkerPrivate* aWorkerPrivate) {
502 MOZ_ASSERT(aWorkerPrivate);
504 return new ServiceWorkerOpRunnable(this, aWorkerPrivate);
507 void ServiceWorkerOp::RejectAll(nsresult aStatus) {
508 MOZ_ASSERT(!mPromiseHolder.IsEmpty());
509 mPromiseHolder.Reject(aStatus, __func__);
512 class CheckScriptEvaluationOp final : public ServiceWorkerOp {
513 using ServiceWorkerOp::ServiceWorkerOp;
515 public:
516 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(CheckScriptEvaluationOp, override)
518 private:
519 ~CheckScriptEvaluationOp() = default;
521 bool Exec(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override {
522 MOZ_ASSERT(aWorkerPrivate);
523 aWorkerPrivate->AssertIsOnWorkerThread();
524 MOZ_ASSERT(aWorkerPrivate->IsServiceWorker());
525 MOZ_ASSERT(!mPromiseHolder.IsEmpty());
527 ServiceWorkerCheckScriptEvaluationOpResult result;
528 result.workerScriptExecutedSuccessfully() =
529 aWorkerPrivate->WorkerScriptExecutedSuccessfully();
530 result.fetchHandlerWasAdded() = aWorkerPrivate->FetchHandlerWasAdded();
532 mPromiseHolder.Resolve(result, __func__);
534 return true;
538 class TerminateServiceWorkerOp final : public ServiceWorkerOp {
539 using ServiceWorkerOp::ServiceWorkerOp;
541 public:
542 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(TerminateServiceWorkerOp, override)
544 private:
545 ~TerminateServiceWorkerOp() = default;
547 bool Exec(JSContext*, WorkerPrivate*) override {
548 MOZ_ASSERT_UNREACHABLE(
549 "Worker termination should be handled in "
550 "`ServiceWorkerOp::MaybeStart()`");
552 return false;
556 class UpdateServiceWorkerStateOp final : public ServiceWorkerOp {
557 using ServiceWorkerOp::ServiceWorkerOp;
559 public:
560 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(UpdateServiceWorkerStateOp, override);
562 private:
563 class UpdateStateOpRunnable final : public WorkerControlRunnable {
564 public:
565 NS_DECL_ISUPPORTS_INHERITED
567 UpdateStateOpRunnable(RefPtr<UpdateServiceWorkerStateOp> aOwner,
568 WorkerPrivate* aWorkerPrivate)
569 : WorkerControlRunnable("UpdateStateOpRunnable"),
570 mOwner(std::move(aOwner)) {
571 MOZ_ASSERT(mOwner);
572 MOZ_ASSERT(aWorkerPrivate);
573 aWorkerPrivate->AssertIsOnWorkerThread();
576 virtual bool PreDispatch(WorkerPrivate* aWorkerPrivate) override {
577 aWorkerPrivate->AssertIsOnWorkerThread();
578 return true;
581 virtual void PostDispatch(WorkerPrivate* aWorkerPrivate,
582 bool aDispatchResult) override {
583 aWorkerPrivate->AssertIsOnWorkerThread();
586 private:
587 ~UpdateStateOpRunnable() = default;
589 bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override {
590 MOZ_ASSERT(aWorkerPrivate);
591 aWorkerPrivate->AssertIsOnWorkerThread();
592 MOZ_ASSERT(aWorkerPrivate->IsServiceWorker());
594 if (mOwner) {
595 Unused << mOwner->Exec(aCx, aWorkerPrivate);
596 mOwner = nullptr;
599 return true;
602 nsresult Cancel() override {
603 MOZ_ASSERT(mOwner);
605 mOwner->RejectAll(NS_ERROR_DOM_ABORT_ERR);
606 mOwner = nullptr;
608 return NS_OK;
611 RefPtr<UpdateServiceWorkerStateOp> mOwner;
614 ~UpdateServiceWorkerStateOp() = default;
616 RefPtr<WorkerThreadRunnable> GetRunnable(
617 WorkerPrivate* aWorkerPrivate) override {
618 MOZ_ASSERT(aWorkerPrivate);
619 aWorkerPrivate->IsOnWorkerThread();
620 MOZ_ASSERT(mArgs.type() ==
621 ServiceWorkerOpArgs::TServiceWorkerUpdateStateOpArgs);
623 return new UpdateStateOpRunnable(this, aWorkerPrivate);
626 bool Exec(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override {
627 MOZ_ASSERT(aWorkerPrivate);
628 aWorkerPrivate->AssertIsOnWorkerThread();
629 MOZ_ASSERT(aWorkerPrivate->IsServiceWorker());
630 MOZ_ASSERT(!mPromiseHolder.IsEmpty());
632 ServiceWorkerState state =
633 mArgs.get_ServiceWorkerUpdateStateOpArgs().state();
634 aWorkerPrivate->UpdateServiceWorkerState(state);
636 mPromiseHolder.Resolve(NS_OK, __func__);
638 return true;
642 NS_IMPL_ISUPPORTS_INHERITED0(UpdateServiceWorkerStateOp::UpdateStateOpRunnable,
643 WorkerControlRunnable)
645 void ExtendableEventOp::FinishedWithResult(ExtendableEventResult aResult) {
646 MOZ_ASSERT(IsCurrentThreadRunningWorker());
647 MOZ_ASSERT(!mPromiseHolder.IsEmpty());
649 mPromiseHolder.Resolve(aResult == Resolved ? NS_OK : NS_ERROR_FAILURE,
650 __func__);
653 class LifeCycleEventOp final : public ExtendableEventOp {
654 using ExtendableEventOp::ExtendableEventOp;
656 public:
657 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(LifeCycleEventOp, override)
659 private:
660 ~LifeCycleEventOp() = default;
662 bool Exec(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override {
663 MOZ_ASSERT(aWorkerPrivate);
664 aWorkerPrivate->AssertIsOnWorkerThread();
665 MOZ_ASSERT(aWorkerPrivate->IsServiceWorker());
666 MOZ_ASSERT(!mPromiseHolder.IsEmpty());
668 RefPtr<ExtendableEvent> event;
669 RefPtr<EventTarget> target = aWorkerPrivate->GlobalScope();
671 const nsString& eventName =
672 mArgs.get_ServiceWorkerLifeCycleEventOpArgs().eventName();
674 if (eventName.EqualsASCII("install") || eventName.EqualsASCII("activate")) {
675 ExtendableEventInit init;
676 init.mBubbles = false;
677 init.mCancelable = false;
678 event = ExtendableEvent::Constructor(target, eventName, init);
679 } else {
680 MOZ_CRASH("Unexpected lifecycle event");
683 event->SetTrusted(true);
685 nsresult rv = DispatchExtendableEventOnWorkerScope(
686 aCx, aWorkerPrivate->GlobalScope(), event, this);
688 if (NS_WARN_IF(DispatchFailed(rv))) {
689 RejectAll(rv);
692 return !DispatchFailed(rv);
697 * PushEventOp
699 class PushEventOp final : public ExtendableEventOp {
700 using ExtendableEventOp::ExtendableEventOp;
702 public:
703 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(PushEventOp, override)
705 private:
706 ~PushEventOp() = default;
708 bool Exec(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override {
709 MOZ_ASSERT(aWorkerPrivate);
710 aWorkerPrivate->AssertIsOnWorkerThread();
711 MOZ_ASSERT(aWorkerPrivate->IsServiceWorker());
712 MOZ_ASSERT(!mPromiseHolder.IsEmpty());
714 ErrorResult result;
716 auto scopeExit = MakeScopeExit([&] {
717 MOZ_ASSERT(result.Failed());
719 RejectAll(result.StealNSResult());
720 ReportError(aWorkerPrivate);
723 const ServiceWorkerPushEventOpArgs& args =
724 mArgs.get_ServiceWorkerPushEventOpArgs();
726 RootedDictionary<PushEventInit> pushEventInit(aCx);
728 if (args.data().type() != OptionalPushData::Tvoid_t) {
729 const auto& bytes = args.data().get_ArrayOfuint8_t();
730 JSObject* data = Uint8Array::Create(aCx, bytes, result);
732 // The ScopeExit above will deal with the exceptions (through
733 // StealNSResult).
734 result.WouldReportJSException();
735 if (result.Failed()) {
736 return false;
739 DebugOnly<bool> inited =
740 pushEventInit.mData.Construct().SetAsArrayBufferView().Init(data);
741 MOZ_ASSERT(inited);
744 pushEventInit.mBubbles = false;
745 pushEventInit.mCancelable = false;
747 GlobalObject globalObj(aCx, aWorkerPrivate->GlobalScope()->GetWrapper());
748 RefPtr<PushEvent> pushEvent =
749 PushEvent::Constructor(globalObj, u"push"_ns, pushEventInit, result);
751 if (NS_WARN_IF(result.Failed())) {
752 return false;
755 pushEvent->SetTrusted(true);
757 scopeExit.release();
759 nsresult rv = DispatchExtendableEventOnWorkerScope(
760 aCx, aWorkerPrivate->GlobalScope(), pushEvent, this);
762 if (NS_FAILED(rv)) {
763 if (NS_WARN_IF(DispatchFailed(rv))) {
764 RejectAll(rv);
767 // We don't cancel WorkerPrivate when catching an exception.
768 ReportError(aWorkerPrivate,
769 nsIPushErrorReporter::DELIVERY_UNCAUGHT_EXCEPTION);
772 return !DispatchFailed(rv);
775 void FinishedWithResult(ExtendableEventResult aResult) override {
776 MOZ_ASSERT(IsCurrentThreadRunningWorker());
778 WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
780 if (aResult == Rejected) {
781 ReportError(workerPrivate,
782 nsIPushErrorReporter::DELIVERY_UNHANDLED_REJECTION);
785 ExtendableEventOp::FinishedWithResult(aResult);
788 void ReportError(
789 WorkerPrivate* aWorkerPrivate,
790 uint16_t aError = nsIPushErrorReporter::DELIVERY_INTERNAL_ERROR) {
791 MOZ_ASSERT(aWorkerPrivate);
792 aWorkerPrivate->AssertIsOnWorkerThread();
793 MOZ_ASSERT(aWorkerPrivate->IsServiceWorker());
795 if (NS_WARN_IF(aError > nsIPushErrorReporter::DELIVERY_INTERNAL_ERROR) ||
796 mArgs.get_ServiceWorkerPushEventOpArgs().messageId().IsEmpty()) {
797 return;
800 nsString messageId = mArgs.get_ServiceWorkerPushEventOpArgs().messageId();
801 nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(
802 __func__, [messageId = std::move(messageId), error = aError] {
803 nsCOMPtr<nsIPushErrorReporter> reporter =
804 do_GetService("@mozilla.org/push/Service;1");
806 if (reporter) {
807 nsresult rv = reporter->ReportDeliveryError(messageId, error);
808 Unused << NS_WARN_IF(NS_FAILED(rv));
812 MOZ_ALWAYS_SUCCEEDS(aWorkerPrivate->DispatchToMainThread(r.forget()));
816 class PushSubscriptionChangeEventOp final : public ExtendableEventOp {
817 using ExtendableEventOp::ExtendableEventOp;
819 public:
820 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(PushSubscriptionChangeEventOp, override)
822 private:
823 ~PushSubscriptionChangeEventOp() = default;
825 bool Exec(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override {
826 MOZ_ASSERT(aWorkerPrivate);
827 aWorkerPrivate->AssertIsOnWorkerThread();
828 MOZ_ASSERT(aWorkerPrivate->IsServiceWorker());
829 MOZ_ASSERT(!mPromiseHolder.IsEmpty());
831 RefPtr<EventTarget> target = aWorkerPrivate->GlobalScope();
833 ExtendableEventInit init;
834 init.mBubbles = false;
835 init.mCancelable = false;
837 RefPtr<ExtendableEvent> event = ExtendableEvent::Constructor(
838 target, u"pushsubscriptionchange"_ns, init);
839 event->SetTrusted(true);
841 nsresult rv = DispatchExtendableEventOnWorkerScope(
842 aCx, aWorkerPrivate->GlobalScope(), event, this);
844 if (NS_WARN_IF(DispatchFailed(rv))) {
845 RejectAll(rv);
848 return !DispatchFailed(rv);
852 class NotificationEventOp : public ExtendableEventOp,
853 public nsITimerCallback,
854 public nsINamed {
855 using ExtendableEventOp::ExtendableEventOp;
857 public:
858 NS_DECL_THREADSAFE_ISUPPORTS
860 private:
861 ~NotificationEventOp() {
862 MOZ_DIAGNOSTIC_ASSERT(!mTimer);
863 MOZ_DIAGNOSTIC_ASSERT(!mWorkerRef);
866 void ClearWindowAllowed(WorkerPrivate* aWorkerPrivate) {
867 MOZ_ASSERT(aWorkerPrivate);
868 aWorkerPrivate->AssertIsOnWorkerThread();
869 MOZ_ASSERT(aWorkerPrivate->IsServiceWorker());
871 if (!mTimer) {
872 return;
875 // This might be executed after the global was unrooted, in which case
876 // GlobalScope() will return null. Making the check here just to be safe.
877 WorkerGlobalScope* globalScope = aWorkerPrivate->GlobalScope();
878 if (!globalScope) {
879 return;
882 globalScope->ConsumeWindowInteraction();
883 mTimer->Cancel();
884 mTimer = nullptr;
886 mWorkerRef = nullptr;
889 void StartClearWindowTimer(WorkerPrivate* aWorkerPrivate) {
890 MOZ_ASSERT(aWorkerPrivate);
891 aWorkerPrivate->AssertIsOnWorkerThread();
892 MOZ_ASSERT(!mTimer);
894 nsresult rv;
895 nsCOMPtr<nsITimer> timer =
896 NS_NewTimer(aWorkerPrivate->ControlEventTarget());
897 if (NS_WARN_IF(!timer)) {
898 return;
901 MOZ_ASSERT(!mWorkerRef);
902 RefPtr<NotificationEventOp> self = this;
903 mWorkerRef = StrongWorkerRef::Create(
904 aWorkerPrivate, "NotificationEventOp", [self = std::move(self)] {
905 // We could try to hold the worker alive until the timer fires, but
906 // other APIs are not likely to work in this partially shutdown state.
907 // We might as well let the worker thread exit.
908 self->ClearWindowAllowed(self->mWorkerRef->Private());
911 if (!mWorkerRef) {
912 return;
915 aWorkerPrivate->GlobalScope()->AllowWindowInteraction();
916 timer.swap(mTimer);
918 // We swap first and then initialize the timer so that even if initializing
919 // fails, we still clean the interaction count correctly.
920 uint32_t delay = mArgs.get_ServiceWorkerNotificationEventOpArgs()
921 .disableOpenClickDelay();
922 rv = mTimer->InitWithCallback(this, delay, nsITimer::TYPE_ONE_SHOT);
924 if (NS_WARN_IF(NS_FAILED(rv))) {
925 ClearWindowAllowed(aWorkerPrivate);
926 return;
930 // ExtendableEventOp interface
931 bool Exec(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override {
932 MOZ_ASSERT(aWorkerPrivate);
933 aWorkerPrivate->AssertIsOnWorkerThread();
934 MOZ_ASSERT(aWorkerPrivate->IsServiceWorker());
935 MOZ_ASSERT(!mPromiseHolder.IsEmpty());
937 RefPtr<EventTarget> target = aWorkerPrivate->GlobalScope();
939 ServiceWorkerNotificationEventOpArgs& args =
940 mArgs.get_ServiceWorkerNotificationEventOpArgs();
942 auto result = Notification::ConstructFromFields(
943 aWorkerPrivate->GlobalScope(), args.id(), args.title(), args.dir(),
944 args.lang(), args.body(), args.tag(), args.icon(), args.data(),
945 args.scope());
947 if (NS_WARN_IF(result.isErr())) {
948 return false;
951 NotificationEventInit init;
952 init.mNotification = result.unwrap();
953 init.mBubbles = false;
954 init.mCancelable = false;
956 RefPtr<NotificationEvent> notificationEvent =
957 NotificationEvent::Constructor(target, args.eventName(), init);
959 notificationEvent->SetTrusted(true);
961 if (args.eventName().EqualsLiteral("notificationclick")) {
962 StartClearWindowTimer(aWorkerPrivate);
965 nsresult rv = DispatchExtendableEventOnWorkerScope(
966 aCx, aWorkerPrivate->GlobalScope(), notificationEvent, this);
968 if (NS_WARN_IF(DispatchFailed(rv))) {
969 // This will reject mPromiseHolder.
970 FinishedWithResult(Rejected);
973 return !DispatchFailed(rv);
976 void FinishedWithResult(ExtendableEventResult aResult) override {
977 MOZ_ASSERT(IsCurrentThreadRunningWorker());
979 WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
980 MOZ_ASSERT(workerPrivate);
982 ClearWindowAllowed(workerPrivate);
984 ExtendableEventOp::FinishedWithResult(aResult);
987 // nsITimerCallback interface
988 NS_IMETHOD Notify(nsITimer* aTimer) override {
989 MOZ_DIAGNOSTIC_ASSERT(mTimer == aTimer);
990 WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
991 ClearWindowAllowed(workerPrivate);
992 return NS_OK;
995 // nsINamed interface
996 NS_IMETHOD GetName(nsACString& aName) override {
997 aName.AssignLiteral("NotificationEventOp");
998 return NS_OK;
1001 nsCOMPtr<nsITimer> mTimer;
1002 RefPtr<StrongWorkerRef> mWorkerRef;
1005 NS_IMPL_ISUPPORTS(NotificationEventOp, nsITimerCallback, nsINamed)
1007 class MessageEventOp final : public ExtendableEventOp {
1008 using ExtendableEventOp::ExtendableEventOp;
1010 public:
1011 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MessageEventOp, override)
1013 MessageEventOp(ServiceWorkerOpArgs&& aArgs,
1014 std::function<void(const ServiceWorkerOpResult&)>&& aCallback)
1015 : ExtendableEventOp(std::move(aArgs), std::move(aCallback)),
1016 mData(new ServiceWorkerCloneData()) {
1017 mData->CopyFromClonedMessageData(
1018 mArgs.get_ServiceWorkerMessageEventOpArgs().clonedData());
1021 private:
1022 ~MessageEventOp() = default;
1024 bool Exec(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override {
1025 MOZ_ASSERT(aWorkerPrivate);
1026 aWorkerPrivate->AssertIsOnWorkerThread();
1027 MOZ_ASSERT(aWorkerPrivate->IsServiceWorker());
1028 MOZ_ASSERT(!mPromiseHolder.IsEmpty());
1030 JS::Rooted<JS::Value> messageData(aCx);
1031 nsCOMPtr<nsIGlobalObject> sgo = aWorkerPrivate->GlobalScope();
1032 ErrorResult rv;
1033 if (!mData->IsErrorMessageData()) {
1034 mData->Read(aCx, &messageData, rv);
1037 // If mData is an error message data, then it means that it failed to
1038 // serialize on the caller side because it contains a shared memory object.
1039 // If deserialization fails, we will fire a messageerror event.
1040 const bool deserializationFailed =
1041 rv.Failed() || mData->IsErrorMessageData();
1043 Sequence<OwningNonNull<MessagePort>> ports;
1044 if (!mData->TakeTransferredPortsAsSequence(ports)) {
1045 RejectAll(NS_ERROR_FAILURE);
1046 rv.SuppressException();
1047 return false;
1050 RootedDictionary<ExtendableMessageEventInit> init(aCx);
1052 init.mBubbles = false;
1053 init.mCancelable = false;
1055 // On a messageerror event, we disregard ports:
1056 // https://w3c.github.io/ServiceWorker/#service-worker-postmessage
1057 if (!deserializationFailed) {
1058 init.mData = messageData;
1059 init.mPorts = std::move(ports);
1062 PostMessageSource& ipcSource =
1063 mArgs.get_ServiceWorkerMessageEventOpArgs().source();
1064 nsCString originSource;
1065 switch (ipcSource.type()) {
1066 case PostMessageSource::TClientInfoAndState:
1067 originSource = ipcSource.get_ClientInfoAndState().info().url();
1068 init.mSource.SetValue().SetAsClient() =
1069 new Client(sgo, ipcSource.get_ClientInfoAndState());
1070 break;
1071 case PostMessageSource::TIPCServiceWorkerDescriptor:
1072 originSource = ipcSource.get_IPCServiceWorkerDescriptor().scriptURL();
1073 init.mSource.SetValue().SetAsServiceWorker() = ServiceWorker::Create(
1074 sgo, ServiceWorkerDescriptor(
1075 ipcSource.get_IPCServiceWorkerDescriptor()));
1076 break;
1077 default:
1078 MOZ_ASSERT_UNREACHABLE("Unexpected source type");
1079 return false;
1082 nsCOMPtr<nsIURI> url;
1083 nsresult result = NS_NewURI(getter_AddRefs(url), originSource);
1084 if (NS_WARN_IF(NS_FAILED(result))) {
1085 RejectAll(result);
1086 rv.SuppressException();
1087 return false;
1090 OriginAttributes attrs;
1091 nsCOMPtr<nsIPrincipal> principal =
1092 BasePrincipal::CreateContentPrincipal(url, attrs);
1093 if (!principal) {
1094 return false;
1097 nsCString origin;
1098 result = principal->GetOriginNoSuffix(origin);
1099 if (NS_WARN_IF(NS_FAILED(result))) {
1100 RejectAll(result);
1101 rv.SuppressException();
1102 return false;
1105 CopyUTF8toUTF16(origin, init.mOrigin);
1107 rv.SuppressException();
1108 RefPtr<EventTarget> target = aWorkerPrivate->GlobalScope();
1109 RefPtr<ExtendableMessageEvent> extendableEvent =
1110 ExtendableMessageEvent::Constructor(
1111 target, deserializationFailed ? u"messageerror"_ns : u"message"_ns,
1112 init);
1114 extendableEvent->SetTrusted(true);
1116 nsresult rv2 = DispatchExtendableEventOnWorkerScope(
1117 aCx, aWorkerPrivate->GlobalScope(), extendableEvent, this);
1119 if (NS_WARN_IF(DispatchFailed(rv2))) {
1120 RejectAll(rv2);
1123 return !DispatchFailed(rv2);
1126 RefPtr<ServiceWorkerCloneData> mData;
1129 class UpdateIsOnContentBlockingAllowListOp final : public ExtendableEventOp {
1130 using ExtendableEventOp::ExtendableEventOp;
1132 public:
1133 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(UpdateIsOnContentBlockingAllowListOp,
1134 override);
1136 private:
1137 ~UpdateIsOnContentBlockingAllowListOp() = default;
1139 bool Exec(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override {
1140 MOZ_ASSERT(aWorkerPrivate);
1141 aWorkerPrivate->AssertIsOnWorkerThread();
1142 MOZ_ASSERT(aWorkerPrivate->IsServiceWorker());
1143 MOZ_ASSERT(!mPromiseHolder.IsEmpty());
1145 bool onContentBlockingAllowList =
1146 mArgs.get_ServiceWorkerUpdateIsOnContentBlockingAllowListOpArgs()
1147 .onContentBlockingAllowList();
1148 aWorkerPrivate->UpdateIsOnContentBlockingAllowList(
1149 onContentBlockingAllowList);
1151 return true;
1156 * Used for ScopeExit-style network request cancelation in
1157 * `ResolvedCallback()` (e.g. if `FetchEvent::RespondWith()` is resolved with
1158 * a non-JS object).
1160 class MOZ_STACK_CLASS FetchEventOp::AutoCancel {
1161 public:
1162 explicit AutoCancel(FetchEventOp* aOwner)
1163 : mOwner(aOwner),
1164 mLine(0),
1165 mColumn(0),
1166 mMessageName("InterceptionFailedWithURL"_ns) {
1167 MOZ_ASSERT(IsCurrentThreadRunningWorker());
1168 MOZ_ASSERT(mOwner);
1170 nsAutoString requestURL;
1171 mOwner->GetRequestURL(requestURL);
1172 mParams.AppendElement(requestURL);
1175 ~AutoCancel() {
1176 if (mOwner) {
1177 if (mSourceSpec.IsEmpty()) {
1178 mOwner->AsyncLog(mMessageName, std::move(mParams));
1179 } else {
1180 mOwner->AsyncLog(mSourceSpec, mLine, mColumn, mMessageName,
1181 std::move(mParams));
1184 MOZ_ASSERT(!mOwner->mRespondWithPromiseHolder.IsEmpty());
1185 mOwner->mHandled->MaybeRejectWithNetworkError("AutoCancel"_ns);
1186 mOwner->mRespondWithPromiseHolder.Reject(
1187 CancelInterceptionArgs(
1188 NS_ERROR_INTERCEPTION_FAILED,
1189 FetchEventTimeStamps(mOwner->mFetchHandlerStart,
1190 mOwner->mFetchHandlerFinish)),
1191 __func__);
1195 // This function steals the error message from a ErrorResult.
1196 void SetCancelErrorResult(JSContext* aCx, ErrorResult& aRv) {
1197 MOZ_DIAGNOSTIC_ASSERT(aRv.Failed());
1198 MOZ_DIAGNOSTIC_ASSERT(!JS_IsExceptionPending(aCx));
1200 // Storing the error as exception in the JSContext.
1201 if (!aRv.MaybeSetPendingException(aCx)) {
1202 return;
1205 MOZ_ASSERT(!aRv.Failed());
1207 // Let's take the pending exception.
1208 JS::ExceptionStack exnStack(aCx);
1209 if (!JS::StealPendingExceptionStack(aCx, &exnStack)) {
1210 return;
1213 // Converting the exception in a JS::ErrorReportBuilder.
1214 JS::ErrorReportBuilder report(aCx);
1215 if (!report.init(aCx, exnStack, JS::ErrorReportBuilder::WithSideEffects)) {
1216 JS_ClearPendingException(aCx);
1217 return;
1220 MOZ_ASSERT(mOwner);
1221 MOZ_ASSERT(mMessageName.EqualsLiteral("InterceptionFailedWithURL"));
1222 MOZ_ASSERT(mParams.Length() == 1);
1224 // Let's store the error message here.
1225 mMessageName.Assign(report.toStringResult().c_str());
1226 mParams.Clear();
1229 template <typename... Params>
1230 void SetCancelMessage(const nsACString& aMessageName, Params&&... aParams) {
1231 MOZ_ASSERT(mOwner);
1232 MOZ_ASSERT(mMessageName.EqualsLiteral("InterceptionFailedWithURL"));
1233 MOZ_ASSERT(mParams.Length() == 1);
1234 mMessageName = aMessageName;
1235 mParams.Clear();
1236 StringArrayAppender::Append(mParams, sizeof...(Params),
1237 std::forward<Params>(aParams)...);
1240 template <typename... Params>
1241 void SetCancelMessageAndLocation(const nsACString& aSourceSpec,
1242 uint32_t aLine, uint32_t aColumn,
1243 const nsACString& aMessageName,
1244 Params&&... aParams) {
1245 MOZ_ASSERT(mOwner);
1246 MOZ_ASSERT(mMessageName.EqualsLiteral("InterceptionFailedWithURL"));
1247 MOZ_ASSERT(mParams.Length() == 1);
1249 mSourceSpec = aSourceSpec;
1250 mLine = aLine;
1251 mColumn = aColumn;
1253 mMessageName = aMessageName;
1254 mParams.Clear();
1255 StringArrayAppender::Append(mParams, sizeof...(Params),
1256 std::forward<Params>(aParams)...);
1259 void Reset() { mOwner = nullptr; }
1261 private:
1262 FetchEventOp* MOZ_NON_OWNING_REF mOwner;
1263 nsCString mSourceSpec;
1264 uint32_t mLine;
1265 uint32_t mColumn;
1266 nsCString mMessageName;
1267 nsTArray<nsString> mParams;
1270 NS_IMPL_ISUPPORTS0(FetchEventOp)
1272 void FetchEventOp::SetActor(RefPtr<FetchEventOpProxyChild> aActor) {
1273 MOZ_ASSERT(RemoteWorkerService::Thread()->IsOnCurrentThread());
1274 MOZ_ASSERT(!Started());
1275 MOZ_ASSERT(!mActor);
1277 mActor = std::move(aActor);
1280 void FetchEventOp::RevokeActor(FetchEventOpProxyChild* aActor) {
1281 MOZ_ASSERT(aActor);
1282 MOZ_ASSERT_IF(mActor, mActor == aActor);
1284 mActor = nullptr;
1287 RefPtr<FetchEventRespondWithPromise> FetchEventOp::GetRespondWithPromise() {
1288 MOZ_ASSERT(RemoteWorkerService::Thread()->IsOnCurrentThread());
1289 MOZ_ASSERT(!Started());
1290 MOZ_ASSERT(mRespondWithPromiseHolder.IsEmpty());
1292 return mRespondWithPromiseHolder.Ensure(__func__);
1295 void FetchEventOp::RespondWithCalledAt(const nsCString& aRespondWithScriptSpec,
1296 uint32_t aRespondWithLineNumber,
1297 uint32_t aRespondWithColumnNumber) {
1298 MOZ_ASSERT(IsCurrentThreadRunningWorker());
1299 MOZ_ASSERT(!mRespondWithClosure);
1301 mRespondWithClosure.emplace(aRespondWithScriptSpec, aRespondWithLineNumber,
1302 aRespondWithColumnNumber);
1305 void FetchEventOp::ReportCanceled(const nsCString& aPreventDefaultScriptSpec,
1306 uint32_t aPreventDefaultLineNumber,
1307 uint32_t aPreventDefaultColumnNumber) {
1308 MOZ_ASSERT(IsCurrentThreadRunningWorker());
1309 MOZ_ASSERT(mActor);
1310 MOZ_ASSERT(!mPromiseHolder.IsEmpty());
1312 nsString requestURL;
1313 GetRequestURL(requestURL);
1315 AsyncLog(aPreventDefaultScriptSpec, aPreventDefaultLineNumber,
1316 aPreventDefaultColumnNumber, "InterceptionCanceledWithURL"_ns,
1317 {std::move(requestURL)});
1320 FetchEventOp::~FetchEventOp() {
1321 mRespondWithPromiseHolder.RejectIfExists(
1322 CancelInterceptionArgs(
1323 NS_ERROR_DOM_ABORT_ERR,
1324 FetchEventTimeStamps(mFetchHandlerStart, mFetchHandlerFinish)),
1325 __func__);
1328 void FetchEventOp::RejectAll(nsresult aStatus) {
1329 MOZ_ASSERT(!mRespondWithPromiseHolder.IsEmpty());
1330 MOZ_ASSERT(!mPromiseHolder.IsEmpty());
1332 if (mFetchHandlerStart.IsNull()) {
1333 mFetchHandlerStart = TimeStamp::Now();
1335 if (mFetchHandlerFinish.IsNull()) {
1336 mFetchHandlerFinish = TimeStamp::Now();
1339 mRespondWithPromiseHolder.Reject(
1340 CancelInterceptionArgs(
1341 aStatus,
1342 FetchEventTimeStamps(mFetchHandlerStart, mFetchHandlerFinish)),
1343 __func__);
1344 mPromiseHolder.Reject(aStatus, __func__);
1347 void FetchEventOp::FinishedWithResult(ExtendableEventResult aResult) {
1348 MOZ_ASSERT(IsCurrentThreadRunningWorker());
1349 MOZ_ASSERT(!mPromiseHolder.IsEmpty());
1350 MOZ_ASSERT(!mResult);
1352 mResult.emplace(aResult);
1355 * This should only return early if neither waitUntil() nor respondWith()
1356 * are called. The early return is so that mRespondWithPromiseHolder has a
1357 * chance to settle before mPromiseHolder does.
1359 if (!mPostDispatchChecksDone) {
1360 return;
1363 MaybeFinished();
1366 void FetchEventOp::MaybeFinished() {
1367 MOZ_ASSERT(IsCurrentThreadRunningWorker());
1368 MOZ_ASSERT(!mPromiseHolder.IsEmpty());
1370 if (mResult) {
1371 // It's possible that mRespondWithPromiseHolder wasn't settled. That happens
1372 // if the worker was terminated before the respondWith promise settled.
1374 mHandled = nullptr;
1375 mPreloadResponse = nullptr;
1376 mPreloadResponseAvailablePromiseRequestHolder.DisconnectIfExists();
1377 mPreloadResponseTimingPromiseRequestHolder.DisconnectIfExists();
1378 mPreloadResponseEndPromiseRequestHolder.DisconnectIfExists();
1380 ServiceWorkerFetchEventOpResult result(
1381 mResult.value() == Resolved ? NS_OK : NS_ERROR_FAILURE);
1383 mPromiseHolder.Resolve(result, __func__);
1387 bool FetchEventOp::Exec(JSContext* aCx, WorkerPrivate* aWorkerPrivate) {
1388 aWorkerPrivate->AssertIsOnWorkerThread();
1389 MOZ_ASSERT(aWorkerPrivate->IsServiceWorker());
1390 MOZ_ASSERT(!mRespondWithPromiseHolder.IsEmpty());
1391 MOZ_ASSERT(!mPromiseHolder.IsEmpty());
1393 nsresult rv = DispatchFetchEvent(aCx, aWorkerPrivate);
1395 if (NS_WARN_IF(NS_FAILED(rv))) {
1396 RejectAll(rv);
1399 return NS_SUCCEEDED(rv);
1402 void FetchEventOp::AsyncLog(const nsCString& aMessageName,
1403 nsTArray<nsString> aParams) {
1404 MOZ_ASSERT(mActor);
1405 MOZ_ASSERT(!mPromiseHolder.IsEmpty());
1406 MOZ_ASSERT(mRespondWithClosure);
1408 const FetchEventRespondWithClosure& closure = mRespondWithClosure.ref();
1410 AsyncLog(closure.respondWithScriptSpec(), closure.respondWithLineNumber(),
1411 closure.respondWithColumnNumber(), aMessageName, std::move(aParams));
1414 void FetchEventOp::AsyncLog(const nsCString& aScriptSpec, uint32_t aLineNumber,
1415 uint32_t aColumnNumber,
1416 const nsCString& aMessageName,
1417 nsTArray<nsString> aParams) {
1418 MOZ_ASSERT(mActor);
1419 MOZ_ASSERT(!mPromiseHolder.IsEmpty());
1421 // Capture `this` because FetchEventOpProxyChild (mActor) is not thread
1422 // safe, so an AddRef from RefPtr<FetchEventOpProxyChild>'s constructor will
1423 // assert.
1424 RefPtr<FetchEventOp> self = this;
1426 nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(
1427 __func__, [self = std::move(self), spec = aScriptSpec, line = aLineNumber,
1428 column = aColumnNumber, messageName = aMessageName,
1429 params = std::move(aParams)] {
1430 if (NS_WARN_IF(!self->mActor)) {
1431 return;
1434 Unused << self->mActor->SendAsyncLog(spec, line, column, messageName,
1435 params);
1438 MOZ_ALWAYS_SUCCEEDS(
1439 RemoteWorkerService::Thread()->Dispatch(r.forget(), NS_DISPATCH_NORMAL));
1442 void FetchEventOp::GetRequestURL(nsAString& aOutRequestURL) {
1443 nsTArray<nsCString>& urls =
1444 mArgs.get_ParentToChildServiceWorkerFetchEventOpArgs()
1445 .common()
1446 .internalRequest()
1447 .urlList();
1448 MOZ_ASSERT(!urls.IsEmpty());
1450 CopyUTF8toUTF16(urls.LastElement(), aOutRequestURL);
1453 void FetchEventOp::ResolvedCallback(JSContext* aCx,
1454 JS::Handle<JS::Value> aValue,
1455 ErrorResult& aRv) {
1456 MOZ_ASSERT(IsCurrentThreadRunningWorker());
1457 MOZ_ASSERT(mRespondWithClosure);
1458 MOZ_ASSERT(!mRespondWithPromiseHolder.IsEmpty());
1459 MOZ_ASSERT(!mPromiseHolder.IsEmpty());
1461 mFetchHandlerFinish = TimeStamp::Now();
1463 nsAutoString requestURL;
1464 GetRequestURL(requestURL);
1466 AutoCancel autoCancel(this);
1468 if (!aValue.isObject()) {
1469 NS_WARNING(
1470 "FetchEvent::RespondWith was passed a promise resolved to a "
1471 "non-Object "
1472 "value");
1474 nsCString sourceSpec;
1475 uint32_t line = 0;
1476 uint32_t column = 0;
1477 nsString valueString;
1478 nsContentUtils::ExtractErrorValues(aCx, aValue, sourceSpec, &line, &column,
1479 valueString);
1481 autoCancel.SetCancelMessageAndLocation(sourceSpec, line, column,
1482 "InterceptedNonResponseWithURL"_ns,
1483 requestURL, valueString);
1484 return;
1487 RefPtr<Response> response;
1488 nsresult rv = UNWRAP_OBJECT(Response, &aValue.toObject(), response);
1489 if (NS_FAILED(rv)) {
1490 nsCString sourceSpec;
1491 uint32_t line = 0;
1492 uint32_t column = 0;
1493 nsString valueString;
1494 nsContentUtils::ExtractErrorValues(aCx, aValue, sourceSpec, &line, &column,
1495 valueString);
1497 autoCancel.SetCancelMessageAndLocation(sourceSpec, line, column,
1498 "InterceptedNonResponseWithURL"_ns,
1499 requestURL, valueString);
1500 return;
1503 WorkerPrivate* worker = GetCurrentThreadWorkerPrivate();
1504 MOZ_ASSERT(worker);
1505 worker->AssertIsOnWorkerThread();
1507 // Section "HTTP Fetch", step 3.3:
1508 // If one of the following conditions is true, return a network error:
1509 // * response's type is "error".
1510 // * request's mode is not "no-cors" and response's type is "opaque".
1511 // * request's redirect mode is not "manual" and response's type is
1512 // "opaqueredirect".
1513 // * request's redirect mode is not "follow" and response's url list
1514 // has more than one item.
1516 if (response->Type() == ResponseType::Error) {
1517 autoCancel.SetCancelMessage("InterceptedErrorResponseWithURL"_ns,
1518 requestURL);
1519 return;
1522 const ParentToChildServiceWorkerFetchEventOpArgs& args =
1523 mArgs.get_ParentToChildServiceWorkerFetchEventOpArgs();
1524 const RequestMode requestMode = args.common().internalRequest().requestMode();
1526 if (response->Type() == ResponseType::Opaque &&
1527 requestMode != RequestMode::No_cors) {
1528 NS_ConvertASCIItoUTF16 modeString(GetEnumString(requestMode));
1530 nsAutoString requestURL;
1531 GetRequestURL(requestURL);
1533 autoCancel.SetCancelMessage("BadOpaqueInterceptionRequestModeWithURL"_ns,
1534 requestURL, modeString);
1535 return;
1538 const RequestRedirect requestRedirectMode =
1539 args.common().internalRequest().requestRedirect();
1541 if (requestRedirectMode != RequestRedirect::Manual &&
1542 response->Type() == ResponseType::Opaqueredirect) {
1543 autoCancel.SetCancelMessage("BadOpaqueRedirectInterceptionWithURL"_ns,
1544 requestURL);
1545 return;
1548 if (requestRedirectMode != RequestRedirect::Follow &&
1549 response->Redirected()) {
1550 autoCancel.SetCancelMessage("BadRedirectModeInterceptionWithURL"_ns,
1551 requestURL);
1552 return;
1555 if (NS_WARN_IF(response->BodyUsed())) {
1556 autoCancel.SetCancelMessage("InterceptedUsedResponseWithURL"_ns,
1557 requestURL);
1558 return;
1561 SafeRefPtr<InternalResponse> ir = response->GetInternalResponse();
1562 if (NS_WARN_IF(!ir)) {
1563 return;
1566 // An extra safety check to make sure our invariant that opaque and cors
1567 // responses always have a URL does not break.
1568 if (NS_WARN_IF((response->Type() == ResponseType::Opaque ||
1569 response->Type() == ResponseType::Cors) &&
1570 ir->GetUnfilteredURL().IsEmpty())) {
1571 MOZ_DIAGNOSTIC_CRASH("Cors or opaque Response without a URL");
1572 return;
1575 if (requestMode == RequestMode::Same_origin &&
1576 response->Type() == ResponseType::Cors) {
1577 // XXXtt: Will have a pref to enable the quirk response in bug 1419684.
1578 // The variadic template provided by StringArrayAppender requires exactly
1579 // an nsString.
1580 NS_ConvertUTF8toUTF16 responseURL(ir->GetUnfilteredURL());
1581 autoCancel.SetCancelMessage("CorsResponseForSameOriginRequest"_ns,
1582 requestURL, responseURL);
1583 return;
1586 nsCOMPtr<nsIInputStream> body;
1587 ir->GetUnfilteredBody(getter_AddRefs(body));
1588 // Errors and redirects may not have a body.
1589 if (body) {
1590 ErrorResult error;
1591 response->SetBodyUsed(aCx, error);
1592 error.WouldReportJSException();
1593 if (NS_WARN_IF(error.Failed())) {
1594 autoCancel.SetCancelErrorResult(aCx, error);
1595 return;
1599 if (!ir->GetChannelInfo().IsInitialized()) {
1600 // This is a synthetic response (I think and hope so).
1601 ir->InitChannelInfo(worker->GetChannelInfo());
1604 autoCancel.Reset();
1606 // https://w3c.github.io/ServiceWorker/#on-fetch-request-algorithm Step 26: If
1607 // eventHandled is not null, then resolve eventHandled.
1609 // mRespondWithPromiseHolder will resolve a MozPromise that will resolve on
1610 // the worker owner's thread, so it's fine to resolve the mHandled promise now
1611 // because content will not interfere with respondWith getting the Response to
1612 // where it's going.
1613 mHandled->MaybeResolveWithUndefined();
1614 mRespondWithPromiseHolder.Resolve(
1615 FetchEventRespondWithResult(std::make_tuple(
1616 std::move(ir), mRespondWithClosure.ref(),
1617 FetchEventTimeStamps(mFetchHandlerStart, mFetchHandlerFinish))),
1618 __func__);
1621 void FetchEventOp::RejectedCallback(JSContext* aCx,
1622 JS::Handle<JS::Value> aValue,
1623 ErrorResult& aRv) {
1624 MOZ_ASSERT(IsCurrentThreadRunningWorker());
1625 MOZ_ASSERT(mRespondWithClosure);
1626 MOZ_ASSERT(!mRespondWithPromiseHolder.IsEmpty());
1627 MOZ_ASSERT(!mPromiseHolder.IsEmpty());
1629 mFetchHandlerFinish = TimeStamp::Now();
1631 FetchEventRespondWithClosure& closure = mRespondWithClosure.ref();
1633 nsCString sourceSpec = closure.respondWithScriptSpec();
1634 uint32_t line = closure.respondWithLineNumber();
1635 uint32_t column = closure.respondWithColumnNumber();
1636 nsString valueString;
1638 nsContentUtils::ExtractErrorValues(aCx, aValue, sourceSpec, &line, &column,
1639 valueString);
1641 nsString requestURL;
1642 GetRequestURL(requestURL);
1644 AsyncLog(sourceSpec, line, column, "InterceptionRejectedResponseWithURL"_ns,
1645 {std::move(requestURL), valueString});
1647 // https://w3c.github.io/ServiceWorker/#on-fetch-request-algorithm Step 25.1:
1648 // If eventHandled is not null, then reject eventHandled with a "NetworkError"
1649 // DOMException in workerRealm.
1650 mHandled->MaybeRejectWithNetworkError(
1651 "FetchEvent.respondWith() Promise rejected"_ns);
1652 mRespondWithPromiseHolder.Resolve(
1653 FetchEventRespondWithResult(CancelInterceptionArgs(
1654 NS_ERROR_INTERCEPTION_FAILED,
1655 FetchEventTimeStamps(mFetchHandlerStart, mFetchHandlerFinish))),
1656 __func__);
1659 nsresult FetchEventOp::DispatchFetchEvent(JSContext* aCx,
1660 WorkerPrivate* aWorkerPrivate) {
1661 MOZ_ASSERT(aCx);
1662 MOZ_ASSERT(aWorkerPrivate);
1663 aWorkerPrivate->AssertIsOnWorkerThread();
1664 MOZ_ASSERT(aWorkerPrivate->IsServiceWorker());
1666 ParentToChildServiceWorkerFetchEventOpArgs& args =
1667 mArgs.get_ParentToChildServiceWorkerFetchEventOpArgs();
1670 * Testing: Failure injection.
1672 * There are a number of different ways that this fetch event could have
1673 * failed that would result in cancellation. This injection point helps
1674 * simulate them without worrying about shifting implementation details with
1675 * full fidelity reproductions of current scenarios.
1677 * Broadly speaking, we expect fetch event scenarios to fail because of:
1678 * - Script load failure, which results in the CompileScriptRunnable closing
1679 * the worker and thereby cancelling all pending operations, including this
1680 * fetch. The `ServiceWorkerOp::Cancel` impl just calls
1681 * RejectAll(NS_ERROR_DOM_ABORT_ERR) which we are able to approximate by
1682 * returning the same nsresult here, as our caller also calls RejectAll.
1683 * (And timing-wise, this rejection will happen in the correct sequence.)
1684 * - An exception gets thrown in the processing of the promise that was passed
1685 * to respondWith and it ends up rejecting. The rejection will be converted
1686 * by `FetchEventOp::RejectedCallback` into a cancellation with
1687 * NS_ERROR_INTERCEPTION_FAILED, and by returning that here we approximate
1688 * that failure mode.
1690 if (NS_FAILED(args.common().testingInjectCancellation())) {
1691 return args.common().testingInjectCancellation();
1695 * Step 1: get the InternalRequest. The InternalRequest can't be constructed
1696 * here from mArgs because the IPCStream has to be deserialized on the
1697 * thread receiving the ServiceWorkerFetchEventOpArgs.
1698 * FetchEventOpProxyChild will have already deserialized the stream on the
1699 * correct thread before creating this op, so we can take its saved
1700 * InternalRequest.
1702 SafeRefPtr<InternalRequest> internalRequest =
1703 mActor->ExtractInternalRequest();
1706 * Step 2: get the worker's global object
1708 GlobalObject globalObject(aCx, aWorkerPrivate->GlobalScope()->GetWrapper());
1709 nsCOMPtr<nsIGlobalObject> globalObjectAsSupports =
1710 do_QueryInterface(globalObject.GetAsSupports());
1711 if (NS_WARN_IF(!globalObjectAsSupports)) {
1712 return NS_ERROR_DOM_INVALID_STATE_ERR;
1716 * Step 3: create the public DOM Request object
1717 * TODO: this Request object should be created with an AbortSignal object
1718 * which should be aborted if the loading is aborted. See but 1394102.
1720 RefPtr<Request> request =
1721 new Request(globalObjectAsSupports, internalRequest.clonePtr(), nullptr);
1722 MOZ_ASSERT_IF(internalRequest->IsNavigationRequest(),
1723 request->Redirect() == RequestRedirect::Manual);
1726 * Step 4a: create the FetchEventInit
1728 RootedDictionary<FetchEventInit> fetchEventInit(aCx);
1729 fetchEventInit.mRequest = request;
1730 fetchEventInit.mBubbles = false;
1731 fetchEventInit.mCancelable = true;
1734 * TODO: only expose the FetchEvent.clientId on subresource requests for
1735 * now. Once we implement .targetClientId we can then start exposing
1736 * .clientId on non-subresource requests as well. See bug 1487534.
1738 if (!args.common().clientId().IsEmpty() &&
1739 !internalRequest->IsNavigationRequest()) {
1740 fetchEventInit.mClientId = args.common().clientId();
1744 * https://w3c.github.io/ServiceWorker/#on-fetch-request-algorithm
1746 * "If request is a non-subresource request and request’s
1747 * destination is not "report", initialize e’s resultingClientId attribute
1748 * to reservedClient’s [resultingClient's] id, and to the empty string
1749 * otherwise." (Step 18.8)
1751 if (!args.common().resultingClientId().IsEmpty() &&
1752 args.common().isNonSubresourceRequest() &&
1753 internalRequest->Destination() != RequestDestination::Report) {
1754 fetchEventInit.mResultingClientId = args.common().resultingClientId();
1758 * Step 4b: create the FetchEvent
1760 RefPtr<FetchEvent> fetchEvent =
1761 FetchEvent::Constructor(globalObject, u"fetch"_ns, fetchEventInit);
1762 fetchEvent->SetTrusted(true);
1763 fetchEvent->PostInit(args.common().workerScriptSpec(), this);
1764 mHandled = fetchEvent->Handled();
1765 mPreloadResponse = fetchEvent->PreloadResponse();
1767 if (args.common().preloadNavigation()) {
1768 RefPtr<FetchEventPreloadResponseAvailablePromise> preloadResponsePromise =
1769 mActor->GetPreloadResponseAvailablePromise();
1770 MOZ_ASSERT(preloadResponsePromise);
1772 // If preloadResponsePromise has already settled then this callback will get
1773 // run synchronously here.
1774 RefPtr<FetchEventOp> self = this;
1775 preloadResponsePromise
1776 ->Then(
1777 GetCurrentSerialEventTarget(), __func__,
1778 [self, globalObjectAsSupports](
1779 SafeRefPtr<InternalResponse>&& aPreloadResponse) {
1780 self->mPreloadResponse->MaybeResolve(
1781 MakeRefPtr<Response>(globalObjectAsSupports,
1782 std::move(aPreloadResponse), nullptr));
1783 self->mPreloadResponseAvailablePromiseRequestHolder.Complete();
1785 [self](int) {
1786 self->mPreloadResponseAvailablePromiseRequestHolder.Complete();
1788 ->Track(mPreloadResponseAvailablePromiseRequestHolder);
1790 RefPtr<PerformanceStorage> performanceStorage =
1791 aWorkerPrivate->GetPerformanceStorage();
1793 RefPtr<FetchEventPreloadResponseTimingPromise>
1794 preloadResponseTimingPromise =
1795 mActor->GetPreloadResponseTimingPromise();
1796 MOZ_ASSERT(preloadResponseTimingPromise);
1797 preloadResponseTimingPromise
1798 ->Then(
1799 GetCurrentSerialEventTarget(), __func__,
1800 [self, performanceStorage,
1801 globalObjectAsSupports](ResponseTiming&& aTiming) {
1802 if (performanceStorage && !aTiming.entryName().IsEmpty() &&
1803 aTiming.initiatorType().Equals(u"navigation"_ns)) {
1804 performanceStorage->AddEntry(
1805 aTiming.entryName(), aTiming.initiatorType(),
1806 MakeUnique<PerformanceTimingData>(aTiming.timingData()));
1808 self->mPreloadResponseTimingPromiseRequestHolder.Complete();
1810 [self](int) {
1811 self->mPreloadResponseTimingPromiseRequestHolder.Complete();
1813 ->Track(mPreloadResponseTimingPromiseRequestHolder);
1815 RefPtr<FetchEventPreloadResponseEndPromise> preloadResponseEndPromise =
1816 mActor->GetPreloadResponseEndPromise();
1817 MOZ_ASSERT(preloadResponseEndPromise);
1818 preloadResponseEndPromise
1819 ->Then(
1820 GetCurrentSerialEventTarget(), __func__,
1821 [self, globalObjectAsSupports](ResponseEndArgs&& aArgs) {
1822 if (aArgs.endReason() == FetchDriverObserver::eAborted) {
1823 self->mPreloadResponse->MaybeReject(NS_ERROR_DOM_ABORT_ERR);
1825 self->mPreloadResponseEndPromiseRequestHolder.Complete();
1827 [self](int) {
1828 self->mPreloadResponseEndPromiseRequestHolder.Complete();
1830 ->Track(mPreloadResponseEndPromiseRequestHolder);
1831 } else {
1832 // preload navigation is disabled, resolved preload response promise with
1833 // undefined as default behavior.
1834 mPreloadResponse->MaybeResolveWithUndefined();
1837 mFetchHandlerStart = TimeStamp::Now();
1840 * Step 5: Dispatch the FetchEvent to the worker's global object
1842 nsresult rv = DispatchExtendableEventOnWorkerScope(
1843 aCx, aWorkerPrivate->GlobalScope(), fetchEvent, this);
1844 bool dispatchFailed = NS_FAILED(rv) && rv != NS_ERROR_XPC_JS_THREW_EXCEPTION;
1846 if (NS_WARN_IF(dispatchFailed)) {
1847 mHandled = nullptr;
1848 mPreloadResponse = nullptr;
1849 return rv;
1853 * At this point, there are 4 (legal) scenarios:
1855 * 1) If neither waitUntil() nor respondWith() are called,
1856 * DispatchExtendableEventOnWorkerScope() will have already called
1857 * FinishedWithResult(), but this call will have recorded the result
1858 * (mResult) and returned early so that mRespondWithPromiseHolder can be
1859 * settled first. mRespondWithPromiseHolder will be settled below, followed
1860 * by a call to MaybeFinished() which settles mPromiseHolder.
1862 * 2) If waitUntil() is called at least once, and respondWith() is not
1863 * called, DispatchExtendableEventOnWorkerScope() will NOT have called
1864 * FinishedWithResult(). We'll settle mRespondWithPromiseHolder first, and
1865 * at some point in the future when the last waitUntil() promise settles,
1866 * FinishedWithResult() will be called, settling mPromiseHolder.
1868 * 3) If waitUntil() is not called, and respondWith() is called,
1869 * DispatchExtendableEventOnWorkerScope() will NOT have called
1870 * FinishedWithResult(). We can also guarantee that
1871 * mRespondWithPromiseHolder will be settled before mPromiseHolder, due to
1872 * the Promise::AppendNativeHandler() call ordering in
1873 * FetchEvent::RespondWith().
1875 * 4) If waitUntil() is called at least once, and respondWith() is also
1876 * called, the effect is similar to scenario 3), with the most imporant
1877 * property being mRespondWithPromiseHolder settling before mPromiseHolder.
1879 * Note that if mPromiseHolder is settled before mRespondWithPromiseHolder,
1880 * FetchEventOpChild will cancel the interception.
1882 if (!fetchEvent->WaitToRespond()) {
1883 MOZ_ASSERT(!mRespondWithPromiseHolder.IsEmpty());
1884 MOZ_ASSERT(!aWorkerPrivate->UsesSystemPrincipal(),
1885 "We don't support system-principal serviceworkers");
1887 mFetchHandlerFinish = TimeStamp::Now();
1889 if (fetchEvent->DefaultPrevented(CallerType::NonSystem)) {
1890 // https://w3c.github.io/ServiceWorker/#on-fetch-request-algorithm
1891 // Step 24.1.1: If eventHandled is not null, then reject eventHandled with
1892 // a "NetworkError" DOMException in workerRealm.
1893 mHandled->MaybeRejectWithNetworkError(
1894 "FetchEvent.preventDefault() called"_ns);
1895 mRespondWithPromiseHolder.Resolve(
1896 FetchEventRespondWithResult(CancelInterceptionArgs(
1897 NS_ERROR_INTERCEPTION_FAILED,
1898 FetchEventTimeStamps(mFetchHandlerStart, mFetchHandlerFinish))),
1899 __func__);
1900 } else {
1901 // https://w3c.github.io/ServiceWorker/#on-fetch-request-algorithm
1902 // Step 24.2: If eventHandled is not null, then resolve eventHandled.
1903 mHandled->MaybeResolveWithUndefined();
1904 mRespondWithPromiseHolder.Resolve(
1905 FetchEventRespondWithResult(ResetInterceptionArgs(
1906 FetchEventTimeStamps(mFetchHandlerStart, mFetchHandlerFinish))),
1907 __func__);
1909 } else {
1910 MOZ_ASSERT(mRespondWithClosure);
1913 mPostDispatchChecksDone = true;
1914 MaybeFinished();
1916 return NS_OK;
1919 class ExtensionAPIEventOp final : public ServiceWorkerOp {
1920 using ServiceWorkerOp::ServiceWorkerOp;
1922 public:
1923 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(ExtensionAPIEventOp, override)
1925 private:
1926 ~ExtensionAPIEventOp() = default;
1928 bool Exec(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override {
1929 MOZ_ASSERT(aWorkerPrivate);
1930 aWorkerPrivate->AssertIsOnWorkerThread();
1931 MOZ_ASSERT(aWorkerPrivate->IsServiceWorker());
1932 MOZ_ASSERT(aWorkerPrivate->ExtensionAPIAllowed());
1933 MOZ_ASSERT(!mPromiseHolder.IsEmpty());
1935 ServiceWorkerExtensionAPIEventOpArgs& args =
1936 mArgs.get_ServiceWorkerExtensionAPIEventOpArgs();
1938 ServiceWorkerExtensionAPIEventOpResult result;
1939 result.extensionAPIEventListenerWasAdded() = false;
1941 if (aWorkerPrivate->WorkerScriptExecutedSuccessfully()) {
1942 GlobalObject globalObj(aCx, aWorkerPrivate->GlobalScope()->GetWrapper());
1943 RefPtr<ServiceWorkerGlobalScope> scope;
1944 UNWRAP_OBJECT(ServiceWorkerGlobalScope, globalObj.Get(), scope);
1945 SafeRefPtr<extensions::ExtensionBrowser> extensionAPI =
1946 scope->AcquireExtensionBrowser();
1947 if (!extensionAPI) {
1948 // If the worker script did never access the WebExtension APIs
1949 // then we can return earlier, no event listener could have been added.
1950 mPromiseHolder.Resolve(result, __func__);
1951 return true;
1953 // Check if a listener has been subscribed on the expected WebExtensions
1954 // API event.
1955 bool hasWakeupListener = extensionAPI->HasWakeupEventListener(
1956 args.apiNamespace(), args.apiEventName());
1957 result.extensionAPIEventListenerWasAdded() = hasWakeupListener;
1958 mPromiseHolder.Resolve(result, __func__);
1959 } else {
1960 mPromiseHolder.Resolve(result, __func__);
1963 return true;
1967 /* static */ already_AddRefed<ServiceWorkerOp> ServiceWorkerOp::Create(
1968 ServiceWorkerOpArgs&& aArgs,
1969 std::function<void(const ServiceWorkerOpResult&)>&& aCallback) {
1970 // Can be on the Worker Launcher thread for Terminate operations or on the
1971 // Worker thread for other operations.
1972 RefPtr<ServiceWorkerOp> op;
1974 switch (aArgs.type()) {
1975 case ServiceWorkerOpArgs::TServiceWorkerCheckScriptEvaluationOpArgs:
1976 op = MakeRefPtr<CheckScriptEvaluationOp>(std::move(aArgs),
1977 std::move(aCallback));
1978 break;
1979 case ServiceWorkerOpArgs::TServiceWorkerUpdateStateOpArgs:
1980 op = MakeRefPtr<UpdateServiceWorkerStateOp>(std::move(aArgs),
1981 std::move(aCallback));
1982 break;
1983 case ServiceWorkerOpArgs::TServiceWorkerTerminateWorkerOpArgs:
1984 op = MakeRefPtr<TerminateServiceWorkerOp>(std::move(aArgs),
1985 std::move(aCallback));
1986 break;
1987 case ServiceWorkerOpArgs::TServiceWorkerLifeCycleEventOpArgs:
1988 op = MakeRefPtr<LifeCycleEventOp>(std::move(aArgs), std::move(aCallback));
1989 break;
1990 case ServiceWorkerOpArgs::TServiceWorkerPushEventOpArgs:
1991 op = MakeRefPtr<PushEventOp>(std::move(aArgs), std::move(aCallback));
1992 break;
1993 case ServiceWorkerOpArgs::TServiceWorkerPushSubscriptionChangeEventOpArgs:
1994 op = MakeRefPtr<PushSubscriptionChangeEventOp>(std::move(aArgs),
1995 std::move(aCallback));
1996 break;
1997 case ServiceWorkerOpArgs::TServiceWorkerNotificationEventOpArgs:
1998 op = MakeRefPtr<NotificationEventOp>(std::move(aArgs),
1999 std::move(aCallback));
2000 break;
2001 case ServiceWorkerOpArgs::TServiceWorkerMessageEventOpArgs:
2002 op = MakeRefPtr<MessageEventOp>(std::move(aArgs), std::move(aCallback));
2003 break;
2004 case ServiceWorkerOpArgs::TParentToChildServiceWorkerFetchEventOpArgs:
2005 op = MakeRefPtr<FetchEventOp>(std::move(aArgs), std::move(aCallback));
2006 break;
2007 case ServiceWorkerOpArgs::TServiceWorkerExtensionAPIEventOpArgs:
2008 op = MakeRefPtr<ExtensionAPIEventOp>(std::move(aArgs),
2009 std::move(aCallback));
2010 break;
2011 case ServiceWorkerOpArgs::
2012 TServiceWorkerUpdateIsOnContentBlockingAllowListOpArgs:
2013 op = MakeRefPtr<UpdateIsOnContentBlockingAllowListOp>(
2014 std::move(aArgs), std::move(aCallback));
2015 break;
2016 default:
2017 MOZ_CRASH("Unknown Service Worker operation!");
2018 return nullptr;
2021 return op.forget();
2024 } // namespace mozilla::dom