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"
11 #include "ServiceWorkerOpPromise.h"
12 #include "js/Exception.h" // JS::ExceptionStack, JS::StealPendingExceptionStack
16 #include "nsContentUtils.h"
20 #include "nsIPushErrorReporter.h"
21 #include "nsISupportsImpl.h"
24 #include "nsServiceManagerUtils.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
{
71 class ExtendableEventKeepAliveHandler final
72 : public ExtendableEvent::ExtensionsHandler
,
73 public PromiseNativeHandler
{
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
)) {
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!");
106 MOZ_ASSERT(!mPendingPromisesCount
);
110 ++mPendingPromisesCount
;
111 aPromise
.AppendNativeHandler(this);
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
);
130 MOZ_ASSERT(IsCurrentThreadRunningWorker());
131 MOZ_ASSERT(!GetDispatchFlag());
133 if (mPendingPromisesCount
) {
138 mCallback
->FinishedWithResult(mRejected
? Rejected
: Resolved
);
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
152 * p.then(() => e.waitUntil(otherPromise));
154 class MaybeDoneRunner
: public MicroTaskRunnable
{
156 explicit MaybeDoneRunner(RefPtr
<ExtendableEventKeepAliveHandler
> aHandler
)
157 : mHandler(std::move(aHandler
)) {}
159 void Run(AutoSlowOperation
& /* unused */) override
{
160 mHandler
->MaybeDone();
164 RefPtr
<ExtendableEventKeepAliveHandler
> mHandler
;
167 explicit ExtendableEventKeepAliveHandler(
168 RefPtr
<ExtendableEventCallback
> aCallback
)
169 : mCallback(std::move(aCallback
)) {}
171 ~ExtendableEventKeepAliveHandler() { Cleanup(); }
174 MOZ_ASSERT(IsCurrentThreadRunningWorker());
177 mCallback
->FinishedWithResult(Rejected
);
181 mWorkerRef
= 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()) {
201 CycleCollectedJSContext
* cx
= CycleCollectedJSContext::Get();
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
) {
234 MOZ_ASSERT(aWorkerScope
);
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
);
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
;
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
{
277 NS_DECL_ISUPPORTS_INHERITED
279 ServiceWorkerOpRunnable(RefPtr
<ServiceWorkerOp
> aOwner
,
280 WorkerPrivate
* aWorkerPrivate
)
281 : WorkerDebuggeeRunnable(aWorkerPrivate
, "ServiceWorkerOpRunnable",
283 mOwner(std::move(aOwner
)) {
284 AssertIsOnMainThread();
286 MOZ_ASSERT(aWorkerPrivate
);
290 ~ServiceWorkerOpRunnable() = default;
292 bool WorkerRun(JSContext
* aCx
, WorkerPrivate
* aWorkerPrivate
) override
{
293 MOZ_ASSERT(aWorkerPrivate
);
294 aWorkerPrivate
->AssertIsOnWorkerThread();
295 MOZ_ASSERT(aWorkerPrivate
->IsServiceWorker());
298 if (aWorkerPrivate
->GlobalScope()->IsDying()) {
303 bool rv
= mOwner
->Exec(aCx
, aWorkerPrivate
);
304 Unused
<< NS_WARN_IF(!rv
);
310 nsresult
Cancel() override
{
313 mOwner
->RejectAll(NS_ERROR_DOM_ABORT_ERR
);
319 RefPtr
<ServiceWorkerOp
> mOwner
;
322 NS_IMPL_ISUPPORTS_INHERITED0(ServiceWorkerOp::ServiceWorkerOpRunnable
,
325 bool ServiceWorkerOp::MaybeStart(RemoteWorkerChild
* aOwner
,
326 RemoteWorkerChild::State
& aState
) {
327 MOZ_ASSERT(!mStarted
);
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
);
339 // Allow termination to happen while the Service Worker is initializing.
340 if (aState
.is
<Pending
>() && !IsTerminationOp()) {
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
);
351 MOZ_ASSERT(aState
.is
<RemoteWorkerChild::Running
>() || IsTerminationOp());
353 RefPtr
<ServiceWorkerOp
> self
= this;
355 if (IsTerminationOp()) {
356 aOwner
->GetTerminationPromise()->Then(
357 GetCurrentSerialEventTarget(), __func__
,
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__
);
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
);
384 MOZ_ALWAYS_SUCCEEDS(SchedulerGroup::Dispatch(r
.forget()));
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
);
400 if (IsTerminationOp()) {
401 aOwner
->CloseWorkerOnMainThread();
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__
);
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());
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());
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
;
472 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(CheckScriptEvaluationOp
, override
)
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__
);
494 class TerminateServiceWorkerOp final
: public ServiceWorkerOp
{
495 using ServiceWorkerOp::ServiceWorkerOp
;
498 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(TerminateServiceWorkerOp
, override
)
501 ~TerminateServiceWorkerOp() = default;
503 bool Exec(JSContext
*, WorkerPrivate
*) override
{
504 MOZ_ASSERT_UNREACHABLE(
505 "Worker termination should be handled in "
506 "`ServiceWorkerOp::MaybeStart()`");
512 class UpdateServiceWorkerStateOp final
: public ServiceWorkerOp
{
513 using ServiceWorkerOp::ServiceWorkerOp
;
516 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(UpdateServiceWorkerStateOp
, override
);
519 class UpdateStateOpRunnable final
: public MainThreadWorkerControlRunnable
{
521 NS_DECL_ISUPPORTS_INHERITED
523 UpdateStateOpRunnable(RefPtr
<UpdateServiceWorkerStateOp
> aOwner
,
524 WorkerPrivate
* aWorkerPrivate
)
525 : MainThreadWorkerControlRunnable(aWorkerPrivate
),
526 mOwner(std::move(aOwner
)) {
527 AssertIsOnMainThread();
529 MOZ_ASSERT(aWorkerPrivate
);
533 ~UpdateStateOpRunnable() = default;
535 bool WorkerRun(JSContext
* aCx
, WorkerPrivate
* aWorkerPrivate
) override
{
536 MOZ_ASSERT(aWorkerPrivate
);
537 aWorkerPrivate
->AssertIsOnWorkerThread();
538 MOZ_ASSERT(aWorkerPrivate
->IsServiceWorker());
541 Unused
<< mOwner
->Exec(aCx
, aWorkerPrivate
);
548 nsresult
Cancel() override
{
551 mOwner
->RejectAll(NS_ERROR_DOM_ABORT_ERR
);
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__
);
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
,
598 class LifeCycleEventOp final
: public ExtendableEventOp
{
599 using ExtendableEventOp::ExtendableEventOp
;
602 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(LifeCycleEventOp
, override
)
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
);
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
))) {
637 return !DispatchFailed(rv
);
644 class PushEventOp final
: public ExtendableEventOp
{
645 using ExtendableEventOp::ExtendableEventOp
;
648 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(PushEventOp
, override
)
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());
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()) {
681 DebugOnly
<bool> inited
=
682 pushEventInit
.mData
.Construct().SetAsArrayBufferView().Init(data
);
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())) {
697 pushEvent
->SetTrusted(true);
701 nsresult rv
= DispatchExtendableEventOnWorkerScope(
702 aCx
, aWorkerPrivate
->GlobalScope(), pushEvent
, this);
705 if (NS_WARN_IF(DispatchFailed(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
);
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()) {
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");
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
;
762 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(PushSubscriptionChangeEventOp
, override
)
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
))) {
790 return !DispatchFailed(rv
);
794 class NotificationEventOp
: public ExtendableEventOp
,
795 public nsITimerCallback
,
797 using ExtendableEventOp::ExtendableEventOp
;
800 NS_DECL_THREADSAFE_ISUPPORTS
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());
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();
824 globalScope
->ConsumeWindowInteraction();
828 mWorkerRef
= nullptr;
831 void StartClearWindowTimer(WorkerPrivate
* aWorkerPrivate
) {
832 MOZ_ASSERT(aWorkerPrivate
);
833 aWorkerPrivate
->AssertIsOnWorkerThread();
837 nsCOMPtr
<nsITimer
> timer
=
838 NS_NewTimer(aWorkerPrivate
->ControlEventTarget());
839 if (NS_WARN_IF(!timer
)) {
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());
857 aWorkerPrivate
->GlobalScope()->AllowWindowInteraction();
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
);
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(),
889 if (NS_WARN_IF(result
.isErr())) {
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
);
937 // nsINamed interface
938 NS_IMETHOD
GetName(nsACString
& aName
) override
{
939 aName
.AssignLiteral("NotificationEventOp");
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
;
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());
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();
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();
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()
1010 if (NS_WARN_IF(NS_FAILED(result
))) {
1012 rv
.SuppressException();
1016 OriginAttributes attrs
;
1017 nsCOMPtr
<nsIPrincipal
> principal
=
1018 BasePrincipal::CreateContentPrincipal(url
, attrs
);
1024 result
= principal
->GetOriginNoSuffix(origin
);
1025 if (NS_WARN_IF(NS_FAILED(result
))) {
1027 rv
.SuppressException();
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
,
1043 extendableEvent
->SetTrusted(true);
1045 nsresult rv2
= DispatchExtendableEventOnWorkerScope(
1046 aCx
, aWorkerPrivate
->GlobalScope(), extendableEvent
, this);
1048 if (NS_WARN_IF(DispatchFailed(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
1063 class MOZ_STACK_CLASS
FetchEventOp::AutoCancel
{
1065 explicit AutoCancel(FetchEventOp
* aOwner
)
1069 mMessageName("InterceptionFailedWithURL"_ns
) {
1070 MOZ_ASSERT(IsCurrentThreadRunningWorker());
1073 nsAutoString requestURL
;
1074 mOwner
->GetRequestURL(requestURL
);
1075 mParams
.AppendElement(requestURL
);
1080 if (mSourceSpec
.IsEmpty()) {
1081 mOwner
->AsyncLog(mMessageName
, std::move(mParams
));
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
)),
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
)) {
1108 MOZ_ASSERT(!aRv
.Failed());
1110 // Let's take the pending exception.
1111 JS::ExceptionStack
exnStack(aCx
);
1112 if (!JS::StealPendingExceptionStack(aCx
, &exnStack
)) {
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
);
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());
1132 template <typename
... Params
>
1133 void SetCancelMessage(const nsACString
& aMessageName
, Params
&&... aParams
) {
1135 MOZ_ASSERT(mMessageName
.EqualsLiteral("InterceptionFailedWithURL"));
1136 MOZ_ASSERT(mParams
.Length() == 1);
1137 mMessageName
= aMessageName
;
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
) {
1149 MOZ_ASSERT(mMessageName
.EqualsLiteral("InterceptionFailedWithURL"));
1150 MOZ_ASSERT(mParams
.Length() == 1);
1152 mSourceSpec
= aSourceSpec
;
1156 mMessageName
= aMessageName
;
1158 StringArrayAppender::Append(mParams
, sizeof...(Params
),
1159 std::forward
<Params
>(aParams
)...);
1162 void Reset() { mOwner
= nullptr; }
1165 FetchEventOp
* MOZ_NON_OWNING_REF mOwner
;
1166 nsCString mSourceSpec
;
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
) {
1185 MOZ_ASSERT_IF(mActor
, mActor
== aActor
);
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());
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
)),
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(
1245 FetchEventTimeStamps(mFetchHandlerStart
, mFetchHandlerFinish
)),
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
) {
1269 void FetchEventOp::MaybeFinished() {
1270 MOZ_ASSERT(IsCurrentThreadRunningWorker());
1271 MOZ_ASSERT(!mPromiseHolder
.IsEmpty());
1274 // It's possible that mRespondWithPromiseHolder wasn't settled. That happens
1275 // if the worker was terminated before the respondWith promise settled.
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
))) {
1302 return NS_SUCCEEDED(rv
);
1305 void FetchEventOp::AsyncLog(const nsCString
& aMessageName
,
1306 nsTArray
<nsString
> aParams
) {
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
) {
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
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
)) {
1337 Unused
<< self
->mActor
->SendAsyncLog(spec
, line
, column
, messageName
,
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()
1351 MOZ_ASSERT(!urls
.IsEmpty());
1353 CopyUTF8toUTF16(urls
.LastElement(), aOutRequestURL
);
1356 void FetchEventOp::ResolvedCallback(JSContext
* aCx
,
1357 JS::Handle
<JS::Value
> aValue
,
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()) {
1373 "FetchEvent::RespondWith was passed a promise resolved to a "
1377 nsCString sourceSpec
;
1379 uint32_t column
= 0;
1380 nsString valueString
;
1381 nsContentUtils::ExtractErrorValues(aCx
, aValue
, sourceSpec
, &line
, &column
,
1384 autoCancel
.SetCancelMessageAndLocation(sourceSpec
, line
, column
,
1385 "InterceptedNonResponseWithURL"_ns
,
1386 requestURL
, valueString
);
1390 RefPtr
<Response
> response
;
1391 nsresult rv
= UNWRAP_OBJECT(Response
, &aValue
.toObject(), response
);
1392 if (NS_FAILED(rv
)) {
1393 nsCString sourceSpec
;
1395 uint32_t column
= 0;
1396 nsString valueString
;
1397 nsContentUtils::ExtractErrorValues(aCx
, aValue
, sourceSpec
, &line
, &column
,
1400 autoCancel
.SetCancelMessageAndLocation(sourceSpec
, line
, column
,
1401 "InterceptedNonResponseWithURL"_ns
,
1402 requestURL
, valueString
);
1406 WorkerPrivate
* worker
= GetCurrentThreadWorkerPrivate();
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
,
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
);
1442 const RequestRedirect requestRedirectMode
=
1443 args
.common().internalRequest().requestRedirect();
1445 if (requestRedirectMode
!= RequestRedirect::Manual
&&
1446 response
->Type() == ResponseType::Opaqueredirect
) {
1447 autoCancel
.SetCancelMessage("BadOpaqueRedirectInterceptionWithURL"_ns
,
1452 if (requestRedirectMode
!= RequestRedirect::Follow
&&
1453 response
->Redirected()) {
1454 autoCancel
.SetCancelMessage("BadRedirectModeInterceptionWithURL"_ns
,
1459 if (NS_WARN_IF(response
->BodyUsed())) {
1460 autoCancel
.SetCancelMessage("InterceptedUsedResponseWithURL"_ns
,
1465 SafeRefPtr
<InternalResponse
> ir
= response
->GetInternalResponse();
1466 if (NS_WARN_IF(!ir
)) {
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");
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
1486 NS_ConvertUTF8toUTF16
responseURL(ir
->GetUnfilteredURL());
1487 autoCancel
.SetCancelMessage("CorsResponseForSameOriginRequest"_ns
,
1488 requestURL
, responseURL
);
1492 nsCOMPtr
<nsIInputStream
> body
;
1493 ir
->GetUnfilteredBody(getter_AddRefs(body
));
1494 // Errors and redirects may not have a body.
1497 response
->SetBodyUsed(aCx
, error
);
1498 error
.WouldReportJSException();
1499 if (NS_WARN_IF(error
.Failed())) {
1500 autoCancel
.SetCancelErrorResult(aCx
, error
);
1505 if (!ir
->GetChannelInfo().IsInitialized()) {
1506 // This is a synthetic response (I think and hope so).
1507 ir
->InitChannelInfo(worker
->GetChannelInfo());
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
))),
1527 void FetchEventOp::RejectedCallback(JSContext
* aCx
,
1528 JS::Handle
<JS::Value
> aValue
,
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
,
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
))),
1565 nsresult
FetchEventOp::DispatchFetchEvent(JSContext
* aCx
,
1566 WorkerPrivate
* aWorkerPrivate
) {
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
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
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();
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
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();
1717 self
->mPreloadResponseTimingPromiseRequestHolder
.Complete();
1719 ->Track(mPreloadResponseTimingPromiseRequestHolder
);
1721 RefPtr
<FetchEventPreloadResponseEndPromise
> preloadResponseEndPromise
=
1722 mActor
->GetPreloadResponseEndPromise();
1723 MOZ_ASSERT(preloadResponseEndPromise
);
1724 preloadResponseEndPromise
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();
1734 self
->mPreloadResponseEndPromiseRequestHolder
.Complete();
1736 ->Track(mPreloadResponseEndPromiseRequestHolder
);
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
)) {
1754 mPreloadResponse
= nullptr;
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
))),
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
))),
1816 MOZ_ASSERT(mRespondWithClosure
);
1819 mPostDispatchChecksDone
= true;
1825 class ExtensionAPIEventOp final
: public ServiceWorkerOp
{
1826 using ServiceWorkerOp::ServiceWorkerOp
;
1829 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(ExtensionAPIEventOp
, override
)
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__
);
1859 // Check if a listener has been subscribed on the expected WebExtensions
1861 bool hasWakeupListener
= extensionAPI
->HasWakeupEventListener(
1862 args
.apiNamespace(), args
.apiEventName());
1863 result
.extensionAPIEventListenerWasAdded() = hasWakeupListener
;
1864 mPromiseHolder
.Resolve(result
, __func__
);
1866 mPromiseHolder
.Resolve(result
, __func__
);
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
));
1885 case ServiceWorkerOpArgs::TServiceWorkerUpdateStateOpArgs
:
1886 op
= MakeRefPtr
<UpdateServiceWorkerStateOp
>(std::move(aArgs
),
1887 std::move(aCallback
));
1889 case ServiceWorkerOpArgs::TServiceWorkerTerminateWorkerOpArgs
:
1890 op
= MakeRefPtr
<TerminateServiceWorkerOp
>(std::move(aArgs
),
1891 std::move(aCallback
));
1893 case ServiceWorkerOpArgs::TServiceWorkerLifeCycleEventOpArgs
:
1894 op
= MakeRefPtr
<LifeCycleEventOp
>(std::move(aArgs
), std::move(aCallback
));
1896 case ServiceWorkerOpArgs::TServiceWorkerPushEventOpArgs
:
1897 op
= MakeRefPtr
<PushEventOp
>(std::move(aArgs
), std::move(aCallback
));
1899 case ServiceWorkerOpArgs::TServiceWorkerPushSubscriptionChangeEventOpArgs
:
1900 op
= MakeRefPtr
<PushSubscriptionChangeEventOp
>(std::move(aArgs
),
1901 std::move(aCallback
));
1903 case ServiceWorkerOpArgs::TServiceWorkerNotificationEventOpArgs
:
1904 op
= MakeRefPtr
<NotificationEventOp
>(std::move(aArgs
),
1905 std::move(aCallback
));
1907 case ServiceWorkerOpArgs::TServiceWorkerMessageEventOpArgs
:
1908 op
= MakeRefPtr
<MessageEventOp
>(std::move(aArgs
), std::move(aCallback
));
1910 case ServiceWorkerOpArgs::TParentToChildServiceWorkerFetchEventOpArgs
:
1911 op
= MakeRefPtr
<FetchEventOp
>(std::move(aArgs
), std::move(aCallback
));
1913 case ServiceWorkerOpArgs::TServiceWorkerExtensionAPIEventOpArgs
:
1914 op
= MakeRefPtr
<ExtensionAPIEventOp
>(std::move(aArgs
),
1915 std::move(aCallback
));
1918 MOZ_CRASH("Unknown Service Worker operation!");
1925 } // namespace mozilla::dom