Bug 1892041 - Part 1: Update test262 features. r=spidermonkey-reviewers,dminor
[gecko.git] / dom / serviceworkers / ServiceWorkerPrivate.cpp
blob664874d991762bfef02e22710e1fe1c68da29556
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 <utility>
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"
51 #include "nsDebug.h"
52 #include "nsError.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"
59 #include "nsINamed.h"
60 #include "nsIObserverService.h"
61 #include "nsIRedirectHistoryEntry.h"
62 #include "nsIScriptError.h"
63 #include "nsIScriptSecurityManager.h"
64 #include "nsISupportsImpl.h"
65 #include "nsIURI.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;
95 #ifdef LOG
96 # undef LOG
97 #endif
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);
120 * KeepAliveToken
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)
137 * RAIIActorPtrHolder
139 ServiceWorkerPrivate::RAIIActorPtrHolder::RAIIActorPtrHolder(
140 already_AddRefed<RemoteWorkerControllerChild> aActor)
141 : mActor(aActor) {
142 AssertIsOnMainThread();
143 MOZ_ASSERT(mActor);
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();
159 return get();
162 RemoteWorkerControllerChild* ServiceWorkerPrivate::RAIIActorPtrHolder::get()
163 const {
164 AssertIsOnMainThread();
166 return mActor.get();
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();
184 MOZ_ASSERT(mOwner);
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();
205 MOZ_ASSERT(mOwner);
206 MOZ_ASSERT(mOwner->mInfo);
208 return mOwner->SendPushEventInternal(std::move(mRegistration),
209 std::move(mArgs));
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();
228 MOZ_ASSERT(mOwner);
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);
244 namespace {
246 class HeaderFiller final : public nsIHttpHeaderVisitor {
247 public:
248 NS_DECL_ISUPPORTS
250 explicit HeaderFiller(HeadersGuardEnum aGuard)
251 : mInternalHeaders(new InternalHeaders(aGuard)) {
252 MOZ_ASSERT(mInternalHeaders);
255 NS_IMETHOD
256 VisitHeader(const nsACString& aHeader, const nsACString& aValue) override {
257 ErrorResult result;
258 mInternalHeaders->Append(aHeader, aValue, result);
260 if (NS_WARN_IF(result.Failed())) {
261 return result.StealNSResult();
264 return NS_OK;
267 RefPtr<InternalHeaders> Extract() {
268 return RefPtr<InternalHeaders>(std::move(mInternalHeaders));
271 private:
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);
302 nsAutoCString spec;
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 nsAutoCString referrer;
328 ReferrerPolicy referrerPolicy = ReferrerPolicy::_empty;
329 ReferrerPolicy environmentReferrerPolicy = ReferrerPolicy::_empty;
331 nsCOMPtr<nsIReferrerInfo> referrerInfo = httpChannel->GetReferrerInfo();
332 if (referrerInfo) {
333 referrerPolicy = referrerInfo->ReferrerPolicy();
334 Unused << referrerInfo->GetComputedReferrerSpec(referrer);
337 uint32_t loadFlags;
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();
352 ErrorResult result;
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);
421 if (uploadChannel) {
422 nsCOMPtr<nsIInputStream> uploadStream;
423 MOZ_TRY(uploadChannel->CloneUploadStream(&aIPCRequest.bodySize(),
424 getter_AddRefs(uploadStream)));
426 if (uploadStream) {
427 Maybe<BodyStreamVariant>& body = aIPCRequest.body();
428 body.emplace(ParentToParentStream());
430 MOZ_TRY(
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());
443 return NS_OK;
446 } // anonymous namespace
449 * ServiceWorkerPrivate
451 ServiceWorkerPrivate::ServiceWorkerPrivate(ServiceWorkerInfo* aInfo)
452 : mInfo(aInfo), mDebuggerCount(0), mTokenCount(0) {
453 MOZ_ASSERT(NS_IsMainThread());
454 MOZ_ASSERT(aInfo);
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()));
463 #else
464 MOZ_ALWAYS_SUCCEEDS(Initialize());
465 #endif
468 ServiceWorkerPrivate::~ServiceWorkerPrivate() {
469 MOZ_ASSERT(!mTokenCount);
470 MOZ_ASSERT(!mInfo);
471 MOZ_ASSERT(!mControllerChild);
472 MOZ_ASSERT(mIdlePromiseHolder.IsEmpty());
474 mIdleWorkerTimer->Cancel();
477 nsresult ServiceWorkerPrivate::Initialize() {
478 AssertIsOnMainThread();
479 MOZ_ASSERT(mInfo);
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))) {
488 return rv;
491 if (NS_WARN_IF(!uri)) {
492 return NS_ERROR_FAILURE;
495 URIParams baseScriptURL;
496 SerializeURI(uri, baseScriptURL);
498 nsString id;
499 rv = mInfo->GetId(id);
501 if (NS_WARN_IF(NS_FAILED(rv))) {
502 return rv;
505 PrincipalInfo principalInfo;
506 rv = PrincipalToPrincipalInfo(principal, &principalInfo);
507 if (NS_WARN_IF(NS_FAILED(rv))) {
508 return 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.
544 nsAutoString scheme;
545 nsAutoString pkBaseDomain;
546 int32_t unused;
547 bool unused2;
549 if (OriginAttributes::ParsePartitionKey(
550 principal->OriginAttributesRef().mPartitionKey, scheme,
551 pkBaseDomain, unused, unused2)) {
552 nsCOMPtr<nsIURI> firstPartyURI;
553 rv = NS_NewURI(getter_AddRefs(firstPartyURI),
554 scheme + u"://"_ns + pkBaseDomain);
555 if (NS_SUCCEEDED(rv)) {
556 overriddenFingerprintingSettings =
557 nsRFPService::GetOverriddenFingerprintingSettingsForURI(
558 firstPartyURI, uri);
559 if (overriddenFingerprintingSettings.isSome()) {
560 overriddenFingerprintingSettingsArg.emplace(
561 uint64_t(overriddenFingerprintingSettings.ref()));
565 } else if (!principal->OriginAttributesRef().mFirstPartyDomain.IsEmpty()) {
566 // Using the first party domain to know the context of the service worker.
567 // We will run into here if FirstPartyIsolation is enabled. In this case,
568 // the PartitionKey won't get populated.
569 nsCOMPtr<nsIURI> firstPartyURI;
570 // Because the service worker is only available in secure contexts, so we
571 // don't need to consider http and only use https as scheme to create
572 // the first-party URI
573 rv = NS_NewURI(
574 getter_AddRefs(firstPartyURI),
575 u"https://"_ns + principal->OriginAttributesRef().mFirstPartyDomain);
576 if (NS_SUCCEEDED(rv)) {
577 // If the first party domain is not a third-party domain, the service
578 // worker is running in first-party context.
579 bool isThirdParty;
580 rv = principal->IsThirdPartyURI(firstPartyURI, &isThirdParty);
581 NS_ENSURE_SUCCESS(rv, rv);
583 overriddenFingerprintingSettings =
584 isThirdParty
585 ? nsRFPService::GetOverriddenFingerprintingSettingsForURI(
586 firstPartyURI, uri)
587 : nsRFPService::GetOverriddenFingerprintingSettingsForURI(
588 uri, nullptr);
590 if (overriddenFingerprintingSettings.isSome()) {
591 overriddenFingerprintingSettingsArg.emplace(
592 uint64_t(overriddenFingerprintingSettings.ref()));
595 } else {
596 net::CookieJarSettings::Cast(cookieJarSettings)
597 ->SetPartitionKey(uri, false);
599 // The service worker is for a first-party context, we can use the uri of
600 // the service worker as the first-party domain to get the fingerprinting
601 // protection overrides.
602 overriddenFingerprintingSettings =
603 nsRFPService::GetOverriddenFingerprintingSettingsForURI(uri, nullptr);
605 if (overriddenFingerprintingSettings.isSome()) {
606 overriddenFingerprintingSettingsArg.emplace(
607 uint64_t(overriddenFingerprintingSettings.ref()));
611 net::CookieJarSettingsArgs cjsData;
612 net::CookieJarSettings::Cast(cookieJarSettings)->Serialize(cjsData);
614 nsCOMPtr<nsIPrincipal> partitionedPrincipal;
615 rv = StoragePrincipalHelper::CreatePartitionedPrincipalForServiceWorker(
616 principal, cookieJarSettings, getter_AddRefs(partitionedPrincipal));
617 if (NS_WARN_IF(NS_FAILED(rv))) {
618 return rv;
621 PrincipalInfo partitionedPrincipalInfo;
622 rv =
623 PrincipalToPrincipalInfo(partitionedPrincipal, &partitionedPrincipalInfo);
624 if (NS_WARN_IF(NS_FAILED(rv))) {
625 return rv;
628 StorageAccess storageAccess =
629 StorageAllowedForServiceWorker(principal, cookieJarSettings);
631 ServiceWorkerData serviceWorkerData;
632 serviceWorkerData.cacheName() = mInfo->CacheName();
633 serviceWorkerData.loadFlags() = static_cast<uint32_t>(
634 mInfo->GetImportsLoadFlags() | nsIChannel::LOAD_BYPASS_SERVICE_WORKER);
635 serviceWorkerData.id() = std::move(id);
637 nsAutoCString domain;
638 rv = uri->GetHost(domain);
639 if (NS_WARN_IF(NS_FAILED(rv))) {
640 return rv;
643 auto remoteType = RemoteWorkerManager::GetRemoteType(
644 principal, WorkerKind::WorkerKindService);
645 if (NS_WARN_IF(remoteType.isErr())) {
646 return remoteType.unwrapErr();
649 // Determine if the service worker is registered under a third-party context
650 // by checking if it's running under a partitioned principal.
651 bool isThirdPartyContextToTopWindow =
652 !principal->OriginAttributesRef().mPartitionKey.IsEmpty();
654 mRemoteWorkerData = RemoteWorkerData(
655 NS_ConvertUTF8toUTF16(mInfo->ScriptSpec()), baseScriptURL, baseScriptURL,
656 /* name */ VoidString(),
657 /* workerType */ WorkerType::Classic,
658 /* credentials */ RequestCredentials::Omit,
659 /* loading principal */ principalInfo, principalInfo,
660 partitionedPrincipalInfo,
661 /* useRegularPrincipal */ true,
663 // ServiceWorkers run as first-party, no storage-access permission needed.
664 /* usingStorageAccess */ false,
666 cjsData, domain,
667 /* isSecureContext */ true,
668 /* clientInfo*/ Nothing(),
670 // The RemoteWorkerData CTOR doesn't allow to set the referrerInfo via
671 // already_AddRefed<>. Let's set it to null.
672 /* referrerInfo */ nullptr,
674 storageAccess, isThirdPartyContextToTopWindow,
675 nsContentUtils::ShouldResistFingerprinting_dangerous(
676 principal,
677 "Service Workers exist outside a Document or Channel; as a property "
678 "of the domain (and origin attributes). We don't have a "
679 "CookieJarSettings to perform the nested check, but we can rely on"
680 "the FPI/dFPI partition key check. The WorkerPrivate's "
681 "ShouldResistFingerprinting function for the ServiceWorker depends "
682 "on this boolean and will also consider an explicit RFPTarget.",
683 RFPTarget::IsAlwaysEnabledForPrecompute),
684 overriddenFingerprintingSettingsArg,
685 // Origin trials are associated to a window, so it doesn't make sense on
686 // service workers.
687 OriginTrials(), std::move(serviceWorkerData), regInfo->AgentClusterId(),
688 remoteType.unwrap());
690 mRemoteWorkerData.referrerInfo() = MakeAndAddRef<ReferrerInfo>();
692 // This fills in the rest of mRemoteWorkerData.serviceWorkerData().
693 RefreshRemoteWorkerData(regInfo);
695 return NS_OK;
698 nsresult ServiceWorkerPrivate::CheckScriptEvaluation(
699 RefPtr<LifeCycleEventCallback> aCallback) {
700 MOZ_ASSERT(NS_IsMainThread());
701 MOZ_ASSERT(aCallback);
703 RefPtr<ServiceWorkerPrivate> self = this;
706 * We need to capture the actor associated with the current Service Worker so
707 * we can terminate it if script evaluation failed.
709 nsresult rv = SpawnWorkerIfNeeded();
711 if (NS_WARN_IF(NS_FAILED(rv))) {
712 aCallback->SetResult(false);
713 aCallback->Run();
715 return rv;
718 MOZ_ASSERT(mControllerChild);
720 RefPtr<RAIIActorPtrHolder> holder = mControllerChild;
722 return ExecServiceWorkerOp(
723 ServiceWorkerCheckScriptEvaluationOpArgs(),
724 [self = std::move(self), holder = std::move(holder),
725 callback = aCallback](ServiceWorkerOpResult&& aResult) mutable {
726 if (aResult.type() == ServiceWorkerOpResult::
727 TServiceWorkerCheckScriptEvaluationOpResult) {
728 auto& result =
729 aResult.get_ServiceWorkerCheckScriptEvaluationOpResult();
731 if (result.workerScriptExecutedSuccessfully()) {
732 self->SetHandlesFetch(result.fetchHandlerWasAdded());
733 if (self->mHandlesFetch == Unknown) {
734 self->mHandlesFetch =
735 result.fetchHandlerWasAdded() ? Enabled : Disabled;
736 // Update telemetry for # of running SW - the already-running SW
737 // handles fetch
738 if (self->mHandlesFetch == Enabled) {
739 self->UpdateRunning(0, 1);
743 callback->SetResult(result.workerScriptExecutedSuccessfully());
744 callback->Run();
745 return;
750 * If script evaluation failed, first terminate the Service Worker
751 * before invoking the callback.
753 MOZ_ASSERT_IF(aResult.type() == ServiceWorkerOpResult::Tnsresult,
754 NS_FAILED(aResult.get_nsresult()));
756 // If a termination operation was already issued using `holder`...
757 if (self->mControllerChild != holder) {
758 holder->OnDestructor()->Then(
759 GetCurrentSerialEventTarget(), __func__,
760 [callback = std::move(callback)](
761 const GenericPromise::ResolveOrRejectValue&) {
762 callback->SetResult(false);
763 callback->Run();
766 return;
769 RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
770 MOZ_ASSERT(swm);
772 auto shutdownStateId = swm->MaybeInitServiceWorkerShutdownProgress();
774 RefPtr<GenericNonExclusivePromise> promise =
775 self->ShutdownInternal(shutdownStateId);
777 swm->BlockShutdownOn(promise, shutdownStateId);
779 promise->Then(
780 GetCurrentSerialEventTarget(), __func__,
781 [callback = std::move(callback)](
782 const GenericNonExclusivePromise::ResolveOrRejectValue&) {
783 callback->SetResult(false);
784 callback->Run();
787 [callback = aCallback] {
788 callback->SetResult(false);
789 callback->Run();
793 nsresult ServiceWorkerPrivate::SendMessageEvent(
794 RefPtr<ServiceWorkerCloneData>&& aData,
795 const ClientInfoAndState& aClientInfoAndState) {
796 AssertIsOnMainThread();
797 MOZ_ASSERT(aData);
799 auto scopeExit = MakeScopeExit([&] { Shutdown(); });
801 PBackgroundChild* bgChild = BackgroundChild::GetForCurrentThread();
803 if (NS_WARN_IF(!bgChild)) {
804 return NS_ERROR_DOM_INVALID_STATE_ERR;
807 ServiceWorkerMessageEventOpArgs args;
808 args.clientInfoAndState() = aClientInfoAndState;
809 if (!aData->BuildClonedMessageData(args.clonedData())) {
810 return NS_ERROR_DOM_DATA_CLONE_ERR;
813 scopeExit.release();
815 return ExecServiceWorkerOp(
816 std::move(args), [](ServiceWorkerOpResult&& aResult) {
817 MOZ_ASSERT(aResult.type() == ServiceWorkerOpResult::Tnsresult);
821 nsresult ServiceWorkerPrivate::SendLifeCycleEvent(
822 const nsAString& aEventType, RefPtr<LifeCycleEventCallback> aCallback) {
823 AssertIsOnMainThread();
824 MOZ_ASSERT(aCallback);
826 return ExecServiceWorkerOp(
827 ServiceWorkerLifeCycleEventOpArgs(nsString(aEventType)),
828 [callback = aCallback](ServiceWorkerOpResult&& aResult) {
829 MOZ_ASSERT(aResult.type() == ServiceWorkerOpResult::Tnsresult);
831 callback->SetResult(NS_SUCCEEDED(aResult.get_nsresult()));
832 callback->Run();
834 [callback = aCallback] {
835 callback->SetResult(false);
836 callback->Run();
840 nsresult ServiceWorkerPrivate::SendPushEvent(
841 const nsAString& aMessageId, const Maybe<nsTArray<uint8_t>>& aData,
842 RefPtr<ServiceWorkerRegistrationInfo> aRegistration) {
843 AssertIsOnMainThread();
844 MOZ_ASSERT(mInfo);
845 MOZ_ASSERT(aRegistration);
847 ServiceWorkerPushEventOpArgs args;
848 args.messageId() = nsString(aMessageId);
850 if (aData) {
851 args.data() = aData.ref();
852 } else {
853 args.data() = void_t();
856 if (mInfo->State() == ServiceWorkerState::Activating) {
857 UniquePtr<PendingFunctionalEvent> pendingEvent =
858 MakeUnique<PendingPushEvent>(this, std::move(aRegistration),
859 std::move(args));
861 mPendingFunctionalEvents.AppendElement(std::move(pendingEvent));
863 return NS_OK;
866 MOZ_ASSERT(mInfo->State() == ServiceWorkerState::Activated);
868 return SendPushEventInternal(std::move(aRegistration), std::move(args));
871 nsresult ServiceWorkerPrivate::SendPushEventInternal(
872 RefPtr<ServiceWorkerRegistrationInfo>&& aRegistration,
873 ServiceWorkerPushEventOpArgs&& aArgs) {
874 AssertIsOnMainThread();
875 MOZ_ASSERT(aRegistration);
877 return ExecServiceWorkerOp(
878 std::move(aArgs),
879 [registration = aRegistration](ServiceWorkerOpResult&& aResult) {
880 MOZ_ASSERT(aResult.type() == ServiceWorkerOpResult::Tnsresult);
882 registration->MaybeScheduleTimeCheckAndUpdate();
884 [registration = aRegistration]() {
885 registration->MaybeScheduleTimeCheckAndUpdate();
889 nsresult ServiceWorkerPrivate::SendPushSubscriptionChangeEvent() {
890 AssertIsOnMainThread();
892 return ExecServiceWorkerOp(
893 ServiceWorkerPushSubscriptionChangeEventOpArgs(),
894 [](ServiceWorkerOpResult&& aResult) {
895 MOZ_ASSERT(aResult.type() == ServiceWorkerOpResult::Tnsresult);
899 nsresult ServiceWorkerPrivate::SendNotificationEvent(
900 const nsAString& aEventName, const nsAString& aID, const nsAString& aTitle,
901 const nsAString& aDir, const nsAString& aLang, const nsAString& aBody,
902 const nsAString& aTag, const nsAString& aIcon, const nsAString& aData,
903 const nsAString& aBehavior, const nsAString& aScope) {
904 MOZ_ASSERT(NS_IsMainThread());
906 if (aEventName.EqualsLiteral(NOTIFICATION_CLICK_EVENT_NAME)) {
907 gDOMDisableOpenClickDelay =
908 Preferences::GetInt("dom.serviceWorkers.disable_open_click_delay");
909 } else if (!aEventName.EqualsLiteral(NOTIFICATION_CLOSE_EVENT_NAME)) {
910 MOZ_ASSERT_UNREACHABLE("Invalid notification event name");
911 return NS_ERROR_FAILURE;
914 ServiceWorkerNotificationEventOpArgs args;
915 args.eventName() = nsString(aEventName);
916 args.id() = nsString(aID);
917 args.title() = nsString(aTitle);
918 args.dir() = nsString(aDir);
919 args.lang() = nsString(aLang);
920 args.body() = nsString(aBody);
921 args.tag() = nsString(aTag);
922 args.icon() = nsString(aIcon);
923 args.data() = nsString(aData);
924 args.behavior() = nsString(aBehavior);
925 args.scope() = nsString(aScope);
926 args.disableOpenClickDelay() = gDOMDisableOpenClickDelay;
928 return ExecServiceWorkerOp(
929 std::move(args), [](ServiceWorkerOpResult&& aResult) {
930 MOZ_ASSERT(aResult.type() == ServiceWorkerOpResult::Tnsresult);
934 nsresult ServiceWorkerPrivate::SendFetchEvent(
935 nsCOMPtr<nsIInterceptedChannel> aChannel, nsILoadGroup* aLoadGroup,
936 const nsAString& aClientId, const nsAString& aResultingClientId) {
937 MOZ_ASSERT(NS_IsMainThread());
938 MOZ_ASSERT(aChannel);
940 RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
941 if (NS_WARN_IF(!mInfo || !swm)) {
942 return NS_ERROR_FAILURE;
945 nsCOMPtr<nsIChannel> channel;
946 nsresult rv = aChannel->GetChannel(getter_AddRefs(channel));
947 NS_ENSURE_SUCCESS(rv, rv);
948 bool isNonSubresourceRequest =
949 nsContentUtils::IsNonSubresourceRequest(channel);
951 RefPtr<ServiceWorkerRegistrationInfo> registration;
952 if (isNonSubresourceRequest) {
953 registration = swm->GetRegistration(mInfo->Principal(), mInfo->Scope());
954 } else {
955 nsCOMPtr<nsILoadInfo> loadInfo = channel->LoadInfo();
957 // We'll check for a null registration below rather than an error code here.
958 Unused << swm->GetClientRegistration(loadInfo->GetClientInfo().ref(),
959 getter_AddRefs(registration));
962 // Its possible the registration is removed between starting the interception
963 // and actually dispatching the fetch event. In these cases we simply
964 // want to restart the original network request. Since this is a normal
965 // condition we handle the reset here instead of returning an error which
966 // would in turn trigger a console report.
967 if (!registration) {
968 nsresult rv = aChannel->ResetInterception(false);
969 if (NS_FAILED(rv)) {
970 NS_WARNING("Failed to resume intercepted network request");
971 aChannel->CancelInterception(rv);
973 return NS_OK;
976 // Handle Fetch algorithm - step 16. If the service worker didn't register
977 // any fetch event handlers, then abort the interception and maybe trigger
978 // the soft update algorithm.
979 if (!mInfo->HandlesFetch()) {
980 nsresult rv = aChannel->ResetInterception(false);
981 if (NS_FAILED(rv)) {
982 NS_WARNING("Failed to resume intercepted network request");
983 aChannel->CancelInterception(rv);
986 // Trigger soft updates if necessary.
987 registration->MaybeScheduleTimeCheckAndUpdate();
989 return NS_OK;
992 auto scopeExit = MakeScopeExit([&] {
993 aChannel->CancelInterception(NS_ERROR_INTERCEPTION_FAILED);
994 Shutdown();
997 IPCInternalRequest request;
998 MOZ_TRY_VAR(request, GetIPCInternalRequest(aChannel));
1000 scopeExit.release();
1002 bool preloadNavigation = isNonSubresourceRequest &&
1003 request.method().LowerCaseEqualsASCII("get") &&
1004 registration->GetNavigationPreloadState().enabled();
1006 RefPtr<FetchServicePromises> preloadResponsePromises;
1007 if (preloadNavigation) {
1008 preloadResponsePromises = SetupNavigationPreload(aChannel, registration);
1011 ParentToParentServiceWorkerFetchEventOpArgs args(
1012 ServiceWorkerFetchEventOpArgsCommon(
1013 mInfo->ScriptSpec(), request, nsString(aClientId),
1014 nsString(aResultingClientId), isNonSubresourceRequest,
1015 preloadNavigation, mInfo->TestingInjectCancellation()),
1016 Nothing(), Nothing(), Nothing());
1018 if (mInfo->State() == ServiceWorkerState::Activating) {
1019 UniquePtr<PendingFunctionalEvent> pendingEvent =
1020 MakeUnique<PendingFetchEvent>(this, std::move(registration),
1021 std::move(args), std::move(aChannel),
1022 std::move(preloadResponsePromises));
1024 mPendingFunctionalEvents.AppendElement(std::move(pendingEvent));
1026 return NS_OK;
1029 MOZ_ASSERT(mInfo->State() == ServiceWorkerState::Activated);
1031 return SendFetchEventInternal(std::move(registration), std::move(args),
1032 std::move(aChannel),
1033 std::move(preloadResponsePromises));
1036 nsresult ServiceWorkerPrivate::SendFetchEventInternal(
1037 RefPtr<ServiceWorkerRegistrationInfo>&& aRegistration,
1038 ParentToParentServiceWorkerFetchEventOpArgs&& aArgs,
1039 nsCOMPtr<nsIInterceptedChannel>&& aChannel,
1040 RefPtr<FetchServicePromises>&& aPreloadResponseReadyPromises) {
1041 AssertIsOnMainThread();
1043 auto scopeExit = MakeScopeExit([&] { Shutdown(); });
1045 if (NS_WARN_IF(!mInfo)) {
1046 return NS_ERROR_DOM_INVALID_STATE_ERR;
1049 MOZ_TRY(SpawnWorkerIfNeeded());
1050 MOZ_TRY(MaybeStoreStreamForBackgroundThread(
1051 aChannel, aArgs.common().internalRequest()));
1053 scopeExit.release();
1055 MOZ_ASSERT(mControllerChild);
1057 RefPtr<RAIIActorPtrHolder> holder = mControllerChild;
1059 FetchEventOpChild::SendFetchEvent(
1060 mControllerChild->get(), std::move(aArgs), std::move(aChannel),
1061 std::move(aRegistration), std::move(aPreloadResponseReadyPromises),
1062 CreateEventKeepAliveToken())
1063 ->Then(GetCurrentSerialEventTarget(), __func__,
1064 [holder = std::move(holder)](
1065 const GenericPromise::ResolveOrRejectValue& aResult) {
1066 Unused << NS_WARN_IF(aResult.IsReject());
1069 return NS_OK;
1072 Result<RefPtr<ServiceWorkerPrivate::PromiseExtensionWorkerHasListener>,
1073 nsresult>
1074 ServiceWorkerPrivate::WakeForExtensionAPIEvent(
1075 const nsAString& aExtensionAPINamespace,
1076 const nsAString& aExtensionAPIEventName) {
1077 AssertIsOnMainThread();
1079 ServiceWorkerExtensionAPIEventOpArgs args;
1080 args.apiNamespace() = nsString(aExtensionAPINamespace);
1081 args.apiEventName() = nsString(aExtensionAPIEventName);
1083 auto promise =
1084 MakeRefPtr<PromiseExtensionWorkerHasListener::Private>(__func__);
1086 nsresult rv = ExecServiceWorkerOp(
1087 std::move(args),
1088 [promise](ServiceWorkerOpResult&& aResult) {
1089 MOZ_ASSERT(
1090 aResult.type() ==
1091 ServiceWorkerOpResult::TServiceWorkerExtensionAPIEventOpResult);
1092 auto& result = aResult.get_ServiceWorkerExtensionAPIEventOpResult();
1093 promise->Resolve(result.extensionAPIEventListenerWasAdded(), __func__);
1095 [promise]() { promise->Reject(NS_ERROR_FAILURE, __func__); });
1097 if (NS_FAILED(rv)) {
1098 promise->Reject(rv, __func__);
1101 RefPtr<PromiseExtensionWorkerHasListener> outPromise(promise);
1102 return outPromise;
1105 nsresult ServiceWorkerPrivate::SpawnWorkerIfNeeded() {
1106 AssertIsOnMainThread();
1108 if (mControllerChild) {
1109 RenewKeepAliveToken();
1110 return NS_OK;
1113 if (!mInfo) {
1114 return NS_ERROR_DOM_INVALID_STATE_ERR;
1117 mServiceWorkerLaunchTimeStart = TimeStamp::Now();
1119 PBackgroundChild* bgChild = BackgroundChild::GetForCurrentThread();
1121 if (NS_WARN_IF(!bgChild)) {
1122 return NS_ERROR_DOM_INVALID_STATE_ERR;
1125 // If the worker principal is an extension principal, then we should not spawn
1126 // a worker if there is no WebExtensionPolicy associated to that principal
1127 // or if the WebExtensionPolicy is not active.
1128 auto* principal = mInfo->Principal();
1129 if (principal->SchemeIs("moz-extension")) {
1130 auto* addonPolicy = BasePrincipal::Cast(principal)->AddonPolicy();
1131 if (!addonPolicy || !addonPolicy->Active()) {
1132 NS_WARNING(
1133 "Trying to wake up a service worker for a disabled webextension.");
1134 return NS_ERROR_DOM_INVALID_STATE_ERR;
1138 RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
1140 if (NS_WARN_IF(!swm)) {
1141 return NS_ERROR_DOM_ABORT_ERR;
1144 RefPtr<ServiceWorkerRegistrationInfo> regInfo =
1145 swm->GetRegistration(principal, mInfo->Scope());
1147 if (NS_WARN_IF(!regInfo)) {
1148 return NS_ERROR_DOM_INVALID_STATE_ERR;
1151 RefreshRemoteWorkerData(regInfo);
1153 RefPtr<RemoteWorkerControllerChild> controllerChild =
1154 new RemoteWorkerControllerChild(this);
1156 if (NS_WARN_IF(!bgChild->SendPRemoteWorkerControllerConstructor(
1157 controllerChild, mRemoteWorkerData))) {
1158 return NS_ERROR_DOM_INVALID_STATE_ERR;
1161 mControllerChild = new RAIIActorPtrHolder(controllerChild.forget());
1163 // Update Running count here because we may Terminate before we get
1164 // CreationSucceeded(). We'll update if it handles Fetch if that changes
1165 // (
1166 UpdateRunning(1, mHandlesFetch == Enabled ? 1 : 0);
1168 return NS_OK;
1171 void ServiceWorkerPrivate::TerminateWorker() {
1172 MOZ_ASSERT(NS_IsMainThread());
1173 mIdleWorkerTimer->Cancel();
1174 mIdleKeepAliveToken = nullptr;
1175 Shutdown();
1178 void ServiceWorkerPrivate::NoteDeadServiceWorkerInfo() {
1179 MOZ_ASSERT(NS_IsMainThread());
1181 TerminateWorker();
1182 mInfo = nullptr;
1185 void ServiceWorkerPrivate::UpdateState(ServiceWorkerState aState) {
1186 AssertIsOnMainThread();
1188 if (!mControllerChild) {
1189 return;
1192 nsresult rv = ExecServiceWorkerOp(
1193 ServiceWorkerUpdateStateOpArgs(aState),
1194 [](ServiceWorkerOpResult&& aResult) {
1195 MOZ_ASSERT(aResult.type() == ServiceWorkerOpResult::Tnsresult);
1198 if (NS_WARN_IF(NS_FAILED(rv))) {
1199 Shutdown();
1200 return;
1203 if (aState != ServiceWorkerState::Activated) {
1204 return;
1207 for (auto& event : mPendingFunctionalEvents) {
1208 Unused << NS_WARN_IF(NS_FAILED(event->Send()));
1211 mPendingFunctionalEvents.Clear();
1214 nsresult ServiceWorkerPrivate::GetDebugger(nsIWorkerDebugger** aResult) {
1215 MOZ_ASSERT(NS_IsMainThread());
1216 MOZ_ASSERT(aResult);
1218 return NS_ERROR_NOT_IMPLEMENTED;
1221 nsresult ServiceWorkerPrivate::AttachDebugger() {
1222 MOZ_ASSERT(NS_IsMainThread());
1224 // When the first debugger attaches to a worker, we spawn a worker if needed,
1225 // and cancel the idle timeout. The idle timeout should not be reset until
1226 // the last debugger detached from the worker.
1227 if (!mDebuggerCount) {
1228 nsresult rv = SpawnWorkerIfNeeded();
1229 NS_ENSURE_SUCCESS(rv, rv);
1232 * Renewing the idle KeepAliveToken for spawning workers happens
1233 * asynchronously, rather than synchronously.
1234 * The asynchronous renewal is because the actual spawning of workers occurs
1235 * in a content process, so we will only renew once notified that the worker
1236 * has been successfully created
1238 * This means that the DevTools way of starting up a worker by calling
1239 * `AttachDebugger` immediately followed by `DetachDebugger` will spawn and
1240 * immediately terminate a worker (because `mTokenCount` is possibly 0
1241 * due to the idle KeepAliveToken being created asynchronously). So, just
1242 * renew the KeepAliveToken right now.
1244 RenewKeepAliveToken();
1245 mIdleWorkerTimer->Cancel();
1248 ++mDebuggerCount;
1250 return NS_OK;
1253 nsresult ServiceWorkerPrivate::DetachDebugger() {
1254 MOZ_ASSERT(NS_IsMainThread());
1256 if (!mDebuggerCount) {
1257 return NS_ERROR_UNEXPECTED;
1260 --mDebuggerCount;
1262 // When the last debugger detaches from a worker, we either reset the idle
1263 // timeout, or terminate the worker if there are no more active tokens.
1264 if (!mDebuggerCount) {
1265 if (mTokenCount) {
1266 ResetIdleTimeout();
1267 } else {
1268 TerminateWorker();
1272 return NS_OK;
1275 bool ServiceWorkerPrivate::IsIdle() const {
1276 MOZ_ASSERT(NS_IsMainThread());
1277 return mTokenCount == 0 || (mTokenCount == 1 && mIdleKeepAliveToken);
1280 RefPtr<GenericPromise> ServiceWorkerPrivate::GetIdlePromise() {
1281 #ifdef DEBUG
1282 MOZ_ASSERT(NS_IsMainThread());
1283 MOZ_ASSERT(!IsIdle());
1284 MOZ_ASSERT(!mIdlePromiseObtained, "Idle promise may only be obtained once!");
1285 mIdlePromiseObtained = true;
1286 #endif
1288 return mIdlePromiseHolder.Ensure(__func__);
1291 namespace {
1293 class ServiceWorkerPrivateTimerCallback final : public nsITimerCallback,
1294 public nsINamed {
1295 public:
1296 using Method = void (ServiceWorkerPrivate::*)(nsITimer*);
1298 ServiceWorkerPrivateTimerCallback(ServiceWorkerPrivate* aServiceWorkerPrivate,
1299 Method aMethod)
1300 : mServiceWorkerPrivate(aServiceWorkerPrivate), mMethod(aMethod) {}
1302 NS_IMETHOD
1303 Notify(nsITimer* aTimer) override {
1304 (mServiceWorkerPrivate->*mMethod)(aTimer);
1305 mServiceWorkerPrivate = nullptr;
1306 return NS_OK;
1309 NS_IMETHOD
1310 GetName(nsACString& aName) override {
1311 aName.AssignLiteral("ServiceWorkerPrivateTimerCallback");
1312 return NS_OK;
1315 private:
1316 ~ServiceWorkerPrivateTimerCallback() = default;
1318 RefPtr<ServiceWorkerPrivate> mServiceWorkerPrivate;
1319 Method mMethod;
1321 NS_DECL_THREADSAFE_ISUPPORTS
1324 NS_IMPL_ISUPPORTS(ServiceWorkerPrivateTimerCallback, nsITimerCallback,
1325 nsINamed);
1327 } // anonymous namespace
1329 void ServiceWorkerPrivate::NoteIdleWorkerCallback(nsITimer* aTimer) {
1330 MOZ_ASSERT(NS_IsMainThread());
1332 MOZ_ASSERT(aTimer == mIdleWorkerTimer, "Invalid timer!");
1334 // Release ServiceWorkerPrivate's token, since the grace period has ended.
1335 mIdleKeepAliveToken = nullptr;
1337 if (mControllerChild) {
1338 // If we still have a living worker at this point it means that either there
1339 // are pending waitUntil promises or the worker is doing some long-running
1340 // computation. Wait a bit more until we forcibly terminate the worker.
1341 uint32_t timeout =
1342 Preferences::GetInt("dom.serviceWorkers.idle_extended_timeout");
1343 nsCOMPtr<nsITimerCallback> cb = new ServiceWorkerPrivateTimerCallback(
1344 this, &ServiceWorkerPrivate::TerminateWorkerCallback);
1345 DebugOnly<nsresult> rv = mIdleWorkerTimer->InitWithCallback(
1346 cb, timeout, nsITimer::TYPE_ONE_SHOT);
1347 MOZ_ASSERT(NS_SUCCEEDED(rv));
1351 void ServiceWorkerPrivate::TerminateWorkerCallback(nsITimer* aTimer) {
1352 MOZ_ASSERT(NS_IsMainThread());
1354 MOZ_ASSERT(aTimer == this->mIdleWorkerTimer, "Invalid timer!");
1356 // mInfo must be non-null at this point because NoteDeadServiceWorkerInfo
1357 // which zeroes it calls TerminateWorker which cancels our timer which will
1358 // ensure we don't get invoked even if the nsTimerEvent is in the event queue.
1359 ServiceWorkerManager::LocalizeAndReportToAllClients(
1360 mInfo->Scope(), "ServiceWorkerGraceTimeoutTermination",
1361 nsTArray<nsString>{NS_ConvertUTF8toUTF16(mInfo->Scope())});
1363 TerminateWorker();
1366 void ServiceWorkerPrivate::RenewKeepAliveToken() {
1367 // We should have an active worker if we're renewing the keep alive token.
1368 MOZ_ASSERT(mControllerChild);
1370 // If there is at least one debugger attached to the worker, the idle worker
1371 // timeout was canceled when the first debugger attached to the worker. It
1372 // should not be reset until the last debugger detaches from the worker.
1373 if (!mDebuggerCount) {
1374 ResetIdleTimeout();
1377 if (!mIdleKeepAliveToken) {
1378 mIdleKeepAliveToken = new KeepAliveToken(this);
1382 void ServiceWorkerPrivate::ResetIdleTimeout() {
1383 uint32_t timeout = Preferences::GetInt("dom.serviceWorkers.idle_timeout");
1384 nsCOMPtr<nsITimerCallback> cb = new ServiceWorkerPrivateTimerCallback(
1385 this, &ServiceWorkerPrivate::NoteIdleWorkerCallback);
1386 DebugOnly<nsresult> rv =
1387 mIdleWorkerTimer->InitWithCallback(cb, timeout, nsITimer::TYPE_ONE_SHOT);
1388 MOZ_ASSERT(NS_SUCCEEDED(rv));
1391 void ServiceWorkerPrivate::AddToken() {
1392 MOZ_ASSERT(NS_IsMainThread());
1393 ++mTokenCount;
1396 void ServiceWorkerPrivate::ReleaseToken() {
1397 MOZ_ASSERT(NS_IsMainThread());
1399 MOZ_ASSERT(mTokenCount > 0);
1400 --mTokenCount;
1402 if (IsIdle()) {
1403 mIdlePromiseHolder.ResolveIfExists(true, __func__);
1405 if (!mTokenCount) {
1406 TerminateWorker();
1409 // mInfo can be nullptr here if NoteDeadServiceWorkerInfo() is called while
1410 // the KeepAliveToken is being proxy released as a runnable.
1411 else if (mInfo) {
1412 RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
1413 if (swm) {
1414 swm->WorkerIsIdle(mInfo);
1420 already_AddRefed<KeepAliveToken>
1421 ServiceWorkerPrivate::CreateEventKeepAliveToken() {
1422 MOZ_ASSERT(NS_IsMainThread());
1424 // When the WorkerPrivate is in a separate process, we first hold a normal
1425 // KeepAliveToken. Then, after we're notified that the worker is alive, we
1426 // create the idle KeepAliveToken.
1427 MOZ_ASSERT(mIdleKeepAliveToken || mControllerChild);
1429 RefPtr<KeepAliveToken> ref = new KeepAliveToken(this);
1430 return ref.forget();
1433 void ServiceWorkerPrivate::SetHandlesFetch(bool aValue) {
1434 MOZ_ASSERT(NS_IsMainThread());
1436 if (NS_WARN_IF(!mInfo)) {
1437 return;
1440 mInfo->SetHandlesFetch(aValue);
1443 RefPtr<GenericPromise> ServiceWorkerPrivate::SetSkipWaitingFlag() {
1444 AssertIsOnMainThread();
1445 MOZ_ASSERT(mInfo);
1447 RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
1449 if (!swm) {
1450 return GenericPromise::CreateAndReject(NS_ERROR_FAILURE, __func__);
1453 RefPtr<ServiceWorkerRegistrationInfo> regInfo =
1454 swm->GetRegistration(mInfo->Principal(), mInfo->Scope());
1456 if (!regInfo) {
1457 return GenericPromise::CreateAndReject(NS_ERROR_FAILURE, __func__);
1460 mInfo->SetSkipWaitingFlag();
1462 RefPtr<GenericPromise::Private> promise =
1463 new GenericPromise::Private(__func__);
1465 regInfo->TryToActivateAsync([promise] { promise->Resolve(true, __func__); });
1467 return promise;
1470 /* static */
1471 void ServiceWorkerPrivate::UpdateRunning(int32_t aDelta, int32_t aFetchDelta) {
1472 // Record values for time we were running at the current values
1473 RefPtr<ServiceWorkerManager> manager(ServiceWorkerManager::GetInstance());
1474 manager->RecordTelemetry(sRunningServiceWorkers, sRunningServiceWorkersFetch);
1476 MOZ_ASSERT(((int64_t)sRunningServiceWorkers) + aDelta >= 0);
1477 sRunningServiceWorkers += aDelta;
1478 if (sRunningServiceWorkers > sRunningServiceWorkersMax) {
1479 sRunningServiceWorkersMax = sRunningServiceWorkers;
1480 LOG(("ServiceWorker max now %d", sRunningServiceWorkersMax));
1481 Telemetry::ScalarSet(Telemetry::ScalarID::SERVICEWORKER_RUNNING_MAX,
1482 u"All"_ns, sRunningServiceWorkersMax);
1484 MOZ_ASSERT(((int64_t)sRunningServiceWorkersFetch) + aFetchDelta >= 0);
1485 sRunningServiceWorkersFetch += aFetchDelta;
1486 if (sRunningServiceWorkersFetch > sRunningServiceWorkersFetchMax) {
1487 sRunningServiceWorkersFetchMax = sRunningServiceWorkersFetch;
1488 LOG(("ServiceWorker Fetch max now %d", sRunningServiceWorkersFetchMax));
1489 Telemetry::ScalarSet(Telemetry::ScalarID::SERVICEWORKER_RUNNING_MAX,
1490 u"Fetch"_ns, sRunningServiceWorkersFetchMax);
1492 LOG(("ServiceWorkers running now %d/%d", sRunningServiceWorkers,
1493 sRunningServiceWorkersFetch));
1496 void ServiceWorkerPrivate::CreationFailed() {
1497 MOZ_ASSERT(NS_IsMainThread());
1498 MOZ_ASSERT(mControllerChild);
1500 if (mRemoteWorkerData.remoteType().Find(SERVICEWORKER_REMOTE_TYPE) !=
1501 kNotFound) {
1502 Telemetry::AccumulateTimeDelta(
1503 Telemetry::SERVICE_WORKER_ISOLATED_LAUNCH_TIME,
1504 mServiceWorkerLaunchTimeStart);
1505 } else {
1506 Telemetry::AccumulateTimeDelta(Telemetry::SERVICE_WORKER_LAUNCH_TIME_2,
1507 mServiceWorkerLaunchTimeStart);
1510 Shutdown();
1513 void ServiceWorkerPrivate::CreationSucceeded() {
1514 AssertIsOnMainThread();
1515 MOZ_ASSERT(NS_IsMainThread());
1516 MOZ_ASSERT(mInfo);
1517 MOZ_ASSERT(mControllerChild);
1519 if (mRemoteWorkerData.remoteType().Find(SERVICEWORKER_REMOTE_TYPE) !=
1520 kNotFound) {
1521 Telemetry::AccumulateTimeDelta(
1522 Telemetry::SERVICE_WORKER_ISOLATED_LAUNCH_TIME,
1523 mServiceWorkerLaunchTimeStart);
1524 } else {
1525 Telemetry::AccumulateTimeDelta(Telemetry::SERVICE_WORKER_LAUNCH_TIME_2,
1526 mServiceWorkerLaunchTimeStart);
1529 RenewKeepAliveToken();
1531 RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
1532 nsCOMPtr<nsIPrincipal> principal = mInfo->Principal();
1533 RefPtr<ServiceWorkerRegistrationInfo> regInfo =
1534 swm->GetRegistration(principal, mInfo->Scope());
1535 if (regInfo) {
1536 // If it's already set, we're done and the running count is already set
1537 if (mHandlesFetch == Unknown) {
1538 if (regInfo->GetActive()) {
1539 mHandlesFetch =
1540 regInfo->GetActive()->HandlesFetch() ? Enabled : Disabled;
1541 if (mHandlesFetch == Enabled) {
1542 UpdateRunning(0, 1);
1545 // else we're likely still in Evaluating state, and don't know if it
1546 // handles fetch. If so, defer updating the counter for Fetch until we
1547 // finish evaluation. We already updated the Running count for All in
1548 // SpawnWorkerIfNeeded().
1553 void ServiceWorkerPrivate::ErrorReceived(const ErrorValue& aError) {
1554 AssertIsOnMainThread();
1555 MOZ_ASSERT(mInfo);
1556 MOZ_ASSERT(mControllerChild);
1558 RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
1559 MOZ_ASSERT(swm);
1561 ServiceWorkerInfo* info = mInfo;
1563 swm->HandleError(nullptr, info->Principal(), info->Scope(),
1564 NS_ConvertUTF8toUTF16(info->ScriptSpec()), u""_ns, u""_ns,
1565 u""_ns, 0, 0, nsIScriptError::errorFlag, JSEXN_ERR);
1568 void ServiceWorkerPrivate::Terminated() {
1569 AssertIsOnMainThread();
1570 MOZ_ASSERT(mInfo);
1571 MOZ_ASSERT(mControllerChild);
1573 Shutdown();
1576 void ServiceWorkerPrivate::RefreshRemoteWorkerData(
1577 const RefPtr<ServiceWorkerRegistrationInfo>& aRegistration) {
1578 AssertIsOnMainThread();
1579 MOZ_ASSERT(mInfo);
1581 ServiceWorkerData& serviceWorkerData =
1582 mRemoteWorkerData.serviceWorkerData().get_ServiceWorkerData();
1583 serviceWorkerData.descriptor() = mInfo->Descriptor().ToIPC();
1584 serviceWorkerData.registrationDescriptor() =
1585 aRegistration->Descriptor().ToIPC();
1588 RefPtr<FetchServicePromises> ServiceWorkerPrivate::SetupNavigationPreload(
1589 nsCOMPtr<nsIInterceptedChannel>& aChannel,
1590 const RefPtr<ServiceWorkerRegistrationInfo>& aRegistration) {
1591 MOZ_ASSERT(XRE_IsParentProcess());
1592 AssertIsOnMainThread();
1594 // create IPC request from the intercepted channel.
1595 auto result = GetIPCInternalRequest(aChannel);
1596 if (result.isErr()) {
1597 return nullptr;
1599 IPCInternalRequest ipcRequest = result.unwrap();
1601 // Step 1. Clone the request for preload
1602 // Create the InternalResponse from the created IPCRequest.
1603 SafeRefPtr<InternalRequest> preloadRequest =
1604 MakeSafeRefPtr<InternalRequest>(ipcRequest);
1605 // Copy the request body from uploadChannel
1606 nsCOMPtr<nsIUploadChannel2> uploadChannel = do_QueryInterface(aChannel);
1607 if (uploadChannel) {
1608 nsCOMPtr<nsIInputStream> uploadStream;
1609 nsresult rv = uploadChannel->CloneUploadStream(
1610 &ipcRequest.bodySize(), getter_AddRefs(uploadStream));
1611 // Fail to get the request's body, stop navigation preload by returning
1612 // nullptr.
1613 if (NS_WARN_IF(NS_FAILED(rv))) {
1614 return FetchService::NetworkErrorResponse(rv);
1616 preloadRequest->SetBody(uploadStream, ipcRequest.bodySize());
1619 // Set SkipServiceWorker for the navigation preload request
1620 preloadRequest->SetSkipServiceWorker();
1622 // Step 2. Append Service-Worker-Navigation-Preload header with
1623 // registration->GetNavigationPreloadState().headerValue() on
1624 // request's header list.
1625 IgnoredErrorResult err;
1626 auto headersGuard = preloadRequest->Headers()->Guard();
1627 preloadRequest->Headers()->SetGuard(HeadersGuardEnum::None, err);
1628 preloadRequest->Headers()->Append(
1629 "Service-Worker-Navigation-Preload"_ns,
1630 aRegistration->GetNavigationPreloadState().headerValue(), err);
1631 preloadRequest->Headers()->SetGuard(headersGuard, err);
1633 // Step 3. Perform fetch through FetchService with the cloned request
1634 if (!err.Failed()) {
1635 nsCOMPtr<nsIChannel> underlyingChannel;
1636 MOZ_ALWAYS_SUCCEEDS(
1637 aChannel->GetChannel(getter_AddRefs(underlyingChannel)));
1638 RefPtr<FetchService> fetchService = FetchService::GetInstance();
1639 return fetchService->Fetch(AsVariant(FetchService::NavigationPreloadArgs{
1640 std::move(preloadRequest), underlyingChannel}));
1642 return FetchService::NetworkErrorResponse(NS_ERROR_UNEXPECTED);
1645 void ServiceWorkerPrivate::Shutdown() {
1646 AssertIsOnMainThread();
1648 if (mControllerChild) {
1649 RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
1651 MOZ_ASSERT(swm,
1652 "All Service Workers should start shutting down before the "
1653 "ServiceWorkerManager does!");
1655 auto shutdownStateId = swm->MaybeInitServiceWorkerShutdownProgress();
1657 RefPtr<GenericNonExclusivePromise> promise =
1658 ShutdownInternal(shutdownStateId);
1659 swm->BlockShutdownOn(promise, shutdownStateId);
1662 MOZ_ASSERT(!mControllerChild);
1665 RefPtr<GenericNonExclusivePromise> ServiceWorkerPrivate::ShutdownInternal(
1666 uint32_t aShutdownStateId) {
1667 AssertIsOnMainThread();
1668 MOZ_ASSERT(mControllerChild);
1670 mPendingFunctionalEvents.Clear();
1672 mControllerChild->get()->RevokeObserver(this);
1674 if (StaticPrefs::dom_serviceWorkers_testing_enabled()) {
1675 nsCOMPtr<nsIObserverService> os = services::GetObserverService();
1676 if (os) {
1677 os->NotifyObservers(nullptr, "service-worker-shutdown", nullptr);
1681 RefPtr<GenericNonExclusivePromise::Private> promise =
1682 new GenericNonExclusivePromise::Private(__func__);
1684 Unused << ExecServiceWorkerOp(
1685 ServiceWorkerTerminateWorkerOpArgs(aShutdownStateId),
1686 [promise](ServiceWorkerOpResult&& aResult) {
1687 MOZ_ASSERT(aResult.type() == ServiceWorkerOpResult::Tnsresult);
1688 promise->Resolve(true, __func__);
1690 [promise]() { promise->Reject(NS_ERROR_DOM_ABORT_ERR, __func__); });
1693 * After dispatching a termination operation, no new operations should
1694 * be routed through this actor anymore.
1696 mControllerChild = nullptr;
1698 // Update here, since Evaluation failures directly call ShutdownInternal
1699 UpdateRunning(-1, mHandlesFetch == Enabled ? -1 : 0);
1701 return promise;
1704 nsresult ServiceWorkerPrivate::ExecServiceWorkerOp(
1705 ServiceWorkerOpArgs&& aArgs,
1706 std::function<void(ServiceWorkerOpResult&&)>&& aSuccessCallback,
1707 std::function<void()>&& aFailureCallback) {
1708 AssertIsOnMainThread();
1709 MOZ_ASSERT(
1710 aArgs.type() !=
1711 ServiceWorkerOpArgs::TParentToChildServiceWorkerFetchEventOpArgs,
1712 "FetchEvent operations should be sent through FetchEventOp(Proxy) "
1713 "actors!");
1714 MOZ_ASSERT(aSuccessCallback);
1716 nsresult rv = SpawnWorkerIfNeeded();
1718 if (NS_WARN_IF(NS_FAILED(rv))) {
1719 aFailureCallback();
1720 return rv;
1723 MOZ_ASSERT(mControllerChild);
1725 RefPtr<ServiceWorkerPrivate> self = this;
1726 RefPtr<RAIIActorPtrHolder> holder = mControllerChild;
1727 RefPtr<KeepAliveToken> token =
1728 aArgs.type() == ServiceWorkerOpArgs::TServiceWorkerTerminateWorkerOpArgs
1729 ? nullptr
1730 : CreateEventKeepAliveToken();
1733 * NOTE: moving `aArgs` won't do anything until IPDL `SendMethod()` methods
1734 * can accept rvalue references rather than just const references.
1736 mControllerChild->get()->SendExecServiceWorkerOp(aArgs)->Then(
1737 GetCurrentSerialEventTarget(), __func__,
1738 [self = std::move(self), holder = std::move(holder),
1739 token = std::move(token), onSuccess = std::move(aSuccessCallback),
1740 onFailure = std::move(aFailureCallback)](
1741 PRemoteWorkerControllerChild::ExecServiceWorkerOpPromise::
1742 ResolveOrRejectValue&& aResult) {
1743 if (NS_WARN_IF(aResult.IsReject())) {
1744 onFailure();
1745 return;
1748 onSuccess(std::move(aResult.ResolveValue()));
1751 return NS_OK;
1754 } // namespace mozilla::dom