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 "ServiceWorkerPrivate.h"
9 #include "ServiceWorkerCloneData.h"
10 #include "ServiceWorkerManager.h"
11 #include "nsContentUtils.h"
12 #include "nsICacheInfoChannel.h"
13 #include "nsIHttpChannelInternal.h"
14 #include "nsIHttpHeaderVisitor.h"
16 #include "nsINetworkInterceptController.h"
17 #include "nsIPushErrorReporter.h"
18 #include "nsISupportsImpl.h"
19 #include "nsITimedChannel.h"
20 #include "nsIUploadChannel2.h"
21 #include "nsNetUtil.h"
22 #include "nsProxyRelease.h"
23 #include "nsQueryObject.h"
24 #include "nsStreamUtils.h"
25 #include "nsStringStream.h"
26 #include "mozilla/Assertions.h"
27 #include "mozilla/CycleCollectedJSContext.h" // for MicroTaskRunnable
28 #include "mozilla/JSObjectHolder.h"
29 #include "mozilla/dom/Client.h"
30 #include "mozilla/dom/ClientIPCTypes.h"
31 #include "mozilla/dom/FetchUtil.h"
32 #include "mozilla/dom/IndexedDatabaseManager.h"
33 #include "mozilla/dom/InternalHeaders.h"
34 #include "mozilla/dom/NotificationEvent.h"
35 #include "mozilla/dom/PromiseNativeHandler.h"
36 #include "mozilla/dom/PushEventBinding.h"
37 #include "mozilla/dom/RequestBinding.h"
38 #include "mozilla/dom/WorkerDebugger.h"
39 #include "mozilla/dom/WorkerRef.h"
40 #include "mozilla/dom/WorkerRunnable.h"
41 #include "mozilla/dom/WorkerScope.h"
42 #include "mozilla/dom/ipc/StructuredCloneData.h"
43 #include "mozilla/net/CookieSettings.h"
44 #include "mozilla/net/NeckoChannelParams.h"
45 #include "mozilla/StaticPrefs.h"
46 #include "mozilla/Unused.h"
47 #include "nsIReferrerInfo.h"
49 using namespace mozilla
;
50 using namespace mozilla::dom
;
55 using mozilla::ipc::PrincipalInfo
;
57 NS_IMPL_CYCLE_COLLECTING_NATIVE_ADDREF(ServiceWorkerPrivate
)
58 NS_IMPL_CYCLE_COLLECTING_NATIVE_RELEASE(ServiceWorkerPrivate
)
59 NS_IMPL_CYCLE_COLLECTION(ServiceWorkerPrivate
, mSupportsArray
)
60 NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(ServiceWorkerPrivate
, AddRef
)
61 NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(ServiceWorkerPrivate
, Release
)
63 // Tracks the "dom.serviceWorkers.disable_open_click_delay" preference. Modified
64 // on main thread, read on worker threads.
65 // It is updated every time a "notificationclick" event is dispatched. While
66 // this is done without synchronization, at the worst, the thread will just get
67 // an older value within which a popup is allowed to be displayed, which will
68 // still be a valid value since it was set prior to dispatching the runnable.
69 Atomic
<uint32_t> gDOMDisableOpenClickDelay(0);
71 // Used to keep track of pending waitUntil as well as in-flight extendable
72 // events. When the last token is released, we attempt to terminate the worker.
73 class KeepAliveToken final
: public nsISupports
{
77 explicit KeepAliveToken(ServiceWorkerPrivate
* aPrivate
) : mPrivate(aPrivate
) {
78 MOZ_ASSERT(NS_IsMainThread());
85 MOZ_ASSERT(NS_IsMainThread());
86 mPrivate
->ReleaseToken();
89 RefPtr
<ServiceWorkerPrivate
> mPrivate
;
92 NS_IMPL_ISUPPORTS0(KeepAliveToken
)
94 ServiceWorkerPrivate::ServiceWorkerPrivate(ServiceWorkerInfo
* aInfo
)
95 : mInfo(aInfo
), mDebuggerCount(0), mTokenCount(0) {
96 MOZ_ASSERT(NS_IsMainThread());
99 mIdleWorkerTimer
= NS_NewTimer();
100 MOZ_ASSERT(mIdleWorkerTimer
);
103 ServiceWorkerPrivate::~ServiceWorkerPrivate() {
104 MOZ_ASSERT(!mWorkerPrivate
);
105 MOZ_ASSERT(!mTokenCount
);
107 MOZ_ASSERT(mSupportsArray
.IsEmpty());
109 mIdleWorkerTimer
->Cancel();
114 class CheckScriptEvaluationWithCallback final
: public WorkerRunnable
{
115 nsMainThreadPtrHandle
<ServiceWorkerPrivate
> mServiceWorkerPrivate
;
116 nsMainThreadPtrHandle
<KeepAliveToken
> mKeepAliveToken
;
118 // The script evaluation result must be reported even if the runnable
120 RefPtr
<LifeCycleEventCallback
> mScriptEvaluationCallback
;
127 CheckScriptEvaluationWithCallback(
128 WorkerPrivate
* aWorkerPrivate
,
129 ServiceWorkerPrivate
* aServiceWorkerPrivate
,
130 KeepAliveToken
* aKeepAliveToken
,
131 LifeCycleEventCallback
* aScriptEvaluationCallback
)
132 : WorkerRunnable(aWorkerPrivate
),
133 mServiceWorkerPrivate(new nsMainThreadPtrHolder
<ServiceWorkerPrivate
>(
134 "CheckScriptEvaluationWithCallback::mServiceWorkerPrivate",
135 aServiceWorkerPrivate
)),
136 mKeepAliveToken(new nsMainThreadPtrHolder
<KeepAliveToken
>(
137 "CheckScriptEvaluationWithCallback::mKeepAliveToken",
139 mScriptEvaluationCallback(aScriptEvaluationCallback
)
145 MOZ_ASSERT(NS_IsMainThread());
148 ~CheckScriptEvaluationWithCallback() { MOZ_ASSERT(mDone
); }
150 bool WorkerRun(JSContext
* aCx
, WorkerPrivate
* aWorkerPrivate
) override
{
151 aWorkerPrivate
->AssertIsOnWorkerThread();
153 bool fetchHandlerWasAdded
= aWorkerPrivate
->FetchHandlerWasAdded();
154 nsCOMPtr
<nsIRunnable
> runnable
= NewRunnableMethod
<bool>(
155 "dom::CheckScriptEvaluationWithCallback::ReportFetchFlag", this,
156 &CheckScriptEvaluationWithCallback::ReportFetchFlag
,
157 fetchHandlerWasAdded
);
158 aWorkerPrivate
->DispatchToMainThread(runnable
.forget());
160 ReportScriptEvaluationResult(
161 aWorkerPrivate
->WorkerScriptExecutedSuccessfully());
166 void ReportFetchFlag(bool aFetchHandlerWasAdded
) {
167 MOZ_ASSERT(NS_IsMainThread());
168 mServiceWorkerPrivate
->SetHandlesFetch(aFetchHandlerWasAdded
);
171 nsresult
Cancel() override
{
172 ReportScriptEvaluationResult(false);
173 return WorkerRunnable::Cancel();
177 void ReportScriptEvaluationResult(bool aScriptEvaluationResult
) {
181 mScriptEvaluationCallback
->SetResult(aScriptEvaluationResult
);
183 mWorkerPrivate
->DispatchToMainThread(mScriptEvaluationCallback
));
187 } // anonymous namespace
189 nsresult
ServiceWorkerPrivate::CheckScriptEvaluation(
190 LifeCycleEventCallback
* aScriptEvaluationCallback
) {
191 nsresult rv
= SpawnWorkerIfNeeded(LifeCycleEvent
);
192 NS_ENSURE_SUCCESS(rv
, rv
);
194 RefPtr
<KeepAliveToken
> token
= CreateEventKeepAliveToken();
195 RefPtr
<WorkerRunnable
> r
= new CheckScriptEvaluationWithCallback(
196 mWorkerPrivate
, this, token
, aScriptEvaluationCallback
);
197 if (NS_WARN_IF(!r
->Dispatch())) {
198 return NS_ERROR_FAILURE
;
206 enum ExtendableEventResult
{ Rejected
= 0, Resolved
};
208 class ExtendableEventCallback
{
210 virtual void FinishedWithResult(ExtendableEventResult aResult
) = 0;
212 NS_INLINE_DECL_PURE_VIRTUAL_REFCOUNTING
215 class KeepAliveHandler final
: public ExtendableEvent::ExtensionsHandler
,
216 public PromiseNativeHandler
{
217 // This class manages lifetime extensions added by calling WaitUntil()
218 // or RespondWith(). We allow new extensions as long as we still hold
219 // |mKeepAliveToken|. Once the last promise was settled, we queue a microtask
220 // which releases the token and prevents further extensions. By doing this,
221 // we give other pending microtasks a chance to continue adding extensions.
223 RefPtr
<StrongWorkerRef
> mWorkerRef
;
224 nsMainThreadPtrHandle
<KeepAliveToken
> mKeepAliveToken
;
226 // We start holding a self reference when the first extension promise is
227 // added. As far as I can tell, the only case where this is useful is when
228 // we're waiting indefinitely on a promise that's no longer reachable
229 // and will never be settled.
230 // The cycle is broken when the last promise was settled or when the
231 // worker is shutting down.
232 RefPtr
<KeepAliveHandler
> mSelfRef
;
234 // Called when the last promise was settled.
235 RefPtr
<ExtendableEventCallback
> mCallback
;
237 uint32_t mPendingPromisesCount
;
239 // We don't actually care what values the promises resolve to, only whether
240 // any of them were rejected.
246 explicit KeepAliveHandler(
247 const nsMainThreadPtrHandle
<KeepAliveToken
>& aKeepAliveToken
,
248 ExtendableEventCallback
* aCallback
)
249 : mKeepAliveToken(aKeepAliveToken
),
250 mCallback(aCallback
),
251 mPendingPromisesCount(0),
253 MOZ_ASSERT(mKeepAliveToken
);
257 MOZ_ASSERT(IsCurrentThreadRunningWorker());
259 RefPtr
<KeepAliveHandler
> self
= this;
260 mWorkerRef
= StrongWorkerRef::Create(GetCurrentThreadWorkerPrivate(),
262 [self
]() { self
->MaybeCleanup(); });
264 if (NS_WARN_IF(!mWorkerRef
)) {
271 bool WaitOnPromise(Promise
& aPromise
) override
{
272 if (!mKeepAliveToken
) {
273 MOZ_ASSERT(!mSelfRef
, "We shouldn't be holding a self reference!");
277 MOZ_ASSERT(!mPendingPromisesCount
);
281 ++mPendingPromisesCount
;
282 aPromise
.AppendNativeHandler(this);
287 void ResolvedCallback(JSContext
* aCx
, JS::Handle
<JS::Value
> aValue
) override
{
288 RemovePromise(Resolved
);
291 void RejectedCallback(JSContext
* aCx
, JS::Handle
<JS::Value
> aValue
) override
{
292 RemovePromise(Rejected
);
296 MOZ_ASSERT(IsCurrentThreadRunningWorker());
298 if (mPendingPromisesCount
|| !mKeepAliveToken
) {
302 mCallback
->FinishedWithResult(mRejected
? Rejected
: Resolved
);
309 ~KeepAliveHandler() { MaybeCleanup(); }
311 void MaybeCleanup() {
312 MOZ_ASSERT(IsCurrentThreadRunningWorker());
314 if (!mKeepAliveToken
) {
318 mWorkerRef
= nullptr;
319 mKeepAliveToken
= nullptr;
323 class MaybeDoneRunner
: public MicroTaskRunnable
{
325 explicit MaybeDoneRunner(KeepAliveHandler
* aHandler
) : mHandler(aHandler
) {}
326 virtual void Run(AutoSlowOperation
& aAso
) override
{
327 mHandler
->MaybeDone();
330 RefPtr
<KeepAliveHandler
> mHandler
;
333 void RemovePromise(ExtendableEventResult aResult
) {
334 MOZ_ASSERT(IsCurrentThreadRunningWorker());
335 MOZ_DIAGNOSTIC_ASSERT(mPendingPromisesCount
> 0);
337 // Note: mSelfRef and mKeepAliveToken can be nullptr here
338 // if MaybeCleanup() was called just before a promise
339 // settled. This can happen, for example, if the
340 // worker thread is being terminated for running too
341 // long, browser shutdown, etc.
343 mRejected
|= (aResult
== Rejected
);
345 --mPendingPromisesCount
;
346 if (mPendingPromisesCount
) {
350 CycleCollectedJSContext
* cx
= CycleCollectedJSContext::Get();
353 RefPtr
<MaybeDoneRunner
> r
= new MaybeDoneRunner(this);
354 cx
->DispatchToMicroTask(r
.forget());
358 NS_IMPL_ISUPPORTS0(KeepAliveHandler
)
360 class RegistrationUpdateRunnable
: public Runnable
{
361 nsMainThreadPtrHandle
<ServiceWorkerRegistrationInfo
> mRegistration
;
362 const bool mNeedTimeCheck
;
365 RegistrationUpdateRunnable(
366 nsMainThreadPtrHandle
<ServiceWorkerRegistrationInfo
>& aRegistration
,
368 : Runnable("dom::RegistrationUpdateRunnable"),
369 mRegistration(aRegistration
),
370 mNeedTimeCheck(aNeedTimeCheck
) {
371 MOZ_DIAGNOSTIC_ASSERT(mRegistration
);
376 if (mNeedTimeCheck
) {
377 mRegistration
->MaybeScheduleTimeCheckAndUpdate();
379 mRegistration
->MaybeScheduleUpdate();
385 class ExtendableEventWorkerRunnable
: public WorkerRunnable
{
387 nsMainThreadPtrHandle
<KeepAliveToken
> mKeepAliveToken
;
390 ExtendableEventWorkerRunnable(WorkerPrivate
* aWorkerPrivate
,
391 KeepAliveToken
* aKeepAliveToken
)
392 : WorkerRunnable(aWorkerPrivate
) {
393 MOZ_ASSERT(NS_IsMainThread());
394 MOZ_ASSERT(aWorkerPrivate
);
395 MOZ_ASSERT(aKeepAliveToken
);
397 mKeepAliveToken
= new nsMainThreadPtrHolder
<KeepAliveToken
>(
398 "ExtendableEventWorkerRunnable::mKeepAliveToken", aKeepAliveToken
);
401 nsresult
DispatchExtendableEventOnWorkerScope(
402 JSContext
* aCx
, WorkerGlobalScope
* aWorkerScope
, ExtendableEvent
* aEvent
,
403 ExtendableEventCallback
* aCallback
) {
404 MOZ_ASSERT(aWorkerScope
);
406 nsCOMPtr
<nsIGlobalObject
> sgo
= aWorkerScope
;
407 WidgetEvent
* internalEvent
= aEvent
->WidgetEventPtr();
409 RefPtr
<KeepAliveHandler
> keepAliveHandler
=
410 new KeepAliveHandler(mKeepAliveToken
, aCallback
);
411 if (NS_WARN_IF(!keepAliveHandler
->Init())) {
412 return NS_ERROR_FAILURE
;
415 // This must always be set *before* dispatching the event, otherwise
416 // waitUntil calls will fail.
417 aEvent
->SetKeepAliveHandler(keepAliveHandler
);
420 aWorkerScope
->DispatchEvent(*aEvent
, result
);
421 if (NS_WARN_IF(result
.Failed())) {
422 result
.SuppressException();
423 return NS_ERROR_FAILURE
;
426 // [[ If e’s extend lifetime promises is empty, unset e’s extensions allowed
427 // flag and abort these steps. ]]
428 keepAliveHandler
->MaybeDone();
430 // We don't block the event when getting an exception but still report the
432 // Report exception message. Note: This will not stop the event.
433 if (internalEvent
->mFlags
.mExceptionWasRaised
) {
434 result
.SuppressException();
435 return NS_ERROR_XPC_JS_THREW_EXCEPTION
;
442 class SendMessageEventRunnable final
: public ExtendableEventWorkerRunnable
{
443 const ClientInfoAndState mClientInfoAndState
;
444 RefPtr
<ServiceWorkerCloneData
> mData
;
447 SendMessageEventRunnable(WorkerPrivate
* aWorkerPrivate
,
448 KeepAliveToken
* aKeepAliveToken
,
449 const ClientInfoAndState
& aClientInfoAndState
,
450 RefPtr
<ServiceWorkerCloneData
>&& aData
)
451 : ExtendableEventWorkerRunnable(aWorkerPrivate
, aKeepAliveToken
),
452 mClientInfoAndState(aClientInfoAndState
),
453 mData(std::move(aData
)) {
454 MOZ_ASSERT(NS_IsMainThread());
455 MOZ_DIAGNOSTIC_ASSERT(mData
);
458 bool WorkerRun(JSContext
* aCx
, WorkerPrivate
* aWorkerPrivate
) override
{
459 JS::Rooted
<JS::Value
> messageData(aCx
);
460 nsCOMPtr
<nsIGlobalObject
> sgo
= aWorkerPrivate
->GlobalScope();
462 mData
->Read(aCx
, &messageData
, rv
);
464 // If deserialization fails, we will fire a messageerror event
465 bool deserializationFailed
= rv
.ErrorCodeIs(NS_ERROR_DOM_DATA_CLONE_ERR
);
467 if (!deserializationFailed
&& NS_WARN_IF(rv
.Failed())) {
471 Sequence
<OwningNonNull
<MessagePort
>> ports
;
472 if (!mData
->TakeTransferredPortsAsSequence(ports
)) {
476 RootedDictionary
<ExtendableMessageEventInit
> init(aCx
);
478 init
.mBubbles
= false;
479 init
.mCancelable
= false;
481 // On a messageerror event, we disregard ports:
482 // https://w3c.github.io/ServiceWorker/#service-worker-postmessage
483 if (!deserializationFailed
) {
484 init
.mData
= messageData
;
488 init
.mSource
.SetValue().SetAsClient() =
489 new Client(sgo
, mClientInfoAndState
);
492 RefPtr
<EventTarget
> target
= aWorkerPrivate
->GlobalScope();
493 RefPtr
<ExtendableMessageEvent
> extendableEvent
=
494 ExtendableMessageEvent::Constructor(
496 deserializationFailed
? NS_LITERAL_STRING("messageerror")
497 : NS_LITERAL_STRING("message"),
499 if (NS_WARN_IF(rv
.Failed())) {
500 rv
.SuppressException();
504 extendableEvent
->SetTrusted(true);
506 return NS_SUCCEEDED(DispatchExtendableEventOnWorkerScope(
507 aCx
, aWorkerPrivate
->GlobalScope(), extendableEvent
, nullptr));
511 } // anonymous namespace
513 nsresult
ServiceWorkerPrivate::SendMessageEvent(
514 RefPtr
<ServiceWorkerCloneData
>&& aData
,
515 const ClientInfoAndState
& aClientInfoAndState
) {
516 MOZ_ASSERT(NS_IsMainThread());
518 nsresult rv
= SpawnWorkerIfNeeded(MessageEvent
);
519 NS_ENSURE_SUCCESS(rv
, rv
);
521 RefPtr
<KeepAliveToken
> token
= CreateEventKeepAliveToken();
522 RefPtr
<SendMessageEventRunnable
> runnable
= new SendMessageEventRunnable(
523 mWorkerPrivate
, token
, aClientInfoAndState
, std::move(aData
));
525 if (!runnable
->Dispatch()) {
526 return NS_ERROR_FAILURE
;
534 // Handle functional event
535 // 9.9.7 If the time difference in seconds calculated by the current time minus
536 // registration's last update check time is greater than 86400, invoke Soft
538 class ExtendableFunctionalEventWorkerRunnable
539 : public ExtendableEventWorkerRunnable
{
541 nsMainThreadPtrHandle
<ServiceWorkerRegistrationInfo
> mRegistration
;
544 ExtendableFunctionalEventWorkerRunnable(
545 WorkerPrivate
* aWorkerPrivate
, KeepAliveToken
* aKeepAliveToken
,
546 nsMainThreadPtrHandle
<ServiceWorkerRegistrationInfo
>& aRegistration
)
547 : ExtendableEventWorkerRunnable(aWorkerPrivate
, aKeepAliveToken
),
548 mRegistration(aRegistration
) {
549 MOZ_DIAGNOSTIC_ASSERT(aRegistration
);
552 void PostRun(JSContext
* aCx
, WorkerPrivate
* aWorkerPrivate
,
553 bool aRunResult
) override
{
554 // Sub-class PreRun() or WorkerRun() methods could clear our mRegistration.
556 nsCOMPtr
<nsIRunnable
> runnable
=
557 new RegistrationUpdateRunnable(mRegistration
, true /* time check */);
558 aWorkerPrivate
->DispatchToMainThread(runnable
.forget());
561 ExtendableEventWorkerRunnable::PostRun(aCx
, aWorkerPrivate
, aRunResult
);
566 * Fires 'install' event on the ServiceWorkerGlobalScope. Modifies busy count
567 * since it fires the event. This is ok since there can't be nested
568 * ServiceWorkers, so the parent thread -> worker thread requirement for
569 * runnables is satisfied.
571 class LifecycleEventWorkerRunnable
: public ExtendableEventWorkerRunnable
{
573 RefPtr
<LifeCycleEventCallback
> mCallback
;
576 LifecycleEventWorkerRunnable(WorkerPrivate
* aWorkerPrivate
,
577 KeepAliveToken
* aToken
,
578 const nsAString
& aEventName
,
579 LifeCycleEventCallback
* aCallback
)
580 : ExtendableEventWorkerRunnable(aWorkerPrivate
, aToken
),
581 mEventName(aEventName
),
582 mCallback(aCallback
) {
583 MOZ_ASSERT(NS_IsMainThread());
586 bool WorkerRun(JSContext
* aCx
, WorkerPrivate
* aWorkerPrivate
) override
{
587 MOZ_ASSERT(aWorkerPrivate
);
588 return DispatchLifecycleEvent(aCx
, aWorkerPrivate
);
591 nsresult
Cancel() override
{
592 mCallback
->SetResult(false);
593 MOZ_ALWAYS_SUCCEEDS(mWorkerPrivate
->DispatchToMainThread(mCallback
));
595 return WorkerRunnable::Cancel();
599 bool DispatchLifecycleEvent(JSContext
* aCx
, WorkerPrivate
* aWorkerPrivate
);
603 * Used to handle ExtendableEvent::waitUntil() and catch abnormal worker
604 * termination during the execution of life cycle events. It is responsible
605 * with advancing the job queue for install/activate tasks.
607 class LifeCycleEventWatcher final
: public ExtendableEventCallback
{
608 RefPtr
<StrongWorkerRef
> mWorkerRef
;
609 RefPtr
<LifeCycleEventCallback
> mCallback
;
611 ~LifeCycleEventWatcher() {
612 // XXXcatalinb: If all the promises passed to waitUntil go out of scope,
613 // the resulting Promise.all will be cycle collected and it will drop its
614 // native handlers (including this object). Instead of waiting for a timeout
615 // we report the failure now.
620 NS_INLINE_DECL_REFCOUNTING(LifeCycleEventWatcher
, override
)
622 explicit LifeCycleEventWatcher(LifeCycleEventCallback
* aCallback
)
623 : mCallback(aCallback
) {
624 MOZ_ASSERT(IsCurrentThreadRunningWorker());
628 WorkerPrivate
* workerPrivate
= GetCurrentThreadWorkerPrivate();
629 MOZ_ASSERT(workerPrivate
);
631 // We need to listen for worker termination in case the event handler
632 // never completes or never resolves the waitUntil promise. There are
633 // two possible scenarios:
634 // 1. The keepAlive token expires and the worker is terminated, in which
635 // case the registration/update promise will be rejected
636 // 2. A new service worker is registered which will terminate the current
637 // installing worker.
638 RefPtr
<LifeCycleEventWatcher
> self
= this;
640 StrongWorkerRef::Create(workerPrivate
, "LifeCycleEventWatcher",
641 [self
]() { self
->ReportResult(false); });
642 if (NS_WARN_IF(!mWorkerRef
)) {
643 mCallback
->SetResult(false);
644 // Using DispatchToMainThreadForMessaging so that state update on
645 // the main thread doesn't happen too soon.
646 nsresult rv
= workerPrivate
->DispatchToMainThreadForMessaging(mCallback
);
647 Unused
<< NS_WARN_IF(NS_FAILED(rv
));
654 void ReportResult(bool aResult
) {
655 MOZ_ASSERT(IsCurrentThreadRunningWorker());
661 mCallback
->SetResult(aResult
);
662 // Using DispatchToMainThreadForMessaging so that state update on
663 // the main thread doesn't happen too soon.
665 mWorkerRef
->Private()->DispatchToMainThreadForMessaging(mCallback
);
666 if (NS_WARN_IF(NS_FAILED(rv
))) {
667 MOZ_CRASH("Failed to dispatch life cycle event handler.");
670 mWorkerRef
= nullptr;
673 void FinishedWithResult(ExtendableEventResult aResult
) override
{
674 MOZ_ASSERT(IsCurrentThreadRunningWorker());
675 ReportResult(aResult
== Resolved
);
677 // Note, all WaitUntil() rejections are reported to client consoles
678 // by the WaitUntilHandler in ServiceWorkerEvents. This ensures that
679 // errors in non-lifecycle events like FetchEvent and PushEvent are
680 // reported properly.
684 bool LifecycleEventWorkerRunnable::DispatchLifecycleEvent(
685 JSContext
* aCx
, WorkerPrivate
* aWorkerPrivate
) {
686 aWorkerPrivate
->AssertIsOnWorkerThread();
687 MOZ_ASSERT(aWorkerPrivate
->IsServiceWorker());
689 RefPtr
<ExtendableEvent
> event
;
690 RefPtr
<EventTarget
> target
= aWorkerPrivate
->GlobalScope();
692 if (mEventName
.EqualsASCII("install") || mEventName
.EqualsASCII("activate")) {
693 ExtendableEventInit init
;
694 init
.mBubbles
= false;
695 init
.mCancelable
= false;
696 event
= ExtendableEvent::Constructor(target
, mEventName
, init
);
698 MOZ_CRASH("Unexpected lifecycle event");
701 event
->SetTrusted(true);
703 // It is important to initialize the watcher before actually dispatching
704 // the event in order to catch worker termination while the event handler
705 // is still executing. This can happen with infinite loops, for example.
706 RefPtr
<LifeCycleEventWatcher
> watcher
= new LifeCycleEventWatcher(mCallback
);
708 if (!watcher
->Init()) {
712 nsresult rv
= DispatchExtendableEventOnWorkerScope(
713 aCx
, aWorkerPrivate
->GlobalScope(), event
, watcher
);
714 // Do not fail event processing when an exception is thrown.
715 if (NS_FAILED(rv
) && rv
!= NS_ERROR_XPC_JS_THREW_EXCEPTION
) {
716 watcher
->ReportResult(false);
722 } // anonymous namespace
724 nsresult
ServiceWorkerPrivate::SendLifeCycleEvent(
725 const nsAString
& aEventType
, LifeCycleEventCallback
* aCallback
) {
726 nsresult rv
= SpawnWorkerIfNeeded(LifeCycleEvent
);
727 NS_ENSURE_SUCCESS(rv
, rv
);
729 RefPtr
<KeepAliveToken
> token
= CreateEventKeepAliveToken();
730 RefPtr
<WorkerRunnable
> r
= new LifecycleEventWorkerRunnable(
731 mWorkerPrivate
, token
, aEventType
, aCallback
);
732 if (NS_WARN_IF(!r
->Dispatch())) {
733 return NS_ERROR_FAILURE
;
741 class PushErrorReporter final
: public ExtendableEventCallback
{
742 WorkerPrivate
* mWorkerPrivate
;
745 ~PushErrorReporter() {}
748 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(PushErrorReporter
, override
)
750 PushErrorReporter(WorkerPrivate
* aWorkerPrivate
, const nsAString
& aMessageId
)
751 : mWorkerPrivate(aWorkerPrivate
), mMessageId(aMessageId
) {
752 mWorkerPrivate
->AssertIsOnWorkerThread();
755 void FinishedWithResult(ExtendableEventResult aResult
) override
{
756 if (aResult
== Rejected
) {
757 Report(nsIPushErrorReporter::DELIVERY_UNHANDLED_REJECTION
);
762 uint16_t aReason
= nsIPushErrorReporter::DELIVERY_INTERNAL_ERROR
) {
763 WorkerPrivate
* workerPrivate
= mWorkerPrivate
;
764 mWorkerPrivate
->AssertIsOnWorkerThread();
766 if (NS_WARN_IF(aReason
> nsIPushErrorReporter::DELIVERY_INTERNAL_ERROR
) ||
767 mMessageId
.IsEmpty()) {
770 nsCOMPtr
<nsIRunnable
> runnable
= NewRunnableMethod
<uint16_t>(
771 "dom::PushErrorReporter::ReportOnMainThread", this,
772 &PushErrorReporter::ReportOnMainThread
, aReason
);
774 NS_SUCCEEDED(workerPrivate
->DispatchToMainThread(runnable
.forget())));
777 void ReportOnMainThread(uint16_t aReason
) {
778 MOZ_ASSERT(NS_IsMainThread());
779 nsCOMPtr
<nsIPushErrorReporter
> reporter
=
780 do_GetService("@mozilla.org/push/Service;1");
782 nsresult rv
= reporter
->ReportDeliveryError(mMessageId
, aReason
);
783 Unused
<< NS_WARN_IF(NS_FAILED(rv
));
788 class SendPushEventRunnable final
789 : public ExtendableFunctionalEventWorkerRunnable
{
791 Maybe
<nsTArray
<uint8_t>> mData
;
794 SendPushEventRunnable(
795 WorkerPrivate
* aWorkerPrivate
, KeepAliveToken
* aKeepAliveToken
,
796 const nsAString
& aMessageId
, const Maybe
<nsTArray
<uint8_t>>& aData
,
797 nsMainThreadPtrHandle
<ServiceWorkerRegistrationInfo
> aRegistration
)
798 : ExtendableFunctionalEventWorkerRunnable(aWorkerPrivate
, aKeepAliveToken
,
800 mMessageId(aMessageId
),
802 MOZ_ASSERT(NS_IsMainThread());
803 MOZ_ASSERT(aWorkerPrivate
);
804 MOZ_ASSERT(aWorkerPrivate
->IsServiceWorker());
807 bool WorkerRun(JSContext
* aCx
, WorkerPrivate
* aWorkerPrivate
) override
{
808 MOZ_ASSERT(aWorkerPrivate
);
809 GlobalObject
globalObj(aCx
, aWorkerPrivate
->GlobalScope()->GetWrapper());
811 RefPtr
<PushErrorReporter
> errorReporter
=
812 new PushErrorReporter(aWorkerPrivate
, mMessageId
);
816 const nsTArray
<uint8_t>& bytes
= mData
.ref();
818 Uint8Array::Create(aCx
, bytes
.Length(), bytes
.Elements());
820 errorReporter
->Report();
823 pei
.mData
.Construct().SetAsArrayBufferView().Init(data
);
825 pei
.mBubbles
= false;
826 pei
.mCancelable
= false;
829 RefPtr
<PushEvent
> event
= PushEvent::Constructor(
830 globalObj
, NS_LITERAL_STRING("push"), pei
, result
);
831 if (NS_WARN_IF(result
.Failed())) {
832 result
.SuppressException();
833 errorReporter
->Report();
836 event
->SetTrusted(true);
838 nsresult rv
= DispatchExtendableEventOnWorkerScope(
839 aCx
, aWorkerPrivate
->GlobalScope(), event
, errorReporter
);
841 // We don't cancel WorkerPrivate when catching an excetpion.
842 errorReporter
->Report(nsIPushErrorReporter::DELIVERY_UNCAUGHT_EXCEPTION
);
849 class SendPushSubscriptionChangeEventRunnable final
850 : public ExtendableEventWorkerRunnable
{
852 explicit SendPushSubscriptionChangeEventRunnable(
853 WorkerPrivate
* aWorkerPrivate
, KeepAliveToken
* aKeepAliveToken
)
854 : ExtendableEventWorkerRunnable(aWorkerPrivate
, aKeepAliveToken
) {
855 MOZ_ASSERT(NS_IsMainThread());
856 MOZ_ASSERT(aWorkerPrivate
);
857 MOZ_ASSERT(aWorkerPrivate
->IsServiceWorker());
860 bool WorkerRun(JSContext
* aCx
, WorkerPrivate
* aWorkerPrivate
) override
{
861 MOZ_ASSERT(aWorkerPrivate
);
863 RefPtr
<EventTarget
> target
= aWorkerPrivate
->GlobalScope();
865 ExtendableEventInit init
;
866 init
.mBubbles
= false;
867 init
.mCancelable
= false;
869 RefPtr
<ExtendableEvent
> event
= ExtendableEvent::Constructor(
870 target
, NS_LITERAL_STRING("pushsubscriptionchange"), init
);
872 event
->SetTrusted(true);
874 DispatchExtendableEventOnWorkerScope(aCx
, aWorkerPrivate
->GlobalScope(),
881 } // anonymous namespace
883 nsresult
ServiceWorkerPrivate::SendPushEvent(
884 const nsAString
& aMessageId
, const Maybe
<nsTArray
<uint8_t>>& aData
,
885 ServiceWorkerRegistrationInfo
* aRegistration
) {
886 nsresult rv
= SpawnWorkerIfNeeded(PushEvent
);
887 NS_ENSURE_SUCCESS(rv
, rv
);
889 RefPtr
<KeepAliveToken
> token
= CreateEventKeepAliveToken();
891 nsMainThreadPtrHandle
<ServiceWorkerRegistrationInfo
> regInfo(
892 new nsMainThreadPtrHolder
<ServiceWorkerRegistrationInfo
>(
893 "ServiceWorkerRegistrationInfoProxy", aRegistration
, false));
895 RefPtr
<WorkerRunnable
> r
= new SendPushEventRunnable(
896 mWorkerPrivate
, token
, aMessageId
, aData
, regInfo
);
898 if (mInfo
->State() == ServiceWorkerState::Activating
) {
899 mPendingFunctionalEvents
.AppendElement(r
.forget());
903 MOZ_ASSERT(mInfo
->State() == ServiceWorkerState::Activated
);
905 if (NS_WARN_IF(!r
->Dispatch())) {
906 return NS_ERROR_FAILURE
;
912 nsresult
ServiceWorkerPrivate::SendPushSubscriptionChangeEvent() {
913 nsresult rv
= SpawnWorkerIfNeeded(PushSubscriptionChangeEvent
);
914 NS_ENSURE_SUCCESS(rv
, rv
);
916 RefPtr
<KeepAliveToken
> token
= CreateEventKeepAliveToken();
917 RefPtr
<WorkerRunnable
> r
=
918 new SendPushSubscriptionChangeEventRunnable(mWorkerPrivate
, token
);
919 if (NS_WARN_IF(!r
->Dispatch())) {
920 return NS_ERROR_FAILURE
;
928 class AllowWindowInteractionHandler final
: public ExtendableEventCallback
,
929 public nsITimerCallback
,
931 nsCOMPtr
<nsITimer
> mTimer
;
932 RefPtr
<StrongWorkerRef
> mWorkerRef
;
934 ~AllowWindowInteractionHandler() {
935 // We must either fail to initialize or call ClearWindowAllowed.
936 MOZ_DIAGNOSTIC_ASSERT(!mTimer
);
937 MOZ_DIAGNOSTIC_ASSERT(!mWorkerRef
);
940 void ClearWindowAllowed(WorkerPrivate
* aWorkerPrivate
) {
941 MOZ_ASSERT(aWorkerPrivate
);
942 aWorkerPrivate
->AssertIsOnWorkerThread();
948 // XXXcatalinb: This *might* be executed after the global was unrooted, in
949 // which case GlobalScope() will return null. Making the check here just
951 WorkerGlobalScope
* globalScope
= aWorkerPrivate
->GlobalScope();
956 globalScope
->ConsumeWindowInteraction();
960 mWorkerRef
= nullptr;
963 void StartClearWindowTimer(WorkerPrivate
* aWorkerPrivate
) {
964 MOZ_ASSERT(aWorkerPrivate
);
965 aWorkerPrivate
->AssertIsOnWorkerThread();
969 nsCOMPtr
<nsITimer
> timer
=
970 NS_NewTimer(aWorkerPrivate
->ControlEventTarget());
971 if (NS_WARN_IF(!timer
)) {
975 MOZ_ASSERT(!mWorkerRef
);
976 RefPtr
<AllowWindowInteractionHandler
> self
= this;
977 mWorkerRef
= StrongWorkerRef::Create(
978 aWorkerPrivate
, "AllowWindowInteractionHandler", [self
]() {
979 // We could try to hold the worker alive until the timer fires, but
980 // other APIs are not likely to work in this partially shutdown state.
981 // We might as well let the worker thread exit.
982 self
->ClearWindowAllowed(self
->mWorkerRef
->Private());
989 aWorkerPrivate
->GlobalScope()->AllowWindowInteraction();
992 // We swap first and then initialize the timer so that even if initializing
993 // fails, we still clean the busy count and interaction count correctly.
994 // The timer can't be initialized before modifying the busy count since the
995 // timer thread could run and call the timeout but the worker may
996 // already be terminating and modifying the busy count could fail.
997 rv
= mTimer
->InitWithCallback(this, gDOMDisableOpenClickDelay
,
998 nsITimer::TYPE_ONE_SHOT
);
999 if (NS_WARN_IF(NS_FAILED(rv
))) {
1000 ClearWindowAllowed(aWorkerPrivate
);
1005 // nsITimerCallback virtual methods
1007 Notify(nsITimer
* aTimer
) override
{
1008 MOZ_DIAGNOSTIC_ASSERT(mTimer
== aTimer
);
1009 WorkerPrivate
* workerPrivate
= GetCurrentThreadWorkerPrivate();
1010 ClearWindowAllowed(workerPrivate
);
1014 // nsINamed virtual methods
1016 GetName(nsACString
& aName
) override
{
1017 aName
.AssignLiteral("AllowWindowInteractionHandler");
1022 NS_DECL_THREADSAFE_ISUPPORTS
1024 explicit AllowWindowInteractionHandler(WorkerPrivate
* aWorkerPrivate
) {
1025 StartClearWindowTimer(aWorkerPrivate
);
1028 void FinishedWithResult(ExtendableEventResult
/* aResult */) override
{
1029 WorkerPrivate
* workerPrivate
= GetCurrentThreadWorkerPrivate();
1030 ClearWindowAllowed(workerPrivate
);
1034 NS_IMPL_ISUPPORTS(AllowWindowInteractionHandler
, nsITimerCallback
, nsINamed
)
1036 class SendNotificationEventRunnable final
1037 : public ExtendableEventWorkerRunnable
{
1038 const nsString mEventName
;
1040 const nsString mTitle
;
1041 const nsString mDir
;
1042 const nsString mLang
;
1043 const nsString mBody
;
1044 const nsString mTag
;
1045 const nsString mIcon
;
1046 const nsString mData
;
1047 const nsString mBehavior
;
1048 const nsString mScope
;
1051 SendNotificationEventRunnable(WorkerPrivate
* aWorkerPrivate
,
1052 KeepAliveToken
* aKeepAliveToken
,
1053 const nsAString
& aEventName
,
1054 const nsAString
& aID
, const nsAString
& aTitle
,
1055 const nsAString
& aDir
, const nsAString
& aLang
,
1056 const nsAString
& aBody
, const nsAString
& aTag
,
1057 const nsAString
& aIcon
, const nsAString
& aData
,
1058 const nsAString
& aBehavior
,
1059 const nsAString
& aScope
)
1060 : ExtendableEventWorkerRunnable(aWorkerPrivate
, aKeepAliveToken
),
1061 mEventName(aEventName
),
1070 mBehavior(aBehavior
),
1072 MOZ_ASSERT(NS_IsMainThread());
1073 MOZ_ASSERT(aWorkerPrivate
);
1074 MOZ_ASSERT(aWorkerPrivate
->IsServiceWorker());
1077 bool WorkerRun(JSContext
* aCx
, WorkerPrivate
* aWorkerPrivate
) override
{
1078 MOZ_ASSERT(aWorkerPrivate
);
1080 RefPtr
<EventTarget
> target
= do_QueryObject(aWorkerPrivate
->GlobalScope());
1083 RefPtr
<Notification
> notification
= Notification::ConstructFromFields(
1084 aWorkerPrivate
->GlobalScope(), mID
, mTitle
, mDir
, mLang
, mBody
, mTag
,
1085 mIcon
, mData
, mScope
, result
);
1086 if (NS_WARN_IF(result
.Failed())) {
1090 NotificationEventInit nei
;
1091 nei
.mNotification
= notification
;
1092 nei
.mBubbles
= false;
1093 nei
.mCancelable
= false;
1095 RefPtr
<NotificationEvent
> event
=
1096 NotificationEvent::Constructor(target
, mEventName
, nei
, result
);
1097 if (NS_WARN_IF(result
.Failed())) {
1101 event
->SetTrusted(true);
1103 RefPtr
<AllowWindowInteractionHandler
> allowWindowInteraction
;
1104 if (mEventName
.EqualsLiteral(NOTIFICATION_CLICK_EVENT_NAME
)) {
1105 allowWindowInteraction
=
1106 new AllowWindowInteractionHandler(aWorkerPrivate
);
1109 nsresult rv
= DispatchExtendableEventOnWorkerScope(
1110 aCx
, aWorkerPrivate
->GlobalScope(), event
, allowWindowInteraction
);
1111 // Don't reject when catching an exception
1112 if (NS_FAILED(rv
) && rv
!= NS_ERROR_XPC_JS_THREW_EXCEPTION
&&
1113 allowWindowInteraction
) {
1114 allowWindowInteraction
->FinishedWithResult(Rejected
);
1123 nsresult
ServiceWorkerPrivate::SendNotificationEvent(
1124 const nsAString
& aEventName
, const nsAString
& aID
, const nsAString
& aTitle
,
1125 const nsAString
& aDir
, const nsAString
& aLang
, const nsAString
& aBody
,
1126 const nsAString
& aTag
, const nsAString
& aIcon
, const nsAString
& aData
,
1127 const nsAString
& aBehavior
, const nsAString
& aScope
) {
1129 if (aEventName
.EqualsLiteral(NOTIFICATION_CLICK_EVENT_NAME
)) {
1130 why
= NotificationClickEvent
;
1131 gDOMDisableOpenClickDelay
=
1132 Preferences::GetInt("dom.serviceWorkers.disable_open_click_delay");
1133 } else if (aEventName
.EqualsLiteral(NOTIFICATION_CLOSE_EVENT_NAME
)) {
1134 why
= NotificationCloseEvent
;
1136 MOZ_ASSERT_UNREACHABLE("Invalid notification event name");
1137 return NS_ERROR_FAILURE
;
1140 nsresult rv
= SpawnWorkerIfNeeded(why
);
1141 NS_ENSURE_SUCCESS(rv
, rv
);
1143 RefPtr
<KeepAliveToken
> token
= CreateEventKeepAliveToken();
1145 RefPtr
<WorkerRunnable
> r
= new SendNotificationEventRunnable(
1146 mWorkerPrivate
, token
, aEventName
, aID
, aTitle
, aDir
, aLang
, aBody
, aTag
,
1147 aIcon
, aData
, aBehavior
, aScope
);
1148 if (NS_WARN_IF(!r
->Dispatch())) {
1149 return NS_ERROR_FAILURE
;
1157 // Inheriting ExtendableEventWorkerRunnable so that the worker is not terminated
1158 // while handling the fetch event, though that's very unlikely.
1159 class FetchEventRunnable
: public ExtendableFunctionalEventWorkerRunnable
,
1160 public nsIHttpHeaderVisitor
{
1161 nsMainThreadPtrHandle
<nsIInterceptedChannel
> mInterceptedChannel
;
1162 const nsCString mScriptSpec
;
1163 nsTArray
<nsCString
> mHeaderNames
;
1164 nsTArray
<nsCString
> mHeaderValues
;
1166 nsCString mFragment
;
1169 nsString mResultingClientId
;
1171 bool mMarkLaunchServiceWorkerEnd
;
1172 RequestCache mCacheMode
;
1173 RequestMode mRequestMode
;
1174 RequestRedirect mRequestRedirect
;
1175 RequestCredentials mRequestCredentials
;
1176 nsContentPolicyType mContentPolicyType
;
1177 nsCOMPtr
<nsIInputStream
> mUploadStream
;
1178 int64_t mUploadStreamContentLength
;
1179 nsCString mReferrer
;
1180 ReferrerPolicy mReferrerPolicy
;
1181 nsString mIntegrity
;
1182 const bool mIsNonSubresourceRequest
;
1186 WorkerPrivate
* aWorkerPrivate
, KeepAliveToken
* aKeepAliveToken
,
1187 nsMainThreadPtrHandle
<nsIInterceptedChannel
>& aChannel
,
1188 // CSP checks might require the worker script spec
1190 const nsACString
& aScriptSpec
,
1191 nsMainThreadPtrHandle
<ServiceWorkerRegistrationInfo
>& aRegistration
,
1192 const nsAString
& aClientId
, const nsAString
& aResultingClientId
,
1193 bool aIsReload
, bool aMarkLaunchServiceWorkerEnd
,
1194 bool aIsNonSubresourceRequest
)
1195 : ExtendableFunctionalEventWorkerRunnable(aWorkerPrivate
, aKeepAliveToken
,
1197 mInterceptedChannel(aChannel
),
1198 mScriptSpec(aScriptSpec
),
1199 mClientId(aClientId
),
1200 mResultingClientId(aResultingClientId
),
1201 mIsReload(aIsReload
),
1202 mMarkLaunchServiceWorkerEnd(aMarkLaunchServiceWorkerEnd
),
1203 mCacheMode(RequestCache::Default
),
1204 mRequestMode(RequestMode::No_cors
),
1205 mRequestRedirect(RequestRedirect::Follow
)
1206 // By default we set it to same-origin since normal HTTP fetches always
1207 // send credentials to same-origin websites unless explicitly forbidden.
1209 mRequestCredentials(RequestCredentials::Same_origin
),
1210 mContentPolicyType(nsIContentPolicy::TYPE_INVALID
),
1211 mUploadStreamContentLength(-1),
1212 mReferrer(kFETCH_CLIENT_REFERRER_STR
),
1213 mReferrerPolicy(ReferrerPolicy::_empty
),
1214 mIsNonSubresourceRequest(aIsNonSubresourceRequest
) {
1215 MOZ_ASSERT(aWorkerPrivate
);
1218 NS_DECL_ISUPPORTS_INHERITED
1221 VisitHeader(const nsACString
& aHeader
, const nsACString
& aValue
) override
{
1222 mHeaderNames
.AppendElement(aHeader
);
1223 mHeaderValues
.AppendElement(aValue
);
1228 MOZ_ASSERT(NS_IsMainThread());
1229 nsCOMPtr
<nsIChannel
> channel
;
1230 nsresult rv
= mInterceptedChannel
->GetChannel(getter_AddRefs(channel
));
1231 NS_ENSURE_SUCCESS(rv
, rv
);
1233 nsCOMPtr
<nsIURI
> uri
;
1234 rv
= mInterceptedChannel
->GetSecureUpgradedChannelURI(getter_AddRefs(uri
));
1235 NS_ENSURE_SUCCESS(rv
, rv
);
1237 // Normally we rely on the Request constructor to strip the fragment, but
1238 // when creating the FetchEvent we bypass the constructor. So strip the
1239 // fragment manually here instead. We can't do it later when we create
1240 // the Request because that code executes off the main thread.
1241 nsCOMPtr
<nsIURI
> uriNoFragment
;
1242 rv
= NS_GetURIWithoutRef(uri
, getter_AddRefs(uriNoFragment
));
1243 NS_ENSURE_SUCCESS(rv
, rv
);
1244 rv
= uriNoFragment
->GetSpec(mSpec
);
1245 NS_ENSURE_SUCCESS(rv
, rv
);
1246 rv
= uri
->GetRef(mFragment
);
1247 NS_ENSURE_SUCCESS(rv
, rv
);
1250 rv
= channel
->GetLoadFlags(&loadFlags
);
1251 NS_ENSURE_SUCCESS(rv
, rv
);
1252 nsCOMPtr
<nsILoadInfo
> loadInfo
= channel
->LoadInfo();
1253 mContentPolicyType
= loadInfo
->InternalContentPolicyType();
1255 nsCOMPtr
<nsIHttpChannel
> httpChannel
= do_QueryInterface(channel
);
1256 MOZ_ASSERT(httpChannel
, "How come we don't have an HTTP channel?");
1258 mReferrer
= EmptyCString();
1259 uint32_t referrerPolicy
= 0;
1260 nsCOMPtr
<nsIReferrerInfo
> referrerInfo
= httpChannel
->GetReferrerInfo();
1262 referrerPolicy
= referrerInfo
->GetReferrerPolicy();
1263 nsCOMPtr
<nsIURI
> computedReferrer
= referrerInfo
->GetComputedReferrer();
1264 if (computedReferrer
) {
1265 rv
= computedReferrer
->GetSpec(mReferrer
);
1266 NS_ENSURE_SUCCESS(rv
, rv
);
1269 switch (referrerPolicy
) {
1270 case nsIHttpChannel::REFERRER_POLICY_UNSET
:
1271 mReferrerPolicy
= ReferrerPolicy::_empty
;
1273 case nsIHttpChannel::REFERRER_POLICY_NO_REFERRER
:
1274 mReferrerPolicy
= ReferrerPolicy::No_referrer
;
1276 case nsIHttpChannel::REFERRER_POLICY_ORIGIN
:
1277 mReferrerPolicy
= ReferrerPolicy::Origin
;
1279 case nsIHttpChannel::REFERRER_POLICY_NO_REFERRER_WHEN_DOWNGRADE
:
1280 mReferrerPolicy
= ReferrerPolicy::No_referrer_when_downgrade
;
1282 case nsIHttpChannel::REFERRER_POLICY_ORIGIN_WHEN_XORIGIN
:
1283 mReferrerPolicy
= ReferrerPolicy::Origin_when_cross_origin
;
1285 case nsIHttpChannel::REFERRER_POLICY_UNSAFE_URL
:
1286 mReferrerPolicy
= ReferrerPolicy::Unsafe_url
;
1288 case nsIHttpChannel::REFERRER_POLICY_SAME_ORIGIN
:
1289 mReferrerPolicy
= ReferrerPolicy::Same_origin
;
1291 case nsIHttpChannel::REFERRER_POLICY_STRICT_ORIGIN_WHEN_XORIGIN
:
1292 mReferrerPolicy
= ReferrerPolicy::Strict_origin_when_cross_origin
;
1294 case nsIHttpChannel::REFERRER_POLICY_STRICT_ORIGIN
:
1295 mReferrerPolicy
= ReferrerPolicy::Strict_origin
;
1298 MOZ_ASSERT_UNREACHABLE("Invalid Referrer Policy enum value?");
1302 rv
= httpChannel
->GetRequestMethod(mMethod
);
1303 NS_ENSURE_SUCCESS(rv
, rv
);
1305 nsCOMPtr
<nsIHttpChannelInternal
> internalChannel
=
1306 do_QueryInterface(httpChannel
);
1307 NS_ENSURE_TRUE(internalChannel
, NS_ERROR_NOT_AVAILABLE
);
1309 mRequestMode
= InternalRequest::MapChannelToRequestMode(channel
);
1311 // This is safe due to static_asserts in ServiceWorkerManager.cpp.
1312 uint32_t redirectMode
;
1313 rv
= internalChannel
->GetRedirectMode(&redirectMode
);
1314 MOZ_ASSERT(NS_SUCCEEDED(rv
));
1315 mRequestRedirect
= static_cast<RequestRedirect
>(redirectMode
);
1317 // This is safe due to static_asserts in ServiceWorkerManager.cpp.
1319 rv
= internalChannel
->GetFetchCacheMode(&cacheMode
);
1320 MOZ_ASSERT(NS_SUCCEEDED(rv
));
1321 mCacheMode
= static_cast<RequestCache
>(cacheMode
);
1323 rv
= internalChannel
->GetIntegrityMetadata(mIntegrity
);
1324 MOZ_ASSERT(NS_SUCCEEDED(rv
));
1326 mRequestCredentials
=
1327 InternalRequest::MapChannelToRequestCredentials(channel
);
1329 rv
= httpChannel
->VisitNonDefaultRequestHeaders(this);
1330 NS_ENSURE_SUCCESS(rv
, rv
);
1332 nsCOMPtr
<nsIUploadChannel2
> uploadChannel
= do_QueryInterface(httpChannel
);
1333 if (uploadChannel
) {
1334 MOZ_ASSERT(!mUploadStream
);
1335 nsCOMPtr
<nsIInputStream
> uploadStream
;
1336 rv
= uploadChannel
->CloneUploadStream(&mUploadStreamContentLength
,
1337 getter_AddRefs(uploadStream
));
1338 NS_ENSURE_SUCCESS(rv
, rv
);
1339 mUploadStream
= uploadStream
;
1345 bool WorkerRun(JSContext
* aCx
, WorkerPrivate
* aWorkerPrivate
) override
{
1346 MOZ_ASSERT(aWorkerPrivate
);
1348 if (mMarkLaunchServiceWorkerEnd
) {
1349 mInterceptedChannel
->SetLaunchServiceWorkerEnd(TimeStamp::Now());
1351 // A probe to measure sw launch time for telemetry.
1352 TimeStamp launchStartTime
= TimeStamp();
1353 mInterceptedChannel
->GetLaunchServiceWorkerStart(&launchStartTime
);
1355 TimeStamp launchEndTime
= TimeStamp();
1356 mInterceptedChannel
->GetLaunchServiceWorkerEnd(&launchEndTime
);
1357 Telemetry::AccumulateTimeDelta(Telemetry::SERVICE_WORKER_LAUNCH_TIME
,
1358 launchStartTime
, launchEndTime
);
1361 mInterceptedChannel
->SetDispatchFetchEventEnd(TimeStamp::Now());
1362 return DispatchFetchEvent(aCx
, aWorkerPrivate
);
1365 nsresult
Cancel() override
{
1366 nsCOMPtr
<nsIRunnable
> runnable
= new ResumeRequest(mInterceptedChannel
);
1367 if (NS_FAILED(mWorkerPrivate
->DispatchToMainThread(runnable
))) {
1368 NS_WARNING("Failed to resume channel on FetchEventRunnable::Cancel()!\n");
1370 WorkerRunnable::Cancel();
1375 ~FetchEventRunnable() {}
1377 class ResumeRequest final
: public Runnable
{
1378 nsMainThreadPtrHandle
<nsIInterceptedChannel
> mChannel
;
1381 explicit ResumeRequest(
1382 nsMainThreadPtrHandle
<nsIInterceptedChannel
>& aChannel
)
1383 : Runnable("dom::FetchEventRunnable::ResumeRequest"),
1384 mChannel(aChannel
) {
1385 mChannel
->SetFinishResponseStart(TimeStamp::Now());
1388 NS_IMETHOD
Run() override
{
1389 MOZ_ASSERT(NS_IsMainThread());
1391 TimeStamp timeStamp
= TimeStamp::Now();
1392 mChannel
->SetHandleFetchEventEnd(timeStamp
);
1393 mChannel
->SetChannelResetEnd(timeStamp
);
1394 mChannel
->SaveTimeStamps();
1396 nsresult rv
= mChannel
->ResetInterception();
1397 if (NS_FAILED(rv
)) {
1398 NS_WARNING("Failed to resume intercepted network request");
1399 mChannel
->CancelInterception(rv
);
1405 bool DispatchFetchEvent(JSContext
* aCx
, WorkerPrivate
* aWorkerPrivate
) {
1407 MOZ_ASSERT(aWorkerPrivate
);
1408 MOZ_ASSERT(aWorkerPrivate
->IsServiceWorker());
1409 GlobalObject
globalObj(aCx
, aWorkerPrivate
->GlobalScope()->GetWrapper());
1411 RefPtr
<InternalHeaders
> internalHeaders
=
1412 new InternalHeaders(HeadersGuardEnum::Request
);
1413 MOZ_ASSERT(mHeaderNames
.Length() == mHeaderValues
.Length());
1414 for (uint32_t i
= 0; i
< mHeaderNames
.Length(); i
++) {
1416 internalHeaders
->Set(mHeaderNames
[i
], mHeaderValues
[i
], result
);
1417 if (NS_WARN_IF(result
.Failed())) {
1418 result
.SuppressException();
1424 internalHeaders
->SetGuard(HeadersGuardEnum::Immutable
, result
);
1425 if (NS_WARN_IF(result
.Failed())) {
1426 result
.SuppressException();
1429 RefPtr
<InternalRequest
> internalReq
= new InternalRequest(
1430 mSpec
, mFragment
, mMethod
, internalHeaders
.forget(), mCacheMode
,
1431 mRequestMode
, mRequestRedirect
, mRequestCredentials
,
1432 NS_ConvertUTF8toUTF16(mReferrer
), mReferrerPolicy
, mContentPolicyType
,
1434 internalReq
->SetBody(mUploadStream
, mUploadStreamContentLength
);
1435 // For Telemetry, note that this Request object was created by a Fetch
1437 internalReq
->SetCreatedByFetchEvent();
1439 nsCOMPtr
<nsIChannel
> channel
;
1440 nsresult rv
= mInterceptedChannel
->GetChannel(getter_AddRefs(channel
));
1441 NS_ENSURE_SUCCESS(rv
, false);
1443 nsCOMPtr
<nsICacheInfoChannel
> cic
= do_QueryInterface(channel
);
1444 if (cic
&& !cic
->PreferredAlternativeDataTypes().IsEmpty()) {
1445 // TODO: the internal request probably needs all the preferred types.
1446 nsAutoCString alternativeDataType
;
1447 alternativeDataType
.Assign(
1448 cic
->PreferredAlternativeDataTypes()[0].type());
1449 internalReq
->SetPreferredAlternativeDataType(alternativeDataType
);
1452 nsCOMPtr
<nsIGlobalObject
> global
=
1453 do_QueryInterface(globalObj
.GetAsSupports());
1454 if (NS_WARN_IF(!global
)) {
1458 // TODO This request object should be created with a AbortSignal object
1459 // which should be aborted if the loading is aborted. See bug 1394102.
1460 RefPtr
<Request
> request
= new Request(global
, internalReq
, nullptr);
1462 MOZ_ASSERT_IF(internalReq
->IsNavigationRequest(),
1463 request
->Redirect() == RequestRedirect::Manual
);
1465 RootedDictionary
<FetchEventInit
> init(aCx
);
1466 init
.mRequest
= request
;
1467 init
.mBubbles
= false;
1468 init
.mCancelable
= true;
1469 // Only expose the FetchEvent.clientId on subresource requests for now.
1470 // Once we implement .targetClientId we can then start exposing .clientId
1471 // on non-subresource requests as well. See bug 1487534.
1472 if (!mClientId
.IsEmpty() && !internalReq
->IsNavigationRequest()) {
1473 init
.mClientId
= mClientId
;
1477 * https://w3c.github.io/ServiceWorker/#on-fetch-request-algorithm
1479 * "If request is a non-subresource request and request’s
1480 * destination is not "report", initialize e’s resultingClientId attribute
1481 * to reservedClient’s [resultingClient's] id, and to the empty string
1482 * otherwise." (Step 18.8)
1484 if (!mResultingClientId
.IsEmpty() && mIsNonSubresourceRequest
&&
1485 internalReq
->Destination() != RequestDestination::Report
) {
1486 init
.mResultingClientId
= mResultingClientId
;
1489 init
.mIsReload
= mIsReload
;
1490 RefPtr
<FetchEvent
> event
= FetchEvent::Constructor(
1491 globalObj
, NS_LITERAL_STRING("fetch"), init
, result
);
1492 if (NS_WARN_IF(result
.Failed())) {
1493 result
.SuppressException();
1497 event
->PostInit(mInterceptedChannel
, mRegistration
, mScriptSpec
);
1498 event
->SetTrusted(true);
1500 mInterceptedChannel
->SetHandleFetchEventStart(TimeStamp::Now());
1502 nsresult rv2
= DispatchExtendableEventOnWorkerScope(
1503 aCx
, aWorkerPrivate
->GlobalScope(), event
, nullptr);
1504 if ((NS_WARN_IF(NS_FAILED(rv2
)) &&
1505 rv2
!= NS_ERROR_XPC_JS_THREW_EXCEPTION
) ||
1506 !event
->WaitToRespond()) {
1507 nsCOMPtr
<nsIRunnable
> runnable
;
1508 MOZ_ASSERT(!aWorkerPrivate
->UsesSystemPrincipal(),
1509 "We don't support system-principal serviceworkers");
1510 if (event
->DefaultPrevented(CallerType::NonSystem
)) {
1511 runnable
= new CancelChannelRunnable(mInterceptedChannel
, mRegistration
,
1512 NS_ERROR_INTERCEPTION_FAILED
);
1514 runnable
= new ResumeRequest(mInterceptedChannel
);
1517 MOZ_ALWAYS_SUCCEEDS(
1518 mWorkerPrivate
->DispatchToMainThread(runnable
.forget()));
1525 NS_IMPL_ISUPPORTS_INHERITED(FetchEventRunnable
, WorkerRunnable
,
1526 nsIHttpHeaderVisitor
)
1528 } // anonymous namespace
1530 nsresult
ServiceWorkerPrivate::SendFetchEvent(
1531 nsIInterceptedChannel
* aChannel
, nsILoadGroup
* aLoadGroup
,
1532 const nsAString
& aClientId
, const nsAString
& aResultingClientId
,
1534 MOZ_ASSERT(NS_IsMainThread());
1536 RefPtr
<ServiceWorkerManager
> swm
= ServiceWorkerManager::GetInstance();
1537 if (NS_WARN_IF(!mInfo
|| !swm
)) {
1538 return NS_ERROR_FAILURE
;
1541 RefPtr
<ServiceWorkerRegistrationInfo
> registration
=
1542 swm
->GetRegistration(mInfo
->Principal(), mInfo
->Scope());
1544 // Its possible the registration is removed between starting the interception
1545 // and actually dispatching the fetch event. In these cases we simply
1546 // want to restart the original network request. Since this is a normal
1547 // condition we handle the reset here instead of returning an error which
1548 // would in turn trigger a console report.
1549 if (!registration
) {
1550 nsresult rv
= aChannel
->ResetInterception();
1551 if (NS_FAILED(rv
)) {
1552 NS_WARNING("Failed to resume intercepted network request");
1553 aChannel
->CancelInterception(rv
);
1558 // Handle Fetch algorithm - step 16. If the service worker didn't register
1559 // any fetch event handlers, then abort the interception and maybe trigger
1560 // the soft update algorithm.
1561 if (!mInfo
->HandlesFetch()) {
1562 nsresult rv
= aChannel
->ResetInterception();
1563 if (NS_FAILED(rv
)) {
1564 NS_WARNING("Failed to resume intercepted network request");
1565 aChannel
->CancelInterception(rv
);
1568 // Trigger soft updates if necessary.
1569 registration
->MaybeScheduleTimeCheckAndUpdate();
1574 aChannel
->SetLaunchServiceWorkerStart(TimeStamp::Now());
1575 aChannel
->SetDispatchFetchEventStart(TimeStamp::Now());
1577 bool newWorkerCreated
= false;
1578 nsresult rv
= SpawnWorkerIfNeeded(FetchEvent
, &newWorkerCreated
, aLoadGroup
);
1579 NS_ENSURE_SUCCESS(rv
, rv
);
1581 if (!newWorkerCreated
) {
1582 aChannel
->SetLaunchServiceWorkerEnd(TimeStamp::Now());
1585 nsMainThreadPtrHandle
<nsIInterceptedChannel
> handle(
1586 new nsMainThreadPtrHolder
<nsIInterceptedChannel
>("nsIInterceptedChannel",
1589 nsMainThreadPtrHandle
<ServiceWorkerRegistrationInfo
> regInfo(
1590 new nsMainThreadPtrHolder
<ServiceWorkerRegistrationInfo
>(
1591 "ServiceWorkerRegistrationInfoProxy", registration
, false));
1593 RefPtr
<KeepAliveToken
> token
= CreateEventKeepAliveToken();
1595 nsCOMPtr
<nsIChannel
> channel
;
1596 rv
= aChannel
->GetChannel(getter_AddRefs(channel
));
1597 NS_ENSURE_SUCCESS(rv
, rv
);
1598 bool isNonSubresourceRequest
=
1599 nsContentUtils::IsNonSubresourceRequest(channel
);
1601 RefPtr
<FetchEventRunnable
> r
= new FetchEventRunnable(
1602 mWorkerPrivate
, token
, handle
, mInfo
->ScriptSpec(), regInfo
, aClientId
,
1603 aResultingClientId
, aIsReload
, newWorkerCreated
, isNonSubresourceRequest
);
1605 if (NS_WARN_IF(NS_FAILED(rv
))) {
1609 if (mInfo
->State() == ServiceWorkerState::Activating
) {
1610 mPendingFunctionalEvents
.AppendElement(r
.forget());
1614 MOZ_ASSERT(mInfo
->State() == ServiceWorkerState::Activated
);
1616 if (NS_WARN_IF(!r
->Dispatch())) {
1617 return NS_ERROR_FAILURE
;
1623 nsresult
ServiceWorkerPrivate::SpawnWorkerIfNeeded(WakeUpReason aWhy
,
1624 bool* aNewWorkerCreated
,
1625 nsILoadGroup
* aLoadGroup
) {
1626 MOZ_ASSERT(NS_IsMainThread());
1628 // Defaults to no new worker created, but if there is one, we'll set the value
1629 // to true at the end of this function.
1630 if (aNewWorkerCreated
) {
1631 *aNewWorkerCreated
= false;
1634 // If the worker started shutting down on itself we may have a stale
1635 // reference here. Invoke our termination code to clean it out.
1636 if (mWorkerPrivate
&& mWorkerPrivate
->ParentStatusProtected() > Running
) {
1638 MOZ_DIAGNOSTIC_ASSERT(!mWorkerPrivate
);
1641 if (mWorkerPrivate
) {
1642 // If we have a load group here then use it to update the service worker
1643 // load group. This was added when we needed the load group's tab child
1644 // to pass some security checks. Those security checks are gone, though,
1645 // and we could possibly remove this now. For now we just do it
1646 // opportunistically. When the service worker is running in a separate
1647 // process from the client that initiated the intercepted channel, then
1648 // the load group will be nullptr. UpdateOverrideLoadGroup ignores nullptr
1650 mWorkerPrivate
->UpdateOverridenLoadGroup(aLoadGroup
);
1651 RenewKeepAliveToken(aWhy
);
1656 // Sanity check: mSupportsArray should be empty if we're about to
1657 // spin up a new worker.
1658 MOZ_ASSERT(mSupportsArray
.IsEmpty());
1660 if (NS_WARN_IF(!mInfo
)) {
1661 NS_WARNING("Trying to wake up a dead service worker.");
1662 return NS_ERROR_FAILURE
;
1665 RefPtr
<ServiceWorkerManager
> swm
= ServiceWorkerManager::GetInstance();
1666 NS_ENSURE_TRUE(swm
, NS_ERROR_FAILURE
);
1668 RefPtr
<ServiceWorkerRegistrationInfo
> reg
=
1669 swm
->GetRegistration(mInfo
->Principal(), mInfo
->Scope());
1670 NS_ENSURE_TRUE(reg
, NS_ERROR_FAILURE
);
1672 // TODO(catalinb): Bug 1192138 - Add telemetry for service worker wake-ups.
1674 // Ensure that the IndexedDatabaseManager is initialized
1675 Unused
<< NS_WARN_IF(!IndexedDatabaseManager::GetOrCreate());
1677 WorkerLoadInfo info
;
1678 nsresult rv
= NS_NewURI(getter_AddRefs(info
.mBaseURI
), mInfo
->ScriptSpec(),
1681 if (NS_WARN_IF(NS_FAILED(rv
))) {
1685 info
.mResolvedScriptURI
= info
.mBaseURI
;
1686 MOZ_ASSERT(!mInfo
->CacheName().IsEmpty());
1687 info
.mServiceWorkerCacheName
= mInfo
->CacheName();
1689 info
.mServiceWorkerDescriptor
.emplace(mInfo
->Descriptor());
1690 info
.mServiceWorkerRegistrationDescriptor
.emplace(reg
->Descriptor());
1692 info
.mLoadGroup
= aLoadGroup
;
1694 // If we are loading a script for a ServiceWorker then we must not
1695 // try to intercept it. If the interception matches the current
1696 // ServiceWorker's scope then we could deadlock the load.
1698 mInfo
->GetImportsLoadFlags() | nsIChannel::LOAD_BYPASS_SERVICE_WORKER
;
1700 rv
= info
.mBaseURI
->GetHost(info
.mDomain
);
1701 if (NS_WARN_IF(NS_FAILED(rv
))) {
1705 info
.mPrincipal
= mInfo
->Principal();
1706 info
.mLoadingPrincipal
= info
.mPrincipal
;
1707 // StoragePrincipal for ServiceWorkers is equal to mPrincipal because, at the
1708 // moment, ServiceWorkers are not exposed in partitioned contexts.
1709 info
.mStoragePrincipal
= info
.mPrincipal
;
1711 info
.mCookieSettings
= mozilla::net::CookieSettings::Create();
1712 MOZ_ASSERT(info
.mCookieSettings
);
1714 info
.mStorageAccess
=
1715 StorageAllowedForServiceWorker(info
.mPrincipal
, info
.mCookieSettings
);
1717 info
.mOriginAttributes
= mInfo
->GetOriginAttributes();
1719 // Verify that we don't have any CSP on pristine client.
1720 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
1721 nsCOMPtr
<nsIContentSecurityPolicy
> csp
;
1722 if (info
.mChannel
) {
1723 nsCOMPtr
<nsILoadInfo
> loadinfo
= info
.mChannel
->LoadInfo();
1724 csp
= loadinfo
->GetCsp();
1726 MOZ_DIAGNOSTIC_ASSERT(!csp
);
1729 // Default CSP permissions for now. These will be overrided if necessary
1730 // based on the script CSP headers during load in ScriptLoader.
1731 info
.mEvalAllowed
= true;
1732 info
.mReportCSPViolations
= false;
1734 WorkerPrivate::OverrideLoadInfoLoadGroup(info
, info
.mPrincipal
);
1736 rv
= info
.SetPrincipalsAndCSPOnMainThread(
1737 info
.mPrincipal
, info
.mStoragePrincipal
, info
.mLoadGroup
, nullptr);
1738 if (NS_WARN_IF(NS_FAILED(rv
))) {
1745 NS_ConvertUTF8toUTF16
scriptSpec(mInfo
->ScriptSpec());
1747 mWorkerPrivate
= WorkerPrivate::Constructor(jsapi
.cx(), scriptSpec
, false,
1748 WorkerTypeService
, VoidString(),
1749 EmptyCString(), &info
, error
);
1750 if (NS_WARN_IF(error
.Failed())) {
1751 return error
.StealNSResult();
1754 RenewKeepAliveToken(aWhy
);
1756 if (aNewWorkerCreated
) {
1757 *aNewWorkerCreated
= true;
1763 bool ServiceWorkerPrivate::MaybeStoreISupports(nsISupports
* aSupports
) {
1764 MOZ_ASSERT(NS_IsMainThread());
1766 if (!mWorkerPrivate
) {
1767 MOZ_DIAGNOSTIC_ASSERT(mSupportsArray
.IsEmpty());
1771 MOZ_ASSERT(!mSupportsArray
.Contains(aSupports
));
1772 mSupportsArray
.AppendElement(aSupports
);
1776 void ServiceWorkerPrivate::RemoveISupports(nsISupports
* aSupports
) {
1777 MOZ_ASSERT(NS_IsMainThread());
1778 mSupportsArray
.RemoveElement(aSupports
);
1781 void ServiceWorkerPrivate::TerminateWorker() {
1782 MOZ_ASSERT(NS_IsMainThread());
1784 mIdleWorkerTimer
->Cancel();
1785 mIdleKeepAliveToken
= nullptr;
1786 if (mWorkerPrivate
) {
1787 if (StaticPrefs::dom_serviceWorkers_testing_enabled()) {
1788 nsCOMPtr
<nsIObserverService
> os
= services::GetObserverService();
1790 os
->NotifyObservers(nullptr, "service-worker-shutdown", nullptr);
1794 Unused
<< NS_WARN_IF(!mWorkerPrivate
->Cancel());
1795 RefPtr
<WorkerPrivate
> workerPrivate(mWorkerPrivate
.forget());
1796 mSupportsArray
.Clear();
1798 // Any pending events are never going to fire on this worker. Cancel
1799 // them so that intercepted channels can be reset and other resources
1801 nsTArray
<RefPtr
<WorkerRunnable
>> pendingEvents
;
1802 mPendingFunctionalEvents
.SwapElements(pendingEvents
);
1803 for (uint32_t i
= 0; i
< pendingEvents
.Length(); ++i
) {
1804 pendingEvents
[i
]->Cancel();
1809 void ServiceWorkerPrivate::NoteDeadServiceWorkerInfo() {
1810 MOZ_ASSERT(NS_IsMainThread());
1817 class UpdateStateControlRunnable final
1818 : public MainThreadWorkerControlRunnable
{
1819 const ServiceWorkerState mState
;
1821 bool WorkerRun(JSContext
* aCx
, WorkerPrivate
* aWorkerPrivate
) override
{
1822 MOZ_DIAGNOSTIC_ASSERT(aWorkerPrivate
);
1823 aWorkerPrivate
->UpdateServiceWorkerState(mState
);
1828 UpdateStateControlRunnable(WorkerPrivate
* aWorkerPrivate
,
1829 ServiceWorkerState aState
)
1830 : MainThreadWorkerControlRunnable(aWorkerPrivate
), mState(aState
) {}
1833 } // anonymous namespace
1835 void ServiceWorkerPrivate::UpdateState(ServiceWorkerState aState
) {
1836 MOZ_ASSERT(NS_IsMainThread());
1838 if (!mWorkerPrivate
) {
1839 MOZ_DIAGNOSTIC_ASSERT(mPendingFunctionalEvents
.IsEmpty());
1843 RefPtr
<WorkerRunnable
> r
=
1844 new UpdateStateControlRunnable(mWorkerPrivate
, aState
);
1845 Unused
<< r
->Dispatch();
1847 if (aState
!= ServiceWorkerState::Activated
) {
1851 nsTArray
<RefPtr
<WorkerRunnable
>> pendingEvents
;
1852 mPendingFunctionalEvents
.SwapElements(pendingEvents
);
1854 for (uint32_t i
= 0; i
< pendingEvents
.Length(); ++i
) {
1855 RefPtr
<WorkerRunnable
> r
= pendingEvents
[i
].forget();
1856 if (NS_WARN_IF(!r
->Dispatch())) {
1857 NS_WARNING("Failed to dispatch pending functional event!");
1862 nsresult
ServiceWorkerPrivate::GetDebugger(nsIWorkerDebugger
** aResult
) {
1863 MOZ_ASSERT(NS_IsMainThread());
1864 MOZ_ASSERT(aResult
);
1866 if (!mDebuggerCount
) {
1870 MOZ_ASSERT(mWorkerPrivate
);
1872 nsCOMPtr
<nsIWorkerDebugger
> debugger
= mWorkerPrivate
->Debugger();
1873 debugger
.forget(aResult
);
1878 nsresult
ServiceWorkerPrivate::AttachDebugger() {
1879 MOZ_ASSERT(NS_IsMainThread());
1881 // When the first debugger attaches to a worker, we spawn a worker if needed,
1882 // and cancel the idle timeout. The idle timeout should not be reset until
1883 // the last debugger detached from the worker.
1884 if (!mDebuggerCount
) {
1885 nsresult rv
= SpawnWorkerIfNeeded(AttachEvent
);
1886 NS_ENSURE_SUCCESS(rv
, rv
);
1888 mIdleWorkerTimer
->Cancel();
1896 nsresult
ServiceWorkerPrivate::DetachDebugger() {
1897 MOZ_ASSERT(NS_IsMainThread());
1899 if (!mDebuggerCount
) {
1900 return NS_ERROR_UNEXPECTED
;
1905 // When the last debugger detaches from a worker, we either reset the idle
1906 // timeout, or terminate the worker if there are no more active tokens.
1907 if (!mDebuggerCount
) {
1918 bool ServiceWorkerPrivate::IsIdle() const {
1919 MOZ_ASSERT(NS_IsMainThread());
1920 return mTokenCount
== 0 || (mTokenCount
== 1 && mIdleKeepAliveToken
);
1925 class ServiceWorkerPrivateTimerCallback final
: public nsITimerCallback
,
1928 typedef void (ServiceWorkerPrivate::*Method
)(nsITimer
*);
1930 ServiceWorkerPrivateTimerCallback(ServiceWorkerPrivate
* aServiceWorkerPrivate
,
1932 : mServiceWorkerPrivate(aServiceWorkerPrivate
), mMethod(aMethod
) {}
1935 Notify(nsITimer
* aTimer
) override
{
1936 (mServiceWorkerPrivate
->*mMethod
)(aTimer
);
1937 mServiceWorkerPrivate
= nullptr;
1942 GetName(nsACString
& aName
) override
{
1943 aName
.AssignLiteral("ServiceWorkerPrivateTimerCallback");
1948 ~ServiceWorkerPrivateTimerCallback() = default;
1950 RefPtr
<ServiceWorkerPrivate
> mServiceWorkerPrivate
;
1953 NS_DECL_THREADSAFE_ISUPPORTS
1956 NS_IMPL_ISUPPORTS(ServiceWorkerPrivateTimerCallback
, nsITimerCallback
,
1959 } // anonymous namespace
1961 void ServiceWorkerPrivate::NoteIdleWorkerCallback(nsITimer
* aTimer
) {
1962 MOZ_ASSERT(NS_IsMainThread());
1964 MOZ_ASSERT(aTimer
== mIdleWorkerTimer
, "Invalid timer!");
1966 // Release ServiceWorkerPrivate's token, since the grace period has ended.
1967 mIdleKeepAliveToken
= nullptr;
1969 if (mWorkerPrivate
) {
1970 // If we still have a workerPrivate at this point it means there are pending
1971 // waitUntil promises. Wait a bit more until we forcibly terminate the
1974 Preferences::GetInt("dom.serviceWorkers.idle_extended_timeout");
1975 nsCOMPtr
<nsITimerCallback
> cb
= new ServiceWorkerPrivateTimerCallback(
1976 this, &ServiceWorkerPrivate::TerminateWorkerCallback
);
1977 DebugOnly
<nsresult
> rv
= mIdleWorkerTimer
->InitWithCallback(
1978 cb
, timeout
, nsITimer::TYPE_ONE_SHOT
);
1979 MOZ_ASSERT(NS_SUCCEEDED(rv
));
1983 void ServiceWorkerPrivate::TerminateWorkerCallback(nsITimer
* aTimer
) {
1984 MOZ_ASSERT(NS_IsMainThread());
1986 MOZ_ASSERT(aTimer
== this->mIdleWorkerTimer
, "Invalid timer!");
1988 // mInfo must be non-null at this point because NoteDeadServiceWorkerInfo
1989 // which zeroes it calls TerminateWorker which cancels our timer which will
1990 // ensure we don't get invoked even if the nsTimerEvent is in the event queue.
1991 ServiceWorkerManager::LocalizeAndReportToAllClients(
1992 mInfo
->Scope(), "ServiceWorkerGraceTimeoutTermination",
1993 nsTArray
<nsString
>{NS_ConvertUTF8toUTF16(mInfo
->Scope())});
1998 void ServiceWorkerPrivate::RenewKeepAliveToken(WakeUpReason aWhy
) {
1999 // We should have an active worker if we're renewing the keep alive token.
2000 MOZ_ASSERT(mWorkerPrivate
);
2002 // If there is at least one debugger attached to the worker, the idle worker
2003 // timeout was canceled when the first debugger attached to the worker. It
2004 // should not be reset until the last debugger detaches from the worker.
2005 if (!mDebuggerCount
) {
2009 if (!mIdleKeepAliveToken
) {
2010 mIdleKeepAliveToken
= new KeepAliveToken(this);
2014 void ServiceWorkerPrivate::ResetIdleTimeout() {
2015 uint32_t timeout
= Preferences::GetInt("dom.serviceWorkers.idle_timeout");
2016 nsCOMPtr
<nsITimerCallback
> cb
= new ServiceWorkerPrivateTimerCallback(
2017 this, &ServiceWorkerPrivate::NoteIdleWorkerCallback
);
2018 DebugOnly
<nsresult
> rv
=
2019 mIdleWorkerTimer
->InitWithCallback(cb
, timeout
, nsITimer::TYPE_ONE_SHOT
);
2020 MOZ_ASSERT(NS_SUCCEEDED(rv
));
2023 void ServiceWorkerPrivate::AddToken() {
2024 MOZ_ASSERT(NS_IsMainThread());
2028 void ServiceWorkerPrivate::ReleaseToken() {
2029 MOZ_ASSERT(NS_IsMainThread());
2031 MOZ_ASSERT(mTokenCount
> 0);
2037 // mInfo can be nullptr here if NoteDeadServiceWorkerInfo() is called while
2038 // the KeepAliveToken is being proxy released as a runnable.
2039 else if (mInfo
&& IsIdle()) {
2040 RefPtr
<ServiceWorkerManager
> swm
= ServiceWorkerManager::GetInstance();
2042 swm
->WorkerIsIdle(mInfo
);
2047 already_AddRefed
<KeepAliveToken
>
2048 ServiceWorkerPrivate::CreateEventKeepAliveToken() {
2049 MOZ_ASSERT(NS_IsMainThread());
2050 MOZ_ASSERT(mWorkerPrivate
);
2051 MOZ_ASSERT(mIdleKeepAliveToken
);
2052 RefPtr
<KeepAliveToken
> ref
= new KeepAliveToken(this);
2053 return ref
.forget();
2056 void ServiceWorkerPrivate::SetHandlesFetch(bool aValue
) {
2057 MOZ_ASSERT(NS_IsMainThread());
2059 if (NS_WARN_IF(!mInfo
)) {
2063 mInfo
->SetHandlesFetch(aValue
);
2067 } // namespace mozilla