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/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
;
77 class ExtendableEventKeepAliveHandler final
78 : public ExtendableEvent::ExtensionsHandler
,
79 public PromiseNativeHandler
{
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
)) {
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!");
112 MOZ_ASSERT(!mPendingPromisesCount
);
116 ++mPendingPromisesCount
;
117 aPromise
.AppendNativeHandler(this);
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
);
136 MOZ_ASSERT(IsCurrentThreadRunningWorker());
137 MOZ_ASSERT(!GetDispatchFlag());
139 if (mPendingPromisesCount
) {
144 mCallback
->FinishedWithResult(mRejected
? Rejected
: Resolved
);
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
158 * p.then(() => e.waitUntil(otherPromise));
160 class MaybeDoneRunner
: public MicroTaskRunnable
{
162 explicit MaybeDoneRunner(RefPtr
<ExtendableEventKeepAliveHandler
> aHandler
)
163 : mHandler(std::move(aHandler
)) {}
165 void Run(AutoSlowOperation
& /* unused */) override
{
166 mHandler
->MaybeDone();
170 RefPtr
<ExtendableEventKeepAliveHandler
> mHandler
;
173 explicit ExtendableEventKeepAliveHandler(
174 RefPtr
<ExtendableEventCallback
> aCallback
)
175 : mCallback(std::move(aCallback
)) {}
177 ~ExtendableEventKeepAliveHandler() { Cleanup(); }
180 MOZ_ASSERT(IsCurrentThreadRunningWorker());
183 mCallback
->FinishedWithResult(Rejected
);
187 mWorkerRef
= 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()) {
207 CycleCollectedJSContext
* cx
= CycleCollectedJSContext::Get();
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
) {
240 MOZ_ASSERT(aWorkerScope
);
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
);
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
;
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
{
283 NS_DECL_ISUPPORTS_INHERITED
285 ServiceWorkerOpRunnable(RefPtr
<ServiceWorkerOp
> aOwner
,
286 WorkerPrivate
* aWorkerPrivate
)
287 : WorkerDebuggeeRunnable("ServiceWorkerOpRunnable"),
288 mOwner(std::move(aOwner
)) {
290 MOZ_ASSERT(aWorkerPrivate
);
294 ~ServiceWorkerOpRunnable() = default;
296 bool WorkerRun(JSContext
* aCx
, WorkerPrivate
* aWorkerPrivate
) override
{
297 MOZ_ASSERT(aWorkerPrivate
);
298 aWorkerPrivate
->AssertIsOnWorkerThread();
299 MOZ_ASSERT(aWorkerPrivate
->IsServiceWorker());
302 if (aWorkerPrivate
->GlobalScope()->IsDying()) {
307 bool rv
= mOwner
->Exec(aCx
, aWorkerPrivate
);
308 Unused
<< NS_WARN_IF(!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
{
323 mOwner
->RejectAll(NS_ERROR_DOM_ABORT_ERR
);
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
);
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
);
349 // Allow termination to happen while the Service Worker is initializing.
350 if (aState
.is
<Pending
>() && !IsTerminationOp()) {
354 if (NS_WARN_IF(aState
.is
<Canceled
>()) || NS_WARN_IF(aState
.is
<Killed
>())) {
355 RejectAll(NS_ERROR_DOM_INVALID_STATE_ERR
);
360 MOZ_ASSERT(aState
.is
<Running
>() || IsTerminationOp());
362 RefPtr
<ServiceWorkerOp
> self
= this;
364 if (IsTerminationOp()) {
365 aOwner
->GetTerminationPromise()->Then(
366 GetCurrentSerialEventTarget(), __func__
,
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__
);
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
);
393 MOZ_ALWAYS_SUCCEEDS(SchedulerGroup::Dispatch(r
.forget()));
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
);
410 if (IsTerminationOp()) {
411 aOwner
->CloseWorkerOnMainThread();
413 auto lock
= aOwner
->mState
.Lock();
414 MOZ_ASSERT(lock
->is
<Running
>());
416 RefPtr
<WorkerThreadRunnable
> workerRunnable
=
417 GetRunnable(lock
->as
<Running
>().mWorkerPrivate
);
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
);
431 if (NS_WARN_IF(!aOwner
->CanSend())) {
432 RejectAll(NS_ERROR_DOM_ABORT_ERR
);
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
);
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
);
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__
);
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());
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());
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
;
516 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(CheckScriptEvaluationOp
, override
)
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__
);
538 class TerminateServiceWorkerOp final
: public ServiceWorkerOp
{
539 using ServiceWorkerOp::ServiceWorkerOp
;
542 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(TerminateServiceWorkerOp
, override
)
545 ~TerminateServiceWorkerOp() = default;
547 bool Exec(JSContext
*, WorkerPrivate
*) override
{
548 MOZ_ASSERT_UNREACHABLE(
549 "Worker termination should be handled in "
550 "`ServiceWorkerOp::MaybeStart()`");
556 class UpdateServiceWorkerStateOp final
: public ServiceWorkerOp
{
557 using ServiceWorkerOp::ServiceWorkerOp
;
560 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(UpdateServiceWorkerStateOp
, override
);
563 class UpdateStateOpRunnable final
: public WorkerControlRunnable
{
565 NS_DECL_ISUPPORTS_INHERITED
567 UpdateStateOpRunnable(RefPtr
<UpdateServiceWorkerStateOp
> aOwner
,
568 WorkerPrivate
* aWorkerPrivate
)
569 : WorkerControlRunnable("UpdateStateOpRunnable"),
570 mOwner(std::move(aOwner
)) {
572 MOZ_ASSERT(aWorkerPrivate
);
573 aWorkerPrivate
->AssertIsOnWorkerThread();
576 virtual bool PreDispatch(WorkerPrivate
* aWorkerPrivate
) override
{
577 aWorkerPrivate
->AssertIsOnWorkerThread();
581 virtual void PostDispatch(WorkerPrivate
* aWorkerPrivate
,
582 bool aDispatchResult
) override
{
583 aWorkerPrivate
->AssertIsOnWorkerThread();
587 ~UpdateStateOpRunnable() = default;
589 bool WorkerRun(JSContext
* aCx
, WorkerPrivate
* aWorkerPrivate
) override
{
590 MOZ_ASSERT(aWorkerPrivate
);
591 aWorkerPrivate
->AssertIsOnWorkerThread();
592 MOZ_ASSERT(aWorkerPrivate
->IsServiceWorker());
595 Unused
<< mOwner
->Exec(aCx
, aWorkerPrivate
);
602 nsresult
Cancel() override
{
605 mOwner
->RejectAll(NS_ERROR_DOM_ABORT_ERR
);
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__
);
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
,
653 class LifeCycleEventOp final
: public ExtendableEventOp
{
654 using ExtendableEventOp::ExtendableEventOp
;
657 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(LifeCycleEventOp
, override
)
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
);
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
))) {
692 return !DispatchFailed(rv
);
699 class PushEventOp final
: public ExtendableEventOp
{
700 using ExtendableEventOp::ExtendableEventOp
;
703 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(PushEventOp
, override
)
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());
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
734 result
.WouldReportJSException();
735 if (result
.Failed()) {
739 DebugOnly
<bool> inited
=
740 pushEventInit
.mData
.Construct().SetAsArrayBufferView().Init(data
);
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())) {
755 pushEvent
->SetTrusted(true);
759 nsresult rv
= DispatchExtendableEventOnWorkerScope(
760 aCx
, aWorkerPrivate
->GlobalScope(), pushEvent
, this);
763 if (NS_WARN_IF(DispatchFailed(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
);
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()) {
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");
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
;
820 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(PushSubscriptionChangeEventOp
, override
)
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
))) {
848 return !DispatchFailed(rv
);
852 class NotificationEventOp
: public ExtendableEventOp
,
853 public nsITimerCallback
,
855 using ExtendableEventOp::ExtendableEventOp
;
858 NS_DECL_THREADSAFE_ISUPPORTS
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());
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();
882 globalScope
->ConsumeWindowInteraction();
886 mWorkerRef
= nullptr;
889 void StartClearWindowTimer(WorkerPrivate
* aWorkerPrivate
) {
890 MOZ_ASSERT(aWorkerPrivate
);
891 aWorkerPrivate
->AssertIsOnWorkerThread();
895 nsCOMPtr
<nsITimer
> timer
=
896 NS_NewTimer(aWorkerPrivate
->ControlEventTarget());
897 if (NS_WARN_IF(!timer
)) {
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());
915 aWorkerPrivate
->GlobalScope()->AllowWindowInteraction();
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
);
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(),
947 if (NS_WARN_IF(result
.isErr())) {
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
);
995 // nsINamed interface
996 NS_IMETHOD
GetName(nsACString
& aName
) override
{
997 aName
.AssignLiteral("NotificationEventOp");
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
;
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());
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();
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();
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());
1071 case PostMessageSource::TIPCServiceWorkerDescriptor
:
1072 originSource
= ipcSource
.get_IPCServiceWorkerDescriptor().scriptURL();
1073 init
.mSource
.SetValue().SetAsServiceWorker() = ServiceWorker::Create(
1074 sgo
, ServiceWorkerDescriptor(
1075 ipcSource
.get_IPCServiceWorkerDescriptor()));
1078 MOZ_ASSERT_UNREACHABLE("Unexpected source type");
1082 nsCOMPtr
<nsIURI
> url
;
1083 nsresult result
= NS_NewURI(getter_AddRefs(url
), originSource
);
1084 if (NS_WARN_IF(NS_FAILED(result
))) {
1086 rv
.SuppressException();
1090 OriginAttributes attrs
;
1091 nsCOMPtr
<nsIPrincipal
> principal
=
1092 BasePrincipal::CreateContentPrincipal(url
, attrs
);
1098 result
= principal
->GetOriginNoSuffix(origin
);
1099 if (NS_WARN_IF(NS_FAILED(result
))) {
1101 rv
.SuppressException();
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
,
1114 extendableEvent
->SetTrusted(true);
1116 nsresult rv2
= DispatchExtendableEventOnWorkerScope(
1117 aCx
, aWorkerPrivate
->GlobalScope(), extendableEvent
, this);
1119 if (NS_WARN_IF(DispatchFailed(rv2
))) {
1123 return !DispatchFailed(rv2
);
1126 RefPtr
<ServiceWorkerCloneData
> mData
;
1129 class UpdateIsOnContentBlockingAllowListOp final
: public ExtendableEventOp
{
1130 using ExtendableEventOp::ExtendableEventOp
;
1133 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(UpdateIsOnContentBlockingAllowListOp
,
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
);
1156 * Used for ScopeExit-style network request cancelation in
1157 * `ResolvedCallback()` (e.g. if `FetchEvent::RespondWith()` is resolved with
1160 class MOZ_STACK_CLASS
FetchEventOp::AutoCancel
{
1162 explicit AutoCancel(FetchEventOp
* aOwner
)
1166 mMessageName("InterceptionFailedWithURL"_ns
) {
1167 MOZ_ASSERT(IsCurrentThreadRunningWorker());
1170 nsAutoString requestURL
;
1171 mOwner
->GetRequestURL(requestURL
);
1172 mParams
.AppendElement(requestURL
);
1177 if (mSourceSpec
.IsEmpty()) {
1178 mOwner
->AsyncLog(mMessageName
, std::move(mParams
));
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
)),
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
)) {
1205 MOZ_ASSERT(!aRv
.Failed());
1207 // Let's take the pending exception.
1208 JS::ExceptionStack
exnStack(aCx
);
1209 if (!JS::StealPendingExceptionStack(aCx
, &exnStack
)) {
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
);
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());
1229 template <typename
... Params
>
1230 void SetCancelMessage(const nsACString
& aMessageName
, Params
&&... aParams
) {
1232 MOZ_ASSERT(mMessageName
.EqualsLiteral("InterceptionFailedWithURL"));
1233 MOZ_ASSERT(mParams
.Length() == 1);
1234 mMessageName
= aMessageName
;
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
) {
1246 MOZ_ASSERT(mMessageName
.EqualsLiteral("InterceptionFailedWithURL"));
1247 MOZ_ASSERT(mParams
.Length() == 1);
1249 mSourceSpec
= aSourceSpec
;
1253 mMessageName
= aMessageName
;
1255 StringArrayAppender::Append(mParams
, sizeof...(Params
),
1256 std::forward
<Params
>(aParams
)...);
1259 void Reset() { mOwner
= nullptr; }
1262 FetchEventOp
* MOZ_NON_OWNING_REF mOwner
;
1263 nsCString mSourceSpec
;
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
) {
1282 MOZ_ASSERT_IF(mActor
, mActor
== aActor
);
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());
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
)),
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(
1342 FetchEventTimeStamps(mFetchHandlerStart
, mFetchHandlerFinish
)),
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
) {
1366 void FetchEventOp::MaybeFinished() {
1367 MOZ_ASSERT(IsCurrentThreadRunningWorker());
1368 MOZ_ASSERT(!mPromiseHolder
.IsEmpty());
1371 // It's possible that mRespondWithPromiseHolder wasn't settled. That happens
1372 // if the worker was terminated before the respondWith promise settled.
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
))) {
1399 return NS_SUCCEEDED(rv
);
1402 void FetchEventOp::AsyncLog(const nsCString
& aMessageName
,
1403 nsTArray
<nsString
> aParams
) {
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
) {
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
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
)) {
1434 Unused
<< self
->mActor
->SendAsyncLog(spec
, line
, column
, messageName
,
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()
1448 MOZ_ASSERT(!urls
.IsEmpty());
1450 CopyUTF8toUTF16(urls
.LastElement(), aOutRequestURL
);
1453 void FetchEventOp::ResolvedCallback(JSContext
* aCx
,
1454 JS::Handle
<JS::Value
> aValue
,
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()) {
1470 "FetchEvent::RespondWith was passed a promise resolved to a "
1474 nsCString sourceSpec
;
1476 uint32_t column
= 0;
1477 nsString valueString
;
1478 nsContentUtils::ExtractErrorValues(aCx
, aValue
, sourceSpec
, &line
, &column
,
1481 autoCancel
.SetCancelMessageAndLocation(sourceSpec
, line
, column
,
1482 "InterceptedNonResponseWithURL"_ns
,
1483 requestURL
, valueString
);
1487 RefPtr
<Response
> response
;
1488 nsresult rv
= UNWRAP_OBJECT(Response
, &aValue
.toObject(), response
);
1489 if (NS_FAILED(rv
)) {
1490 nsCString sourceSpec
;
1492 uint32_t column
= 0;
1493 nsString valueString
;
1494 nsContentUtils::ExtractErrorValues(aCx
, aValue
, sourceSpec
, &line
, &column
,
1497 autoCancel
.SetCancelMessageAndLocation(sourceSpec
, line
, column
,
1498 "InterceptedNonResponseWithURL"_ns
,
1499 requestURL
, valueString
);
1503 WorkerPrivate
* worker
= GetCurrentThreadWorkerPrivate();
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
,
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
);
1538 const RequestRedirect requestRedirectMode
=
1539 args
.common().internalRequest().requestRedirect();
1541 if (requestRedirectMode
!= RequestRedirect::Manual
&&
1542 response
->Type() == ResponseType::Opaqueredirect
) {
1543 autoCancel
.SetCancelMessage("BadOpaqueRedirectInterceptionWithURL"_ns
,
1548 if (requestRedirectMode
!= RequestRedirect::Follow
&&
1549 response
->Redirected()) {
1550 autoCancel
.SetCancelMessage("BadRedirectModeInterceptionWithURL"_ns
,
1555 if (NS_WARN_IF(response
->BodyUsed())) {
1556 autoCancel
.SetCancelMessage("InterceptedUsedResponseWithURL"_ns
,
1561 SafeRefPtr
<InternalResponse
> ir
= response
->GetInternalResponse();
1562 if (NS_WARN_IF(!ir
)) {
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");
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
1580 NS_ConvertUTF8toUTF16
responseURL(ir
->GetUnfilteredURL());
1581 autoCancel
.SetCancelMessage("CorsResponseForSameOriginRequest"_ns
,
1582 requestURL
, responseURL
);
1586 nsCOMPtr
<nsIInputStream
> body
;
1587 ir
->GetUnfilteredBody(getter_AddRefs(body
));
1588 // Errors and redirects may not have a body.
1591 response
->SetBodyUsed(aCx
, error
);
1592 error
.WouldReportJSException();
1593 if (NS_WARN_IF(error
.Failed())) {
1594 autoCancel
.SetCancelErrorResult(aCx
, error
);
1599 if (!ir
->GetChannelInfo().IsInitialized()) {
1600 // This is a synthetic response (I think and hope so).
1601 ir
->InitChannelInfo(worker
->GetChannelInfo());
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
))),
1621 void FetchEventOp::RejectedCallback(JSContext
* aCx
,
1622 JS::Handle
<JS::Value
> aValue
,
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
,
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
))),
1659 nsresult
FetchEventOp::DispatchFetchEvent(JSContext
* aCx
,
1660 WorkerPrivate
* aWorkerPrivate
) {
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
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
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();
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
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();
1811 self
->mPreloadResponseTimingPromiseRequestHolder
.Complete();
1813 ->Track(mPreloadResponseTimingPromiseRequestHolder
);
1815 RefPtr
<FetchEventPreloadResponseEndPromise
> preloadResponseEndPromise
=
1816 mActor
->GetPreloadResponseEndPromise();
1817 MOZ_ASSERT(preloadResponseEndPromise
);
1818 preloadResponseEndPromise
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();
1828 self
->mPreloadResponseEndPromiseRequestHolder
.Complete();
1830 ->Track(mPreloadResponseEndPromiseRequestHolder
);
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
)) {
1848 mPreloadResponse
= nullptr;
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
))),
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
))),
1910 MOZ_ASSERT(mRespondWithClosure
);
1913 mPostDispatchChecksDone
= true;
1919 class ExtensionAPIEventOp final
: public ServiceWorkerOp
{
1920 using ServiceWorkerOp::ServiceWorkerOp
;
1923 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(ExtensionAPIEventOp
, override
)
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__
);
1953 // Check if a listener has been subscribed on the expected WebExtensions
1955 bool hasWakeupListener
= extensionAPI
->HasWakeupEventListener(
1956 args
.apiNamespace(), args
.apiEventName());
1957 result
.extensionAPIEventListenerWasAdded() = hasWakeupListener
;
1958 mPromiseHolder
.Resolve(result
, __func__
);
1960 mPromiseHolder
.Resolve(result
, __func__
);
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
));
1979 case ServiceWorkerOpArgs::TServiceWorkerUpdateStateOpArgs
:
1980 op
= MakeRefPtr
<UpdateServiceWorkerStateOp
>(std::move(aArgs
),
1981 std::move(aCallback
));
1983 case ServiceWorkerOpArgs::TServiceWorkerTerminateWorkerOpArgs
:
1984 op
= MakeRefPtr
<TerminateServiceWorkerOp
>(std::move(aArgs
),
1985 std::move(aCallback
));
1987 case ServiceWorkerOpArgs::TServiceWorkerLifeCycleEventOpArgs
:
1988 op
= MakeRefPtr
<LifeCycleEventOp
>(std::move(aArgs
), std::move(aCallback
));
1990 case ServiceWorkerOpArgs::TServiceWorkerPushEventOpArgs
:
1991 op
= MakeRefPtr
<PushEventOp
>(std::move(aArgs
), std::move(aCallback
));
1993 case ServiceWorkerOpArgs::TServiceWorkerPushSubscriptionChangeEventOpArgs
:
1994 op
= MakeRefPtr
<PushSubscriptionChangeEventOp
>(std::move(aArgs
),
1995 std::move(aCallback
));
1997 case ServiceWorkerOpArgs::TServiceWorkerNotificationEventOpArgs
:
1998 op
= MakeRefPtr
<NotificationEventOp
>(std::move(aArgs
),
1999 std::move(aCallback
));
2001 case ServiceWorkerOpArgs::TServiceWorkerMessageEventOpArgs
:
2002 op
= MakeRefPtr
<MessageEventOp
>(std::move(aArgs
), std::move(aCallback
));
2004 case ServiceWorkerOpArgs::TParentToChildServiceWorkerFetchEventOpArgs
:
2005 op
= MakeRefPtr
<FetchEventOp
>(std::move(aArgs
), std::move(aCallback
));
2007 case ServiceWorkerOpArgs::TServiceWorkerExtensionAPIEventOpArgs
:
2008 op
= MakeRefPtr
<ExtensionAPIEventOp
>(std::move(aArgs
),
2009 std::move(aCallback
));
2011 case ServiceWorkerOpArgs::
2012 TServiceWorkerUpdateIsOnContentBlockingAllowListOpArgs
:
2013 op
= MakeRefPtr
<UpdateIsOnContentBlockingAllowListOp
>(
2014 std::move(aArgs
), std::move(aCallback
));
2017 MOZ_CRASH("Unknown Service Worker operation!");
2024 } // namespace mozilla::dom