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"
11 #include "MainThreadUtils.h"
12 #include "ServiceWorkerCloneData.h"
13 #include "ServiceWorkerManager.h"
14 #include "ServiceWorkerRegistrationInfo.h"
15 #include "ServiceWorkerUtils.h"
16 #include "js/ErrorReport.h"
17 #include "mozIThirdPartyUtil.h"
18 #include "mozilla/Assertions.h"
19 #include "mozilla/CycleCollectedJSContext.h" // for MicroTaskRunnable
20 #include "mozilla/ErrorResult.h"
21 #include "mozilla/JSObjectHolder.h"
22 #include "mozilla/Maybe.h"
23 #include "mozilla/OriginAttributes.h"
24 #include "mozilla/Preferences.h"
25 #include "mozilla/RemoteLazyInputStreamStorage.h"
26 #include "mozilla/Result.h"
27 #include "mozilla/ResultExtensions.h"
28 #include "mozilla/ScopeExit.h"
29 #include "mozilla/Services.h"
30 #include "mozilla/StaticPrefs_dom.h"
31 #include "mozilla/StoragePrincipalHelper.h"
32 #include "mozilla/Telemetry.h"
33 #include "mozilla/Unused.h"
34 #include "mozilla/dom/ClientIPCTypes.h"
35 #include "mozilla/dom/DOMTypes.h"
36 #include "mozilla/dom/FetchEventOpChild.h"
37 #include "mozilla/dom/InternalHeaders.h"
38 #include "mozilla/dom/InternalRequest.h"
39 #include "mozilla/dom/ReferrerInfo.h"
40 #include "mozilla/dom/RemoteType.h"
41 #include "mozilla/dom/RemoteWorkerControllerChild.h"
42 #include "mozilla/dom/RemoteWorkerManager.h" // RemoteWorkerManager::GetRemoteType
43 #include "mozilla/dom/ServiceWorkerBinding.h"
44 #include "mozilla/extensions/WebExtensionPolicy.h" // WebExtensionPolicy
45 #include "mozilla/ipc/BackgroundChild.h"
46 #include "mozilla/ipc/IPCStreamUtils.h"
47 #include "mozilla/ipc/PBackgroundChild.h"
48 #include "mozilla/ipc/URIUtils.h"
49 #include "mozilla/net/CookieJarSettings.h"
50 #include "nsContentUtils.h"
53 #include "nsICacheInfoChannel.h"
54 #include "nsIChannel.h"
55 #include "nsIHttpChannel.h"
56 #include "nsIHttpChannelInternal.h"
57 #include "nsIHttpHeaderVisitor.h"
58 #include "nsINetworkInterceptController.h"
60 #include "nsIObserverService.h"
61 #include "nsIRedirectHistoryEntry.h"
62 #include "nsIScriptError.h"
63 #include "nsIScriptSecurityManager.h"
64 #include "nsISupportsImpl.h"
66 #include "nsIUploadChannel2.h"
67 #include "nsNetUtil.h"
68 #include "nsProxyRelease.h"
69 #include "nsQueryObject.h"
70 #include "nsRFPService.h"
71 #include "nsStreamUtils.h"
72 #include "nsStringStream.h"
73 #include "nsThreadUtils.h"
75 #include "mozilla/dom/Client.h"
76 #include "mozilla/dom/FetchUtil.h"
77 #include "mozilla/dom/IndexedDatabaseManager.h"
78 #include "mozilla/dom/NotificationEvent.h"
79 #include "mozilla/dom/PromiseNativeHandler.h"
80 #include "mozilla/dom/PushEventBinding.h"
81 #include "mozilla/dom/RequestBinding.h"
82 #include "mozilla/dom/RootedDictionary.h"
83 #include "mozilla/dom/WorkerDebugger.h"
84 #include "mozilla/dom/WorkerRef.h"
85 #include "mozilla/dom/WorkerRunnable.h"
86 #include "mozilla/dom/WorkerScope.h"
87 #include "mozilla/dom/ipc/StructuredCloneData.h"
88 #include "mozilla/ipc/BackgroundUtils.h"
89 #include "mozilla/net/NeckoChannelParams.h"
90 #include "mozilla/StaticPrefs_privacy.h"
91 #include "nsIReferrerInfo.h"
93 extern mozilla::LazyLogModule sWorkerTelemetryLog
;
98 #define LOG(_args) MOZ_LOG(sWorkerTelemetryLog, LogLevel::Debug, _args);
100 using namespace mozilla
;
101 using namespace mozilla::dom
;
102 using namespace mozilla::ipc
;
104 namespace mozilla::dom
{
106 uint32_t ServiceWorkerPrivate::sRunningServiceWorkers
= 0;
107 uint32_t ServiceWorkerPrivate::sRunningServiceWorkersFetch
= 0;
108 uint32_t ServiceWorkerPrivate::sRunningServiceWorkersMax
= 0;
109 uint32_t ServiceWorkerPrivate::sRunningServiceWorkersFetchMax
= 0;
111 // Tracks the "dom.serviceWorkers.disable_open_click_delay" preference. Modified
112 // on main thread, read on worker threads.
113 // It is updated every time a "notificationclick" event is dispatched. While
114 // this is done without synchronization, at the worst, the thread will just get
115 // an older value within which a popup is allowed to be displayed, which will
116 // still be a valid value since it was set prior to dispatching the runnable.
117 Atomic
<uint32_t> gDOMDisableOpenClickDelay(0);
122 KeepAliveToken::KeepAliveToken(ServiceWorkerPrivate
* aPrivate
)
123 : mPrivate(aPrivate
) {
124 MOZ_ASSERT(NS_IsMainThread());
125 MOZ_ASSERT(aPrivate
);
126 mPrivate
->AddToken();
129 KeepAliveToken::~KeepAliveToken() {
130 MOZ_ASSERT(NS_IsMainThread());
131 mPrivate
->ReleaseToken();
134 NS_IMPL_ISUPPORTS0(KeepAliveToken
)
139 ServiceWorkerPrivate::RAIIActorPtrHolder::RAIIActorPtrHolder(
140 already_AddRefed
<RemoteWorkerControllerChild
> aActor
)
142 AssertIsOnMainThread();
144 MOZ_ASSERT(mActor
->Manager());
147 ServiceWorkerPrivate::RAIIActorPtrHolder::~RAIIActorPtrHolder() {
148 AssertIsOnMainThread();
150 mDestructorPromiseHolder
.ResolveIfExists(true, __func__
);
152 mActor
->MaybeSendDelete();
155 RemoteWorkerControllerChild
*
156 ServiceWorkerPrivate::RAIIActorPtrHolder::operator->() const {
157 AssertIsOnMainThread();
162 RemoteWorkerControllerChild
* ServiceWorkerPrivate::RAIIActorPtrHolder::get()
164 AssertIsOnMainThread();
169 RefPtr
<GenericPromise
>
170 ServiceWorkerPrivate::RAIIActorPtrHolder::OnDestructor() {
171 AssertIsOnMainThread();
173 return mDestructorPromiseHolder
.Ensure(__func__
);
177 * PendingFunctionEvent
179 ServiceWorkerPrivate::PendingFunctionalEvent::PendingFunctionalEvent(
180 ServiceWorkerPrivate
* aOwner
,
181 RefPtr
<ServiceWorkerRegistrationInfo
>&& aRegistration
)
182 : mOwner(aOwner
), mRegistration(std::move(aRegistration
)) {
183 AssertIsOnMainThread();
185 MOZ_ASSERT(mOwner
->mInfo
);
186 MOZ_ASSERT(mOwner
->mInfo
->State() == ServiceWorkerState::Activating
);
187 MOZ_ASSERT(mRegistration
);
190 ServiceWorkerPrivate::PendingFunctionalEvent::~PendingFunctionalEvent() {
191 AssertIsOnMainThread();
194 ServiceWorkerPrivate::PendingPushEvent::PendingPushEvent(
195 ServiceWorkerPrivate
* aOwner
,
196 RefPtr
<ServiceWorkerRegistrationInfo
>&& aRegistration
,
197 ServiceWorkerPushEventOpArgs
&& aArgs
)
198 : PendingFunctionalEvent(aOwner
, std::move(aRegistration
)),
199 mArgs(std::move(aArgs
)) {
200 AssertIsOnMainThread();
203 nsresult
ServiceWorkerPrivate::PendingPushEvent::Send() {
204 AssertIsOnMainThread();
206 MOZ_ASSERT(mOwner
->mInfo
);
208 return mOwner
->SendPushEventInternal(std::move(mRegistration
),
212 ServiceWorkerPrivate::PendingFetchEvent::PendingFetchEvent(
213 ServiceWorkerPrivate
* aOwner
,
214 RefPtr
<ServiceWorkerRegistrationInfo
>&& aRegistration
,
215 ParentToParentServiceWorkerFetchEventOpArgs
&& aArgs
,
216 nsCOMPtr
<nsIInterceptedChannel
>&& aChannel
,
217 RefPtr
<FetchServicePromises
>&& aPreloadResponseReadyPromises
)
218 : PendingFunctionalEvent(aOwner
, std::move(aRegistration
)),
219 mArgs(std::move(aArgs
)),
220 mChannel(std::move(aChannel
)),
221 mPreloadResponseReadyPromises(std::move(aPreloadResponseReadyPromises
)) {
222 AssertIsOnMainThread();
223 MOZ_ASSERT(mChannel
);
226 nsresult
ServiceWorkerPrivate::PendingFetchEvent::Send() {
227 AssertIsOnMainThread();
229 MOZ_ASSERT(mOwner
->mInfo
);
231 return mOwner
->SendFetchEventInternal(
232 std::move(mRegistration
), std::move(mArgs
), std::move(mChannel
),
233 std::move(mPreloadResponseReadyPromises
));
236 ServiceWorkerPrivate::PendingFetchEvent::~PendingFetchEvent() {
237 AssertIsOnMainThread();
239 if (NS_WARN_IF(mChannel
)) {
240 mChannel
->CancelInterception(NS_ERROR_INTERCEPTION_FAILED
);
246 class HeaderFiller final
: public nsIHttpHeaderVisitor
{
250 explicit HeaderFiller(HeadersGuardEnum aGuard
)
251 : mInternalHeaders(new InternalHeaders(aGuard
)) {
252 MOZ_ASSERT(mInternalHeaders
);
256 VisitHeader(const nsACString
& aHeader
, const nsACString
& aValue
) override
{
258 mInternalHeaders
->Append(aHeader
, aValue
, result
);
260 if (NS_WARN_IF(result
.Failed())) {
261 return result
.StealNSResult();
267 RefPtr
<InternalHeaders
> Extract() {
268 return RefPtr
<InternalHeaders
>(std::move(mInternalHeaders
));
272 ~HeaderFiller() = default;
274 RefPtr
<InternalHeaders
> mInternalHeaders
;
277 NS_IMPL_ISUPPORTS(HeaderFiller
, nsIHttpHeaderVisitor
)
279 Result
<IPCInternalRequest
, nsresult
> GetIPCInternalRequest(
280 nsIInterceptedChannel
* aChannel
) {
281 AssertIsOnMainThread();
283 nsCOMPtr
<nsIURI
> uri
;
284 MOZ_TRY(aChannel
->GetSecureUpgradedChannelURI(getter_AddRefs(uri
)));
286 nsCOMPtr
<nsIURI
> uriNoFragment
;
287 MOZ_TRY(NS_GetURIWithoutRef(uri
, getter_AddRefs(uriNoFragment
)));
289 nsCOMPtr
<nsIChannel
> underlyingChannel
;
290 MOZ_TRY(aChannel
->GetChannel(getter_AddRefs(underlyingChannel
)));
292 nsCOMPtr
<nsIHttpChannel
> httpChannel
= do_QueryInterface(underlyingChannel
);
293 MOZ_ASSERT(httpChannel
, "How come we don't have an HTTP channel?");
295 nsCOMPtr
<nsIHttpChannelInternal
> internalChannel
=
296 do_QueryInterface(httpChannel
);
297 NS_ENSURE_TRUE(internalChannel
, Err(NS_ERROR_NOT_AVAILABLE
));
299 nsCOMPtr
<nsICacheInfoChannel
> cacheInfoChannel
=
300 do_QueryInterface(underlyingChannel
);
303 MOZ_TRY(uriNoFragment
->GetSpec(spec
));
305 nsAutoCString fragment
;
306 MOZ_TRY(uri
->GetRef(fragment
));
308 nsAutoCString method
;
309 MOZ_TRY(httpChannel
->GetRequestMethod(method
));
311 // This is safe due to static_asserts in ServiceWorkerManager.cpp
312 uint32_t cacheModeInt
;
313 MOZ_ALWAYS_SUCCEEDS(internalChannel
->GetFetchCacheMode(&cacheModeInt
));
314 RequestCache cacheMode
= static_cast<RequestCache
>(cacheModeInt
);
316 RequestMode requestMode
=
317 InternalRequest::MapChannelToRequestMode(underlyingChannel
);
319 // This is safe due to static_asserts in ServiceWorkerManager.cpp
320 uint32_t redirectMode
;
321 MOZ_ALWAYS_SUCCEEDS(internalChannel
->GetRedirectMode(&redirectMode
));
322 RequestRedirect requestRedirect
= static_cast<RequestRedirect
>(redirectMode
);
324 RequestCredentials requestCredentials
=
325 InternalRequest::MapChannelToRequestCredentials(underlyingChannel
);
327 nsAutoString referrer
;
328 ReferrerPolicy referrerPolicy
= ReferrerPolicy::_empty
;
329 ReferrerPolicy environmentReferrerPolicy
= ReferrerPolicy::_empty
;
331 nsCOMPtr
<nsIReferrerInfo
> referrerInfo
= httpChannel
->GetReferrerInfo();
333 referrerPolicy
= referrerInfo
->ReferrerPolicy();
334 Unused
<< referrerInfo
->GetComputedReferrerSpec(referrer
);
338 MOZ_TRY(underlyingChannel
->GetLoadFlags(&loadFlags
));
340 nsCOMPtr
<nsILoadInfo
> loadInfo
= underlyingChannel
->LoadInfo();
341 nsContentPolicyType contentPolicyType
= loadInfo
->InternalContentPolicyType();
343 nsAutoString integrity
;
344 MOZ_TRY(internalChannel
->GetIntegrityMetadata(integrity
));
346 RefPtr
<HeaderFiller
> headerFiller
=
347 MakeRefPtr
<HeaderFiller
>(HeadersGuardEnum::Request
);
348 MOZ_TRY(httpChannel
->VisitNonDefaultRequestHeaders(headerFiller
));
350 RefPtr
<InternalHeaders
> internalHeaders
= headerFiller
->Extract();
353 internalHeaders
->SetGuard(HeadersGuardEnum::Immutable
, result
);
354 if (NS_WARN_IF(result
.Failed())) {
355 return Err(result
.StealNSResult());
358 nsTArray
<HeadersEntry
> ipcHeaders
;
359 HeadersGuardEnum ipcHeadersGuard
;
360 internalHeaders
->ToIPC(ipcHeaders
, ipcHeadersGuard
);
362 nsAutoCString alternativeDataType
;
363 if (cacheInfoChannel
&&
364 !cacheInfoChannel
->PreferredAlternativeDataTypes().IsEmpty()) {
365 // TODO: the internal request probably needs all the preferred types.
366 alternativeDataType
.Assign(
367 cacheInfoChannel
->PreferredAlternativeDataTypes()[0].type());
370 Maybe
<PrincipalInfo
> principalInfo
;
371 Maybe
<PrincipalInfo
> interceptionPrincipalInfo
;
372 if (loadInfo
->TriggeringPrincipal()) {
373 principalInfo
.emplace();
374 interceptionPrincipalInfo
.emplace();
375 MOZ_ALWAYS_SUCCEEDS(PrincipalToPrincipalInfo(
376 loadInfo
->TriggeringPrincipal(), principalInfo
.ptr()));
377 MOZ_ALWAYS_SUCCEEDS(PrincipalToPrincipalInfo(
378 loadInfo
->TriggeringPrincipal(), interceptionPrincipalInfo
.ptr()));
381 nsTArray
<RedirectHistoryEntryInfo
> redirectChain
;
382 for (const nsCOMPtr
<nsIRedirectHistoryEntry
>& redirectEntry
:
383 loadInfo
->RedirectChain()) {
384 RedirectHistoryEntryInfo
* entry
= redirectChain
.AppendElement();
385 MOZ_ALWAYS_SUCCEEDS(RHEntryToRHEntryInfo(redirectEntry
, entry
));
388 bool isThirdPartyChannel
;
389 // ThirdPartyUtil* thirdPartyUtil = ThirdPartyUtil::GetInstance();
390 nsCOMPtr
<mozIThirdPartyUtil
> thirdPartyUtil
=
391 do_GetService(THIRDPARTYUTIL_CONTRACTID
);
392 if (thirdPartyUtil
) {
393 nsCOMPtr
<nsIURI
> uri
;
394 MOZ_TRY(underlyingChannel
->GetURI(getter_AddRefs(uri
)));
395 MOZ_TRY(thirdPartyUtil
->IsThirdPartyChannel(underlyingChannel
, uri
,
396 &isThirdPartyChannel
));
399 nsILoadInfo::CrossOriginEmbedderPolicy embedderPolicy
=
400 loadInfo
->GetLoadingEmbedderPolicy();
402 // Note: all the arguments are copied rather than moved, which would be more
403 // efficient, because there's no move-friendly constructor generated.
404 return IPCInternalRequest(
405 method
, {spec
}, ipcHeadersGuard
, ipcHeaders
, Nothing(), -1,
406 alternativeDataType
, contentPolicyType
, referrer
, referrerPolicy
,
407 environmentReferrerPolicy
, requestMode
, requestCredentials
, cacheMode
,
408 requestRedirect
, integrity
, fragment
, principalInfo
,
409 interceptionPrincipalInfo
, contentPolicyType
, redirectChain
,
410 isThirdPartyChannel
, embedderPolicy
);
413 nsresult
MaybeStoreStreamForBackgroundThread(nsIInterceptedChannel
* aChannel
,
414 IPCInternalRequest
& aIPCRequest
) {
415 nsCOMPtr
<nsIChannel
> channel
;
416 MOZ_ALWAYS_SUCCEEDS(aChannel
->GetChannel(getter_AddRefs(channel
)));
418 Maybe
<BodyStreamVariant
> body
;
419 nsCOMPtr
<nsIUploadChannel2
> uploadChannel
= do_QueryInterface(channel
);
422 nsCOMPtr
<nsIInputStream
> uploadStream
;
423 MOZ_TRY(uploadChannel
->CloneUploadStream(&aIPCRequest
.bodySize(),
424 getter_AddRefs(uploadStream
)));
427 Maybe
<BodyStreamVariant
>& body
= aIPCRequest
.body();
428 body
.emplace(ParentToParentStream());
431 nsID::GenerateUUIDInPlace(body
->get_ParentToParentStream().uuid()));
433 auto storageOrErr
= RemoteLazyInputStreamStorage::Get();
434 if (NS_WARN_IF(storageOrErr
.isErr())) {
435 return storageOrErr
.unwrapErr();
438 auto storage
= storageOrErr
.unwrap();
439 storage
->AddStream(uploadStream
, body
->get_ParentToParentStream().uuid());
446 } // anonymous namespace
449 * ServiceWorkerPrivate
451 ServiceWorkerPrivate::ServiceWorkerPrivate(ServiceWorkerInfo
* aInfo
)
452 : mInfo(aInfo
), mDebuggerCount(0), mTokenCount(0) {
453 MOZ_ASSERT(NS_IsMainThread());
455 MOZ_ASSERT(!mControllerChild
);
457 mIdleWorkerTimer
= NS_NewTimer();
458 MOZ_ASSERT(mIdleWorkerTimer
);
460 // Assert in all debug builds as well as non-debug Nightly and Dev Edition.
461 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
462 MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(Initialize()));
464 MOZ_ALWAYS_SUCCEEDS(Initialize());
468 ServiceWorkerPrivate::~ServiceWorkerPrivate() {
469 MOZ_ASSERT(!mTokenCount
);
471 MOZ_ASSERT(!mControllerChild
);
472 MOZ_ASSERT(mIdlePromiseHolder
.IsEmpty());
474 mIdleWorkerTimer
->Cancel();
477 nsresult
ServiceWorkerPrivate::Initialize() {
478 AssertIsOnMainThread();
481 nsCOMPtr
<nsIPrincipal
> principal
= mInfo
->Principal();
483 nsCOMPtr
<nsIURI
> uri
;
484 auto* basePrin
= BasePrincipal::Cast(principal
);
485 nsresult rv
= basePrin
->GetURI(getter_AddRefs(uri
));
487 if (NS_WARN_IF(NS_FAILED(rv
))) {
491 if (NS_WARN_IF(!uri
)) {
492 return NS_ERROR_FAILURE
;
495 URIParams baseScriptURL
;
496 SerializeURI(uri
, baseScriptURL
);
499 rv
= mInfo
->GetId(id
);
501 if (NS_WARN_IF(NS_FAILED(rv
))) {
505 PrincipalInfo principalInfo
;
506 rv
= PrincipalToPrincipalInfo(principal
, &principalInfo
);
507 if (NS_WARN_IF(NS_FAILED(rv
))) {
511 RefPtr
<ServiceWorkerManager
> swm
= ServiceWorkerManager::GetInstance();
513 if (NS_WARN_IF(!swm
)) {
514 return NS_ERROR_DOM_ABORT_ERR
;
517 RefPtr
<ServiceWorkerRegistrationInfo
> regInfo
=
518 swm
->GetRegistration(principal
, mInfo
->Scope());
520 if (NS_WARN_IF(!regInfo
)) {
521 return NS_ERROR_DOM_INVALID_STATE_ERR
;
524 nsCOMPtr
<nsICookieJarSettings
> cookieJarSettings
=
525 net::CookieJarSettings::Create(principal
);
526 MOZ_ASSERT(cookieJarSettings
);
528 // We can populate the partitionKey and the fingerprinting protection
529 // overrides using the originAttribute of the principal. If it has
530 // partitionKey set, It's a foreign partitioned principal and it implies that
531 // it's a third-party service worker. So, the cookieJarSettings can directly
532 // use the partitionKey from it. For first-party case, we can populate the
533 // partitionKey from the principal URI.
534 Maybe
<uint64_t> overriddenFingerprintingSettingsArg
;
535 Maybe
<RFPTarget
> overriddenFingerprintingSettings
;
536 if (!principal
->OriginAttributesRef().mPartitionKey
.IsEmpty()) {
537 net::CookieJarSettings::Cast(cookieJarSettings
)
538 ->SetPartitionKey(principal
->OriginAttributesRef().mPartitionKey
);
540 // The service worker is for a third-party context, we get first-party
541 // domain from the partitionKey and the third-party domain from the
542 // principal of the service worker. Then, we can get the fingerprinting
543 // protection overrides using them.
545 nsAutoString pkBaseDomain
;
548 if (OriginAttributes::ParsePartitionKey(
549 principal
->OriginAttributesRef().mPartitionKey
, scheme
,
550 pkBaseDomain
, unused
)) {
551 nsCOMPtr
<nsIURI
> firstPartyURI
;
552 rv
= NS_NewURI(getter_AddRefs(firstPartyURI
),
553 scheme
+ u
"://"_ns
+ pkBaseDomain
);
554 if (NS_SUCCEEDED(rv
)) {
555 overriddenFingerprintingSettings
=
556 nsRFPService::GetOverriddenFingerprintingSettingsForURI(
558 if (overriddenFingerprintingSettings
.isSome()) {
559 overriddenFingerprintingSettingsArg
.emplace(
560 uint64_t(overriddenFingerprintingSettings
.ref()));
564 } else if (!principal
->OriginAttributesRef().mFirstPartyDomain
.IsEmpty()) {
565 // Using the first party domain to know the context of the service worker.
566 // We will run into here if FirstPartyIsolation is enabled. In this case,
567 // the PartitionKey won't get populated.
568 nsCOMPtr
<nsIURI
> firstPartyURI
;
569 // Because the service worker is only available in secure contexts, so we
570 // don't need to consider http and only use https as scheme to create
571 // the first-party URI
573 getter_AddRefs(firstPartyURI
),
574 u
"https://"_ns
+ principal
->OriginAttributesRef().mFirstPartyDomain
);
575 if (NS_SUCCEEDED(rv
)) {
576 // If the first party domain is not a third-party domain, the service
577 // worker is running in first-party context.
579 rv
= principal
->IsThirdPartyURI(firstPartyURI
, &isThirdParty
);
580 NS_ENSURE_SUCCESS(rv
, rv
);
582 overriddenFingerprintingSettings
=
584 ? nsRFPService::GetOverriddenFingerprintingSettingsForURI(
586 : nsRFPService::GetOverriddenFingerprintingSettingsForURI(
589 if (overriddenFingerprintingSettings
.isSome()) {
590 overriddenFingerprintingSettingsArg
.emplace(
591 uint64_t(overriddenFingerprintingSettings
.ref()));
595 net::CookieJarSettings::Cast(cookieJarSettings
)->SetPartitionKey(uri
);
597 // The service worker is for a first-party context, we can use the uri of
598 // the service worker as the first-party domain to get the fingerprinting
599 // protection overrides.
600 overriddenFingerprintingSettings
=
601 nsRFPService::GetOverriddenFingerprintingSettingsForURI(uri
, nullptr);
603 if (overriddenFingerprintingSettings
.isSome()) {
604 overriddenFingerprintingSettingsArg
.emplace(
605 uint64_t(overriddenFingerprintingSettings
.ref()));
609 net::CookieJarSettingsArgs cjsData
;
610 net::CookieJarSettings::Cast(cookieJarSettings
)->Serialize(cjsData
);
612 nsCOMPtr
<nsIPrincipal
> partitionedPrincipal
;
613 rv
= StoragePrincipalHelper::CreatePartitionedPrincipalForServiceWorker(
614 principal
, cookieJarSettings
, getter_AddRefs(partitionedPrincipal
));
615 if (NS_WARN_IF(NS_FAILED(rv
))) {
619 PrincipalInfo partitionedPrincipalInfo
;
621 PrincipalToPrincipalInfo(partitionedPrincipal
, &partitionedPrincipalInfo
);
622 if (NS_WARN_IF(NS_FAILED(rv
))) {
626 StorageAccess storageAccess
=
627 StorageAllowedForServiceWorker(principal
, cookieJarSettings
);
629 ServiceWorkerData serviceWorkerData
;
630 serviceWorkerData
.cacheName() = mInfo
->CacheName();
631 serviceWorkerData
.loadFlags() = static_cast<uint32_t>(
632 mInfo
->GetImportsLoadFlags() | nsIChannel::LOAD_BYPASS_SERVICE_WORKER
);
633 serviceWorkerData
.id() = std::move(id
);
635 nsAutoCString domain
;
636 rv
= uri
->GetHost(domain
);
637 if (NS_WARN_IF(NS_FAILED(rv
))) {
641 auto remoteType
= RemoteWorkerManager::GetRemoteType(
642 principal
, WorkerKind::WorkerKindService
);
643 if (NS_WARN_IF(remoteType
.isErr())) {
644 return remoteType
.unwrapErr();
647 // Determine if the service worker is registered under a third-party context
648 // by checking if it's running under a partitioned principal.
649 bool isThirdPartyContextToTopWindow
=
650 !principal
->OriginAttributesRef().mPartitionKey
.IsEmpty();
652 mRemoteWorkerData
= RemoteWorkerData(
653 NS_ConvertUTF8toUTF16(mInfo
->ScriptSpec()), baseScriptURL
, baseScriptURL
,
654 /* name */ VoidString(),
655 /* workerType */ WorkerType::Classic
,
656 /* credentials */ RequestCredentials::Omit
,
657 /* loading principal */ principalInfo
, principalInfo
,
658 partitionedPrincipalInfo
,
659 /* useRegularPrincipal */ true,
661 // ServiceWorkers run as first-party, no storage-access permission needed.
662 /* usingStorageAccess */ false,
665 /* isSecureContext */ true,
666 /* clientInfo*/ Nothing(),
668 // The RemoteWorkerData CTOR doesn't allow to set the referrerInfo via
669 // already_AddRefed<>. Let's set it to null.
670 /* referrerInfo */ nullptr,
672 storageAccess
, isThirdPartyContextToTopWindow
,
673 nsContentUtils::ShouldResistFingerprinting_dangerous(
675 "Service Workers exist outside a Document or Channel; as a property "
676 "of the domain (and origin attributes). We don't have a "
677 "CookieJarSettings to perform the nested check, but we can rely on"
678 "the FPI/dFPI partition key check. The WorkerPrivate's "
679 "ShouldResistFingerprinting function for the ServiceWorker depends "
680 "on this boolean and will also consider an explicit RFPTarget.",
681 RFPTarget::IsAlwaysEnabledForPrecompute
),
682 overriddenFingerprintingSettingsArg
,
683 // Origin trials are associated to a window, so it doesn't make sense on
685 OriginTrials(), std::move(serviceWorkerData
), regInfo
->AgentClusterId(),
686 remoteType
.unwrap());
688 mRemoteWorkerData
.referrerInfo() = MakeAndAddRef
<ReferrerInfo
>();
690 // This fills in the rest of mRemoteWorkerData.serviceWorkerData().
691 RefreshRemoteWorkerData(regInfo
);
696 nsresult
ServiceWorkerPrivate::CheckScriptEvaluation(
697 RefPtr
<LifeCycleEventCallback
> aCallback
) {
698 MOZ_ASSERT(NS_IsMainThread());
699 MOZ_ASSERT(aCallback
);
701 RefPtr
<ServiceWorkerPrivate
> self
= this;
704 * We need to capture the actor associated with the current Service Worker so
705 * we can terminate it if script evaluation failed.
707 nsresult rv
= SpawnWorkerIfNeeded();
709 if (NS_WARN_IF(NS_FAILED(rv
))) {
710 aCallback
->SetResult(false);
716 MOZ_ASSERT(mControllerChild
);
718 RefPtr
<RAIIActorPtrHolder
> holder
= mControllerChild
;
720 return ExecServiceWorkerOp(
721 ServiceWorkerCheckScriptEvaluationOpArgs(),
722 [self
= std::move(self
), holder
= std::move(holder
),
723 callback
= aCallback
](ServiceWorkerOpResult
&& aResult
) mutable {
724 if (aResult
.type() == ServiceWorkerOpResult::
725 TServiceWorkerCheckScriptEvaluationOpResult
) {
727 aResult
.get_ServiceWorkerCheckScriptEvaluationOpResult();
729 if (result
.workerScriptExecutedSuccessfully()) {
730 self
->SetHandlesFetch(result
.fetchHandlerWasAdded());
731 if (self
->mHandlesFetch
== Unknown
) {
732 self
->mHandlesFetch
=
733 result
.fetchHandlerWasAdded() ? Enabled
: Disabled
;
734 // Update telemetry for # of running SW - the already-running SW
736 if (self
->mHandlesFetch
== Enabled
) {
737 self
->UpdateRunning(0, 1);
741 callback
->SetResult(result
.workerScriptExecutedSuccessfully());
748 * If script evaluation failed, first terminate the Service Worker
749 * before invoking the callback.
751 MOZ_ASSERT_IF(aResult
.type() == ServiceWorkerOpResult::Tnsresult
,
752 NS_FAILED(aResult
.get_nsresult()));
754 // If a termination operation was already issued using `holder`...
755 if (self
->mControllerChild
!= holder
) {
756 holder
->OnDestructor()->Then(
757 GetCurrentSerialEventTarget(), __func__
,
758 [callback
= std::move(callback
)](
759 const GenericPromise::ResolveOrRejectValue
&) {
760 callback
->SetResult(false);
767 RefPtr
<ServiceWorkerManager
> swm
= ServiceWorkerManager::GetInstance();
770 auto shutdownStateId
= swm
->MaybeInitServiceWorkerShutdownProgress();
772 RefPtr
<GenericNonExclusivePromise
> promise
=
773 self
->ShutdownInternal(shutdownStateId
);
775 swm
->BlockShutdownOn(promise
, shutdownStateId
);
778 GetCurrentSerialEventTarget(), __func__
,
779 [callback
= std::move(callback
)](
780 const GenericNonExclusivePromise::ResolveOrRejectValue
&) {
781 callback
->SetResult(false);
785 [callback
= aCallback
] {
786 callback
->SetResult(false);
791 nsresult
ServiceWorkerPrivate::SendMessageEvent(
792 RefPtr
<ServiceWorkerCloneData
>&& aData
,
793 const ClientInfoAndState
& aClientInfoAndState
) {
794 AssertIsOnMainThread();
797 auto scopeExit
= MakeScopeExit([&] { Shutdown(); });
799 PBackgroundChild
* bgChild
= BackgroundChild::GetForCurrentThread();
801 if (NS_WARN_IF(!bgChild
)) {
802 return NS_ERROR_DOM_INVALID_STATE_ERR
;
805 ServiceWorkerMessageEventOpArgs args
;
806 args
.clientInfoAndState() = aClientInfoAndState
;
807 if (!aData
->BuildClonedMessageData(args
.clonedData())) {
808 return NS_ERROR_DOM_DATA_CLONE_ERR
;
813 return ExecServiceWorkerOp(
814 std::move(args
), [](ServiceWorkerOpResult
&& aResult
) {
815 MOZ_ASSERT(aResult
.type() == ServiceWorkerOpResult::Tnsresult
);
819 nsresult
ServiceWorkerPrivate::SendLifeCycleEvent(
820 const nsAString
& aEventType
, RefPtr
<LifeCycleEventCallback
> aCallback
) {
821 AssertIsOnMainThread();
822 MOZ_ASSERT(aCallback
);
824 return ExecServiceWorkerOp(
825 ServiceWorkerLifeCycleEventOpArgs(nsString(aEventType
)),
826 [callback
= aCallback
](ServiceWorkerOpResult
&& aResult
) {
827 MOZ_ASSERT(aResult
.type() == ServiceWorkerOpResult::Tnsresult
);
829 callback
->SetResult(NS_SUCCEEDED(aResult
.get_nsresult()));
832 [callback
= aCallback
] {
833 callback
->SetResult(false);
838 nsresult
ServiceWorkerPrivate::SendPushEvent(
839 const nsAString
& aMessageId
, const Maybe
<nsTArray
<uint8_t>>& aData
,
840 RefPtr
<ServiceWorkerRegistrationInfo
> aRegistration
) {
841 AssertIsOnMainThread();
843 MOZ_ASSERT(aRegistration
);
845 ServiceWorkerPushEventOpArgs args
;
846 args
.messageId() = nsString(aMessageId
);
849 args
.data() = aData
.ref();
851 args
.data() = void_t();
854 if (mInfo
->State() == ServiceWorkerState::Activating
) {
855 UniquePtr
<PendingFunctionalEvent
> pendingEvent
=
856 MakeUnique
<PendingPushEvent
>(this, std::move(aRegistration
),
859 mPendingFunctionalEvents
.AppendElement(std::move(pendingEvent
));
864 MOZ_ASSERT(mInfo
->State() == ServiceWorkerState::Activated
);
866 return SendPushEventInternal(std::move(aRegistration
), std::move(args
));
869 nsresult
ServiceWorkerPrivate::SendPushEventInternal(
870 RefPtr
<ServiceWorkerRegistrationInfo
>&& aRegistration
,
871 ServiceWorkerPushEventOpArgs
&& aArgs
) {
872 AssertIsOnMainThread();
873 MOZ_ASSERT(aRegistration
);
875 return ExecServiceWorkerOp(
877 [registration
= aRegistration
](ServiceWorkerOpResult
&& aResult
) {
878 MOZ_ASSERT(aResult
.type() == ServiceWorkerOpResult::Tnsresult
);
880 registration
->MaybeScheduleTimeCheckAndUpdate();
882 [registration
= aRegistration
]() {
883 registration
->MaybeScheduleTimeCheckAndUpdate();
887 nsresult
ServiceWorkerPrivate::SendPushSubscriptionChangeEvent() {
888 AssertIsOnMainThread();
890 return ExecServiceWorkerOp(
891 ServiceWorkerPushSubscriptionChangeEventOpArgs(),
892 [](ServiceWorkerOpResult
&& aResult
) {
893 MOZ_ASSERT(aResult
.type() == ServiceWorkerOpResult::Tnsresult
);
897 nsresult
ServiceWorkerPrivate::SendNotificationEvent(
898 const nsAString
& aEventName
, const nsAString
& aID
, const nsAString
& aTitle
,
899 const nsAString
& aDir
, const nsAString
& aLang
, const nsAString
& aBody
,
900 const nsAString
& aTag
, const nsAString
& aIcon
, const nsAString
& aData
,
901 const nsAString
& aBehavior
, const nsAString
& aScope
) {
902 MOZ_ASSERT(NS_IsMainThread());
904 if (aEventName
.EqualsLiteral(NOTIFICATION_CLICK_EVENT_NAME
)) {
905 gDOMDisableOpenClickDelay
=
906 Preferences::GetInt("dom.serviceWorkers.disable_open_click_delay");
907 } else if (!aEventName
.EqualsLiteral(NOTIFICATION_CLOSE_EVENT_NAME
)) {
908 MOZ_ASSERT_UNREACHABLE("Invalid notification event name");
909 return NS_ERROR_FAILURE
;
912 ServiceWorkerNotificationEventOpArgs args
;
913 args
.eventName() = nsString(aEventName
);
914 args
.id() = nsString(aID
);
915 args
.title() = nsString(aTitle
);
916 args
.dir() = nsString(aDir
);
917 args
.lang() = nsString(aLang
);
918 args
.body() = nsString(aBody
);
919 args
.tag() = nsString(aTag
);
920 args
.icon() = nsString(aIcon
);
921 args
.data() = nsString(aData
);
922 args
.behavior() = nsString(aBehavior
);
923 args
.scope() = nsString(aScope
);
924 args
.disableOpenClickDelay() = gDOMDisableOpenClickDelay
;
926 return ExecServiceWorkerOp(
927 std::move(args
), [](ServiceWorkerOpResult
&& aResult
) {
928 MOZ_ASSERT(aResult
.type() == ServiceWorkerOpResult::Tnsresult
);
932 nsresult
ServiceWorkerPrivate::SendFetchEvent(
933 nsCOMPtr
<nsIInterceptedChannel
> aChannel
, nsILoadGroup
* aLoadGroup
,
934 const nsAString
& aClientId
, const nsAString
& aResultingClientId
) {
935 MOZ_ASSERT(NS_IsMainThread());
936 MOZ_ASSERT(aChannel
);
938 RefPtr
<ServiceWorkerManager
> swm
= ServiceWorkerManager::GetInstance();
939 if (NS_WARN_IF(!mInfo
|| !swm
)) {
940 return NS_ERROR_FAILURE
;
943 nsCOMPtr
<nsIChannel
> channel
;
944 nsresult rv
= aChannel
->GetChannel(getter_AddRefs(channel
));
945 NS_ENSURE_SUCCESS(rv
, rv
);
946 bool isNonSubresourceRequest
=
947 nsContentUtils::IsNonSubresourceRequest(channel
);
949 RefPtr
<ServiceWorkerRegistrationInfo
> registration
;
950 if (isNonSubresourceRequest
) {
951 registration
= swm
->GetRegistration(mInfo
->Principal(), mInfo
->Scope());
953 nsCOMPtr
<nsILoadInfo
> loadInfo
= channel
->LoadInfo();
955 // We'll check for a null registration below rather than an error code here.
956 Unused
<< swm
->GetClientRegistration(loadInfo
->GetClientInfo().ref(),
957 getter_AddRefs(registration
));
960 // Its possible the registration is removed between starting the interception
961 // and actually dispatching the fetch event. In these cases we simply
962 // want to restart the original network request. Since this is a normal
963 // condition we handle the reset here instead of returning an error which
964 // would in turn trigger a console report.
966 nsresult rv
= aChannel
->ResetInterception(false);
968 NS_WARNING("Failed to resume intercepted network request");
969 aChannel
->CancelInterception(rv
);
974 // Handle Fetch algorithm - step 16. If the service worker didn't register
975 // any fetch event handlers, then abort the interception and maybe trigger
976 // the soft update algorithm.
977 if (!mInfo
->HandlesFetch()) {
978 nsresult rv
= aChannel
->ResetInterception(false);
980 NS_WARNING("Failed to resume intercepted network request");
981 aChannel
->CancelInterception(rv
);
984 // Trigger soft updates if necessary.
985 registration
->MaybeScheduleTimeCheckAndUpdate();
990 auto scopeExit
= MakeScopeExit([&] {
991 aChannel
->CancelInterception(NS_ERROR_INTERCEPTION_FAILED
);
995 IPCInternalRequest request
;
996 MOZ_TRY_VAR(request
, GetIPCInternalRequest(aChannel
));
1000 bool preloadNavigation
= isNonSubresourceRequest
&&
1001 request
.method().LowerCaseEqualsASCII("get") &&
1002 registration
->GetNavigationPreloadState().enabled();
1004 RefPtr
<FetchServicePromises
> preloadResponsePromises
;
1005 if (preloadNavigation
) {
1006 preloadResponsePromises
= SetupNavigationPreload(aChannel
, registration
);
1009 ParentToParentServiceWorkerFetchEventOpArgs
args(
1010 ServiceWorkerFetchEventOpArgsCommon(
1011 mInfo
->ScriptSpec(), request
, nsString(aClientId
),
1012 nsString(aResultingClientId
), isNonSubresourceRequest
,
1013 preloadNavigation
, mInfo
->TestingInjectCancellation()),
1014 Nothing(), Nothing(), Nothing());
1016 if (mInfo
->State() == ServiceWorkerState::Activating
) {
1017 UniquePtr
<PendingFunctionalEvent
> pendingEvent
=
1018 MakeUnique
<PendingFetchEvent
>(this, std::move(registration
),
1019 std::move(args
), std::move(aChannel
),
1020 std::move(preloadResponsePromises
));
1022 mPendingFunctionalEvents
.AppendElement(std::move(pendingEvent
));
1027 MOZ_ASSERT(mInfo
->State() == ServiceWorkerState::Activated
);
1029 return SendFetchEventInternal(std::move(registration
), std::move(args
),
1030 std::move(aChannel
),
1031 std::move(preloadResponsePromises
));
1034 nsresult
ServiceWorkerPrivate::SendFetchEventInternal(
1035 RefPtr
<ServiceWorkerRegistrationInfo
>&& aRegistration
,
1036 ParentToParentServiceWorkerFetchEventOpArgs
&& aArgs
,
1037 nsCOMPtr
<nsIInterceptedChannel
>&& aChannel
,
1038 RefPtr
<FetchServicePromises
>&& aPreloadResponseReadyPromises
) {
1039 AssertIsOnMainThread();
1041 auto scopeExit
= MakeScopeExit([&] { Shutdown(); });
1043 if (NS_WARN_IF(!mInfo
)) {
1044 return NS_ERROR_DOM_INVALID_STATE_ERR
;
1047 MOZ_TRY(SpawnWorkerIfNeeded());
1048 MOZ_TRY(MaybeStoreStreamForBackgroundThread(
1049 aChannel
, aArgs
.common().internalRequest()));
1051 scopeExit
.release();
1053 MOZ_ASSERT(mControllerChild
);
1055 RefPtr
<RAIIActorPtrHolder
> holder
= mControllerChild
;
1057 FetchEventOpChild::SendFetchEvent(
1058 mControllerChild
->get(), std::move(aArgs
), std::move(aChannel
),
1059 std::move(aRegistration
), std::move(aPreloadResponseReadyPromises
),
1060 CreateEventKeepAliveToken())
1061 ->Then(GetCurrentSerialEventTarget(), __func__
,
1062 [holder
= std::move(holder
)](
1063 const GenericPromise::ResolveOrRejectValue
& aResult
) {
1064 Unused
<< NS_WARN_IF(aResult
.IsReject());
1070 Result
<RefPtr
<ServiceWorkerPrivate::PromiseExtensionWorkerHasListener
>,
1072 ServiceWorkerPrivate::WakeForExtensionAPIEvent(
1073 const nsAString
& aExtensionAPINamespace
,
1074 const nsAString
& aExtensionAPIEventName
) {
1075 AssertIsOnMainThread();
1077 ServiceWorkerExtensionAPIEventOpArgs args
;
1078 args
.apiNamespace() = nsString(aExtensionAPINamespace
);
1079 args
.apiEventName() = nsString(aExtensionAPIEventName
);
1082 MakeRefPtr
<PromiseExtensionWorkerHasListener::Private
>(__func__
);
1084 nsresult rv
= ExecServiceWorkerOp(
1086 [promise
](ServiceWorkerOpResult
&& aResult
) {
1089 ServiceWorkerOpResult::TServiceWorkerExtensionAPIEventOpResult
);
1090 auto& result
= aResult
.get_ServiceWorkerExtensionAPIEventOpResult();
1091 promise
->Resolve(result
.extensionAPIEventListenerWasAdded(), __func__
);
1093 [promise
]() { promise
->Reject(NS_ERROR_FAILURE
, __func__
); });
1095 if (NS_FAILED(rv
)) {
1096 promise
->Reject(rv
, __func__
);
1099 RefPtr
<PromiseExtensionWorkerHasListener
> outPromise(promise
);
1103 nsresult
ServiceWorkerPrivate::SpawnWorkerIfNeeded() {
1104 AssertIsOnMainThread();
1106 if (mControllerChild
) {
1107 RenewKeepAliveToken();
1112 return NS_ERROR_DOM_INVALID_STATE_ERR
;
1115 mServiceWorkerLaunchTimeStart
= TimeStamp::Now();
1117 PBackgroundChild
* bgChild
= BackgroundChild::GetForCurrentThread();
1119 if (NS_WARN_IF(!bgChild
)) {
1120 return NS_ERROR_DOM_INVALID_STATE_ERR
;
1123 // If the worker principal is an extension principal, then we should not spawn
1124 // a worker if there is no WebExtensionPolicy associated to that principal
1125 // or if the WebExtensionPolicy is not active.
1126 auto* principal
= mInfo
->Principal();
1127 if (principal
->SchemeIs("moz-extension")) {
1128 auto* addonPolicy
= BasePrincipal::Cast(principal
)->AddonPolicy();
1129 if (!addonPolicy
|| !addonPolicy
->Active()) {
1131 "Trying to wake up a service worker for a disabled webextension.");
1132 return NS_ERROR_DOM_INVALID_STATE_ERR
;
1136 RefPtr
<ServiceWorkerManager
> swm
= ServiceWorkerManager::GetInstance();
1138 if (NS_WARN_IF(!swm
)) {
1139 return NS_ERROR_DOM_ABORT_ERR
;
1142 RefPtr
<ServiceWorkerRegistrationInfo
> regInfo
=
1143 swm
->GetRegistration(principal
, mInfo
->Scope());
1145 if (NS_WARN_IF(!regInfo
)) {
1146 return NS_ERROR_DOM_INVALID_STATE_ERR
;
1149 RefreshRemoteWorkerData(regInfo
);
1151 RefPtr
<RemoteWorkerControllerChild
> controllerChild
=
1152 new RemoteWorkerControllerChild(this);
1154 if (NS_WARN_IF(!bgChild
->SendPRemoteWorkerControllerConstructor(
1155 controllerChild
, mRemoteWorkerData
))) {
1156 return NS_ERROR_DOM_INVALID_STATE_ERR
;
1159 mControllerChild
= new RAIIActorPtrHolder(controllerChild
.forget());
1161 // Update Running count here because we may Terminate before we get
1162 // CreationSucceeded(). We'll update if it handles Fetch if that changes
1164 UpdateRunning(1, mHandlesFetch
== Enabled
? 1 : 0);
1169 void ServiceWorkerPrivate::TerminateWorker() {
1170 MOZ_ASSERT(NS_IsMainThread());
1171 mIdleWorkerTimer
->Cancel();
1172 mIdleKeepAliveToken
= nullptr;
1176 void ServiceWorkerPrivate::NoteDeadServiceWorkerInfo() {
1177 MOZ_ASSERT(NS_IsMainThread());
1183 void ServiceWorkerPrivate::UpdateState(ServiceWorkerState aState
) {
1184 AssertIsOnMainThread();
1186 if (!mControllerChild
) {
1190 nsresult rv
= ExecServiceWorkerOp(
1191 ServiceWorkerUpdateStateOpArgs(aState
),
1192 [](ServiceWorkerOpResult
&& aResult
) {
1193 MOZ_ASSERT(aResult
.type() == ServiceWorkerOpResult::Tnsresult
);
1196 if (NS_WARN_IF(NS_FAILED(rv
))) {
1201 if (aState
!= ServiceWorkerState::Activated
) {
1205 for (auto& event
: mPendingFunctionalEvents
) {
1206 Unused
<< NS_WARN_IF(NS_FAILED(event
->Send()));
1209 mPendingFunctionalEvents
.Clear();
1212 nsresult
ServiceWorkerPrivate::GetDebugger(nsIWorkerDebugger
** aResult
) {
1213 MOZ_ASSERT(NS_IsMainThread());
1214 MOZ_ASSERT(aResult
);
1216 return NS_ERROR_NOT_IMPLEMENTED
;
1219 nsresult
ServiceWorkerPrivate::AttachDebugger() {
1220 MOZ_ASSERT(NS_IsMainThread());
1222 // When the first debugger attaches to a worker, we spawn a worker if needed,
1223 // and cancel the idle timeout. The idle timeout should not be reset until
1224 // the last debugger detached from the worker.
1225 if (!mDebuggerCount
) {
1226 nsresult rv
= SpawnWorkerIfNeeded();
1227 NS_ENSURE_SUCCESS(rv
, rv
);
1230 * Renewing the idle KeepAliveToken for spawning workers happens
1231 * asynchronously, rather than synchronously.
1232 * The asynchronous renewal is because the actual spawning of workers occurs
1233 * in a content process, so we will only renew once notified that the worker
1234 * has been successfully created
1236 * This means that the DevTools way of starting up a worker by calling
1237 * `AttachDebugger` immediately followed by `DetachDebugger` will spawn and
1238 * immediately terminate a worker (because `mTokenCount` is possibly 0
1239 * due to the idle KeepAliveToken being created asynchronously). So, just
1240 * renew the KeepAliveToken right now.
1242 RenewKeepAliveToken();
1243 mIdleWorkerTimer
->Cancel();
1251 nsresult
ServiceWorkerPrivate::DetachDebugger() {
1252 MOZ_ASSERT(NS_IsMainThread());
1254 if (!mDebuggerCount
) {
1255 return NS_ERROR_UNEXPECTED
;
1260 // When the last debugger detaches from a worker, we either reset the idle
1261 // timeout, or terminate the worker if there are no more active tokens.
1262 if (!mDebuggerCount
) {
1273 bool ServiceWorkerPrivate::IsIdle() const {
1274 MOZ_ASSERT(NS_IsMainThread());
1275 return mTokenCount
== 0 || (mTokenCount
== 1 && mIdleKeepAliveToken
);
1278 RefPtr
<GenericPromise
> ServiceWorkerPrivate::GetIdlePromise() {
1280 MOZ_ASSERT(NS_IsMainThread());
1281 MOZ_ASSERT(!IsIdle());
1282 MOZ_ASSERT(!mIdlePromiseObtained
, "Idle promise may only be obtained once!");
1283 mIdlePromiseObtained
= true;
1286 return mIdlePromiseHolder
.Ensure(__func__
);
1291 class ServiceWorkerPrivateTimerCallback final
: public nsITimerCallback
,
1294 using Method
= void (ServiceWorkerPrivate::*)(nsITimer
*);
1296 ServiceWorkerPrivateTimerCallback(ServiceWorkerPrivate
* aServiceWorkerPrivate
,
1298 : mServiceWorkerPrivate(aServiceWorkerPrivate
), mMethod(aMethod
) {}
1301 Notify(nsITimer
* aTimer
) override
{
1302 (mServiceWorkerPrivate
->*mMethod
)(aTimer
);
1303 mServiceWorkerPrivate
= nullptr;
1308 GetName(nsACString
& aName
) override
{
1309 aName
.AssignLiteral("ServiceWorkerPrivateTimerCallback");
1314 ~ServiceWorkerPrivateTimerCallback() = default;
1316 RefPtr
<ServiceWorkerPrivate
> mServiceWorkerPrivate
;
1319 NS_DECL_THREADSAFE_ISUPPORTS
1322 NS_IMPL_ISUPPORTS(ServiceWorkerPrivateTimerCallback
, nsITimerCallback
,
1325 } // anonymous namespace
1327 void ServiceWorkerPrivate::NoteIdleWorkerCallback(nsITimer
* aTimer
) {
1328 MOZ_ASSERT(NS_IsMainThread());
1330 MOZ_ASSERT(aTimer
== mIdleWorkerTimer
, "Invalid timer!");
1332 // Release ServiceWorkerPrivate's token, since the grace period has ended.
1333 mIdleKeepAliveToken
= nullptr;
1335 if (mControllerChild
) {
1336 // If we still have a living worker at this point it means that either there
1337 // are pending waitUntil promises or the worker is doing some long-running
1338 // computation. Wait a bit more until we forcibly terminate the worker.
1340 Preferences::GetInt("dom.serviceWorkers.idle_extended_timeout");
1341 nsCOMPtr
<nsITimerCallback
> cb
= new ServiceWorkerPrivateTimerCallback(
1342 this, &ServiceWorkerPrivate::TerminateWorkerCallback
);
1343 DebugOnly
<nsresult
> rv
= mIdleWorkerTimer
->InitWithCallback(
1344 cb
, timeout
, nsITimer::TYPE_ONE_SHOT
);
1345 MOZ_ASSERT(NS_SUCCEEDED(rv
));
1349 void ServiceWorkerPrivate::TerminateWorkerCallback(nsITimer
* aTimer
) {
1350 MOZ_ASSERT(NS_IsMainThread());
1352 MOZ_ASSERT(aTimer
== this->mIdleWorkerTimer
, "Invalid timer!");
1354 // mInfo must be non-null at this point because NoteDeadServiceWorkerInfo
1355 // which zeroes it calls TerminateWorker which cancels our timer which will
1356 // ensure we don't get invoked even if the nsTimerEvent is in the event queue.
1357 ServiceWorkerManager::LocalizeAndReportToAllClients(
1358 mInfo
->Scope(), "ServiceWorkerGraceTimeoutTermination",
1359 nsTArray
<nsString
>{NS_ConvertUTF8toUTF16(mInfo
->Scope())});
1364 void ServiceWorkerPrivate::RenewKeepAliveToken() {
1365 // We should have an active worker if we're renewing the keep alive token.
1366 MOZ_ASSERT(mControllerChild
);
1368 // If there is at least one debugger attached to the worker, the idle worker
1369 // timeout was canceled when the first debugger attached to the worker. It
1370 // should not be reset until the last debugger detaches from the worker.
1371 if (!mDebuggerCount
) {
1375 if (!mIdleKeepAliveToken
) {
1376 mIdleKeepAliveToken
= new KeepAliveToken(this);
1380 void ServiceWorkerPrivate::ResetIdleTimeout() {
1381 uint32_t timeout
= Preferences::GetInt("dom.serviceWorkers.idle_timeout");
1382 nsCOMPtr
<nsITimerCallback
> cb
= new ServiceWorkerPrivateTimerCallback(
1383 this, &ServiceWorkerPrivate::NoteIdleWorkerCallback
);
1384 DebugOnly
<nsresult
> rv
=
1385 mIdleWorkerTimer
->InitWithCallback(cb
, timeout
, nsITimer::TYPE_ONE_SHOT
);
1386 MOZ_ASSERT(NS_SUCCEEDED(rv
));
1389 void ServiceWorkerPrivate::AddToken() {
1390 MOZ_ASSERT(NS_IsMainThread());
1394 void ServiceWorkerPrivate::ReleaseToken() {
1395 MOZ_ASSERT(NS_IsMainThread());
1397 MOZ_ASSERT(mTokenCount
> 0);
1401 mIdlePromiseHolder
.ResolveIfExists(true, __func__
);
1407 // mInfo can be nullptr here if NoteDeadServiceWorkerInfo() is called while
1408 // the KeepAliveToken is being proxy released as a runnable.
1410 RefPtr
<ServiceWorkerManager
> swm
= ServiceWorkerManager::GetInstance();
1412 swm
->WorkerIsIdle(mInfo
);
1418 already_AddRefed
<KeepAliveToken
>
1419 ServiceWorkerPrivate::CreateEventKeepAliveToken() {
1420 MOZ_ASSERT(NS_IsMainThread());
1422 // When the WorkerPrivate is in a separate process, we first hold a normal
1423 // KeepAliveToken. Then, after we're notified that the worker is alive, we
1424 // create the idle KeepAliveToken.
1425 MOZ_ASSERT(mIdleKeepAliveToken
|| mControllerChild
);
1427 RefPtr
<KeepAliveToken
> ref
= new KeepAliveToken(this);
1428 return ref
.forget();
1431 void ServiceWorkerPrivate::SetHandlesFetch(bool aValue
) {
1432 MOZ_ASSERT(NS_IsMainThread());
1434 if (NS_WARN_IF(!mInfo
)) {
1438 mInfo
->SetHandlesFetch(aValue
);
1441 RefPtr
<GenericPromise
> ServiceWorkerPrivate::SetSkipWaitingFlag() {
1442 AssertIsOnMainThread();
1445 RefPtr
<ServiceWorkerManager
> swm
= ServiceWorkerManager::GetInstance();
1448 return GenericPromise::CreateAndReject(NS_ERROR_FAILURE
, __func__
);
1451 RefPtr
<ServiceWorkerRegistrationInfo
> regInfo
=
1452 swm
->GetRegistration(mInfo
->Principal(), mInfo
->Scope());
1455 return GenericPromise::CreateAndReject(NS_ERROR_FAILURE
, __func__
);
1458 mInfo
->SetSkipWaitingFlag();
1460 RefPtr
<GenericPromise::Private
> promise
=
1461 new GenericPromise::Private(__func__
);
1463 regInfo
->TryToActivateAsync([promise
] { promise
->Resolve(true, __func__
); });
1469 void ServiceWorkerPrivate::UpdateRunning(int32_t aDelta
, int32_t aFetchDelta
) {
1470 // Record values for time we were running at the current values
1471 RefPtr
<ServiceWorkerManager
> manager(ServiceWorkerManager::GetInstance());
1472 manager
->RecordTelemetry(sRunningServiceWorkers
, sRunningServiceWorkersFetch
);
1474 MOZ_ASSERT(((int64_t)sRunningServiceWorkers
) + aDelta
>= 0);
1475 sRunningServiceWorkers
+= aDelta
;
1476 if (sRunningServiceWorkers
> sRunningServiceWorkersMax
) {
1477 sRunningServiceWorkersMax
= sRunningServiceWorkers
;
1478 LOG(("ServiceWorker max now %d", sRunningServiceWorkersMax
));
1479 Telemetry::ScalarSet(Telemetry::ScalarID::SERVICEWORKER_RUNNING_MAX
,
1480 u
"All"_ns
, sRunningServiceWorkersMax
);
1482 MOZ_ASSERT(((int64_t)sRunningServiceWorkersFetch
) + aFetchDelta
>= 0);
1483 sRunningServiceWorkersFetch
+= aFetchDelta
;
1484 if (sRunningServiceWorkersFetch
> sRunningServiceWorkersFetchMax
) {
1485 sRunningServiceWorkersFetchMax
= sRunningServiceWorkersFetch
;
1486 LOG(("ServiceWorker Fetch max now %d", sRunningServiceWorkersFetchMax
));
1487 Telemetry::ScalarSet(Telemetry::ScalarID::SERVICEWORKER_RUNNING_MAX
,
1488 u
"Fetch"_ns
, sRunningServiceWorkersFetchMax
);
1490 LOG(("ServiceWorkers running now %d/%d", sRunningServiceWorkers
,
1491 sRunningServiceWorkersFetch
));
1494 void ServiceWorkerPrivate::CreationFailed() {
1495 MOZ_ASSERT(NS_IsMainThread());
1496 MOZ_ASSERT(mControllerChild
);
1498 if (mRemoteWorkerData
.remoteType().Find(SERVICEWORKER_REMOTE_TYPE
) !=
1500 Telemetry::AccumulateTimeDelta(
1501 Telemetry::SERVICE_WORKER_ISOLATED_LAUNCH_TIME
,
1502 mServiceWorkerLaunchTimeStart
);
1504 Telemetry::AccumulateTimeDelta(Telemetry::SERVICE_WORKER_LAUNCH_TIME_2
,
1505 mServiceWorkerLaunchTimeStart
);
1511 void ServiceWorkerPrivate::CreationSucceeded() {
1512 AssertIsOnMainThread();
1513 MOZ_ASSERT(NS_IsMainThread());
1515 MOZ_ASSERT(mControllerChild
);
1517 if (mRemoteWorkerData
.remoteType().Find(SERVICEWORKER_REMOTE_TYPE
) !=
1519 Telemetry::AccumulateTimeDelta(
1520 Telemetry::SERVICE_WORKER_ISOLATED_LAUNCH_TIME
,
1521 mServiceWorkerLaunchTimeStart
);
1523 Telemetry::AccumulateTimeDelta(Telemetry::SERVICE_WORKER_LAUNCH_TIME_2
,
1524 mServiceWorkerLaunchTimeStart
);
1527 RenewKeepAliveToken();
1529 RefPtr
<ServiceWorkerManager
> swm
= ServiceWorkerManager::GetInstance();
1530 nsCOMPtr
<nsIPrincipal
> principal
= mInfo
->Principal();
1531 RefPtr
<ServiceWorkerRegistrationInfo
> regInfo
=
1532 swm
->GetRegistration(principal
, mInfo
->Scope());
1534 // If it's already set, we're done and the running count is already set
1535 if (mHandlesFetch
== Unknown
) {
1536 if (regInfo
->GetActive()) {
1538 regInfo
->GetActive()->HandlesFetch() ? Enabled
: Disabled
;
1539 if (mHandlesFetch
== Enabled
) {
1540 UpdateRunning(0, 1);
1543 // else we're likely still in Evaluating state, and don't know if it
1544 // handles fetch. If so, defer updating the counter for Fetch until we
1545 // finish evaluation. We already updated the Running count for All in
1546 // SpawnWorkerIfNeeded().
1551 void ServiceWorkerPrivate::ErrorReceived(const ErrorValue
& aError
) {
1552 AssertIsOnMainThread();
1554 MOZ_ASSERT(mControllerChild
);
1556 RefPtr
<ServiceWorkerManager
> swm
= ServiceWorkerManager::GetInstance();
1559 ServiceWorkerInfo
* info
= mInfo
;
1561 swm
->HandleError(nullptr, info
->Principal(), info
->Scope(),
1562 NS_ConvertUTF8toUTF16(info
->ScriptSpec()), u
""_ns
, u
""_ns
,
1563 u
""_ns
, 0, 0, nsIScriptError::errorFlag
, JSEXN_ERR
);
1566 void ServiceWorkerPrivate::Terminated() {
1567 AssertIsOnMainThread();
1569 MOZ_ASSERT(mControllerChild
);
1574 void ServiceWorkerPrivate::RefreshRemoteWorkerData(
1575 const RefPtr
<ServiceWorkerRegistrationInfo
>& aRegistration
) {
1576 AssertIsOnMainThread();
1579 ServiceWorkerData
& serviceWorkerData
=
1580 mRemoteWorkerData
.serviceWorkerData().get_ServiceWorkerData();
1581 serviceWorkerData
.descriptor() = mInfo
->Descriptor().ToIPC();
1582 serviceWorkerData
.registrationDescriptor() =
1583 aRegistration
->Descriptor().ToIPC();
1586 RefPtr
<FetchServicePromises
> ServiceWorkerPrivate::SetupNavigationPreload(
1587 nsCOMPtr
<nsIInterceptedChannel
>& aChannel
,
1588 const RefPtr
<ServiceWorkerRegistrationInfo
>& aRegistration
) {
1589 MOZ_ASSERT(XRE_IsParentProcess());
1590 AssertIsOnMainThread();
1592 // create IPC request from the intercepted channel.
1593 auto result
= GetIPCInternalRequest(aChannel
);
1594 if (result
.isErr()) {
1597 IPCInternalRequest ipcRequest
= result
.unwrap();
1599 // Step 1. Clone the request for preload
1600 // Create the InternalResponse from the created IPCRequest.
1601 SafeRefPtr
<InternalRequest
> preloadRequest
=
1602 MakeSafeRefPtr
<InternalRequest
>(ipcRequest
);
1603 // Copy the request body from uploadChannel
1604 nsCOMPtr
<nsIUploadChannel2
> uploadChannel
= do_QueryInterface(aChannel
);
1605 if (uploadChannel
) {
1606 nsCOMPtr
<nsIInputStream
> uploadStream
;
1607 nsresult rv
= uploadChannel
->CloneUploadStream(
1608 &ipcRequest
.bodySize(), getter_AddRefs(uploadStream
));
1609 // Fail to get the request's body, stop navigation preload by returning
1611 if (NS_WARN_IF(NS_FAILED(rv
))) {
1612 return FetchService::NetworkErrorResponse(rv
);
1614 preloadRequest
->SetBody(uploadStream
, ipcRequest
.bodySize());
1617 // Set SkipServiceWorker for the navigation preload request
1618 preloadRequest
->SetSkipServiceWorker();
1620 // Step 2. Append Service-Worker-Navigation-Preload header with
1621 // registration->GetNavigationPreloadState().headerValue() on
1622 // request's header list.
1623 IgnoredErrorResult err
;
1624 auto headersGuard
= preloadRequest
->Headers()->Guard();
1625 preloadRequest
->Headers()->SetGuard(HeadersGuardEnum::None
, err
);
1626 preloadRequest
->Headers()->Append(
1627 "Service-Worker-Navigation-Preload"_ns
,
1628 aRegistration
->GetNavigationPreloadState().headerValue(), err
);
1629 preloadRequest
->Headers()->SetGuard(headersGuard
, err
);
1631 // Step 3. Perform fetch through FetchService with the cloned request
1632 if (!err
.Failed()) {
1633 nsCOMPtr
<nsIChannel
> underlyingChannel
;
1634 MOZ_ALWAYS_SUCCEEDS(
1635 aChannel
->GetChannel(getter_AddRefs(underlyingChannel
)));
1636 RefPtr
<FetchService
> fetchService
= FetchService::GetInstance();
1637 return fetchService
->Fetch(AsVariant(FetchService::NavigationPreloadArgs
{
1638 std::move(preloadRequest
), underlyingChannel
}));
1640 return FetchService::NetworkErrorResponse(NS_ERROR_UNEXPECTED
);
1643 void ServiceWorkerPrivate::Shutdown() {
1644 AssertIsOnMainThread();
1646 if (mControllerChild
) {
1647 RefPtr
<ServiceWorkerManager
> swm
= ServiceWorkerManager::GetInstance();
1650 "All Service Workers should start shutting down before the "
1651 "ServiceWorkerManager does!");
1653 auto shutdownStateId
= swm
->MaybeInitServiceWorkerShutdownProgress();
1655 RefPtr
<GenericNonExclusivePromise
> promise
=
1656 ShutdownInternal(shutdownStateId
);
1657 swm
->BlockShutdownOn(promise
, shutdownStateId
);
1660 MOZ_ASSERT(!mControllerChild
);
1663 RefPtr
<GenericNonExclusivePromise
> ServiceWorkerPrivate::ShutdownInternal(
1664 uint32_t aShutdownStateId
) {
1665 AssertIsOnMainThread();
1666 MOZ_ASSERT(mControllerChild
);
1668 mPendingFunctionalEvents
.Clear();
1670 mControllerChild
->get()->RevokeObserver(this);
1672 if (StaticPrefs::dom_serviceWorkers_testing_enabled()) {
1673 nsCOMPtr
<nsIObserverService
> os
= services::GetObserverService();
1675 os
->NotifyObservers(nullptr, "service-worker-shutdown", nullptr);
1679 RefPtr
<GenericNonExclusivePromise::Private
> promise
=
1680 new GenericNonExclusivePromise::Private(__func__
);
1682 Unused
<< ExecServiceWorkerOp(
1683 ServiceWorkerTerminateWorkerOpArgs(aShutdownStateId
),
1684 [promise
](ServiceWorkerOpResult
&& aResult
) {
1685 MOZ_ASSERT(aResult
.type() == ServiceWorkerOpResult::Tnsresult
);
1686 promise
->Resolve(true, __func__
);
1688 [promise
]() { promise
->Reject(NS_ERROR_DOM_ABORT_ERR
, __func__
); });
1691 * After dispatching a termination operation, no new operations should
1692 * be routed through this actor anymore.
1694 mControllerChild
= nullptr;
1696 // Update here, since Evaluation failures directly call ShutdownInternal
1697 UpdateRunning(-1, mHandlesFetch
== Enabled
? -1 : 0);
1702 nsresult
ServiceWorkerPrivate::ExecServiceWorkerOp(
1703 ServiceWorkerOpArgs
&& aArgs
,
1704 std::function
<void(ServiceWorkerOpResult
&&)>&& aSuccessCallback
,
1705 std::function
<void()>&& aFailureCallback
) {
1706 AssertIsOnMainThread();
1709 ServiceWorkerOpArgs::TParentToChildServiceWorkerFetchEventOpArgs
,
1710 "FetchEvent operations should be sent through FetchEventOp(Proxy) "
1712 MOZ_ASSERT(aSuccessCallback
);
1714 nsresult rv
= SpawnWorkerIfNeeded();
1716 if (NS_WARN_IF(NS_FAILED(rv
))) {
1721 MOZ_ASSERT(mControllerChild
);
1723 RefPtr
<ServiceWorkerPrivate
> self
= this;
1724 RefPtr
<RAIIActorPtrHolder
> holder
= mControllerChild
;
1725 RefPtr
<KeepAliveToken
> token
=
1726 aArgs
.type() == ServiceWorkerOpArgs::TServiceWorkerTerminateWorkerOpArgs
1728 : CreateEventKeepAliveToken();
1731 * NOTE: moving `aArgs` won't do anything until IPDL `SendMethod()` methods
1732 * can accept rvalue references rather than just const references.
1734 mControllerChild
->get()->SendExecServiceWorkerOp(aArgs
)->Then(
1735 GetCurrentSerialEventTarget(), __func__
,
1736 [self
= std::move(self
), holder
= std::move(holder
),
1737 token
= std::move(token
), onSuccess
= std::move(aSuccessCallback
),
1738 onFailure
= std::move(aFailureCallback
)](
1739 PRemoteWorkerControllerChild::ExecServiceWorkerOpPromise::
1740 ResolveOrRejectValue
&& aResult
) {
1741 if (NS_WARN_IF(aResult
.IsReject())) {
1746 onSuccess(std::move(aResult
.ResolveValue()));
1752 } // namespace mozilla::dom