Bug 1842773 - Part 18: Update TypedArray length, byteLength, and byteOffset accesses...
[gecko.git] / dom / serviceworkers / ServiceWorkerPrivate.cpp
blob864a59800648f6cb2666c901e1854a62fb05d630
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 nsAutoString 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;
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(
557 firstPartyURI, uri);
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
572 rv = NS_NewURI(
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.
578 bool isThirdParty;
579 rv = principal->IsThirdPartyURI(firstPartyURI, &isThirdParty);
580 NS_ENSURE_SUCCESS(rv, rv);
582 overriddenFingerprintingSettings =
583 isThirdParty
584 ? nsRFPService::GetOverriddenFingerprintingSettingsForURI(
585 firstPartyURI, uri)
586 : nsRFPService::GetOverriddenFingerprintingSettingsForURI(
587 uri, nullptr);
589 if (overriddenFingerprintingSettings.isSome()) {
590 overriddenFingerprintingSettingsArg.emplace(
591 uint64_t(overriddenFingerprintingSettings.ref()));
594 } else {
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))) {
616 return rv;
619 PrincipalInfo partitionedPrincipalInfo;
620 rv =
621 PrincipalToPrincipalInfo(partitionedPrincipal, &partitionedPrincipalInfo);
622 if (NS_WARN_IF(NS_FAILED(rv))) {
623 return 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))) {
638 return 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,
664 cjsData, domain,
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(
674 principal,
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
684 // service workers.
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);
693 return NS_OK;
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);
711 aCallback->Run();
713 return rv;
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) {
726 auto& result =
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
735 // handles fetch
736 if (self->mHandlesFetch == Enabled) {
737 self->UpdateRunning(0, 1);
741 callback->SetResult(result.workerScriptExecutedSuccessfully());
742 callback->Run();
743 return;
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);
761 callback->Run();
764 return;
767 RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
768 MOZ_ASSERT(swm);
770 auto shutdownStateId = swm->MaybeInitServiceWorkerShutdownProgress();
772 RefPtr<GenericNonExclusivePromise> promise =
773 self->ShutdownInternal(shutdownStateId);
775 swm->BlockShutdownOn(promise, shutdownStateId);
777 promise->Then(
778 GetCurrentSerialEventTarget(), __func__,
779 [callback = std::move(callback)](
780 const GenericNonExclusivePromise::ResolveOrRejectValue&) {
781 callback->SetResult(false);
782 callback->Run();
785 [callback = aCallback] {
786 callback->SetResult(false);
787 callback->Run();
791 nsresult ServiceWorkerPrivate::SendMessageEvent(
792 RefPtr<ServiceWorkerCloneData>&& aData,
793 const ClientInfoAndState& aClientInfoAndState) {
794 AssertIsOnMainThread();
795 MOZ_ASSERT(aData);
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;
811 scopeExit.release();
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()));
830 callback->Run();
832 [callback = aCallback] {
833 callback->SetResult(false);
834 callback->Run();
838 nsresult ServiceWorkerPrivate::SendPushEvent(
839 const nsAString& aMessageId, const Maybe<nsTArray<uint8_t>>& aData,
840 RefPtr<ServiceWorkerRegistrationInfo> aRegistration) {
841 AssertIsOnMainThread();
842 MOZ_ASSERT(mInfo);
843 MOZ_ASSERT(aRegistration);
845 ServiceWorkerPushEventOpArgs args;
846 args.messageId() = nsString(aMessageId);
848 if (aData) {
849 args.data() = aData.ref();
850 } else {
851 args.data() = void_t();
854 if (mInfo->State() == ServiceWorkerState::Activating) {
855 UniquePtr<PendingFunctionalEvent> pendingEvent =
856 MakeUnique<PendingPushEvent>(this, std::move(aRegistration),
857 std::move(args));
859 mPendingFunctionalEvents.AppendElement(std::move(pendingEvent));
861 return NS_OK;
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(
876 std::move(aArgs),
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());
952 } else {
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.
965 if (!registration) {
966 nsresult rv = aChannel->ResetInterception(false);
967 if (NS_FAILED(rv)) {
968 NS_WARNING("Failed to resume intercepted network request");
969 aChannel->CancelInterception(rv);
971 return NS_OK;
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);
979 if (NS_FAILED(rv)) {
980 NS_WARNING("Failed to resume intercepted network request");
981 aChannel->CancelInterception(rv);
984 // Trigger soft updates if necessary.
985 registration->MaybeScheduleTimeCheckAndUpdate();
987 return NS_OK;
990 auto scopeExit = MakeScopeExit([&] {
991 aChannel->CancelInterception(NS_ERROR_INTERCEPTION_FAILED);
992 Shutdown();
995 IPCInternalRequest request;
996 MOZ_TRY_VAR(request, GetIPCInternalRequest(aChannel));
998 scopeExit.release();
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));
1024 return NS_OK;
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());
1067 return NS_OK;
1070 Result<RefPtr<ServiceWorkerPrivate::PromiseExtensionWorkerHasListener>,
1071 nsresult>
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);
1081 auto promise =
1082 MakeRefPtr<PromiseExtensionWorkerHasListener::Private>(__func__);
1084 nsresult rv = ExecServiceWorkerOp(
1085 std::move(args),
1086 [promise](ServiceWorkerOpResult&& aResult) {
1087 MOZ_ASSERT(
1088 aResult.type() ==
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);
1100 return outPromise;
1103 nsresult ServiceWorkerPrivate::SpawnWorkerIfNeeded() {
1104 AssertIsOnMainThread();
1106 if (mControllerChild) {
1107 RenewKeepAliveToken();
1108 return NS_OK;
1111 if (!mInfo) {
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()) {
1130 NS_WARNING(
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
1163 // (
1164 UpdateRunning(1, mHandlesFetch == Enabled ? 1 : 0);
1166 return NS_OK;
1169 void ServiceWorkerPrivate::TerminateWorker() {
1170 MOZ_ASSERT(NS_IsMainThread());
1171 mIdleWorkerTimer->Cancel();
1172 mIdleKeepAliveToken = nullptr;
1173 Shutdown();
1176 void ServiceWorkerPrivate::NoteDeadServiceWorkerInfo() {
1177 MOZ_ASSERT(NS_IsMainThread());
1179 TerminateWorker();
1180 mInfo = nullptr;
1183 void ServiceWorkerPrivate::UpdateState(ServiceWorkerState aState) {
1184 AssertIsOnMainThread();
1186 if (!mControllerChild) {
1187 return;
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))) {
1197 Shutdown();
1198 return;
1201 if (aState != ServiceWorkerState::Activated) {
1202 return;
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();
1246 ++mDebuggerCount;
1248 return NS_OK;
1251 nsresult ServiceWorkerPrivate::DetachDebugger() {
1252 MOZ_ASSERT(NS_IsMainThread());
1254 if (!mDebuggerCount) {
1255 return NS_ERROR_UNEXPECTED;
1258 --mDebuggerCount;
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) {
1263 if (mTokenCount) {
1264 ResetIdleTimeout();
1265 } else {
1266 TerminateWorker();
1270 return NS_OK;
1273 bool ServiceWorkerPrivate::IsIdle() const {
1274 MOZ_ASSERT(NS_IsMainThread());
1275 return mTokenCount == 0 || (mTokenCount == 1 && mIdleKeepAliveToken);
1278 RefPtr<GenericPromise> ServiceWorkerPrivate::GetIdlePromise() {
1279 #ifdef DEBUG
1280 MOZ_ASSERT(NS_IsMainThread());
1281 MOZ_ASSERT(!IsIdle());
1282 MOZ_ASSERT(!mIdlePromiseObtained, "Idle promise may only be obtained once!");
1283 mIdlePromiseObtained = true;
1284 #endif
1286 return mIdlePromiseHolder.Ensure(__func__);
1289 namespace {
1291 class ServiceWorkerPrivateTimerCallback final : public nsITimerCallback,
1292 public nsINamed {
1293 public:
1294 using Method = void (ServiceWorkerPrivate::*)(nsITimer*);
1296 ServiceWorkerPrivateTimerCallback(ServiceWorkerPrivate* aServiceWorkerPrivate,
1297 Method aMethod)
1298 : mServiceWorkerPrivate(aServiceWorkerPrivate), mMethod(aMethod) {}
1300 NS_IMETHOD
1301 Notify(nsITimer* aTimer) override {
1302 (mServiceWorkerPrivate->*mMethod)(aTimer);
1303 mServiceWorkerPrivate = nullptr;
1304 return NS_OK;
1307 NS_IMETHOD
1308 GetName(nsACString& aName) override {
1309 aName.AssignLiteral("ServiceWorkerPrivateTimerCallback");
1310 return NS_OK;
1313 private:
1314 ~ServiceWorkerPrivateTimerCallback() = default;
1316 RefPtr<ServiceWorkerPrivate> mServiceWorkerPrivate;
1317 Method mMethod;
1319 NS_DECL_THREADSAFE_ISUPPORTS
1322 NS_IMPL_ISUPPORTS(ServiceWorkerPrivateTimerCallback, nsITimerCallback,
1323 nsINamed);
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.
1339 uint32_t timeout =
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())});
1361 TerminateWorker();
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) {
1372 ResetIdleTimeout();
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());
1391 ++mTokenCount;
1394 void ServiceWorkerPrivate::ReleaseToken() {
1395 MOZ_ASSERT(NS_IsMainThread());
1397 MOZ_ASSERT(mTokenCount > 0);
1398 --mTokenCount;
1400 if (IsIdle()) {
1401 mIdlePromiseHolder.ResolveIfExists(true, __func__);
1403 if (!mTokenCount) {
1404 TerminateWorker();
1407 // mInfo can be nullptr here if NoteDeadServiceWorkerInfo() is called while
1408 // the KeepAliveToken is being proxy released as a runnable.
1409 else if (mInfo) {
1410 RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
1411 if (swm) {
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)) {
1435 return;
1438 mInfo->SetHandlesFetch(aValue);
1441 RefPtr<GenericPromise> ServiceWorkerPrivate::SetSkipWaitingFlag() {
1442 AssertIsOnMainThread();
1443 MOZ_ASSERT(mInfo);
1445 RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
1447 if (!swm) {
1448 return GenericPromise::CreateAndReject(NS_ERROR_FAILURE, __func__);
1451 RefPtr<ServiceWorkerRegistrationInfo> regInfo =
1452 swm->GetRegistration(mInfo->Principal(), mInfo->Scope());
1454 if (!regInfo) {
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__); });
1465 return promise;
1468 /* static */
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) !=
1499 kNotFound) {
1500 Telemetry::AccumulateTimeDelta(
1501 Telemetry::SERVICE_WORKER_ISOLATED_LAUNCH_TIME,
1502 mServiceWorkerLaunchTimeStart);
1503 } else {
1504 Telemetry::AccumulateTimeDelta(Telemetry::SERVICE_WORKER_LAUNCH_TIME_2,
1505 mServiceWorkerLaunchTimeStart);
1508 Shutdown();
1511 void ServiceWorkerPrivate::CreationSucceeded() {
1512 AssertIsOnMainThread();
1513 MOZ_ASSERT(NS_IsMainThread());
1514 MOZ_ASSERT(mInfo);
1515 MOZ_ASSERT(mControllerChild);
1517 if (mRemoteWorkerData.remoteType().Find(SERVICEWORKER_REMOTE_TYPE) !=
1518 kNotFound) {
1519 Telemetry::AccumulateTimeDelta(
1520 Telemetry::SERVICE_WORKER_ISOLATED_LAUNCH_TIME,
1521 mServiceWorkerLaunchTimeStart);
1522 } else {
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());
1533 if (regInfo) {
1534 // If it's already set, we're done and the running count is already set
1535 if (mHandlesFetch == Unknown) {
1536 if (regInfo->GetActive()) {
1537 mHandlesFetch =
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();
1553 MOZ_ASSERT(mInfo);
1554 MOZ_ASSERT(mControllerChild);
1556 RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
1557 MOZ_ASSERT(swm);
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();
1568 MOZ_ASSERT(mInfo);
1569 MOZ_ASSERT(mControllerChild);
1571 Shutdown();
1574 void ServiceWorkerPrivate::RefreshRemoteWorkerData(
1575 const RefPtr<ServiceWorkerRegistrationInfo>& aRegistration) {
1576 AssertIsOnMainThread();
1577 MOZ_ASSERT(mInfo);
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()) {
1595 return nullptr;
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
1610 // nullptr.
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();
1649 MOZ_ASSERT(swm,
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();
1674 if (os) {
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);
1699 return promise;
1702 nsresult ServiceWorkerPrivate::ExecServiceWorkerOp(
1703 ServiceWorkerOpArgs&& aArgs,
1704 std::function<void(ServiceWorkerOpResult&&)>&& aSuccessCallback,
1705 std::function<void()>&& aFailureCallback) {
1706 AssertIsOnMainThread();
1707 MOZ_ASSERT(
1708 aArgs.type() !=
1709 ServiceWorkerOpArgs::TParentToChildServiceWorkerFetchEventOpArgs,
1710 "FetchEvent operations should be sent through FetchEventOp(Proxy) "
1711 "actors!");
1712 MOZ_ASSERT(aSuccessCallback);
1714 nsresult rv = SpawnWorkerIfNeeded();
1716 if (NS_WARN_IF(NS_FAILED(rv))) {
1717 aFailureCallback();
1718 return rv;
1721 MOZ_ASSERT(mControllerChild);
1723 RefPtr<ServiceWorkerPrivate> self = this;
1724 RefPtr<RAIIActorPtrHolder> holder = mControllerChild;
1725 RefPtr<KeepAliveToken> token =
1726 aArgs.type() == ServiceWorkerOpArgs::TServiceWorkerTerminateWorkerOpArgs
1727 ? nullptr
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())) {
1742 onFailure();
1743 return;
1746 onSuccess(std::move(aResult.ResolveValue()));
1749 return NS_OK;
1752 } // namespace mozilla::dom