ServiceWorkerManager.cpp (119222B)
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/. */ 6 7 #include "ServiceWorkerManager.h" 8 9 #include <algorithm> 10 11 #include "ServiceWorker.h" 12 #include "ServiceWorkerContainer.h" 13 #include "ServiceWorkerEvents.h" 14 #include "ServiceWorkerInfo.h" 15 #include "ServiceWorkerJobQueue.h" 16 #include "ServiceWorkerManagerChild.h" 17 #include "ServiceWorkerPrivate.h" 18 #include "ServiceWorkerQuotaUtils.h" 19 #include "ServiceWorkerRegisterJob.h" 20 #include "ServiceWorkerRegistrar.h" 21 #include "ServiceWorkerRegistration.h" 22 #include "ServiceWorkerScriptCache.h" 23 #include "ServiceWorkerShutdownBlocker.h" 24 #include "ServiceWorkerUnregisterJob.h" 25 #include "ServiceWorkerUpdateJob.h" 26 #include "ServiceWorkerUtils.h" 27 #include "jsapi.h" 28 #include "mozilla/AppShutdown.h" 29 #include "mozilla/BasePrincipal.h" 30 #include "mozilla/ClearOnShutdown.h" 31 #include "mozilla/ContentBlockingAllowList.h" 32 #include "mozilla/ErrorNames.h" 33 #include "mozilla/LoadContext.h" 34 #include "mozilla/MozPromise.h" 35 #include "mozilla/PermissionManager.h" 36 #include "mozilla/Result.h" 37 #include "mozilla/ScopeExit.h" 38 #include "mozilla/StaticPrefs_extensions.h" 39 #include "mozilla/StaticPrefs_privacy.h" 40 #include "mozilla/StoragePrincipalHelper.h" 41 #include "mozilla/dom/BindingUtils.h" 42 #include "mozilla/dom/ClientHandle.h" 43 #include "mozilla/dom/ClientManager.h" 44 #include "mozilla/dom/ClientSource.h" 45 #include "mozilla/dom/ConsoleUtils.h" 46 #include "mozilla/dom/ContentParent.h" 47 #include "mozilla/dom/ErrorEvent.h" 48 #include "mozilla/dom/Headers.h" 49 #include "mozilla/dom/InternalHeaders.h" 50 #include "mozilla/dom/Navigator.h" 51 #include "mozilla/dom/NotificationEvent.h" 52 #include "mozilla/dom/Promise.h" 53 #include "mozilla/dom/PromiseNativeHandler.h" 54 #include "mozilla/dom/Request.h" 55 #include "mozilla/dom/RootedDictionary.h" 56 #include "mozilla/dom/ScriptLoader.h" 57 #include "mozilla/dom/SharedWorker.h" 58 #include "mozilla/dom/TypedArray.h" 59 #include "mozilla/dom/WorkerPrivate.h" 60 #include "mozilla/dom/WorkerRunnable.h" 61 #include "mozilla/dom/WorkerScope.h" 62 #include "mozilla/extensions/WebExtensionPolicy.h" 63 #include "mozilla/glean/DomServiceworkersMetrics.h" 64 #include "mozilla/ipc/BackgroundChild.h" 65 #include "mozilla/ipc/PBackgroundChild.h" 66 #include "mozilla/ipc/PBackgroundSharedTypes.h" 67 #include "nsCOMPtr.h" 68 #include "nsComponentManagerUtils.h" 69 #include "nsContentUtils.h" 70 #include "nsDebug.h" 71 #include "nsICookieJarSettings.h" 72 #include "nsIDUtils.h" 73 #include "nsIHttpChannel.h" 74 #include "nsIHttpChannelInternal.h" 75 #include "nsIMutableArray.h" 76 #include "nsINamed.h" 77 #include "nsINetworkInterceptController.h" 78 #include "nsIPermissionManager.h" 79 #include "nsIPrincipal.h" 80 #include "nsIPushService.h" 81 #include "nsITimer.h" 82 #include "nsIUploadChannel2.h" 83 #include "nsNetUtil.h" 84 #include "nsProxyRelease.h" 85 #include "nsQueryObject.h" 86 #include "nsServiceManagerUtils.h" 87 #include "nsTArray.h" 88 #include "nsXULAppAPI.h" 89 90 #ifdef PostMessage 91 # undef PostMessage 92 #endif 93 94 mozilla::LazyLogModule sWorkerTelemetryLog("WorkerTelemetry"); 95 96 #ifdef LOG 97 # undef LOG 98 #endif 99 #define LOG(_args) MOZ_LOG(sWorkerTelemetryLog, LogLevel::Debug, _args); 100 101 using namespace mozilla; 102 using namespace mozilla::dom; 103 using namespace mozilla::ipc; 104 105 namespace mozilla::dom { 106 107 static_assert( 108 nsIHttpChannelInternal::REDIRECT_MODE_FOLLOW == 109 static_cast<uint32_t>(RequestRedirect::Follow), 110 "RequestRedirect enumeration value should make Necko Redirect mode value."); 111 static_assert( 112 nsIHttpChannelInternal::REDIRECT_MODE_ERROR == 113 static_cast<uint32_t>(RequestRedirect::Error), 114 "RequestRedirect enumeration value should make Necko Redirect mode value."); 115 static_assert( 116 nsIHttpChannelInternal::REDIRECT_MODE_MANUAL == 117 static_cast<uint32_t>(RequestRedirect::Manual), 118 "RequestRedirect enumeration value should make Necko Redirect mode value."); 119 static_assert( 120 3 == ContiguousEnumSize<RequestRedirect>::value, 121 "RequestRedirect enumeration value should make Necko Redirect mode value."); 122 123 static_assert( 124 nsIHttpChannelInternal::FETCH_CACHE_MODE_DEFAULT == 125 static_cast<uint32_t>(RequestCache::Default), 126 "RequestCache enumeration value should match Necko Cache mode value."); 127 static_assert( 128 nsIHttpChannelInternal::FETCH_CACHE_MODE_NO_STORE == 129 static_cast<uint32_t>(RequestCache::No_store), 130 "RequestCache enumeration value should match Necko Cache mode value."); 131 static_assert( 132 nsIHttpChannelInternal::FETCH_CACHE_MODE_RELOAD == 133 static_cast<uint32_t>(RequestCache::Reload), 134 "RequestCache enumeration value should match Necko Cache mode value."); 135 static_assert( 136 nsIHttpChannelInternal::FETCH_CACHE_MODE_NO_CACHE == 137 static_cast<uint32_t>(RequestCache::No_cache), 138 "RequestCache enumeration value should match Necko Cache mode value."); 139 static_assert( 140 nsIHttpChannelInternal::FETCH_CACHE_MODE_FORCE_CACHE == 141 static_cast<uint32_t>(RequestCache::Force_cache), 142 "RequestCache enumeration value should match Necko Cache mode value."); 143 static_assert( 144 nsIHttpChannelInternal::FETCH_CACHE_MODE_ONLY_IF_CACHED == 145 static_cast<uint32_t>(RequestCache::Only_if_cached), 146 "RequestCache enumeration value should match Necko Cache mode value."); 147 static_assert( 148 6 == ContiguousEnumSize<RequestCache>::value, 149 "RequestCache enumeration value should match Necko Cache mode value."); 150 151 static_assert(static_cast<uint16_t>(ServiceWorkerUpdateViaCache::Imports) == 152 nsIServiceWorkerRegistrationInfo::UPDATE_VIA_CACHE_IMPORTS, 153 "nsIServiceWorkerRegistrationInfo::UPDATE_VIA_CACHE_*" 154 " should match ServiceWorkerUpdateViaCache enumeration."); 155 static_assert(static_cast<uint16_t>(ServiceWorkerUpdateViaCache::All) == 156 nsIServiceWorkerRegistrationInfo::UPDATE_VIA_CACHE_ALL, 157 "nsIServiceWorkerRegistrationInfo::UPDATE_VIA_CACHE_*" 158 " should match ServiceWorkerUpdateViaCache enumeration."); 159 static_assert(static_cast<uint16_t>(ServiceWorkerUpdateViaCache::None) == 160 nsIServiceWorkerRegistrationInfo::UPDATE_VIA_CACHE_NONE, 161 "nsIServiceWorkerRegistrationInfo::UPDATE_VIA_CACHE_*" 162 " should match ServiceWorkerUpdateViaCache enumeration."); 163 164 static StaticRefPtr<ServiceWorkerManager> gInstance; 165 166 namespace { 167 168 nsresult PopulateRegistrationData( 169 nsIPrincipal* aPrincipal, 170 const ServiceWorkerRegistrationInfo* aRegistration, 171 ServiceWorkerRegistrationData& aData) { 172 MOZ_ASSERT(aPrincipal); 173 MOZ_ASSERT(aRegistration); 174 175 if (NS_WARN_IF(!BasePrincipal::Cast(aPrincipal)->IsContentPrincipal())) { 176 return NS_ERROR_FAILURE; 177 } 178 179 nsresult rv = PrincipalToPrincipalInfo(aPrincipal, &aData.principal()); 180 if (NS_WARN_IF(NS_FAILED(rv))) { 181 return rv; 182 } 183 184 aData.scope() = aRegistration->Scope(); 185 aData.type() = aRegistration->Type(); 186 187 // TODO: When bug 1426401 is implemented we will need to handle more 188 // than just the active worker here. 189 RefPtr<ServiceWorkerInfo> active = aRegistration->GetActive(); 190 MOZ_ASSERT(active); 191 if (NS_WARN_IF(!active)) { 192 return NS_ERROR_FAILURE; 193 } 194 195 aData.currentWorkerURL() = active->ScriptSpec(); 196 aData.cacheName() = active->CacheName(); 197 aData.currentWorkerHandlesFetch() = active->HandlesFetch(); 198 199 aData.currentWorkerInstalledTime() = active->GetInstalledTime(); 200 aData.currentWorkerActivatedTime() = active->GetActivatedTime(); 201 202 aData.updateViaCache() = 203 static_cast<uint32_t>(aRegistration->GetUpdateViaCache()); 204 205 aData.lastUpdateTime() = aRegistration->GetLastUpdateTime(); 206 207 aData.navigationPreloadState() = aRegistration->GetNavigationPreloadState(); 208 209 aData.numberOfAttemptedActivations() = 210 aRegistration->GetNumberOfAttemptedActivations(); 211 212 aData.isBroken() = aRegistration->IsBroken(); 213 214 aData.cacheAPIId() = aRegistration->GetCacheAPIId(); 215 216 MOZ_ASSERT(ServiceWorkerRegistrationDataIsValid(aData)); 217 218 return NS_OK; 219 } 220 221 class TeardownRunnable final : public Runnable { 222 public: 223 explicit TeardownRunnable(ServiceWorkerManagerChild* aActor) 224 : Runnable("dom::ServiceWorkerManager::TeardownRunnable"), 225 mActor(aActor) { 226 MOZ_ASSERT(mActor); 227 } 228 229 NS_IMETHOD Run() override { 230 MOZ_ASSERT(mActor); 231 PServiceWorkerManagerChild::Send__delete__(mActor); 232 return NS_OK; 233 } 234 235 private: 236 ~TeardownRunnable() = default; 237 238 RefPtr<ServiceWorkerManagerChild> mActor; 239 }; 240 241 constexpr char kFinishShutdownTopic[] = "profile-before-change-qm"; 242 constexpr char kPrivateBrowsingExited[] = "last-pb-context-exited"; 243 244 constexpr auto kPrivateBrowsingOriginPattern = 245 u"{ \"privateBrowsingId\": 1 }"_ns; 246 247 already_AddRefed<nsIAsyncShutdownClient> GetAsyncShutdownBarrier() { 248 AssertIsOnMainThread(); 249 250 nsCOMPtr<nsIAsyncShutdownService> svc = services::GetAsyncShutdownService(); 251 MOZ_ASSERT(svc); 252 253 nsCOMPtr<nsIAsyncShutdownClient> barrier; 254 DebugOnly<nsresult> rv = 255 svc->GetProfileChangeTeardown(getter_AddRefs(barrier)); 256 MOZ_ASSERT(NS_SUCCEEDED(rv)); 257 258 return barrier.forget(); 259 } 260 261 Result<nsCOMPtr<nsIPrincipal>, nsresult> ScopeToPrincipal( 262 nsIURI* aScopeURI, const OriginAttributes& aOriginAttributes) { 263 MOZ_ASSERT(aScopeURI); 264 265 nsCOMPtr<nsIPrincipal> principal = 266 BasePrincipal::CreateContentPrincipal(aScopeURI, aOriginAttributes); 267 if (NS_WARN_IF(!principal)) { 268 return Err(NS_ERROR_FAILURE); 269 } 270 271 return principal; 272 } 273 274 Result<nsCOMPtr<nsIPrincipal>, nsresult> ScopeToPrincipal( 275 const nsACString& aScope, const OriginAttributes& aOriginAttributes) { 276 MOZ_ASSERT(nsContentUtils::IsAbsoluteURL(aScope)); 277 278 nsCOMPtr<nsIURI> scopeURI; 279 MOZ_TRY(NS_NewURI(getter_AddRefs(scopeURI), aScope)); 280 281 return ScopeToPrincipal(scopeURI, aOriginAttributes); 282 } 283 284 } // namespace 285 286 struct ServiceWorkerManager::RegistrationDataPerPrincipal final { 287 // Implements a container of keys for the "scope to registration map": 288 // https://w3c.github.io/ServiceWorker/#dfn-scope-to-registration-map 289 // 290 // where each key is an absolute URL. 291 // 292 // The properties of this map that the spec uses are 293 // 1) insertion, 294 // 2) removal, 295 // 3) iteration of scopes in FIFO order (excluding removed scopes), 296 // 4) and finding, for a given path, the maximal length scope which is a 297 // prefix of the path. 298 // 299 // Additionally, because this is a container of keys for a map, there 300 // shouldn't be duplicate scopes. 301 // 302 // The current implementation uses a dynamic array as the underlying 303 // container, which is not optimal for unbounded container sizes (all 304 // supported operations are in linear time) but may be superior for small 305 // container sizes. 306 // 307 // If this is proven to be too slow, the underlying storage should be replaced 308 // with a linked list of scopes in combination with an ordered map that maps 309 // scopes to linked list elements/iterators. This would reduce all of the 310 // above operations besides iteration (necessarily linear) to logarithmic 311 // time. 312 class ScopeContainer final : private nsTArray<nsCString> { 313 using Base = nsTArray<nsCString>; 314 315 public: 316 using Base::Contains; 317 using Base::IsEmpty; 318 using Base::Length; 319 320 // No using-declaration to avoid importing the non-const overload. 321 decltype(auto) operator[](Base::index_type aIndex) const { 322 return Base::operator[](aIndex); 323 } 324 325 void InsertScope(const nsACString& aScope) { 326 MOZ_DIAGNOSTIC_ASSERT(nsContentUtils::IsAbsoluteURL(aScope)); 327 328 if (Contains(aScope)) { 329 return; 330 } 331 332 AppendElement(aScope); 333 } 334 335 void RemoveScope(const nsACString& aScope) { 336 MOZ_ALWAYS_TRUE(RemoveElement(aScope)); 337 } 338 339 // Implements most of "Match Service Worker Registration": 340 // https://w3c.github.io/ServiceWorker/#scope-match-algorithm 341 Maybe<nsCString> MatchScope(const nsACString& aClientUrl) const { 342 Maybe<nsCString> match; 343 344 for (const nsCString& scope : *this) { 345 if (StringBeginsWith(aClientUrl, scope)) { 346 if (!match || scope.Length() > match->Length()) { 347 match = Some(scope); 348 } 349 } 350 } 351 352 // Step 7.2: 353 // "Assert: matchingScope’s origin and clientURL’s origin are same 354 // origin." 355 MOZ_DIAGNOSTIC_ASSERT_IF(match, IsSameOrigin(*match, aClientUrl)); 356 357 return match; 358 } 359 360 private: 361 bool IsSameOrigin(const nsACString& aMatchingScope, 362 const nsACString& aClientUrl) const { 363 auto parseResult = ScopeToPrincipal(aMatchingScope, OriginAttributes()); 364 365 if (NS_WARN_IF(parseResult.isErr())) { 366 return false; 367 } 368 369 auto scopePrincipal = parseResult.unwrap(); 370 371 parseResult = ScopeToPrincipal(aClientUrl, OriginAttributes()); 372 373 if (NS_WARN_IF(parseResult.isErr())) { 374 return false; 375 } 376 377 auto clientPrincipal = parseResult.unwrap(); 378 379 bool equals = false; 380 381 if (NS_WARN_IF( 382 NS_FAILED(scopePrincipal->Equals(clientPrincipal, &equals)))) { 383 return false; 384 } 385 386 return equals; 387 } 388 }; 389 390 ScopeContainer mScopeContainer; 391 392 // Scope to registration. 393 // The scope should be a fully qualified valid URL. 394 nsRefPtrHashtable<nsCStringHashKey, ServiceWorkerRegistrationInfo> mInfos; 395 396 // Maps scopes to job queues. 397 nsRefPtrHashtable<nsCStringHashKey, ServiceWorkerJobQueue> mJobQueues; 398 399 // Map scopes to scheduled update timers. 400 nsInterfaceHashtable<nsCStringHashKey, nsITimer> mUpdateTimers; 401 402 // The number of times we have done a quota usage check for this origin for 403 // mitigation purposes. See the docs on nsIServiceWorkerRegistrationInfo, 404 // where this value is exposed. 405 int32_t mQuotaUsageCheckCount = 0; 406 }; 407 408 ////////////////////////// 409 // ServiceWorkerManager // 410 ////////////////////////// 411 412 NS_IMPL_ADDREF(ServiceWorkerManager) 413 NS_IMPL_RELEASE(ServiceWorkerManager) 414 415 NS_INTERFACE_MAP_BEGIN(ServiceWorkerManager) 416 NS_INTERFACE_MAP_ENTRY(nsIServiceWorkerManager) 417 NS_INTERFACE_MAP_ENTRY(nsIObserver) 418 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIServiceWorkerManager) 419 NS_INTERFACE_MAP_END 420 421 ServiceWorkerManager::ServiceWorkerManager() 422 : mActor(nullptr), mShuttingDown(false) {} 423 424 ServiceWorkerManager::~ServiceWorkerManager() { 425 // The map will assert if it is not empty when destroyed. 426 mRegistrationInfos.Clear(); 427 428 // This can happen if the browser is started up in ProfileManager mode, in 429 // which case XPCOM will startup and shutdown, but there won't be any 430 // profile-* topic notifications. The shutdown blocker expects to be in a 431 // NotAcceptingPromises state when it's destroyed, and this transition 432 // normally happens in the "profile-change-teardown" notification callback 433 // (which won't be called in ProfileManager mode). 434 if (!mShuttingDown && mShutdownBlocker) { 435 mShutdownBlocker->StopAcceptingPromises(); 436 } 437 } 438 439 void ServiceWorkerManager::BlockShutdownOn(GenericNonExclusivePromise* aPromise, 440 uint32_t aShutdownStateId) { 441 AssertIsOnMainThread(); 442 443 MOZ_ASSERT(mShutdownBlocker); 444 MOZ_ASSERT(aPromise); 445 446 mShutdownBlocker->WaitOnPromise(aPromise, aShutdownStateId); 447 } 448 449 void ServiceWorkerManager::Init(ServiceWorkerRegistrar* aRegistrar) { 450 // ServiceWorkers now only support parent intercept. In parent intercept 451 // mode, only the parent process ServiceWorkerManager has any state or does 452 // anything. 453 // 454 // It is our goal to completely eliminate support for content process 455 // ServiceWorkerManager instances and make getting a SWM instance trigger a 456 // fatal assertion. But until we've reached that point, we make 457 // initialization a no-op so that content process ServiceWorkerManager 458 // instances will simply have no state and no registrations. 459 if (!XRE_IsParentProcess()) { 460 return; 461 } 462 463 nsCOMPtr<nsIAsyncShutdownClient> shutdownBarrier = GetAsyncShutdownBarrier(); 464 465 if (shutdownBarrier) { 466 mShutdownBlocker = ServiceWorkerShutdownBlocker::CreateAndRegisterOn( 467 *shutdownBarrier, *this); 468 MOZ_ASSERT(mShutdownBlocker); 469 } 470 471 // This observer notification will be removed by 472 // ServiceWorkerManager::MaybeFinishShutdown which currently is triggered by 473 // receiving a "profile-before-change-qm" observer notification. That 474 // observer is added by our shutdown blocker which currently fires during the 475 // "profile-change-teardown" phase. 476 nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService(); 477 if (obs) { 478 obs->AddObserver(this, kPrivateBrowsingExited, false); 479 } 480 481 MOZ_DIAGNOSTIC_ASSERT(aRegistrar); 482 483 PBackgroundChild* actorChild = BackgroundChild::GetOrCreateForCurrentThread(); 484 if (NS_WARN_IF(!actorChild)) { 485 MaybeStartShutdown(); 486 return; 487 } 488 489 PServiceWorkerManagerChild* actor = 490 actorChild->SendPServiceWorkerManagerConstructor(); 491 if (!actor) { 492 MaybeStartShutdown(); 493 return; 494 } 495 496 mActor = static_cast<ServiceWorkerManagerChild*>(actor); 497 498 // mActor must be set before LoadRegistrations is called because it can purge 499 // service workers if preferences are disabled. 500 nsTArray<ServiceWorkerRegistrationData> data; 501 aRegistrar->GetRegistrations(data); 502 LoadRegistrations(data); 503 504 mTelemetryLastChange = TimeStamp::Now(); 505 506 mETPPermissionObserver = new ETPPermissionObserver(); 507 } 508 509 void ServiceWorkerManager::RecordTelemetry(uint32_t aNumber, uint32_t aFetch) { 510 // Submit N value pairs to Telemetry for the time we were at those values 511 auto now = TimeStamp::Now(); 512 // round down, with a minimum of 1 repeat. In theory this gives 513 // inaccuracy if there are frequent changes, but that's uncommon. 514 uint32_t repeats = (uint32_t)((now - mTelemetryLastChange).ToMilliseconds()) / 515 mTelemetryPeriodMs; 516 mTelemetryLastChange = now; 517 if (repeats == 0) { 518 repeats = 1; 519 } 520 nsCOMPtr<nsIRunnable> runnable = NS_NewRunnableFunction( 521 "ServiceWorkerTelemetryRunnable", [aNumber, aFetch, repeats]() { 522 LOG(("ServiceWorkers running: %u samples of %u/%u", repeats, aNumber, 523 aFetch)); 524 // Don't allocate infinitely huge arrays if someone visits a SW site 525 // after a few months running. 1 month is about 500K repeats @ 5s 526 // sampling 527 uint32_t num_repeats = std::min(repeats, 1000000U); // 4MB max 528 nsTArray<uint64_t> values; 529 530 uint64_t* array = values.AppendElements(num_repeats); 531 for (uint32_t i = 0; i < num_repeats; i++) { 532 array[i] = aNumber; 533 } 534 glean::service_worker::running 535 .EnumGet(glean::service_worker::RunningLabel::eAll) 536 .AccumulateSamples(values); 537 538 for (uint32_t i = 0; i < num_repeats; i++) { 539 array[i] = aFetch; 540 } 541 glean::service_worker::running 542 .EnumGet(glean::service_worker::RunningLabel::eFetch) 543 .AccumulateSamples(values); 544 }); 545 NS_DispatchBackgroundTask(runnable.forget(), nsIEventTarget::DISPATCH_NORMAL); 546 } 547 548 RefPtr<GenericErrorResultPromise> ServiceWorkerManager::StartControllingClient( 549 const ClientInfo& aClientInfo, 550 ServiceWorkerRegistrationInfo* aRegistrationInfo, 551 bool aControlClientHandle) { 552 MOZ_DIAGNOSTIC_ASSERT(aRegistrationInfo->GetActive()); 553 554 // XXX We can't use a generic lambda (accepting auto&& entry) like elsewhere 555 // with WithEntryHandle, since we get linker errors then using clang+lld. This 556 // might be a toolchain issue? 557 return mControlledClients.WithEntryHandle( 558 aClientInfo.Id(), 559 [&](decltype(mControlledClients)::EntryHandle&& entry) 560 -> RefPtr<GenericErrorResultPromise> { 561 const RefPtr<ServiceWorkerManager> self = this; 562 563 const ServiceWorkerDescriptor& active = 564 aRegistrationInfo->GetActive()->Descriptor(); 565 566 if (entry) { 567 const RefPtr<ServiceWorkerRegistrationInfo> old = 568 std::move(entry.Data()->mRegistrationInfo); 569 570 const RefPtr<GenericErrorResultPromise> promise = 571 aControlClientHandle 572 ? entry.Data()->mClientHandle->Control(active) 573 : GenericErrorResultPromise::CreateAndResolve(false, 574 __func__); 575 576 entry.Data()->mRegistrationInfo = aRegistrationInfo; 577 578 if (old != aRegistrationInfo) { 579 StopControllingRegistration(old); 580 aRegistrationInfo->StartControllingClient(); 581 } 582 583 // Always check to see if we failed to actually control the client. In 584 // that case remove the client from our list of controlled clients. 585 return promise->Then( 586 GetMainThreadSerialEventTarget(), __func__, 587 [](bool) { 588 // do nothing on success 589 return GenericErrorResultPromise::CreateAndResolve(true, 590 __func__); 591 }, 592 [self, aClientInfo](const CopyableErrorResult& aRv) { 593 // failed to control, forget about this client 594 self->StopControllingClient(aClientInfo); 595 return GenericErrorResultPromise::CreateAndReject(aRv, 596 __func__); 597 }); 598 } 599 600 RefPtr<ClientHandle> clientHandle = ClientManager::CreateHandle( 601 aClientInfo, GetMainThreadSerialEventTarget()); 602 603 const RefPtr<GenericErrorResultPromise> promise = 604 aControlClientHandle 605 ? clientHandle->Control(active) 606 : GenericErrorResultPromise::CreateAndResolve(false, __func__); 607 608 aRegistrationInfo->StartControllingClient(); 609 610 entry.Insert( 611 MakeUnique<ControlledClientData>(clientHandle, aRegistrationInfo)); 612 613 clientHandle->OnDetach()->Then( 614 GetMainThreadSerialEventTarget(), __func__, 615 [self, aClientInfo] { self->StopControllingClient(aClientInfo); }); 616 617 // Always check to see if we failed to actually control the client. In 618 // that case removed the client from our list of controlled clients. 619 return promise->Then( 620 GetMainThreadSerialEventTarget(), __func__, 621 [](bool) { 622 // do nothing on success 623 return GenericErrorResultPromise::CreateAndResolve(true, 624 __func__); 625 }, 626 [self, aClientInfo](const CopyableErrorResult& aRv) { 627 // failed to control, forget about this client 628 self->StopControllingClient(aClientInfo); 629 return GenericErrorResultPromise::CreateAndReject(aRv, __func__); 630 }); 631 }); 632 } 633 634 void ServiceWorkerManager::StopControllingClient( 635 const ClientInfo& aClientInfo) { 636 auto entry = mControlledClients.Lookup(aClientInfo.Id()); 637 if (!entry) { 638 return; 639 } 640 641 RefPtr<ServiceWorkerRegistrationInfo> reg = 642 std::move(entry.Data()->mRegistrationInfo); 643 644 entry.Remove(); 645 646 StopControllingRegistration(reg); 647 } 648 649 void ServiceWorkerManager::MaybeStartShutdown() { 650 MOZ_ASSERT(NS_IsMainThread()); 651 652 if (mShuttingDown) { 653 return; 654 } 655 656 mShuttingDown = true; 657 658 for (const auto& dataPtr : mRegistrationInfos.Values()) { 659 for (const auto& timerEntry : dataPtr->mUpdateTimers.Values()) { 660 timerEntry->Cancel(); 661 } 662 dataPtr->mUpdateTimers.Clear(); 663 664 for (const auto& queueEntry : dataPtr->mJobQueues.Values()) { 665 queueEntry->CancelAll(); 666 } 667 dataPtr->mJobQueues.Clear(); 668 669 for (const auto& registrationEntry : dataPtr->mInfos.Values()) { 670 registrationEntry->ShutdownWorkers(); 671 } 672 673 // ServiceWorkerCleanup may try to unregister registrations, so don't clear 674 // mInfos. 675 } 676 677 for (const auto& entry : mControlledClients.Values()) { 678 entry->mRegistrationInfo->ShutdownWorkers(); 679 } 680 681 for (auto iter = mOrphanedRegistrations.iter(); !iter.done(); iter.next()) { 682 iter.get()->ShutdownWorkers(); 683 } 684 685 if (mShutdownBlocker) { 686 mShutdownBlocker->StopAcceptingPromises(); 687 } 688 689 nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService(); 690 if (obs) { 691 obs->AddObserver(this, kFinishShutdownTopic, false); 692 return; 693 } 694 695 MaybeFinishShutdown(); 696 } 697 698 void ServiceWorkerManager::MaybeFinishShutdown() { 699 nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService(); 700 if (obs) { 701 obs->RemoveObserver(this, kFinishShutdownTopic); 702 obs->RemoveObserver(this, kPrivateBrowsingExited); 703 } 704 705 if (!mActor) { 706 return; 707 } 708 709 mActor->ManagerShuttingDown(); 710 711 RefPtr<TeardownRunnable> runnable = new TeardownRunnable(mActor); 712 nsresult rv = NS_DispatchToMainThread(runnable); 713 (void)NS_WARN_IF(NS_FAILED(rv)); 714 mActor = nullptr; 715 mETPPermissionObserver = nullptr; 716 717 // This also submits final telemetry 718 ServiceWorkerPrivate::RunningShutdown(); 719 } 720 721 class ServiceWorkerResolveWindowPromiseOnRegisterCallback final 722 : public ServiceWorkerJob::Callback { 723 public: 724 NS_INLINE_DECL_REFCOUNTING( 725 ServiceWorkerResolveWindowPromiseOnRegisterCallback, override) 726 727 virtual void JobFinished(ServiceWorkerJob* aJob, 728 ErrorResult& aStatus) override { 729 MOZ_ASSERT(NS_IsMainThread()); 730 MOZ_ASSERT(aJob); 731 732 if (aStatus.Failed()) { 733 mPromiseHolder.Reject(CopyableErrorResult(aStatus), __func__); 734 return; 735 } 736 737 MOZ_ASSERT(aJob->GetType() == ServiceWorkerJob::Type::Register); 738 RefPtr<ServiceWorkerRegisterJob> registerJob = 739 static_cast<ServiceWorkerRegisterJob*>(aJob); 740 RefPtr<ServiceWorkerRegistrationInfo> reg = registerJob->GetRegistration(); 741 742 mPromiseHolder.Resolve(reg->Descriptor(), __func__); 743 } 744 745 virtual void JobDiscarded(ErrorResult& aStatus) override { 746 MOZ_ASSERT(NS_IsMainThread()); 747 748 mPromiseHolder.Reject(CopyableErrorResult(aStatus), __func__); 749 } 750 751 RefPtr<ServiceWorkerRegistrationPromise> Promise() { 752 MOZ_ASSERT(NS_IsMainThread()); 753 return mPromiseHolder.Ensure(__func__); 754 } 755 756 private: 757 ~ServiceWorkerResolveWindowPromiseOnRegisterCallback() = default; 758 759 MozPromiseHolder<ServiceWorkerRegistrationPromise> mPromiseHolder; 760 }; 761 762 NS_IMETHODIMP 763 ServiceWorkerManager::RegisterForTest(nsIPrincipal* aPrincipal, 764 const nsAString& aScopeURL, 765 const nsAString& aScriptURL, 766 JSContext* aCx, 767 mozilla::dom::Promise** aPromise) { 768 nsIGlobalObject* global = xpc::CurrentNativeGlobal(aCx); 769 if (NS_WARN_IF(!global)) { 770 return NS_ERROR_FAILURE; 771 } 772 773 ErrorResult erv; 774 RefPtr<Promise> outer = Promise::Create(global, erv); 775 if (NS_WARN_IF(erv.Failed())) { 776 return erv.StealNSResult(); 777 } 778 779 if (!StaticPrefs::dom_serviceWorkers_testing_enabled()) { 780 outer->MaybeRejectWithAbortError( 781 "registerForTest only allowed when dom.serviceWorkers.testing.enabled " 782 "is true"); 783 outer.forget(aPromise); 784 return NS_OK; 785 } 786 787 if (aPrincipal == nullptr) { 788 outer->MaybeRejectWithAbortError("Missing principal"); 789 outer.forget(aPromise); 790 return NS_OK; 791 } 792 793 if (aScriptURL.IsEmpty()) { 794 outer->MaybeRejectWithAbortError("Missing script url"); 795 outer.forget(aPromise); 796 return NS_OK; 797 } 798 799 if (aScopeURL.IsEmpty()) { 800 outer->MaybeRejectWithAbortError("Missing scope url"); 801 outer.forget(aPromise); 802 return NS_OK; 803 } 804 805 // The ClientType isn't really used here, but ClientType::Window 806 // is the least bad choice since this is happening on the main thread. 807 Maybe<ClientInfo> clientInfo = 808 dom::ClientManager::CreateInfo(ClientType::Window, aPrincipal); 809 810 if (!clientInfo.isSome()) { 811 outer->MaybeRejectWithUnknownError("Error creating clientInfo"); 812 outer.forget(aPromise); 813 return NS_OK; 814 } 815 816 auto scope = NS_ConvertUTF16toUTF8(aScopeURL); 817 auto scriptURL = NS_ConvertUTF16toUTF8(aScriptURL); 818 819 auto regPromise = 820 Register(clientInfo.ref(), scope, WorkerType::Classic, scriptURL, 821 dom::ServiceWorkerUpdateViaCache::Imports); 822 823 const RefPtr<ServiceWorkerManager> self(this); 824 const nsCOMPtr<nsIPrincipal> principal(aPrincipal); 825 regPromise->Then( 826 GetMainThreadSerialEventTarget(), __func__, 827 [self, outer, principal, 828 scope](const ServiceWorkerRegistrationDescriptor& regDesc) { 829 RefPtr<ServiceWorkerRegistrationInfo> registration = 830 self->GetRegistration(principal, NS_ConvertUTF16toUTF8(scope)); 831 if (registration) { 832 outer->MaybeResolve(registration); 833 } else { 834 outer->MaybeRejectWithUnknownError( 835 "Failed to retrieve ServiceWorkerRegistrationInfo"); 836 } 837 }, 838 [outer](const mozilla::CopyableErrorResult& err) { 839 CopyableErrorResult result(err); 840 outer->MaybeReject(std::move(result)); 841 }); 842 843 outer.forget(aPromise); 844 845 return NS_OK; 846 } 847 848 RefPtr<ServiceWorkerRegistrationPromise> ServiceWorkerManager::Register( 849 const ClientInfo& aClientInfo, const nsACString& aScopeURL, 850 const WorkerType& aType, const nsACString& aScriptURL, 851 ServiceWorkerUpdateViaCache aUpdateViaCache) { 852 AUTO_PROFILER_MARKER_UNTYPED("SWM Register", DOM, {}); 853 854 nsCOMPtr<nsIURI> scopeURI; 855 nsresult rv = NS_NewURI(getter_AddRefs(scopeURI), aScopeURL); 856 if (NS_FAILED(rv)) { 857 // Odd, since it was serialiazed from an nsIURI. 858 CopyableErrorResult err; 859 err.ThrowInvalidStateError("Scope URL cannot be parsed"); 860 return ServiceWorkerRegistrationPromise::CreateAndReject(err, __func__); 861 } 862 863 nsCOMPtr<nsIURI> scriptURI; 864 rv = NS_NewURI(getter_AddRefs(scriptURI), aScriptURL); 865 if (NS_FAILED(rv)) { 866 // Odd, since it was serialiazed from an nsIURI. 867 CopyableErrorResult err; 868 err.ThrowInvalidStateError("Script URL cannot be parsed"); 869 return ServiceWorkerRegistrationPromise::CreateAndReject(err, __func__); 870 } 871 872 IgnoredErrorResult err; 873 ServiceWorkerScopeAndScriptAreValid(aClientInfo, scopeURI, scriptURI, err); 874 if (err.Failed()) { 875 return ServiceWorkerRegistrationPromise::CreateAndReject( 876 CopyableErrorResult(std::move(err)), __func__); 877 } 878 879 // If the previous validation step passed then we must have a principal. 880 auto principalOrErr = aClientInfo.GetPrincipal(); 881 882 if (NS_WARN_IF(principalOrErr.isErr())) { 883 return ServiceWorkerRegistrationPromise::CreateAndReject( 884 CopyableErrorResult(principalOrErr.unwrapErr()), __func__); 885 } 886 887 nsCOMPtr<nsIPrincipal> principal = principalOrErr.unwrap(); 888 nsAutoCString scopeKey; 889 rv = PrincipalToScopeKey(principal, scopeKey); 890 if (NS_WARN_IF(NS_FAILED(rv))) { 891 return ServiceWorkerRegistrationPromise::CreateAndReject( 892 CopyableErrorResult(rv), __func__); 893 } 894 895 RefPtr<ServiceWorkerJobQueue> queue = 896 GetOrCreateJobQueue(scopeKey, aScopeURL); 897 898 RefPtr<ServiceWorkerResolveWindowPromiseOnRegisterCallback> cb = 899 new ServiceWorkerResolveWindowPromiseOnRegisterCallback(); 900 901 auto lifetime = DetermineLifetimeForClient(aClientInfo); 902 903 RefPtr<ServiceWorkerRegisterJob> job = new ServiceWorkerRegisterJob( 904 principal, aScopeURL, aType, aScriptURL, 905 static_cast<ServiceWorkerUpdateViaCache>(aUpdateViaCache), lifetime); 906 907 job->AppendResultCallback(cb); 908 queue->ScheduleJob(job); 909 910 MOZ_ASSERT(NS_IsMainThread()); 911 912 return cb->Promise(); 913 } 914 915 /* 916 * Implements the async aspects of the getRegistrations algorithm. 917 */ 918 class GetRegistrationsRunnable final : public Runnable { 919 const ClientInfo mClientInfo; 920 RefPtr<ServiceWorkerRegistrationListPromise::Private> mPromise; 921 922 public: 923 explicit GetRegistrationsRunnable(const ClientInfo& aClientInfo) 924 : Runnable("dom::ServiceWorkerManager::GetRegistrationsRunnable"), 925 mClientInfo(aClientInfo), 926 mPromise(new ServiceWorkerRegistrationListPromise::Private(__func__)) {} 927 928 RefPtr<ServiceWorkerRegistrationListPromise> Promise() const { 929 return mPromise; 930 } 931 932 NS_IMETHOD 933 Run() override { 934 auto scopeExit = MakeScopeExit( 935 [&] { mPromise->Reject(NS_ERROR_DOM_INVALID_STATE_ERR, __func__); }); 936 937 RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance(); 938 if (!swm) { 939 return NS_OK; 940 } 941 942 auto principalOrErr = mClientInfo.GetPrincipal(); 943 if (NS_WARN_IF(principalOrErr.isErr())) { 944 return NS_OK; 945 } 946 947 nsCOMPtr<nsIPrincipal> principal = principalOrErr.unwrap(); 948 949 nsTArray<ServiceWorkerRegistrationDescriptor> array; 950 951 if (NS_WARN_IF(!BasePrincipal::Cast(principal)->IsContentPrincipal())) { 952 return NS_OK; 953 } 954 955 nsAutoCString scopeKey; 956 nsresult rv = swm->PrincipalToScopeKey(principal, scopeKey); 957 if (NS_WARN_IF(NS_FAILED(rv))) { 958 return rv; 959 } 960 961 ServiceWorkerManager::RegistrationDataPerPrincipal* data; 962 if (!swm->mRegistrationInfos.Get(scopeKey, &data)) { 963 scopeExit.release(); 964 mPromise->Resolve(array, __func__); 965 return NS_OK; 966 } 967 968 for (uint32_t i = 0; i < data->mScopeContainer.Length(); ++i) { 969 RefPtr<ServiceWorkerRegistrationInfo> info = 970 data->mInfos.GetWeak(data->mScopeContainer[i]); 971 972 NS_ConvertUTF8toUTF16 scope(data->mScopeContainer[i]); 973 974 nsCOMPtr<nsIURI> scopeURI; 975 nsresult rv = NS_NewURI(getter_AddRefs(scopeURI), scope); 976 if (NS_WARN_IF(NS_FAILED(rv))) { 977 break; 978 } 979 980 // Unfortunately we don't seem to have an obvious window id here; in 981 // particular ClientInfo does not have one, and neither do service worker 982 // registrations, as far as I can tell. 983 rv = principal->CheckMayLoadWithReporting( 984 scopeURI, false /* allowIfInheritsPrincipal */, 985 0 /* innerWindowID */); 986 if (NS_WARN_IF(NS_FAILED(rv))) { 987 continue; 988 } 989 990 array.AppendElement(info->Descriptor()); 991 } 992 993 scopeExit.release(); 994 mPromise->Resolve(array, __func__); 995 996 return NS_OK; 997 } 998 }; 999 1000 RefPtr<ServiceWorkerRegistrationListPromise> 1001 ServiceWorkerManager::GetRegistrations(const ClientInfo& aClientInfo) const { 1002 RefPtr<GetRegistrationsRunnable> runnable = 1003 new GetRegistrationsRunnable(aClientInfo); 1004 MOZ_ALWAYS_SUCCEEDS(NS_DispatchToCurrentThread(runnable)); 1005 return runnable->Promise(); 1006 } 1007 1008 /* 1009 * Implements the async aspects of the getRegistration algorithm. 1010 */ 1011 class GetRegistrationRunnable final : public Runnable { 1012 const ClientInfo mClientInfo; 1013 RefPtr<ServiceWorkerRegistrationPromise::Private> mPromise; 1014 nsCString mURL; 1015 1016 public: 1017 GetRegistrationRunnable(const ClientInfo& aClientInfo, const nsACString& aURL) 1018 : Runnable("dom::ServiceWorkerManager::GetRegistrationRunnable"), 1019 mClientInfo(aClientInfo), 1020 mPromise(new ServiceWorkerRegistrationPromise::Private(__func__)), 1021 mURL(aURL) {} 1022 1023 RefPtr<ServiceWorkerRegistrationPromise> Promise() const { return mPromise; } 1024 1025 NS_IMETHOD 1026 Run() override { 1027 RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance(); 1028 if (!swm) { 1029 mPromise->Reject(NS_ERROR_DOM_INVALID_STATE_ERR, __func__); 1030 return NS_OK; 1031 } 1032 1033 auto principalOrErr = mClientInfo.GetPrincipal(); 1034 if (NS_WARN_IF(principalOrErr.isErr())) { 1035 mPromise->Reject(NS_ERROR_DOM_INVALID_STATE_ERR, __func__); 1036 return NS_OK; 1037 } 1038 1039 nsCOMPtr<nsIPrincipal> principal = principalOrErr.unwrap(); 1040 nsCOMPtr<nsIURI> uri; 1041 nsresult rv = NS_NewURI(getter_AddRefs(uri), mURL); 1042 if (NS_WARN_IF(NS_FAILED(rv))) { 1043 mPromise->Reject(rv, __func__); 1044 return NS_OK; 1045 } 1046 1047 // Unfortunately we don't seem to have an obvious window id here; in 1048 // particular ClientInfo does not have one, and neither do service worker 1049 // registrations, as far as I can tell. 1050 rv = principal->CheckMayLoadWithReporting( 1051 uri, false /* allowIfInheritsPrincipal */, 0 /* innerWindowID */); 1052 if (NS_FAILED(rv)) { 1053 mPromise->Reject(NS_ERROR_DOM_SECURITY_ERR, __func__); 1054 return NS_OK; 1055 } 1056 1057 RefPtr<ServiceWorkerRegistrationInfo> registration = 1058 swm->GetServiceWorkerRegistrationInfo(principal, uri); 1059 1060 if (!registration) { 1061 // Reject with NS_OK means "not found". 1062 mPromise->Reject(NS_OK, __func__); 1063 return NS_OK; 1064 } 1065 1066 mPromise->Resolve(registration->Descriptor(), __func__); 1067 1068 return NS_OK; 1069 } 1070 }; 1071 1072 RefPtr<ServiceWorkerRegistrationPromise> ServiceWorkerManager::GetRegistration( 1073 const ClientInfo& aClientInfo, const nsACString& aURL) const { 1074 MOZ_ASSERT(NS_IsMainThread()); 1075 1076 RefPtr<GetRegistrationRunnable> runnable = 1077 new GetRegistrationRunnable(aClientInfo, aURL); 1078 MOZ_ALWAYS_SUCCEEDS(NS_DispatchToCurrentThread(runnable)); 1079 1080 return runnable->Promise(); 1081 } 1082 1083 NS_IMETHODIMP 1084 ServiceWorkerManager::SendPushEvent(const nsACString& aOriginAttributes, 1085 const nsACString& aScope, 1086 const nsTArray<uint8_t>& aDataBytes, 1087 uint8_t optional_argc) { 1088 if (optional_argc == 1) { 1089 // This does one copy here (while constructing the Maybe) and another when 1090 // we end up copying into the SendPushEventRunnable. We could fix that to 1091 // only do one copy by making things between here and there take 1092 // Maybe<nsTArray<uint8_t>>&&, but then we'd need to copy before we know 1093 // whether we really need to in PushMessageDispatcher::NotifyWorkers. Since 1094 // in practice this only affects JS callers that pass data, and we don't 1095 // have any right now, let's not worry about it. 1096 return SendPushEvent(aOriginAttributes, aScope, u""_ns, 1097 Some(aDataBytes.Clone())); 1098 } 1099 MOZ_ASSERT(optional_argc == 0); 1100 return SendPushEvent(aOriginAttributes, aScope, u""_ns, Nothing()); 1101 } 1102 1103 nsresult ServiceWorkerManager::SendCookieChangeEvent( 1104 const OriginAttributes& aOriginAttributes, const nsACString& aScope, 1105 const net::CookieStruct& aCookie, bool aCookieDeleted) { 1106 nsCOMPtr<nsIPrincipal> principal = 1107 MOZ_TRY(ScopeToPrincipal(aScope, aOriginAttributes)); 1108 1109 RefPtr<ServiceWorkerRegistrationInfo> registration = 1110 GetRegistration(principal, aScope); 1111 if (NS_WARN_IF(!registration)) { 1112 return NS_ERROR_FAILURE; 1113 } 1114 1115 MOZ_DIAGNOSTIC_ASSERT(registration->Scope().Equals(aScope)); 1116 1117 ServiceWorkerInfo* serviceWorker = registration->GetActive(); 1118 if (NS_WARN_IF(!serviceWorker)) { 1119 return NS_ERROR_FAILURE; 1120 } 1121 1122 return serviceWorker->WorkerPrivate()->SendCookieChangeEvent( 1123 aCookie, aCookieDeleted, registration); 1124 } 1125 1126 nsresult ServiceWorkerManager::SendPushEvent( 1127 const nsACString& aOriginAttributes, const nsACString& aScope, 1128 const nsAString& aMessageId, const Maybe<nsTArray<uint8_t>>& aData) { 1129 OriginAttributes attrs; 1130 if (!attrs.PopulateFromSuffix(aOriginAttributes)) { 1131 return NS_ERROR_INVALID_ARG; 1132 } 1133 1134 nsCOMPtr<nsIPrincipal> principal = MOZ_TRY(ScopeToPrincipal(aScope, attrs)); 1135 1136 // The registration handling a push notification must have an exact scope 1137 // match. This will try to find an exact match, unlike how fetch may find the 1138 // registration with the longest scope that's a prefix of the fetched URL. 1139 RefPtr<ServiceWorkerRegistrationInfo> registration = 1140 GetRegistration(principal, aScope); 1141 if (NS_WARN_IF(!registration)) { 1142 return NS_ERROR_FAILURE; 1143 } 1144 1145 MOZ_DIAGNOSTIC_ASSERT(registration->Scope().Equals(aScope)); 1146 1147 ServiceWorkerInfo* serviceWorker = registration->GetActive(); 1148 if (NS_WARN_IF(!serviceWorker)) { 1149 return NS_ERROR_FAILURE; 1150 } 1151 1152 return serviceWorker->WorkerPrivate()->SendPushEvent(aMessageId, aData, 1153 registration); 1154 } 1155 1156 NS_IMETHODIMP 1157 ServiceWorkerManager::SendPushSubscriptionChangeEvent( 1158 const nsACString& aOriginAttributes, const nsACString& aScope, 1159 nsIPushSubscription* aOldSubscription) { 1160 OriginAttributes attrs; 1161 if (!attrs.PopulateFromSuffix(aOriginAttributes)) { 1162 return NS_ERROR_INVALID_ARG; 1163 } 1164 1165 ServiceWorkerInfo* info = GetActiveWorkerInfoForScope(attrs, aScope); 1166 if (!info) { 1167 return NS_ERROR_FAILURE; 1168 } 1169 return info->WorkerPrivate()->SendPushSubscriptionChangeEvent( 1170 aOldSubscription); 1171 } 1172 1173 NS_IMETHODIMP 1174 ServiceWorkerManager::SendNotificationClickEvent( 1175 const nsACString& aOriginSuffix, const nsAString& aScope, 1176 const IPCNotification& aNotification, const nsAString& aAction) { 1177 OriginAttributes attrs; 1178 if (!attrs.PopulateFromSuffix(aOriginSuffix)) { 1179 return NS_ERROR_INVALID_ARG; 1180 } 1181 1182 ServiceWorkerInfo* info = 1183 GetActiveWorkerInfoForScope(attrs, NS_ConvertUTF16toUTF8(aScope)); 1184 if (!info) { 1185 return NS_ERROR_FAILURE; 1186 } 1187 1188 ServiceWorkerPrivate* workerPrivate = info->WorkerPrivate(); 1189 1190 return workerPrivate->SendNotificationClickEvent(aNotification, aAction); 1191 } 1192 1193 NS_IMETHODIMP 1194 ServiceWorkerManager::SendNotificationCloseEvent( 1195 const nsACString& aOriginSuffix, const nsAString& aScope, 1196 const IPCNotification& aNotification) { 1197 OriginAttributes attrs; 1198 if (!attrs.PopulateFromSuffix(aOriginSuffix)) { 1199 return NS_ERROR_INVALID_ARG; 1200 } 1201 1202 ServiceWorkerInfo* info = 1203 GetActiveWorkerInfoForScope(attrs, NS_ConvertUTF16toUTF8(aScope)); 1204 if (!info) { 1205 return NS_ERROR_FAILURE; 1206 } 1207 1208 ServiceWorkerPrivate* workerPrivate = info->WorkerPrivate(); 1209 1210 return workerPrivate->SendNotificationCloseEvent(aNotification); 1211 } 1212 1213 RefPtr<ServiceWorkerRegistrationPromise> ServiceWorkerManager::WhenReady( 1214 const ClientInfo& aClientInfo) { 1215 AssertIsOnMainThread(); 1216 1217 for (auto& prd : mPendingReadyList) { 1218 if (prd->mClientHandle->Info().Id() == aClientInfo.Id() && 1219 prd->mClientHandle->Info().PrincipalInfo() == 1220 aClientInfo.PrincipalInfo()) { 1221 return prd->mPromise; 1222 } 1223 } 1224 1225 RefPtr<ServiceWorkerRegistrationInfo> reg = 1226 GetServiceWorkerRegistrationInfo(aClientInfo); 1227 if (reg && reg->GetActive()) { 1228 return ServiceWorkerRegistrationPromise::CreateAndResolve(reg->Descriptor(), 1229 __func__); 1230 } 1231 1232 nsCOMPtr<nsISerialEventTarget> target = GetMainThreadSerialEventTarget(); 1233 1234 RefPtr<ClientHandle> handle = 1235 ClientManager::CreateHandle(aClientInfo, target); 1236 mPendingReadyList.AppendElement(MakeUnique<PendingReadyData>(handle)); 1237 1238 RefPtr<ServiceWorkerManager> self(this); 1239 handle->OnDetach()->Then(target, __func__, 1240 [self = std::move(self), aClientInfo] { 1241 self->RemovePendingReadyPromise(aClientInfo); 1242 }); 1243 1244 return mPendingReadyList.LastElement()->mPromise; 1245 } 1246 1247 void ServiceWorkerManager::CheckPendingReadyPromises() { 1248 nsTArray<UniquePtr<PendingReadyData>> pendingReadyList = 1249 std::move(mPendingReadyList); 1250 for (uint32_t i = 0; i < pendingReadyList.Length(); ++i) { 1251 UniquePtr<PendingReadyData> prd(std::move(pendingReadyList[i])); 1252 1253 RefPtr<ServiceWorkerRegistrationInfo> reg = 1254 GetServiceWorkerRegistrationInfo(prd->mClientHandle->Info()); 1255 1256 if (reg && reg->GetActive()) { 1257 prd->mPromise->Resolve(reg->Descriptor(), __func__); 1258 } else { 1259 mPendingReadyList.AppendElement(std::move(prd)); 1260 } 1261 } 1262 } 1263 1264 void ServiceWorkerManager::RemovePendingReadyPromise( 1265 const ClientInfo& aClientInfo) { 1266 nsTArray<UniquePtr<PendingReadyData>> pendingReadyList = 1267 std::move(mPendingReadyList); 1268 for (uint32_t i = 0; i < pendingReadyList.Length(); ++i) { 1269 UniquePtr<PendingReadyData> prd(std::move(pendingReadyList[i])); 1270 1271 if (prd->mClientHandle->Info().Id() == aClientInfo.Id() && 1272 prd->mClientHandle->Info().PrincipalInfo() == 1273 aClientInfo.PrincipalInfo()) { 1274 prd->mPromise->Reject(NS_ERROR_DOM_ABORT_ERR, __func__); 1275 } else { 1276 mPendingReadyList.AppendElement(std::move(prd)); 1277 } 1278 } 1279 } 1280 1281 void ServiceWorkerManager::NoteInheritedController( 1282 const ClientInfo& aClientInfo, const ServiceWorkerDescriptor& aController) { 1283 MOZ_ASSERT(NS_IsMainThread()); 1284 1285 auto principalOrErr = PrincipalInfoToPrincipal(aController.PrincipalInfo()); 1286 1287 if (NS_WARN_IF(principalOrErr.isErr())) { 1288 return; 1289 } 1290 1291 nsCOMPtr<nsIPrincipal> principal = principalOrErr.unwrap(); 1292 nsCOMPtr<nsIURI> scope; 1293 nsresult rv = NS_NewURI(getter_AddRefs(scope), aController.Scope()); 1294 NS_ENSURE_SUCCESS_VOID(rv); 1295 1296 RefPtr<ServiceWorkerRegistrationInfo> registration = 1297 GetServiceWorkerRegistrationInfo(principal, scope); 1298 NS_ENSURE_TRUE_VOID(registration); 1299 NS_ENSURE_TRUE_VOID(registration->GetActive()); 1300 1301 StartControllingClient(aClientInfo, registration, 1302 false /* aControlClientHandle */); 1303 } 1304 1305 ServiceWorkerInfo* ServiceWorkerManager::GetActiveWorkerInfoForScope( 1306 const OriginAttributes& aOriginAttributes, const nsACString& aScope) { 1307 MOZ_ASSERT(NS_IsMainThread()); 1308 1309 nsCOMPtr<nsIURI> scopeURI; 1310 nsresult rv = NS_NewURI(getter_AddRefs(scopeURI), aScope); 1311 if (NS_FAILED(rv)) { 1312 return nullptr; 1313 } 1314 1315 auto result = ScopeToPrincipal(scopeURI, aOriginAttributes); 1316 if (NS_WARN_IF(result.isErr())) { 1317 return nullptr; 1318 } 1319 1320 auto principal = result.unwrap(); 1321 1322 RefPtr<ServiceWorkerRegistrationInfo> registration = 1323 GetServiceWorkerRegistrationInfo(principal, scopeURI); 1324 if (!registration) { 1325 return nullptr; 1326 } 1327 1328 return registration->GetActive(); 1329 } 1330 1331 ServiceWorkerInfo* ServiceWorkerManager::GetServiceWorkerByClientInfo( 1332 const ClientInfo& aClientInfo) const { 1333 MOZ_ASSERT(NS_IsMainThread()); 1334 1335 auto principalOrErr = aClientInfo.GetPrincipal(); 1336 if (NS_WARN_IF(principalOrErr.isErr())) { 1337 return nullptr; 1338 } 1339 1340 nsCOMPtr<nsIPrincipal> principal = principalOrErr.unwrap(); 1341 1342 nsAutoCString scopeKey; 1343 nsresult rv = PrincipalToScopeKey(principal, scopeKey); 1344 if (NS_WARN_IF(NS_FAILED(rv))) { 1345 return nullptr; 1346 } 1347 1348 RegistrationDataPerPrincipal* data; 1349 if (!mRegistrationInfos.Get(scopeKey, &data)) { 1350 return nullptr; 1351 } 1352 1353 // If the ClientInfo has CSP data populated, we need to normalize that off, so 1354 // make a copy and only propagate the non-CSP fields. 1355 ClientInfo normalized = ClientInfo( 1356 aClientInfo.Id(), aClientInfo.AgentClusterId(), aClientInfo.Type(), 1357 aClientInfo.PrincipalInfo(), aClientInfo.CreationTime(), 1358 aClientInfo.URL(), aClientInfo.FrameType()); 1359 1360 for (const auto& registration : data->mInfos.Values()) { 1361 ServiceWorkerInfo* info = registration->GetByClientInfo(normalized); 1362 if (info) { 1363 return info; 1364 } 1365 } 1366 1367 return nullptr; 1368 } 1369 1370 ServiceWorkerInfo* ServiceWorkerManager::GetServiceWorkerByDescriptor( 1371 const ServiceWorkerDescriptor& aServiceWorker) const { 1372 auto principalOrErr = aServiceWorker.GetPrincipal(); 1373 if (NS_WARN_IF(principalOrErr.isErr())) { 1374 return nullptr; 1375 } 1376 1377 nsCOMPtr<nsIPrincipal> principal = principalOrErr.unwrap(); 1378 1379 RefPtr<ServiceWorkerRegistrationInfo> registration = 1380 GetRegistration(principal, aServiceWorker.Scope()); 1381 if (NS_WARN_IF(!registration)) { 1382 return nullptr; 1383 } 1384 1385 return registration->GetByDescriptor(aServiceWorker); 1386 } 1387 1388 namespace { 1389 1390 class UnregisterJobCallback final : public ServiceWorkerJob::Callback { 1391 nsCOMPtr<nsIServiceWorkerUnregisterCallback> mCallback; 1392 1393 ~UnregisterJobCallback() { MOZ_ASSERT(!mCallback); } 1394 1395 public: 1396 explicit UnregisterJobCallback(nsIServiceWorkerUnregisterCallback* aCallback) 1397 : mCallback(aCallback) { 1398 MOZ_ASSERT(NS_IsMainThread()); 1399 MOZ_ASSERT(mCallback); 1400 } 1401 1402 void JobFinished(ServiceWorkerJob* aJob, ErrorResult& aStatus) override { 1403 MOZ_ASSERT(NS_IsMainThread()); 1404 MOZ_ASSERT(aJob); 1405 MOZ_ASSERT(mCallback); 1406 1407 auto scopeExit = MakeScopeExit([&]() { mCallback = nullptr; }); 1408 1409 if (aStatus.Failed()) { 1410 mCallback->UnregisterFailed(); 1411 return; 1412 } 1413 1414 MOZ_ASSERT(aJob->GetType() == ServiceWorkerJob::Type::Unregister); 1415 RefPtr<ServiceWorkerUnregisterJob> unregisterJob = 1416 static_cast<ServiceWorkerUnregisterJob*>(aJob); 1417 mCallback->UnregisterSucceeded(unregisterJob->GetResult()); 1418 } 1419 1420 void JobDiscarded(ErrorResult&) override { 1421 MOZ_ASSERT(NS_IsMainThread()); 1422 MOZ_ASSERT(mCallback); 1423 1424 mCallback->UnregisterFailed(); 1425 mCallback = nullptr; 1426 } 1427 1428 NS_INLINE_DECL_REFCOUNTING(UnregisterJobCallback, override) 1429 }; 1430 1431 } // anonymous namespace 1432 1433 NS_IMETHODIMP 1434 ServiceWorkerManager::Unregister(nsIPrincipal* aPrincipal, 1435 nsIServiceWorkerUnregisterCallback* aCallback, 1436 const nsAString& aScope) { 1437 MOZ_ASSERT(NS_IsMainThread()); 1438 1439 if (!aPrincipal) { 1440 return NS_ERROR_FAILURE; 1441 } 1442 1443 nsresult rv; 1444 1445 // This is not accessible by content, and callers should always ensure scope is 1446 // a correct URI, so this is wrapped in DEBUG 1447 #ifdef DEBUG 1448 nsCOMPtr<nsIURI> scopeURI; 1449 rv = NS_NewURI(getter_AddRefs(scopeURI), aScope); 1450 if (NS_WARN_IF(NS_FAILED(rv))) { 1451 return NS_ERROR_DOM_SECURITY_ERR; 1452 } 1453 #endif 1454 1455 nsAutoCString scopeKey; 1456 rv = PrincipalToScopeKey(aPrincipal, scopeKey); 1457 if (NS_WARN_IF(NS_FAILED(rv))) { 1458 return rv; 1459 } 1460 1461 NS_ConvertUTF16toUTF8 scope(aScope); 1462 RefPtr<ServiceWorkerJobQueue> queue = GetOrCreateJobQueue(scopeKey, scope); 1463 1464 RefPtr<ServiceWorkerUnregisterJob> job = 1465 new ServiceWorkerUnregisterJob(aPrincipal, scope); 1466 1467 if (aCallback) { 1468 RefPtr<UnregisterJobCallback> cb = new UnregisterJobCallback(aCallback); 1469 job->AppendResultCallback(cb); 1470 } 1471 1472 queue->ScheduleJob(job); 1473 return NS_OK; 1474 } 1475 1476 void ServiceWorkerManager::WorkerIsIdle(ServiceWorkerInfo* aWorker) { 1477 MOZ_ASSERT(NS_IsMainThread()); 1478 MOZ_DIAGNOSTIC_ASSERT(aWorker); 1479 1480 RefPtr<ServiceWorkerRegistrationInfo> reg = 1481 GetRegistration(aWorker->Principal(), aWorker->Scope()); 1482 if (!reg) { 1483 return; 1484 } 1485 1486 // We only care about the active worker becoming idle because it means we 1487 // can now promote a waiting worker to active. 1488 if (reg->GetActive() != aWorker) { 1489 return; 1490 } 1491 1492 // The active worker becoming idle is not a reason to extend a waiting SW's 1493 // lifetime (or spawn it) on its own because that potentially enables 1494 // unlimited lifetime extension. `TryToActivate` will handle upgrading the 1495 // lifetime extension to a full extension if the registration is controlling 1496 // pages (and skipWaiting was used). It's fine for the ServiceWorker to 1497 // receive its activation message prior to its next functional event. 1498 reg->TryToActivateAsync( 1499 ServiceWorkerLifetimeExtension(NoLifetimeExtension{})); 1500 } 1501 1502 already_AddRefed<ServiceWorkerJobQueue> 1503 ServiceWorkerManager::GetOrCreateJobQueue(const nsACString& aKey, 1504 const nsACString& aScope) { 1505 MOZ_ASSERT(!aKey.IsEmpty()); 1506 ServiceWorkerManager::RegistrationDataPerPrincipal* data; 1507 // XXX we could use WithEntryHandle here to avoid a hashtable lookup, except 1508 // that leads to a false positive assertion, see bug 1370674 comment 7. 1509 if (!mRegistrationInfos.Get(aKey, &data)) { 1510 data = mRegistrationInfos 1511 .InsertOrUpdate(aKey, MakeUnique<RegistrationDataPerPrincipal>()) 1512 .get(); 1513 } 1514 1515 RefPtr queue = data->mJobQueues.GetOrInsertNew(aScope); 1516 return queue.forget(); 1517 } 1518 1519 /* static */ 1520 already_AddRefed<ServiceWorkerManager> ServiceWorkerManager::GetInstance() { 1521 if (!gInstance) { 1522 RefPtr<ServiceWorkerRegistrar> swr; 1523 1524 // XXX: Substitute this with an assertion. See comment in Init. 1525 if (XRE_IsParentProcess()) { 1526 // Don't (re-)create the ServiceWorkerManager if we are already shutting 1527 // down. 1528 if (AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdownConfirmed)) { 1529 return nullptr; 1530 } 1531 // Don't create the ServiceWorkerManager until the ServiceWorkerRegistrar 1532 // is initialized. 1533 swr = ServiceWorkerRegistrar::Get(); 1534 if (!swr) { 1535 return nullptr; 1536 } 1537 } 1538 1539 MOZ_ASSERT(NS_IsMainThread()); 1540 1541 gInstance = new ServiceWorkerManager(); 1542 gInstance->Init(swr); 1543 ClearOnShutdown(&gInstance); 1544 } 1545 RefPtr<ServiceWorkerManager> copy = gInstance.get(); 1546 return copy.forget(); 1547 } 1548 1549 void ServiceWorkerManager::ReportToAllClients( 1550 const nsCString& aScope, const nsString& aMessage, 1551 const nsCString& aFilename, const nsString& aLine, uint32_t aLineNumber, 1552 uint32_t aColumnNumber, uint32_t aFlags) { 1553 ConsoleUtils::ReportForServiceWorkerScope( 1554 NS_ConvertUTF8toUTF16(aScope), aMessage, aFilename, aLineNumber, 1555 aColumnNumber, ConsoleUtils::eError); 1556 } 1557 1558 /* static */ 1559 void ServiceWorkerManager::LocalizeAndReportToAllClients( 1560 const nsCString& aScope, const char* aStringKey, 1561 const nsTArray<nsString>& aParamArray, uint32_t aFlags, 1562 const nsCString& aFilename, const nsString& aLine, uint32_t aLineNumber, 1563 uint32_t aColumnNumber) { 1564 RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance(); 1565 if (!swm) { 1566 return; 1567 } 1568 1569 nsresult rv; 1570 nsAutoString message; 1571 rv = nsContentUtils::FormatLocalizedString(nsContentUtils::eDOM_PROPERTIES, 1572 aStringKey, aParamArray, message); 1573 if (NS_SUCCEEDED(rv)) { 1574 swm->ReportToAllClients(aScope, message, aFilename, aLine, aLineNumber, 1575 aColumnNumber, aFlags); 1576 } else { 1577 NS_WARNING("Failed to format and therefore report localized error."); 1578 } 1579 } 1580 1581 void ServiceWorkerManager::HandleError( 1582 JSContext* aCx, nsIPrincipal* aPrincipal, const nsCString& aScope, 1583 const nsCString& aWorkerURL, const nsString& aMessage, 1584 const nsCString& aFilename, const nsString& aLine, uint32_t aLineNumber, 1585 uint32_t aColumnNumber, uint32_t aFlags, JSExnType aExnType) { 1586 MOZ_ASSERT(NS_IsMainThread()); 1587 MOZ_ASSERT(aPrincipal); 1588 1589 nsAutoCString scopeKey; 1590 nsresult rv = PrincipalToScopeKey(aPrincipal, scopeKey); 1591 if (NS_WARN_IF(NS_FAILED(rv))) { 1592 return; 1593 } 1594 1595 ServiceWorkerManager::RegistrationDataPerPrincipal* data; 1596 if (NS_WARN_IF(!mRegistrationInfos.Get(scopeKey, &data))) { 1597 return; 1598 } 1599 1600 // Always report any uncaught exceptions or errors to the console of 1601 // each client. 1602 ReportToAllClients(aScope, aMessage, aFilename, aLine, aLineNumber, 1603 aColumnNumber, aFlags); 1604 } 1605 1606 void ServiceWorkerManager::PurgeServiceWorker( 1607 const ServiceWorkerRegistrationData& aRegistration, 1608 nsIPrincipal* aPrincipal) { 1609 MOZ_ASSERT(mActor); 1610 serviceWorkerScriptCache::PurgeCache(aPrincipal, aRegistration.cacheName()); 1611 MaybeSendUnregister(aPrincipal, aRegistration.scope()); 1612 } 1613 1614 void ServiceWorkerManager::LoadRegistration( 1615 const ServiceWorkerRegistrationData& aRegistration) { 1616 MOZ_ASSERT(NS_IsMainThread()); 1617 1618 auto principalOrErr = PrincipalInfoToPrincipal(aRegistration.principal()); 1619 if (NS_WARN_IF(principalOrErr.isErr())) { 1620 return; 1621 } 1622 nsCOMPtr<nsIPrincipal> principal = principalOrErr.unwrap(); 1623 1624 if (!StaticPrefs::dom_serviceWorkers_enabled()) { 1625 // If service workers are disabled, remove the registration from disk 1626 // instead of loading. 1627 PurgeServiceWorker(aRegistration, principal); 1628 return; 1629 } 1630 1631 // Purge extensions registrations if they are disabled by prefs. 1632 if (!StaticPrefs::extensions_backgroundServiceWorker_enabled_AtStartup()) { 1633 nsCOMPtr<nsIURI> uri = principal->GetURI(); 1634 1635 // We do check the URI scheme here because when this is going to run 1636 // the extension may not have been loaded yet and the WebExtensionPolicy 1637 // may not exist yet. 1638 if (uri->SchemeIs("moz-extension")) { 1639 PurgeServiceWorker(aRegistration, principal); 1640 return; 1641 } 1642 } 1643 1644 RefPtr<ServiceWorkerRegistrationInfo> registration = 1645 GetRegistration(principal, aRegistration.scope()); 1646 if (!registration) { 1647 registration = CreateNewRegistration( 1648 aRegistration.scope(), aRegistration.type(), principal, 1649 static_cast<ServiceWorkerUpdateViaCache>( 1650 aRegistration.updateViaCache()), 1651 aRegistration.navigationPreloadState()); 1652 } else { 1653 // If active worker script matches our expectations for a "current worker", 1654 // then we are done. Since scripts with the same URL might have different 1655 // contents such as updated scripts or scripts with different LoadFlags, we 1656 // use the CacheName to judge whether the two scripts are identical, where 1657 // the CacheName is an UUID generated when a new script is found. 1658 if (registration->GetActive() && 1659 registration->GetActive()->CacheName() == aRegistration.cacheName()) { 1660 // No needs for updates. 1661 return; 1662 } 1663 } 1664 1665 registration->SetLastUpdateTime(aRegistration.lastUpdateTime()); 1666 1667 nsLoadFlags importsLoadFlags = nsIChannel::LOAD_BYPASS_SERVICE_WORKER; 1668 if (aRegistration.updateViaCache() != 1669 static_cast<uint16_t>(ServiceWorkerUpdateViaCache::None)) { 1670 importsLoadFlags |= nsIRequest::VALIDATE_ALWAYS; 1671 } 1672 1673 const nsCString& currentWorkerURL = aRegistration.currentWorkerURL(); 1674 if (!currentWorkerURL.IsEmpty()) { 1675 registration->SetActive(new ServiceWorkerInfo( 1676 registration->Principal(), registration->Scope(), registration->Type(), 1677 registration->Id(), registration->Version(), currentWorkerURL, 1678 aRegistration.cacheName(), importsLoadFlags)); 1679 registration->GetActive()->SetHandlesFetch( 1680 aRegistration.currentWorkerHandlesFetch()); 1681 registration->GetActive()->SetInstalledTime( 1682 aRegistration.currentWorkerInstalledTime()); 1683 registration->GetActive()->SetActivatedTime( 1684 aRegistration.currentWorkerActivatedTime()); 1685 } 1686 } 1687 1688 void ServiceWorkerManager::LoadRegistrations( 1689 const nsTArray<ServiceWorkerRegistrationData>& aRegistrations) { 1690 MOZ_ASSERT(NS_IsMainThread()); 1691 for (uint32_t i = 0, len = aRegistrations.Length(); i < len; ++i) { 1692 LoadRegistration(aRegistrations[i]); 1693 } 1694 } 1695 1696 void ServiceWorkerManager::StoreRegistration( 1697 nsIPrincipal* aPrincipal, ServiceWorkerRegistrationInfo* aRegistration) { 1698 MOZ_ASSERT(aPrincipal); 1699 MOZ_ASSERT(aRegistration); 1700 1701 if (mShuttingDown) { 1702 return; 1703 } 1704 1705 // Do not store private browsing registrations to disk; our in-memory state 1706 // suffices. 1707 if (aPrincipal->GetIsInPrivateBrowsing()) { 1708 // If we are seeing a PBM principal, PBM support must be enabled. 1709 MOZ_ASSERT(StaticPrefs::dom_serviceWorkers_privateBrowsing_enabled()); 1710 return; 1711 } 1712 1713 // Do not store a registration for addons that are not installed, not enabled 1714 // or installed temporarily. 1715 // 1716 // If the dom.serviceWorkers.testing.persistTemporaryInstalledAddons is set 1717 // to true, the registration for a temporary installed addon will still be 1718 // persisted (only meant to be used to make it easier to test some particular 1719 // scenario with a temporary installed addon which doesn't need to be signed 1720 // to be installed on release channel builds). 1721 if (aPrincipal->SchemeIs("moz-extension")) { 1722 RefPtr<extensions::WebExtensionPolicy> addonPolicy = 1723 BasePrincipal::Cast(aPrincipal)->AddonPolicy(); 1724 if (!addonPolicy || !addonPolicy->Active() || 1725 (addonPolicy->TemporarilyInstalled() && 1726 !StaticPrefs:: 1727 dom_serviceWorkers_testing_persistTemporarilyInstalledAddons())) { 1728 return; 1729 } 1730 } 1731 1732 ServiceWorkerRegistrationData data; 1733 nsresult rv = PopulateRegistrationData(aPrincipal, aRegistration, data); 1734 if (NS_WARN_IF(NS_FAILED(rv))) { 1735 return; 1736 } 1737 1738 PrincipalInfo principalInfo; 1739 if (NS_WARN_IF( 1740 NS_FAILED(PrincipalToPrincipalInfo(aPrincipal, &principalInfo)))) { 1741 return; 1742 } 1743 1744 mActor->SendRegister(data); 1745 } 1746 1747 already_AddRefed<ServiceWorkerRegistrationInfo> 1748 ServiceWorkerManager::GetServiceWorkerRegistrationInfo( 1749 const ClientInfo& aClientInfo) const { 1750 auto principalOrErr = aClientInfo.GetPrincipal(); 1751 if (NS_WARN_IF(principalOrErr.isErr())) { 1752 return nullptr; 1753 } 1754 1755 nsCOMPtr<nsIPrincipal> principal = principalOrErr.unwrap(); 1756 nsCOMPtr<nsIURI> uri; 1757 nsresult rv = NS_NewURI(getter_AddRefs(uri), aClientInfo.URL()); 1758 NS_ENSURE_SUCCESS(rv, nullptr); 1759 1760 return GetServiceWorkerRegistrationInfo(principal, uri); 1761 } 1762 1763 already_AddRefed<ServiceWorkerRegistrationInfo> 1764 ServiceWorkerManager::GetServiceWorkerRegistrationInfo(nsIPrincipal* aPrincipal, 1765 nsIURI* aURI) const { 1766 MOZ_ASSERT(aPrincipal); 1767 MOZ_ASSERT(aURI); 1768 1769 nsAutoCString scopeKey; 1770 nsresult rv = PrincipalToScopeKey(aPrincipal, scopeKey); 1771 if (NS_FAILED(rv)) { 1772 return nullptr; 1773 } 1774 1775 return GetServiceWorkerRegistrationInfo(scopeKey, aURI); 1776 } 1777 1778 already_AddRefed<ServiceWorkerRegistrationInfo> 1779 ServiceWorkerManager::GetServiceWorkerRegistrationInfo( 1780 const nsACString& aScopeKey, nsIURI* aURI) const { 1781 MOZ_ASSERT(aURI); 1782 1783 nsAutoCString spec; 1784 nsresult rv = aURI->GetSpec(spec); 1785 if (NS_WARN_IF(NS_FAILED(rv))) { 1786 return nullptr; 1787 } 1788 1789 nsAutoCString scope; 1790 RegistrationDataPerPrincipal* data; 1791 if (!FindScopeForPath(aScopeKey, spec, &data, scope)) { 1792 return nullptr; 1793 } 1794 1795 MOZ_ASSERT(data); 1796 1797 RefPtr<ServiceWorkerRegistrationInfo> registration; 1798 data->mInfos.Get(scope, getter_AddRefs(registration)); 1799 // ordered scopes and registrations better be in sync. 1800 MOZ_ASSERT(registration); 1801 1802 #ifdef DEBUG 1803 nsAutoCString origin; 1804 rv = registration->Principal()->GetOrigin(origin); 1805 MOZ_ASSERT(NS_SUCCEEDED(rv)); 1806 MOZ_ASSERT(origin.Equals(aScopeKey)); 1807 #endif 1808 1809 return registration.forget(); 1810 } 1811 1812 /* static */ 1813 nsresult ServiceWorkerManager::PrincipalToScopeKey(nsIPrincipal* aPrincipal, 1814 nsACString& aKey) { 1815 MOZ_ASSERT(aPrincipal); 1816 1817 if (!BasePrincipal::Cast(aPrincipal)->IsContentPrincipal()) { 1818 return NS_ERROR_FAILURE; 1819 } 1820 1821 nsresult rv = aPrincipal->GetOrigin(aKey); 1822 if (NS_WARN_IF(NS_FAILED(rv))) { 1823 return rv; 1824 } 1825 1826 return NS_OK; 1827 } 1828 1829 /* static */ 1830 nsresult ServiceWorkerManager::PrincipalInfoToScopeKey( 1831 const PrincipalInfo& aPrincipalInfo, nsACString& aKey) { 1832 if (aPrincipalInfo.type() != PrincipalInfo::TContentPrincipalInfo) { 1833 return NS_ERROR_FAILURE; 1834 } 1835 1836 auto content = aPrincipalInfo.get_ContentPrincipalInfo(); 1837 1838 nsAutoCString suffix; 1839 content.attrs().CreateSuffix(suffix); 1840 1841 aKey = content.originNoSuffix(); 1842 aKey.Append(suffix); 1843 1844 return NS_OK; 1845 } 1846 1847 /* static */ 1848 void ServiceWorkerManager::AddScopeAndRegistration( 1849 const nsACString& aScope, ServiceWorkerRegistrationInfo* aInfo) { 1850 MOZ_ASSERT(aInfo); 1851 MOZ_ASSERT(aInfo->Principal()); 1852 MOZ_ASSERT(!aInfo->IsUnregistered()); 1853 1854 RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance(); 1855 if (!swm) { 1856 // browser shutdown 1857 return; 1858 } 1859 1860 nsAutoCString scopeKey; 1861 nsresult rv = swm->PrincipalToScopeKey(aInfo->Principal(), scopeKey); 1862 if (NS_WARN_IF(NS_FAILED(rv))) { 1863 return; 1864 } 1865 1866 MOZ_ASSERT(!scopeKey.IsEmpty()); 1867 1868 auto* const data = swm->mRegistrationInfos.GetOrInsertNew(scopeKey); 1869 data->mScopeContainer.InsertScope(aScope); 1870 data->mInfos.InsertOrUpdate(aScope, RefPtr{aInfo}); 1871 swm->NotifyListenersOnRegister(aInfo); 1872 } 1873 1874 /* static */ 1875 bool ServiceWorkerManager::FindScopeForPath( 1876 const nsACString& aScopeKey, const nsACString& aPath, 1877 RegistrationDataPerPrincipal** aData, nsACString& aMatch) { 1878 MOZ_ASSERT(aData); 1879 1880 RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance(); 1881 1882 if (!swm || !swm->mRegistrationInfos.Get(aScopeKey, aData)) { 1883 return false; 1884 } 1885 1886 Maybe<nsCString> scope = (*aData)->mScopeContainer.MatchScope(aPath); 1887 1888 if (scope) { 1889 // scope.isSome() will still truen true after this; we are just moving the 1890 // string inside the Maybe, so the Maybe will contain an empty string. 1891 aMatch = std::move(*scope); 1892 } 1893 1894 return scope.isSome(); 1895 } 1896 1897 /* static */ 1898 bool ServiceWorkerManager::HasScope(nsIPrincipal* aPrincipal, 1899 const nsACString& aScope) { 1900 RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance(); 1901 if (!swm) { 1902 return false; 1903 } 1904 1905 nsAutoCString scopeKey; 1906 nsresult rv = PrincipalToScopeKey(aPrincipal, scopeKey); 1907 if (NS_WARN_IF(NS_FAILED(rv))) { 1908 return false; 1909 } 1910 1911 RegistrationDataPerPrincipal* data; 1912 if (!swm->mRegistrationInfos.Get(scopeKey, &data)) { 1913 return false; 1914 } 1915 1916 return data->mScopeContainer.Contains(aScope); 1917 } 1918 1919 /* static */ 1920 void ServiceWorkerManager::RemoveScopeAndRegistration( 1921 ServiceWorkerRegistrationInfo* aRegistration) { 1922 RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance(); 1923 if (!swm) { 1924 return; 1925 } 1926 1927 nsAutoCString scopeKey; 1928 nsresult rv = swm->PrincipalToScopeKey(aRegistration->Principal(), scopeKey); 1929 if (NS_WARN_IF(NS_FAILED(rv))) { 1930 return; 1931 } 1932 1933 RegistrationDataPerPrincipal* data; 1934 if (!swm->mRegistrationInfos.Get(scopeKey, &data)) { 1935 return; 1936 } 1937 1938 if (auto entry = data->mUpdateTimers.Lookup(aRegistration->Scope())) { 1939 entry.Data()->Cancel(); 1940 entry.Remove(); 1941 } 1942 1943 // Verify there are no controlled clients for the purged registration. 1944 for (auto iter = swm->mControlledClients.Iter(); !iter.Done(); iter.Next()) { 1945 auto& reg = iter.UserData()->mRegistrationInfo; 1946 if (reg->Scope().Equals(aRegistration->Scope()) && 1947 reg->Principal()->Equals(aRegistration->Principal()) && 1948 reg->IsCorrupt()) { 1949 iter.Remove(); 1950 } 1951 } 1952 1953 RefPtr<ServiceWorkerRegistrationInfo> info; 1954 data->mInfos.Remove(aRegistration->Scope(), getter_AddRefs(info)); 1955 aRegistration->SetUnregistered(); 1956 data->mScopeContainer.RemoveScope(aRegistration->Scope()); 1957 swm->NotifyListenersOnUnregister(info); 1958 1959 swm->MaybeRemoveRegistrationInfo(scopeKey); 1960 } 1961 1962 void ServiceWorkerManager::MaybeRemoveRegistrationInfo( 1963 const nsACString& aScopeKey) { 1964 if (auto entry = mRegistrationInfos.Lookup(aScopeKey)) { 1965 if (entry.Data()->mScopeContainer.IsEmpty() && 1966 entry.Data()->mJobQueues.Count() == 0) { 1967 entry.Remove(); 1968 1969 // Need to reset the mQuotaUsageCheckCount, if 1970 // RegistrationDataPerPrincipal:: mScopeContainer is empty. This 1971 // RegistrationDataPerPrincipal might be reused, such that quota usage 1972 // mitigation can be triggered for the new added registration. 1973 } else if (entry.Data()->mScopeContainer.IsEmpty() && 1974 entry.Data()->mQuotaUsageCheckCount) { 1975 entry.Data()->mQuotaUsageCheckCount = 0; 1976 } 1977 } 1978 } 1979 1980 bool ServiceWorkerManager::StartControlling( 1981 const ClientInfo& aClientInfo, 1982 const ServiceWorkerDescriptor& aServiceWorker) { 1983 MOZ_ASSERT(NS_IsMainThread()); 1984 1985 auto principalOrErr = 1986 PrincipalInfoToPrincipal(aServiceWorker.PrincipalInfo()); 1987 1988 if (NS_WARN_IF(principalOrErr.isErr())) { 1989 return false; 1990 } 1991 1992 nsCOMPtr<nsIPrincipal> principal = principalOrErr.unwrap(); 1993 1994 nsCOMPtr<nsIURI> scope; 1995 nsresult rv = NS_NewURI(getter_AddRefs(scope), aServiceWorker.Scope()); 1996 NS_ENSURE_SUCCESS(rv, false); 1997 1998 RefPtr<ServiceWorkerRegistrationInfo> registration = 1999 GetServiceWorkerRegistrationInfo(principal, scope); 2000 NS_ENSURE_TRUE(registration, false); 2001 NS_ENSURE_TRUE(registration->GetActive(), false); 2002 2003 StartControllingClient(aClientInfo, registration); 2004 2005 return true; 2006 } 2007 2008 void ServiceWorkerManager::MaybeCheckNavigationUpdate( 2009 const ClientInfo& aClientInfo) { 2010 MOZ_ASSERT(NS_IsMainThread()); 2011 // We perform these success path navigation update steps when the 2012 // document tells us its more or less done loading. This avoids 2013 // slowing down page load and also lets pages consistently get 2014 // updatefound events when they fire. 2015 // 2016 // 9.8.20 If respondWithEntered is false, then: 2017 // 9.8.22 Else: (respondWith was entered and succeeded) 2018 // If request is a non-subresource request, then: Invoke Soft Update 2019 // algorithm. 2020 ControlledClientData* data = mControlledClients.Get(aClientInfo.Id()); 2021 if (data && data->mRegistrationInfo) { 2022 data->mRegistrationInfo->MaybeScheduleUpdate(); 2023 } 2024 } 2025 2026 void ServiceWorkerManager::StopControllingRegistration( 2027 ServiceWorkerRegistrationInfo* aRegistration) { 2028 aRegistration->StopControllingClient(); 2029 if (aRegistration->IsControllingClients()) { 2030 return; 2031 } 2032 2033 if (aRegistration->IsUnregistered()) { 2034 if (aRegistration->IsIdle()) { 2035 aRegistration->Clear(); 2036 } else { 2037 aRegistration->ClearWhenIdle(); 2038 } 2039 return; 2040 } 2041 2042 // We used to aggressively terminate the worker at this point, but it 2043 // caused problems. There are more uses for a service worker than actively 2044 // controlled documents. We need to let the worker naturally terminate 2045 // in case it's handling push events, message events, etc. 2046 aRegistration->TryToActivateAsync( 2047 ServiceWorkerLifetimeExtension(NoLifetimeExtension{})); 2048 } 2049 2050 NS_IMETHODIMP 2051 ServiceWorkerManager::GetScopeForUrl(nsIPrincipal* aPrincipal, 2052 const nsAString& aUrl, nsAString& aScope) { 2053 MOZ_ASSERT(aPrincipal); 2054 2055 nsCOMPtr<nsIURI> uri; 2056 nsresult rv = NS_NewURI(getter_AddRefs(uri), aUrl); 2057 if (NS_WARN_IF(NS_FAILED(rv))) { 2058 return NS_ERROR_FAILURE; 2059 } 2060 2061 RefPtr<ServiceWorkerRegistrationInfo> r = 2062 GetServiceWorkerRegistrationInfo(aPrincipal, uri); 2063 if (!r) { 2064 return NS_ERROR_FAILURE; 2065 } 2066 2067 CopyUTF8toUTF16(r->Scope(), aScope); 2068 return NS_OK; 2069 } 2070 2071 namespace { 2072 2073 class ContinueDispatchFetchEventRunnable : public Runnable { 2074 RefPtr<ServiceWorkerPrivate> mServiceWorkerPrivate; 2075 nsCOMPtr<nsIInterceptedChannel> mChannel; 2076 nsCOMPtr<nsILoadGroup> mLoadGroup; 2077 2078 public: 2079 ContinueDispatchFetchEventRunnable( 2080 ServiceWorkerPrivate* aServiceWorkerPrivate, 2081 nsIInterceptedChannel* aChannel, nsILoadGroup* aLoadGroup) 2082 : Runnable( 2083 "dom::ServiceWorkerManager::ContinueDispatchFetchEventRunnable"), 2084 mServiceWorkerPrivate(aServiceWorkerPrivate), 2085 mChannel(aChannel), 2086 mLoadGroup(aLoadGroup) { 2087 MOZ_ASSERT(aServiceWorkerPrivate); 2088 MOZ_ASSERT(aChannel); 2089 } 2090 2091 void HandleError() { 2092 MOZ_ASSERT(NS_IsMainThread()); 2093 NS_WARNING("Unexpected error while dispatching fetch event!"); 2094 nsresult rv = mChannel->ResetInterception(false); 2095 if (NS_FAILED(rv)) { 2096 NS_WARNING("Failed to resume intercepted network request"); 2097 mChannel->CancelInterception(rv); 2098 } 2099 } 2100 2101 NS_IMETHOD 2102 Run() override { 2103 MOZ_ASSERT(NS_IsMainThread()); 2104 2105 nsCOMPtr<nsIChannel> channel; 2106 nsresult rv = mChannel->GetChannel(getter_AddRefs(channel)); 2107 if (NS_WARN_IF(NS_FAILED(rv))) { 2108 HandleError(); 2109 return NS_OK; 2110 } 2111 2112 // The channel might have encountered an unexpected error while ensuring 2113 // the upload stream is cloneable. Check here and reset the interception 2114 // if that happens. 2115 nsresult status; 2116 rv = channel->GetStatus(&status); 2117 if (NS_WARN_IF(NS_FAILED(rv) || NS_FAILED(status))) { 2118 HandleError(); 2119 return NS_OK; 2120 } 2121 2122 nsString clientId; 2123 nsString resultingClientId; 2124 nsCOMPtr<nsILoadInfo> loadInfo = channel->LoadInfo(); 2125 Maybe<ClientInfo> clientInfo = loadInfo->GetClientInfo(); 2126 if (clientInfo.isSome()) { 2127 clientId = NSID_TrimBracketsUTF16(clientInfo->Id()); 2128 } 2129 2130 // Having an initial or reserved client are mutually exclusive events: 2131 // either an initial client is used upon navigating an about:blank 2132 // iframe, or a new, reserved environment/client is created (e.g. 2133 // upon a top-level navigation). See step 4 of 2134 // https://html.spec.whatwg.org/#process-a-navigate-fetch as well as 2135 // https://github.com/w3c/ServiceWorker/issues/1228#issuecomment-345132444 2136 Maybe<ClientInfo> resulting = loadInfo->GetInitialClientInfo(); 2137 2138 if (resulting.isNothing()) { 2139 resulting = loadInfo->GetReservedClientInfo(); 2140 } else { 2141 MOZ_ASSERT(loadInfo->GetReservedClientInfo().isNothing()); 2142 } 2143 2144 if (resulting.isSome()) { 2145 resultingClientId = NSID_TrimBracketsUTF16(resulting->Id()); 2146 } 2147 2148 rv = mServiceWorkerPrivate->SendFetchEvent(mChannel, mLoadGroup, clientId, 2149 resultingClientId); 2150 if (NS_WARN_IF(NS_FAILED(rv))) { 2151 HandleError(); 2152 } 2153 2154 return NS_OK; 2155 } 2156 }; 2157 2158 } // anonymous namespace 2159 2160 void ServiceWorkerManager::DispatchFetchEvent(nsIInterceptedChannel* aChannel, 2161 ErrorResult& aRv) { 2162 MOZ_ASSERT(aChannel); 2163 MOZ_ASSERT(NS_IsMainThread()); 2164 MOZ_ASSERT(XRE_IsParentProcess()); 2165 2166 nsCOMPtr<nsIChannel> internalChannel; 2167 aRv = aChannel->GetChannel(getter_AddRefs(internalChannel)); 2168 if (NS_WARN_IF(aRv.Failed())) { 2169 return; 2170 } 2171 2172 nsCOMPtr<nsILoadGroup> loadGroup; 2173 aRv = internalChannel->GetLoadGroup(getter_AddRefs(loadGroup)); 2174 if (NS_WARN_IF(aRv.Failed())) { 2175 return; 2176 } 2177 2178 nsCOMPtr<nsILoadInfo> loadInfo = internalChannel->LoadInfo(); 2179 RefPtr<ServiceWorkerInfo> serviceWorker; 2180 2181 if (!nsContentUtils::IsNonSubresourceRequest(internalChannel)) { 2182 const Maybe<ServiceWorkerDescriptor>& controller = 2183 loadInfo->GetController(); 2184 if (NS_WARN_IF(controller.isNothing())) { 2185 aRv.Throw(NS_ERROR_FAILURE); 2186 return; 2187 } 2188 2189 RefPtr<ServiceWorkerRegistrationInfo> registration; 2190 nsresult rv = GetClientRegistration(loadInfo->GetClientInfo().ref(), 2191 getter_AddRefs(registration)); 2192 if (NS_WARN_IF(NS_FAILED(rv))) { 2193 aRv.Throw(rv); 2194 return; 2195 } 2196 2197 serviceWorker = registration->GetActive(); 2198 if (NS_WARN_IF(!serviceWorker) || 2199 NS_WARN_IF(serviceWorker->Descriptor().Id() != controller.ref().Id())) { 2200 aRv.Throw(NS_ERROR_FAILURE); 2201 return; 2202 } 2203 } else { 2204 nsCOMPtr<nsIURI> uri; 2205 aRv = aChannel->GetSecureUpgradedChannelURI(getter_AddRefs(uri)); 2206 if (NS_WARN_IF(aRv.Failed())) { 2207 return; 2208 } 2209 2210 // non-subresource request means the URI contains the principal 2211 OriginAttributes attrs = loadInfo->GetOriginAttributes(); 2212 if (StaticPrefs::privacy_partition_serviceWorkers()) { 2213 StoragePrincipalHelper::GetOriginAttributes( 2214 internalChannel, attrs, 2215 StoragePrincipalHelper::eForeignPartitionedPrincipal); 2216 } 2217 2218 nsCOMPtr<nsIPrincipal> principal = 2219 BasePrincipal::CreateContentPrincipal(uri, attrs); 2220 2221 RefPtr<ServiceWorkerRegistrationInfo> registration = 2222 GetServiceWorkerRegistrationInfo(principal, uri); 2223 if (NS_WARN_IF(!registration)) { 2224 aRv.Throw(NS_ERROR_FAILURE); 2225 return; 2226 } 2227 2228 // While we only enter this method if IsAvailable() previously saw 2229 // an active worker, it is possible for that worker to be removed 2230 // before we get to this point. Therefore we must handle a nullptr 2231 // active worker here. 2232 serviceWorker = registration->GetActive(); 2233 if (NS_WARN_IF(!serviceWorker)) { 2234 aRv.Throw(NS_ERROR_FAILURE); 2235 return; 2236 } 2237 2238 // If there is a reserved client it should be marked as controlled before 2239 // the FetchEvent is dispatched. 2240 Maybe<ClientInfo> clientInfo = loadInfo->GetReservedClientInfo(); 2241 2242 // Also override the initial about:blank controller since the real 2243 // network load may be intercepted by a different service worker. If 2244 // the intial about:blank has a controller here its simply been 2245 // inherited from its parent. 2246 if (clientInfo.isNothing()) { 2247 clientInfo = loadInfo->GetInitialClientInfo(); 2248 2249 // TODO: We need to handle the case where the initial about:blank is 2250 // controlled, but the final document load is not. Right now 2251 // the spec does not really say what to do. There currently 2252 // is no way for the controller to be cleared from a client in 2253 // the spec or our implementation. We may want to force a 2254 // new inner window to be created instead of reusing the 2255 // initial about:blank global. See bug 1419620 and the spec 2256 // issue here: https://github.com/w3c/ServiceWorker/issues/1232 2257 } 2258 2259 if (clientInfo.isSome()) { 2260 // ClientChannelHelper is not called for STS upgrades that get 2261 // intercepted by a service worker when interception occurs in 2262 // the content process. Therefore the reserved client is not 2263 // properly cleared in that case leading to a situation where 2264 // a ClientSource with an http:// principal is controlled by 2265 // a ServiceWorker with an https:// principal. 2266 // 2267 // This does not occur when interception is handled by the 2268 // simpler InterceptedHttpChannel approach in the parent. 2269 // 2270 // As a temporary work around check for this principal mismatch 2271 // here and perform the ClientChannelHelper's replacement of 2272 // reserved client automatically. 2273 if (!XRE_IsParentProcess()) { 2274 auto clientPrincipalOrErr = clientInfo.ref().GetPrincipal(); 2275 2276 nsCOMPtr<nsIPrincipal> clientPrincipal; 2277 if (clientPrincipalOrErr.isOk()) { 2278 clientPrincipal = clientPrincipalOrErr.unwrap(); 2279 } 2280 2281 if (!clientPrincipal || !clientPrincipal->Equals(principal)) { 2282 UniquePtr<ClientSource> reservedClient = 2283 loadInfo->TakeReservedClientSource(); 2284 2285 nsCOMPtr<nsISerialEventTarget> target = 2286 reservedClient ? reservedClient->EventTarget() 2287 : GetMainThreadSerialEventTarget(); 2288 2289 reservedClient.reset(); 2290 reservedClient = ClientManager::CreateSource(ClientType::Window, 2291 target, principal); 2292 2293 loadInfo->GiveReservedClientSource(std::move(reservedClient)); 2294 2295 clientInfo = loadInfo->GetReservedClientInfo(); 2296 } 2297 } 2298 2299 // First, attempt to mark the reserved client controlled directly. This 2300 // will update the controlled status in the ClientManagerService in the 2301 // parent. It will also eventually propagate back to the ClientSource. 2302 StartControllingClient(clientInfo.ref(), registration); 2303 } 2304 2305 uint32_t redirectMode = nsIHttpChannelInternal::REDIRECT_MODE_MANUAL; 2306 nsCOMPtr<nsIHttpChannelInternal> http = do_QueryInterface(internalChannel); 2307 MOZ_ALWAYS_SUCCEEDS(http->GetRedirectMode(&redirectMode)); 2308 2309 // Synthetic redirects for non-subresource requests with a "follow" 2310 // redirect mode may switch controllers. This is basically worker 2311 // scripts right now. In this case we need to explicitly clear the 2312 // controller to avoid assertions on the SetController() below. 2313 if (redirectMode == nsIHttpChannelInternal::REDIRECT_MODE_FOLLOW) { 2314 loadInfo->ClearController(); 2315 } 2316 2317 // But we also note the reserved state on the LoadInfo. This allows the 2318 // ClientSource to be updated immediately after the nsIChannel starts. 2319 // This is necessary to have the correct controller in place for immediate 2320 // follow-on requests. 2321 loadInfo->SetController(serviceWorker->Descriptor()); 2322 } 2323 2324 MOZ_DIAGNOSTIC_ASSERT(serviceWorker); 2325 2326 RefPtr<ContinueDispatchFetchEventRunnable> continueRunnable = 2327 new ContinueDispatchFetchEventRunnable(serviceWorker->WorkerPrivate(), 2328 aChannel, loadGroup); 2329 2330 // When this service worker was registered, we also sent down the permissions 2331 // for the runnable. They should have arrived by now, but we still need to 2332 // wait for them if they have not. 2333 RefPtr<PermissionManager> permMgr = PermissionManager::GetInstance(); 2334 if (permMgr) { 2335 permMgr->WhenPermissionsAvailable(serviceWorker->Principal(), 2336 continueRunnable); 2337 } else { 2338 continueRunnable->HandleError(); 2339 } 2340 } 2341 2342 ServiceWorkerLifetimeExtension ServiceWorkerManager::DetermineLifetimeForClient( 2343 const ClientInfo& aClientInfo) { 2344 // For everything but ServiceWorkers this should extend the ServiceWorker's 2345 // lifetime. 2346 if (aClientInfo.Type() == ClientType::Serviceworker) { 2347 // But for the ServiceWorker we need to find out the originating 2348 // ServiceWorker's lifetime and use that. 2349 ServiceWorkerInfo* source = GetServiceWorkerByClientInfo(aClientInfo); 2350 if (source) { 2351 return ServiceWorkerLifetimeExtension(PropagatedLifetimeExtension{ 2352 .mDeadline = source->LifetimeDeadline(), 2353 }); 2354 } 2355 2356 // If we can't find the ServiceWorker, then it's gone and we shouldn't 2357 // extend the lifetime. 2358 return ServiceWorkerLifetimeExtension(NoLifetimeExtension{}); 2359 } 2360 2361 return ServiceWorkerLifetimeExtension(FullLifetimeExtension{}); 2362 } 2363 2364 ServiceWorkerLifetimeExtension 2365 ServiceWorkerManager::DetermineLifetimeForServiceWorker( 2366 const ServiceWorkerDescriptor& aServiceWorker) { 2367 ServiceWorkerInfo* source = GetServiceWorkerByDescriptor(aServiceWorker); 2368 if (source) { 2369 return ServiceWorkerLifetimeExtension(PropagatedLifetimeExtension{ 2370 .mDeadline = source->LifetimeDeadline(), 2371 }); 2372 } 2373 2374 // If we can't find the ServiceWorker, then it's gone and we shouldn't 2375 // extend the lifetime. 2376 return ServiceWorkerLifetimeExtension(NoLifetimeExtension{}); 2377 } 2378 2379 bool ServiceWorkerManager::IsAvailable(nsIPrincipal* aPrincipal, nsIURI* aURI, 2380 nsIChannel* aChannel) { 2381 MOZ_ASSERT(aPrincipal); 2382 MOZ_ASSERT(aURI); 2383 MOZ_ASSERT(aChannel); 2384 2385 RefPtr<ServiceWorkerRegistrationInfo> registration = 2386 GetServiceWorkerRegistrationInfo(aPrincipal, aURI); 2387 2388 if (!registration || !registration->GetActive()) { 2389 return false; 2390 } 2391 2392 // Checking if the matched service worker handles fetch events or not. 2393 // If it does, directly return true and handle the client controlling logic 2394 // in DispatchFetchEvent(). otherwise, do followings then return false. 2395 // 1. Set the matched service worker as the controller of LoadInfo and 2396 // correspoinding ClinetInfo 2397 // 2. Maybe schedule a soft update 2398 if (!registration->GetActive()->HandlesFetch()) { 2399 // Checkin if the channel is not allowed for the service worker. 2400 auto storageAccess = StorageAllowedForChannel(aChannel); 2401 nsCOMPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo(); 2402 2403 if (storageAccess <= StorageAccess::eDeny) { 2404 if (!StaticPrefs::privacy_partition_serviceWorkers()) { 2405 return false; 2406 } 2407 2408 nsCOMPtr<nsICookieJarSettings> cookieJarSettings; 2409 loadInfo->GetCookieJarSettings(getter_AddRefs(cookieJarSettings)); 2410 2411 if (!StoragePartitioningEnabled(storageAccess, cookieJarSettings)) { 2412 return false; 2413 } 2414 } 2415 2416 // ServiceWorkerInterceptController::ShouldPrepareForIntercept() handles the 2417 // subresource cases. Must be non-subresource case here. 2418 MOZ_ASSERT(nsContentUtils::IsNonSubresourceRequest(aChannel)); 2419 2420 Maybe<ClientInfo> clientInfo = loadInfo->GetReservedClientInfo(); 2421 if (clientInfo.isNothing()) { 2422 clientInfo = loadInfo->GetInitialClientInfo(); 2423 } 2424 2425 if (clientInfo.isSome()) { 2426 StartControllingClient(clientInfo.ref(), registration); 2427 } 2428 2429 uint32_t redirectMode = nsIHttpChannelInternal::REDIRECT_MODE_MANUAL; 2430 nsCOMPtr<nsIHttpChannelInternal> http = do_QueryInterface(aChannel); 2431 MOZ_ALWAYS_SUCCEEDS(http->GetRedirectMode(&redirectMode)); 2432 2433 // Synthetic redirects for non-subresource requests with a "follow" 2434 // redirect mode may switch controllers. This is basically worker 2435 // scripts right now. In this case we need to explicitly clear the 2436 // controller to avoid assertions on the SetController() below. 2437 if (redirectMode == nsIHttpChannelInternal::REDIRECT_MODE_FOLLOW) { 2438 loadInfo->ClearController(); 2439 } 2440 2441 loadInfo->SetController(registration->GetActive()->Descriptor()); 2442 2443 // https://w3c.github.io/ServiceWorker/#on-fetch-request-algorithm 17.1 2444 // try schedule a soft-update for non-subresource case. 2445 registration->MaybeScheduleUpdate(); 2446 return false; 2447 } 2448 // Found a matching service worker which handles fetch events, return true. 2449 return true; 2450 } 2451 2452 nsresult ServiceWorkerManager::GetClientRegistration( 2453 const ClientInfo& aClientInfo, 2454 ServiceWorkerRegistrationInfo** aRegistrationInfo) { 2455 ControlledClientData* data = mControlledClients.Get(aClientInfo.Id()); 2456 if (!data || !data->mRegistrationInfo) { 2457 return NS_ERROR_NOT_AVAILABLE; 2458 } 2459 2460 // If the document is controlled, the current worker MUST be non-null. 2461 if (!data->mRegistrationInfo->GetActive()) { 2462 return NS_ERROR_NOT_AVAILABLE; 2463 } 2464 2465 RefPtr<ServiceWorkerRegistrationInfo> ref = data->mRegistrationInfo; 2466 ref.forget(aRegistrationInfo); 2467 return NS_OK; 2468 } 2469 2470 int32_t ServiceWorkerManager::GetPrincipalQuotaUsageCheckCount( 2471 nsIPrincipal* aPrincipal) { 2472 nsAutoCString scopeKey; 2473 nsresult rv = PrincipalToScopeKey(aPrincipal, scopeKey); 2474 if (NS_WARN_IF(NS_FAILED(rv))) { 2475 return -1; 2476 } 2477 2478 RegistrationDataPerPrincipal* data; 2479 if (!mRegistrationInfos.Get(scopeKey, &data)) { 2480 return -1; 2481 } 2482 2483 return data->mQuotaUsageCheckCount; 2484 } 2485 2486 void ServiceWorkerManager::CheckPrincipalQuotaUsage(nsIPrincipal* aPrincipal, 2487 const nsACString& aScope) { 2488 MOZ_ASSERT(NS_IsMainThread()); 2489 MOZ_ASSERT(aPrincipal); 2490 2491 nsAutoCString scopeKey; 2492 nsresult rv = PrincipalToScopeKey(aPrincipal, scopeKey); 2493 if (NS_WARN_IF(NS_FAILED(rv))) { 2494 return; 2495 } 2496 2497 RegistrationDataPerPrincipal* data; 2498 if (!mRegistrationInfos.Get(scopeKey, &data)) { 2499 return; 2500 } 2501 2502 // Had already schedule a quota usage check. 2503 if (data->mQuotaUsageCheckCount != 0) { 2504 return; 2505 } 2506 2507 ++data->mQuotaUsageCheckCount; 2508 2509 // Get the corresponding ServiceWorkerRegistrationInfo here. Unregisteration 2510 // might be triggered later, should get it here before it be removed from 2511 // data.mInfos, such that NotifyListenersOnQuotaCheckFinish() can notify the 2512 // corresponding ServiceWorkerRegistrationInfo after asynchronous quota 2513 // checking finish. 2514 RefPtr<ServiceWorkerRegistrationInfo> info; 2515 data->mInfos.Get(aScope, getter_AddRefs(info)); 2516 MOZ_ASSERT(info); 2517 2518 RefPtr<ServiceWorkerManager> self = this; 2519 2520 ClearQuotaUsageIfNeeded(aPrincipal, [self, info](bool aResult) { 2521 MOZ_ASSERT(NS_IsMainThread()); 2522 self->NotifyListenersOnQuotaUsageCheckFinish(info); 2523 }); 2524 } 2525 2526 void ServiceWorkerManager::SoftUpdate(const OriginAttributes& aOriginAttributes, 2527 const nsACString& aScope) { 2528 MOZ_ASSERT(NS_IsMainThread()); 2529 2530 if (mShuttingDown) { 2531 return; 2532 } 2533 2534 SoftUpdateInternal(aOriginAttributes, aScope, nullptr); 2535 } 2536 2537 namespace { 2538 2539 class UpdateJobCallback final : public ServiceWorkerJob::Callback { 2540 RefPtr<ServiceWorkerUpdateFinishCallback> mCallback; 2541 2542 ~UpdateJobCallback() { MOZ_ASSERT(!mCallback); } 2543 2544 public: 2545 explicit UpdateJobCallback(ServiceWorkerUpdateFinishCallback* aCallback) 2546 : mCallback(aCallback) { 2547 MOZ_ASSERT(NS_IsMainThread()); 2548 MOZ_ASSERT(mCallback); 2549 } 2550 2551 void JobFinished(ServiceWorkerJob* aJob, ErrorResult& aStatus) override { 2552 MOZ_ASSERT(NS_IsMainThread()); 2553 MOZ_ASSERT(aJob); 2554 MOZ_ASSERT(mCallback); 2555 2556 auto scopeExit = MakeScopeExit([&]() { mCallback = nullptr; }); 2557 2558 if (aStatus.Failed()) { 2559 mCallback->UpdateFailed(aStatus); 2560 return; 2561 } 2562 2563 MOZ_DIAGNOSTIC_ASSERT(aJob->GetType() == ServiceWorkerJob::Type::Update); 2564 RefPtr<ServiceWorkerUpdateJob> updateJob = 2565 static_cast<ServiceWorkerUpdateJob*>(aJob); 2566 RefPtr<ServiceWorkerRegistrationInfo> reg = updateJob->GetRegistration(); 2567 mCallback->UpdateSucceeded(reg); 2568 } 2569 2570 void JobDiscarded(ErrorResult& aStatus) override { 2571 MOZ_ASSERT(NS_IsMainThread()); 2572 MOZ_ASSERT(mCallback); 2573 2574 mCallback->UpdateFailed(aStatus); 2575 mCallback = nullptr; 2576 } 2577 2578 NS_INLINE_DECL_REFCOUNTING(UpdateJobCallback, override) 2579 }; 2580 2581 } // anonymous namespace 2582 2583 void ServiceWorkerManager::SoftUpdateInternal( 2584 const OriginAttributes& aOriginAttributes, const nsACString& aScope, 2585 ServiceWorkerUpdateFinishCallback* aCallback) { 2586 MOZ_ASSERT(NS_IsMainThread()); 2587 2588 if (mShuttingDown) { 2589 return; 2590 } 2591 2592 auto result = ScopeToPrincipal(aScope, aOriginAttributes); 2593 if (NS_WARN_IF(result.isErr())) { 2594 return; 2595 } 2596 2597 auto principal = result.unwrap(); 2598 2599 nsAutoCString scopeKey; 2600 nsresult rv = PrincipalToScopeKey(principal, scopeKey); 2601 if (NS_WARN_IF(NS_FAILED(rv))) { 2602 return; 2603 } 2604 2605 RefPtr<ServiceWorkerRegistrationInfo> registration = 2606 GetRegistration(scopeKey, aScope); 2607 if (NS_WARN_IF(!registration)) { 2608 return; 2609 } 2610 2611 // "If registration's installing worker is not null, abort these steps." 2612 if (registration->GetInstalling()) { 2613 return; 2614 } 2615 2616 // "Let newestWorker be the result of running Get Newest Worker algorithm 2617 // passing registration as its argument. 2618 // If newestWorker is null, abort these steps." 2619 RefPtr<ServiceWorkerInfo> newest = registration->Newest(); 2620 if (!newest) { 2621 return; 2622 } 2623 2624 // "If the registration queue for registration is empty, invoke Update 2625 // algorithm, or its equivalent, with client, registration as its argument." 2626 // TODO(catalinb): We don't implement the force bypass cache flag. 2627 // See: https://github.com/slightlyoff/ServiceWorker/issues/759 2628 RefPtr<ServiceWorkerJobQueue> queue = GetOrCreateJobQueue(scopeKey, aScope); 2629 2630 // Soft updates always correspond to functional events. Functional events 2631 // are always events that should result in a fresh, full lifetime extension. 2632 // 2633 // The set of known events at this time is documented at 2634 // https://w3c.github.io/ServiceWorker/#execution-context-events at this time. 2635 // 2636 // Note that Lifecycle events (install, activate) are explicitly not 2637 // functional events. 2638 auto lifetime = ServiceWorkerLifetimeExtension(FullLifetimeExtension{}); 2639 2640 RefPtr<ServiceWorkerUpdateJob> job = new ServiceWorkerUpdateJob( 2641 principal, registration->Scope(), newest->ScriptSpec(), 2642 registration->GetUpdateViaCache(), lifetime); 2643 2644 if (aCallback) { 2645 RefPtr<UpdateJobCallback> cb = new UpdateJobCallback(aCallback); 2646 job->AppendResultCallback(cb); 2647 } 2648 2649 queue->ScheduleJob(job); 2650 } 2651 2652 void ServiceWorkerManager::Update( 2653 const ClientInfo& aClientInfo, nsIPrincipal* aPrincipal, 2654 const nsACString& aScope, nsCString aNewestWorkerScriptUrl, 2655 ServiceWorkerUpdateFinishCallback* aCallback) { 2656 MOZ_ASSERT(NS_IsMainThread()); 2657 MOZ_ASSERT(!aNewestWorkerScriptUrl.IsEmpty()); 2658 2659 UpdateInternal(aClientInfo, aPrincipal, aScope, 2660 std::move(aNewestWorkerScriptUrl), aCallback); 2661 } 2662 2663 void ServiceWorkerManager::UpdateInternal( 2664 const ClientInfo& aClientInfo, nsIPrincipal* aPrincipal, 2665 const nsACString& aScope, nsCString&& aNewestWorkerScriptUrl, 2666 ServiceWorkerUpdateFinishCallback* aCallback) { 2667 MOZ_ASSERT(aPrincipal); 2668 MOZ_ASSERT(aCallback); 2669 MOZ_ASSERT(!aNewestWorkerScriptUrl.IsEmpty()); 2670 2671 nsAutoCString scopeKey; 2672 nsresult rv = PrincipalToScopeKey(aPrincipal, scopeKey); 2673 if (NS_WARN_IF(NS_FAILED(rv))) { 2674 return; 2675 } 2676 2677 RefPtr<ServiceWorkerRegistrationInfo> registration = 2678 GetRegistration(scopeKey, aScope); 2679 if (NS_WARN_IF(!registration)) { 2680 ErrorResult error; 2681 error.ThrowTypeError<MSG_SW_UPDATE_BAD_REGISTRATION>(aScope, "uninstalled"); 2682 aCallback->UpdateFailed(error); 2683 2684 // In case the callback does not consume the exception 2685 error.SuppressException(); 2686 return; 2687 } 2688 2689 RefPtr<ServiceWorkerJobQueue> queue = GetOrCreateJobQueue(scopeKey, aScope); 2690 2691 auto lifetime = DetermineLifetimeForClient(aClientInfo); 2692 2693 // "Let job be the result of running Create Job with update, registration’s 2694 // scope url, newestWorker’s script url, promise, and the context object’s 2695 // relevant settings object." 2696 RefPtr<ServiceWorkerUpdateJob> job = new ServiceWorkerUpdateJob( 2697 aPrincipal, registration->Scope(), std::move(aNewestWorkerScriptUrl), 2698 registration->GetUpdateViaCache(), lifetime); 2699 2700 RefPtr<UpdateJobCallback> cb = new UpdateJobCallback(aCallback); 2701 job->AppendResultCallback(cb); 2702 2703 // "Invoke Schedule Job with job." 2704 queue->ScheduleJob(job); 2705 } 2706 2707 RefPtr<GenericErrorResultPromise> ServiceWorkerManager::MaybeClaimClient( 2708 const ClientInfo& aClientInfo, 2709 ServiceWorkerRegistrationInfo* aWorkerRegistration) { 2710 MOZ_DIAGNOSTIC_ASSERT(aWorkerRegistration); 2711 2712 if (!aWorkerRegistration->GetActive()) { 2713 CopyableErrorResult rv; 2714 rv.ThrowInvalidStateError("Worker is not active"); 2715 return GenericErrorResultPromise::CreateAndReject(rv, __func__); 2716 } 2717 2718 // Same origin check 2719 auto principalOrErr = aClientInfo.GetPrincipal(); 2720 2721 if (NS_WARN_IF(principalOrErr.isErr())) { 2722 CopyableErrorResult rv; 2723 rv.ThrowSecurityError("Could not extract client's principal"); 2724 return GenericErrorResultPromise::CreateAndReject(rv, __func__); 2725 } 2726 2727 nsCOMPtr<nsIPrincipal> principal = principalOrErr.unwrap(); 2728 if (!aWorkerRegistration->Principal()->Equals(principal)) { 2729 CopyableErrorResult rv; 2730 rv.ThrowSecurityError("Worker is for a different origin"); 2731 return GenericErrorResultPromise::CreateAndReject(rv, __func__); 2732 } 2733 2734 // The registration that should be controlling the client 2735 RefPtr<ServiceWorkerRegistrationInfo> matchingRegistration = 2736 GetServiceWorkerRegistrationInfo(aClientInfo); 2737 2738 // The registration currently controlling the client 2739 RefPtr<ServiceWorkerRegistrationInfo> controllingRegistration; 2740 GetClientRegistration(aClientInfo, getter_AddRefs(controllingRegistration)); 2741 2742 if (aWorkerRegistration != matchingRegistration || 2743 aWorkerRegistration == controllingRegistration) { 2744 return GenericErrorResultPromise::CreateAndResolve(true, __func__); 2745 } 2746 2747 return StartControllingClient(aClientInfo, aWorkerRegistration); 2748 } 2749 2750 RefPtr<GenericErrorResultPromise> ServiceWorkerManager::MaybeClaimClient( 2751 const ClientInfo& aClientInfo, 2752 const ServiceWorkerDescriptor& aServiceWorker) { 2753 auto principalOrErr = aServiceWorker.GetPrincipal(); 2754 if (NS_WARN_IF(principalOrErr.isErr())) { 2755 return GenericErrorResultPromise::CreateAndResolve(false, __func__); 2756 } 2757 2758 nsCOMPtr<nsIPrincipal> principal = principalOrErr.unwrap(); 2759 2760 RefPtr<ServiceWorkerRegistrationInfo> registration = 2761 GetRegistration(principal, aServiceWorker.Scope()); 2762 2763 // While ServiceWorkerManager is distributed across child processes its 2764 // possible for us to sometimes get a claim for a new worker that has 2765 // not propagated to this process yet. For now, simply note that we 2766 // are done. The fix for this is to move the SWM to the parent process 2767 // so there are no consistency errors. 2768 if (NS_WARN_IF(!registration) || NS_WARN_IF(!registration->GetActive())) { 2769 return GenericErrorResultPromise::CreateAndResolve(false, __func__); 2770 } 2771 2772 return MaybeClaimClient(aClientInfo, registration); 2773 } 2774 2775 void ServiceWorkerManager::UpdateClientControllers( 2776 ServiceWorkerRegistrationInfo* aRegistration) { 2777 MOZ_ASSERT(NS_IsMainThread()); 2778 2779 RefPtr<ServiceWorkerInfo> activeWorker = aRegistration->GetActive(); 2780 MOZ_DIAGNOSTIC_ASSERT(activeWorker); 2781 2782 AutoTArray<RefPtr<ClientHandle>, 16> handleList; 2783 for (const auto& client : mControlledClients.Values()) { 2784 if (client->mRegistrationInfo != aRegistration) { 2785 continue; 2786 } 2787 2788 handleList.AppendElement(client->mClientHandle); 2789 } 2790 2791 // Fire event after iterating mControlledClients is done to prevent 2792 // modification by reentering from the event handlers during iteration. 2793 for (auto& handle : handleList) { 2794 RefPtr<GenericErrorResultPromise> p = 2795 handle->Control(activeWorker->Descriptor()); 2796 2797 RefPtr<ServiceWorkerManager> self = this; 2798 2799 // If we fail to control the client, then automatically remove it 2800 // from our list of controlled clients. 2801 p->Then( 2802 GetMainThreadSerialEventTarget(), __func__, 2803 [](bool) { 2804 // do nothing on success 2805 }, 2806 [self, clientInfo = handle->Info()](const CopyableErrorResult& aRv) { 2807 // failed to control, forget about this client 2808 self->StopControllingClient(clientInfo); 2809 }); 2810 } 2811 } 2812 2813 void ServiceWorkerManager::EvictFromBFCache( 2814 ServiceWorkerRegistrationInfo* aRegistration) { 2815 MOZ_ASSERT(NS_IsMainThread()); 2816 for (const auto& client : mControlledClients.Values()) { 2817 if (client->mRegistrationInfo == aRegistration) { 2818 client->mClientHandle->EvictFromBFCache(); 2819 } 2820 } 2821 } 2822 2823 already_AddRefed<ServiceWorkerRegistrationInfo> 2824 ServiceWorkerManager::GetRegistration(nsIPrincipal* aPrincipal, 2825 const nsACString& aScope) const { 2826 MOZ_ASSERT(aPrincipal); 2827 2828 nsAutoCString scopeKey; 2829 nsresult rv = PrincipalToScopeKey(aPrincipal, scopeKey); 2830 if (NS_WARN_IF(NS_FAILED(rv))) { 2831 return nullptr; 2832 } 2833 2834 return GetRegistration(scopeKey, aScope); 2835 } 2836 2837 already_AddRefed<ServiceWorkerRegistrationInfo> 2838 ServiceWorkerManager::GetRegistration(const PrincipalInfo& aPrincipalInfo, 2839 const nsACString& aScope) const { 2840 nsAutoCString scopeKey; 2841 nsresult rv = PrincipalInfoToScopeKey(aPrincipalInfo, scopeKey); 2842 if (NS_WARN_IF(NS_FAILED(rv))) { 2843 return nullptr; 2844 } 2845 2846 return GetRegistration(scopeKey, aScope); 2847 } 2848 2849 NS_IMETHODIMP 2850 ServiceWorkerManager::ReloadRegistrationsForTest() { 2851 if (NS_WARN_IF(!StaticPrefs::dom_serviceWorkers_testing_enabled())) { 2852 return NS_ERROR_FAILURE; 2853 } 2854 2855 // Let's keep it simple and fail if there are any controlled client, 2856 // the test case can take care of making sure there is none when this 2857 // method will be called. 2858 if (NS_WARN_IF(!mControlledClients.IsEmpty())) { 2859 return NS_ERROR_FAILURE; 2860 } 2861 2862 for (const auto& info : mRegistrationInfos.Values()) { 2863 for (ServiceWorkerRegistrationInfo* reg : info->mInfos.Values()) { 2864 MOZ_ASSERT(reg); 2865 reg->ForceShutdown(); 2866 } 2867 } 2868 2869 mRegistrationInfos.Clear(); 2870 2871 nsTArray<ServiceWorkerRegistrationData> data; 2872 RefPtr<ServiceWorkerRegistrar> swr = ServiceWorkerRegistrar::Get(); 2873 if (NS_WARN_IF(!swr->ReloadDataForTest())) { 2874 return NS_ERROR_FAILURE; 2875 } 2876 swr->GetRegistrations(data); 2877 LoadRegistrations(data); 2878 2879 return NS_OK; 2880 } 2881 2882 NS_IMETHODIMP 2883 ServiceWorkerManager::RegisterForAddonPrincipal(nsIPrincipal* aPrincipal, 2884 JSContext* aCx, 2885 dom::Promise** aPromise) { 2886 nsIGlobalObject* global = xpc::CurrentNativeGlobal(aCx); 2887 if (NS_WARN_IF(!global)) { 2888 return NS_ERROR_FAILURE; 2889 } 2890 2891 ErrorResult erv; 2892 RefPtr<Promise> outer = Promise::Create(global, erv); 2893 if (NS_WARN_IF(erv.Failed())) { 2894 return erv.StealNSResult(); 2895 } 2896 2897 auto enabled = 2898 StaticPrefs::extensions_backgroundServiceWorker_enabled_AtStartup(); 2899 if (!enabled) { 2900 outer->MaybeRejectWithNotAllowedError( 2901 "Disabled. extensions.backgroundServiceWorker.enabled is false"); 2902 outer.forget(aPromise); 2903 return NS_OK; 2904 } 2905 2906 MOZ_ASSERT(aPrincipal); 2907 auto* addonPolicy = BasePrincipal::Cast(aPrincipal)->AddonPolicy(); 2908 if (!addonPolicy) { 2909 outer->MaybeRejectWithNotAllowedError("Not an extension principal"); 2910 outer.forget(aPromise); 2911 return NS_OK; 2912 } 2913 2914 nsCString scope; 2915 auto result = addonPolicy->GetURL(u""_ns); 2916 if (result.isOk()) { 2917 scope.Assign(NS_ConvertUTF16toUTF8(result.unwrap())); 2918 } else { 2919 outer->MaybeRejectWithUnknownError("Unable to resolve addon scope URL"); 2920 outer.forget(aPromise); 2921 return NS_OK; 2922 } 2923 2924 nsString scriptURL; 2925 addonPolicy->GetBackgroundWorker(scriptURL); 2926 2927 if (scriptURL.IsEmpty()) { 2928 outer->MaybeRejectWithNotFoundError("Missing background worker script url"); 2929 outer.forget(aPromise); 2930 return NS_OK; 2931 } 2932 2933 Maybe<ClientInfo> clientInfo = 2934 dom::ClientManager::CreateInfo(ClientType::All, aPrincipal); 2935 2936 if (!clientInfo.isSome()) { 2937 outer->MaybeRejectWithUnknownError("Error creating clientInfo"); 2938 outer.forget(aPromise); 2939 return NS_OK; 2940 } 2941 2942 auto regPromise = Register(clientInfo.ref(), scope, WorkerType::Classic, 2943 NS_ConvertUTF16toUTF8(scriptURL), 2944 dom::ServiceWorkerUpdateViaCache::Imports); 2945 2946 const RefPtr<ServiceWorkerManager> self(this); 2947 const nsCOMPtr<nsIPrincipal> principal(aPrincipal); 2948 regPromise->Then( 2949 GetMainThreadSerialEventTarget(), __func__, 2950 [self, outer, principal, 2951 scope](const ServiceWorkerRegistrationDescriptor& regDesc) { 2952 RefPtr<ServiceWorkerRegistrationInfo> registration = 2953 self->GetRegistration(principal, scope); 2954 if (registration) { 2955 outer->MaybeResolve(registration); 2956 } else { 2957 outer->MaybeRejectWithUnknownError( 2958 "Failed to retrieve ServiceWorkerRegistrationInfo"); 2959 } 2960 }, 2961 [outer](const mozilla::CopyableErrorResult& err) { 2962 CopyableErrorResult result(err); 2963 outer->MaybeReject(std::move(result)); 2964 }); 2965 2966 outer.forget(aPromise); 2967 2968 return NS_OK; 2969 } 2970 2971 NS_IMETHODIMP 2972 ServiceWorkerManager::GetRegistrationForAddonPrincipal( 2973 nsIPrincipal* aPrincipal, nsIServiceWorkerRegistrationInfo** aInfo) { 2974 MOZ_ASSERT(aPrincipal); 2975 2976 MOZ_ASSERT(aPrincipal); 2977 auto* addonPolicy = BasePrincipal::Cast(aPrincipal)->AddonPolicy(); 2978 if (!addonPolicy) { 2979 return NS_ERROR_FAILURE; 2980 } 2981 2982 nsCString scope; 2983 auto result = addonPolicy->GetURL(u""_ns); 2984 if (result.isOk()) { 2985 scope.Assign(NS_ConvertUTF16toUTF8(result.unwrap())); 2986 } else { 2987 return NS_ERROR_FAILURE; 2988 } 2989 2990 nsCOMPtr<nsIURI> scopeURI; 2991 nsresult rv = NS_NewURI(getter_AddRefs(scopeURI), scope); 2992 if (NS_FAILED(rv)) { 2993 return NS_ERROR_FAILURE; 2994 } 2995 2996 RefPtr<ServiceWorkerRegistrationInfo> info = 2997 GetServiceWorkerRegistrationInfo(aPrincipal, scopeURI); 2998 if (!info) { 2999 aInfo = nullptr; 3000 return NS_OK; 3001 } 3002 info.forget(aInfo); 3003 return NS_OK; 3004 } 3005 3006 NS_IMETHODIMP 3007 ServiceWorkerManager::WakeForExtensionAPIEvent( 3008 const nsAString& aExtensionBaseURL, const nsAString& aAPINamespace, 3009 const nsAString& aAPIEventName, JSContext* aCx, dom::Promise** aPromise) { 3010 nsIGlobalObject* global = xpc::CurrentNativeGlobal(aCx); 3011 if (NS_WARN_IF(!global)) { 3012 return NS_ERROR_FAILURE; 3013 } 3014 3015 ErrorResult erv; 3016 RefPtr<Promise> outer = Promise::Create(global, erv); 3017 if (NS_WARN_IF(erv.Failed())) { 3018 return erv.StealNSResult(); 3019 } 3020 3021 auto enabled = 3022 StaticPrefs::extensions_backgroundServiceWorker_enabled_AtStartup(); 3023 if (!enabled) { 3024 outer->MaybeRejectWithNotAllowedError( 3025 "Disabled. extensions.backgroundServiceWorker.enabled is false"); 3026 outer.forget(aPromise); 3027 return NS_OK; 3028 } 3029 3030 nsCOMPtr<nsIURI> scopeURI; 3031 nsresult rv = NS_NewURI(getter_AddRefs(scopeURI), aExtensionBaseURL); 3032 if (NS_FAILED(rv)) { 3033 outer->MaybeReject(rv); 3034 outer.forget(aPromise); 3035 return NS_OK; 3036 } 3037 3038 nsCOMPtr<nsIPrincipal> principal = MOZ_TRY(ScopeToPrincipal(scopeURI, {})); 3039 3040 auto* addonPolicy = BasePrincipal::Cast(principal)->AddonPolicy(); 3041 if (NS_WARN_IF(!addonPolicy)) { 3042 outer->MaybeRejectWithNotAllowedError( 3043 "Not an extension principal or extension disabled"); 3044 outer.forget(aPromise); 3045 return NS_OK; 3046 } 3047 3048 OriginAttributes attrs; 3049 ServiceWorkerInfo* info = GetActiveWorkerInfoForScope( 3050 attrs, NS_ConvertUTF16toUTF8(aExtensionBaseURL)); 3051 if (NS_WARN_IF(!info)) { 3052 outer->MaybeRejectWithInvalidStateError( 3053 "No active worker for the extension background service worker"); 3054 outer.forget(aPromise); 3055 return NS_OK; 3056 } 3057 3058 ServiceWorkerPrivate* workerPrivate = info->WorkerPrivate(); 3059 auto result = 3060 workerPrivate->WakeForExtensionAPIEvent(aAPINamespace, aAPIEventName); 3061 if (result.isErr()) { 3062 outer->MaybeReject(result.propagateErr()); 3063 outer.forget(aPromise); 3064 return NS_OK; 3065 } 3066 3067 RefPtr<ServiceWorkerPrivate::PromiseExtensionWorkerHasListener> innerPromise = 3068 result.unwrap(); 3069 3070 innerPromise->Then( 3071 GetMainThreadSerialEventTarget(), __func__, 3072 [outer](bool aSubscribedEvent) { outer->MaybeResolve(aSubscribedEvent); }, 3073 [outer](nsresult aErrorResult) { outer->MaybeReject(aErrorResult); }); 3074 3075 outer.forget(aPromise); 3076 return NS_OK; 3077 } 3078 3079 NS_IMETHODIMP 3080 ServiceWorkerManager::GetRegistrationByPrincipal( 3081 nsIPrincipal* aPrincipal, const nsAString& aScope, 3082 nsIServiceWorkerRegistrationInfo** aInfo) { 3083 MOZ_ASSERT(aPrincipal); 3084 MOZ_ASSERT(aInfo); 3085 3086 nsCOMPtr<nsIURI> scopeURI; 3087 nsresult rv = NS_NewURI(getter_AddRefs(scopeURI), aScope); 3088 if (NS_FAILED(rv)) { 3089 return NS_ERROR_FAILURE; 3090 } 3091 3092 RefPtr<ServiceWorkerRegistrationInfo> info = 3093 GetServiceWorkerRegistrationInfo(aPrincipal, scopeURI); 3094 if (!info) { 3095 return NS_ERROR_FAILURE; 3096 } 3097 info.forget(aInfo); 3098 3099 return NS_OK; 3100 } 3101 3102 already_AddRefed<ServiceWorkerRegistrationInfo> 3103 ServiceWorkerManager::GetRegistration(const nsACString& aScopeKey, 3104 const nsACString& aScope) const { 3105 RefPtr<ServiceWorkerRegistrationInfo> reg; 3106 3107 RegistrationDataPerPrincipal* data; 3108 if (!mRegistrationInfos.Get(aScopeKey, &data)) { 3109 return reg.forget(); 3110 } 3111 3112 data->mInfos.Get(aScope, getter_AddRefs(reg)); 3113 return reg.forget(); 3114 } 3115 3116 already_AddRefed<ServiceWorkerRegistrationInfo> 3117 ServiceWorkerManager::CreateNewRegistration( 3118 const nsCString& aScope, const WorkerType& aType, nsIPrincipal* aPrincipal, 3119 ServiceWorkerUpdateViaCache aUpdateViaCache, 3120 IPCNavigationPreloadState aNavigationPreloadState) { 3121 #ifdef DEBUG 3122 MOZ_ASSERT(NS_IsMainThread()); 3123 nsCOMPtr<nsIURI> scopeURI; 3124 nsresult rv = NS_NewURI(getter_AddRefs(scopeURI), aScope); 3125 MOZ_ASSERT(NS_SUCCEEDED(rv)); 3126 3127 RefPtr<ServiceWorkerRegistrationInfo> tmp = 3128 GetRegistration(aPrincipal, aScope); 3129 MOZ_ASSERT(!tmp); 3130 #endif 3131 3132 RefPtr<ServiceWorkerRegistrationInfo> registration = 3133 new ServiceWorkerRegistrationInfo(aScope, aType, aPrincipal, 3134 aUpdateViaCache, 3135 std::move(aNavigationPreloadState)); 3136 3137 // From now on ownership of registration is with 3138 // mServiceWorkerRegistrationInfos. 3139 AddScopeAndRegistration(aScope, registration); 3140 return registration.forget(); 3141 } 3142 3143 void ServiceWorkerManager::MaybeRemoveRegistration( 3144 ServiceWorkerRegistrationInfo* aRegistration) { 3145 MOZ_ASSERT(aRegistration); 3146 RefPtr<ServiceWorkerInfo> newest = aRegistration->Newest(); 3147 if (!newest && HasScope(aRegistration->Principal(), aRegistration->Scope())) { 3148 RemoveRegistration(aRegistration); 3149 } 3150 } 3151 3152 void ServiceWorkerManager::RemoveRegistration( 3153 ServiceWorkerRegistrationInfo* aRegistration) { 3154 // Note, we do not need to call mActor->SendUnregister() here. There are a 3155 // few ways we can get here: 1) Through a normal unregister which calls 3156 // SendUnregister() in the 3157 // unregister job Start() method. 3158 // 2) Through origin storage being purged. These result in ForceUnregister() 3159 // starting unregister jobs which in turn call SendUnregister(). 3160 // 3) Through the failure to install a new service worker. Since we don't 3161 // store the registration until install succeeds, we do not need to call 3162 // SendUnregister here. 3163 MOZ_ASSERT(HasScope(aRegistration->Principal(), aRegistration->Scope())); 3164 3165 RemoveScopeAndRegistration(aRegistration); 3166 } 3167 3168 NS_IMETHODIMP 3169 ServiceWorkerManager::GetAllRegistrations(nsIArray** aResult) { 3170 MOZ_ASSERT(NS_IsMainThread()); 3171 3172 nsCOMPtr<nsIMutableArray> array(do_CreateInstance(NS_ARRAY_CONTRACTID)); 3173 if (!array) { 3174 return NS_ERROR_OUT_OF_MEMORY; 3175 } 3176 3177 for (const auto& info : mRegistrationInfos.Values()) { 3178 for (ServiceWorkerRegistrationInfo* reg : info->mInfos.Values()) { 3179 MOZ_ASSERT(reg); 3180 3181 array->AppendElement(reg); 3182 } 3183 } 3184 3185 array.forget(aResult); 3186 return NS_OK; 3187 } 3188 3189 NS_IMETHODIMP 3190 ServiceWorkerManager::RemoveRegistrationsByOriginAttributes( 3191 const nsAString& aPattern) { 3192 MOZ_ASSERT(XRE_IsParentProcess()); 3193 MOZ_ASSERT(NS_IsMainThread()); 3194 3195 MOZ_ASSERT(!aPattern.IsEmpty()); 3196 3197 OriginAttributesPattern pattern; 3198 MOZ_ALWAYS_TRUE(pattern.Init(aPattern)); 3199 3200 for (const auto& data : mRegistrationInfos.Values()) { 3201 // We can use iteration because ForceUnregister (and Unregister) are 3202 // async. Otherwise doing some R/W operations on an hashtable during 3203 // iteration will crash. 3204 for (ServiceWorkerRegistrationInfo* reg : data->mInfos.Values()) { 3205 MOZ_ASSERT(reg); 3206 MOZ_ASSERT(reg->Principal()); 3207 3208 bool matches = pattern.Matches(reg->Principal()->OriginAttributesRef()); 3209 if (!matches) { 3210 continue; 3211 } 3212 3213 ForceUnregister(data.get(), reg); 3214 } 3215 } 3216 3217 return NS_OK; 3218 } 3219 3220 void ServiceWorkerManager::ForceUnregister( 3221 RegistrationDataPerPrincipal* aRegistrationData, 3222 ServiceWorkerRegistrationInfo* aRegistration) { 3223 MOZ_ASSERT(aRegistrationData); 3224 MOZ_ASSERT(aRegistration); 3225 3226 RefPtr<ServiceWorkerJobQueue> queue; 3227 aRegistrationData->mJobQueues.Get(aRegistration->Scope(), 3228 getter_AddRefs(queue)); 3229 if (queue) { 3230 queue->CancelAll(); 3231 } 3232 3233 if (auto entry = 3234 aRegistrationData->mUpdateTimers.Lookup(aRegistration->Scope())) { 3235 entry.Data()->Cancel(); 3236 entry.Remove(); 3237 } 3238 3239 // Since Unregister is async, it is ok to call it in an enumeration. 3240 Unregister(aRegistration->Principal(), nullptr, 3241 NS_ConvertUTF8toUTF16(aRegistration->Scope())); 3242 } 3243 3244 NS_IMETHODIMP 3245 ServiceWorkerManager::AddListener(nsIServiceWorkerManagerListener* aListener) { 3246 MOZ_ASSERT(NS_IsMainThread()); 3247 3248 if (!aListener || mListeners.Contains(aListener)) { 3249 return NS_ERROR_INVALID_ARG; 3250 } 3251 3252 mListeners.AppendElement(aListener); 3253 3254 return NS_OK; 3255 } 3256 3257 NS_IMETHODIMP 3258 ServiceWorkerManager::RemoveListener( 3259 nsIServiceWorkerManagerListener* aListener) { 3260 MOZ_ASSERT(NS_IsMainThread()); 3261 3262 if (!aListener || !mListeners.Contains(aListener)) { 3263 return NS_ERROR_INVALID_ARG; 3264 } 3265 3266 mListeners.RemoveElement(aListener); 3267 3268 return NS_OK; 3269 } 3270 3271 NS_IMETHODIMP 3272 ServiceWorkerManager::Observe(nsISupports* aSubject, const char* aTopic, 3273 const char16_t* aData) { 3274 if (strcmp(aTopic, kFinishShutdownTopic) == 0) { 3275 MaybeFinishShutdown(); 3276 return NS_OK; 3277 } 3278 3279 if (strcmp(aTopic, kPrivateBrowsingExited) == 0) { 3280 RemoveRegistrationsByOriginAttributes(kPrivateBrowsingOriginPattern); 3281 return NS_OK; 3282 } 3283 3284 MOZ_CRASH("Received message we aren't supposed to be registered for!"); 3285 return NS_OK; 3286 } 3287 3288 NS_IMETHODIMP 3289 ServiceWorkerManager::PropagateUnregister( 3290 nsIPrincipal* aPrincipal, nsIServiceWorkerUnregisterCallback* aCallback, 3291 const nsAString& aScope) { 3292 MOZ_ASSERT(NS_IsMainThread()); 3293 MOZ_ASSERT(aPrincipal); 3294 3295 // Return earlier with an explicit failure if this xpcom method is called 3296 // when the ServiceWorkerManager is not initialized yet or it is already 3297 // shutting down. 3298 if (NS_WARN_IF(!mActor)) { 3299 return NS_ERROR_FAILURE; 3300 } 3301 3302 PrincipalInfo principalInfo; 3303 if (NS_WARN_IF( 3304 NS_FAILED(PrincipalToPrincipalInfo(aPrincipal, &principalInfo)))) { 3305 return NS_ERROR_FAILURE; 3306 } 3307 3308 mActor->SendPropagateUnregister(principalInfo, aScope); 3309 3310 nsresult rv = Unregister(aPrincipal, aCallback, aScope); 3311 if (NS_WARN_IF(NS_FAILED(rv))) { 3312 return rv; 3313 } 3314 3315 return NS_OK; 3316 } 3317 3318 void ServiceWorkerManager::NotifyListenersOnRegister( 3319 nsIServiceWorkerRegistrationInfo* aInfo) { 3320 nsTArray<nsCOMPtr<nsIServiceWorkerManagerListener>> listeners( 3321 mListeners.Clone()); 3322 for (size_t index = 0; index < listeners.Length(); ++index) { 3323 listeners[index]->OnRegister(aInfo); 3324 } 3325 } 3326 3327 void ServiceWorkerManager::NotifyListenersOnUnregister( 3328 nsIServiceWorkerRegistrationInfo* aInfo) { 3329 nsTArray<nsCOMPtr<nsIServiceWorkerManagerListener>> listeners( 3330 mListeners.Clone()); 3331 for (size_t index = 0; index < listeners.Length(); ++index) { 3332 listeners[index]->OnUnregister(aInfo); 3333 } 3334 } 3335 3336 void ServiceWorkerManager::NotifyListenersOnQuotaUsageCheckFinish( 3337 nsIServiceWorkerRegistrationInfo* aRegistration) { 3338 nsTArray<nsCOMPtr<nsIServiceWorkerManagerListener>> listeners( 3339 mListeners.Clone()); 3340 for (size_t index = 0; index < listeners.Length(); ++index) { 3341 listeners[index]->OnQuotaUsageCheckFinish(aRegistration); 3342 } 3343 } 3344 3345 class UpdateTimerCallback final : public nsITimerCallback, public nsINamed { 3346 nsCOMPtr<nsIPrincipal> mPrincipal; 3347 const nsCString mScope; 3348 3349 ~UpdateTimerCallback() = default; 3350 3351 public: 3352 UpdateTimerCallback(nsIPrincipal* aPrincipal, const nsACString& aScope) 3353 : mPrincipal(aPrincipal), mScope(aScope) { 3354 MOZ_ASSERT(NS_IsMainThread()); 3355 MOZ_ASSERT(mPrincipal); 3356 MOZ_ASSERT(!mScope.IsEmpty()); 3357 } 3358 3359 NS_IMETHOD 3360 Notify(nsITimer* aTimer) override { 3361 MOZ_ASSERT(NS_IsMainThread()); 3362 3363 RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance(); 3364 if (!swm) { 3365 // shutting down, do nothing 3366 return NS_OK; 3367 } 3368 3369 swm->UpdateTimerFired(mPrincipal, mScope); 3370 return NS_OK; 3371 } 3372 3373 NS_IMETHOD 3374 GetName(nsACString& aName) override { 3375 aName.AssignLiteral("UpdateTimerCallback"); 3376 return NS_OK; 3377 } 3378 3379 NS_DECL_ISUPPORTS 3380 }; 3381 3382 NS_IMPL_ISUPPORTS(UpdateTimerCallback, nsITimerCallback, nsINamed) 3383 3384 void ServiceWorkerManager::ScheduleUpdateTimer(nsIPrincipal* aPrincipal, 3385 const nsACString& aScope) { 3386 MOZ_ASSERT(NS_IsMainThread()); 3387 MOZ_ASSERT(aPrincipal); 3388 MOZ_ASSERT(!aScope.IsEmpty()); 3389 3390 if (mShuttingDown) { 3391 return; 3392 } 3393 3394 nsAutoCString scopeKey; 3395 nsresult rv = PrincipalToScopeKey(aPrincipal, scopeKey); 3396 if (NS_WARN_IF(NS_FAILED(rv))) { 3397 return; 3398 } 3399 3400 RegistrationDataPerPrincipal* data; 3401 if (!mRegistrationInfos.Get(scopeKey, &data)) { 3402 return; 3403 } 3404 3405 data->mUpdateTimers.WithEntryHandle( 3406 aScope, [&aPrincipal, &aScope](auto&& entry) { 3407 if (entry) { 3408 // In case there is already a timer scheduled, just use the original 3409 // schedule time. We don't want to push it out to a later time since 3410 // that could allow updates to be starved forever if events are 3411 // continuously fired. 3412 return; 3413 } 3414 3415 nsCOMPtr<nsITimerCallback> callback = 3416 new UpdateTimerCallback(aPrincipal, aScope); 3417 3418 const uint32_t UPDATE_DELAY_MS = 1000; 3419 3420 nsCOMPtr<nsITimer> timer; 3421 3422 const nsresult rv = 3423 NS_NewTimerWithCallback(getter_AddRefs(timer), callback, 3424 UPDATE_DELAY_MS, nsITimer::TYPE_ONE_SHOT); 3425 3426 if (NS_WARN_IF(NS_FAILED(rv))) { 3427 return; 3428 } 3429 3430 entry.Insert(std::move(timer)); 3431 }); 3432 } 3433 3434 void ServiceWorkerManager::UpdateTimerFired(nsIPrincipal* aPrincipal, 3435 const nsACString& aScope) { 3436 MOZ_ASSERT(NS_IsMainThread()); 3437 MOZ_ASSERT(aPrincipal); 3438 MOZ_ASSERT(!aScope.IsEmpty()); 3439 3440 if (mShuttingDown) { 3441 return; 3442 } 3443 3444 // First cleanup the timer. 3445 nsAutoCString scopeKey; 3446 nsresult rv = PrincipalToScopeKey(aPrincipal, scopeKey); 3447 if (NS_WARN_IF(NS_FAILED(rv))) { 3448 return; 3449 } 3450 3451 RegistrationDataPerPrincipal* data; 3452 if (!mRegistrationInfos.Get(scopeKey, &data)) { 3453 return; 3454 } 3455 3456 if (auto entry = data->mUpdateTimers.Lookup(aScope)) { 3457 entry.Data()->Cancel(); 3458 entry.Remove(); 3459 } 3460 3461 RefPtr<ServiceWorkerRegistrationInfo> registration; 3462 data->mInfos.Get(aScope, getter_AddRefs(registration)); 3463 if (!registration) { 3464 return; 3465 } 3466 3467 if (!registration->CheckAndClearIfUpdateNeeded()) { 3468 return; 3469 } 3470 3471 OriginAttributes attrs = aPrincipal->OriginAttributesRef(); 3472 3473 SoftUpdate(attrs, aScope); 3474 } 3475 3476 void ServiceWorkerManager::MaybeSendUnregister(nsIPrincipal* aPrincipal, 3477 const nsACString& aScope) { 3478 MOZ_ASSERT(NS_IsMainThread()); 3479 MOZ_ASSERT(aPrincipal); 3480 MOZ_ASSERT(!aScope.IsEmpty()); 3481 3482 if (!mActor) { 3483 return; 3484 } 3485 3486 PrincipalInfo principalInfo; 3487 nsresult rv = PrincipalToPrincipalInfo(aPrincipal, &principalInfo); 3488 if (NS_WARN_IF(NS_FAILED(rv))) { 3489 return; 3490 } 3491 3492 (void)mActor->SendUnregister(principalInfo, NS_ConvertUTF8toUTF16(aScope)); 3493 } 3494 3495 void ServiceWorkerManager::AddOrphanedRegistration( 3496 ServiceWorkerRegistrationInfo* aRegistration) { 3497 MOZ_ASSERT(NS_IsMainThread()); 3498 MOZ_ASSERT(aRegistration); 3499 MOZ_ASSERT(aRegistration->IsUnregistered()); 3500 MOZ_ASSERT(!aRegistration->IsControllingClients()); 3501 MOZ_ASSERT(!aRegistration->IsIdle()); 3502 MOZ_ASSERT(!mOrphanedRegistrations.has(aRegistration)); 3503 3504 MOZ_ALWAYS_TRUE(mOrphanedRegistrations.putNew(aRegistration)); 3505 } 3506 3507 void ServiceWorkerManager::RemoveOrphanedRegistration( 3508 ServiceWorkerRegistrationInfo* aRegistration) { 3509 MOZ_ASSERT(NS_IsMainThread()); 3510 MOZ_ASSERT(aRegistration); 3511 MOZ_ASSERT(aRegistration->IsUnregistered()); 3512 MOZ_ASSERT(!aRegistration->IsControllingClients()); 3513 MOZ_ASSERT(aRegistration->IsIdle()); 3514 MOZ_ASSERT(mOrphanedRegistrations.has(aRegistration)); 3515 3516 mOrphanedRegistrations.remove(aRegistration); 3517 } 3518 3519 uint32_t ServiceWorkerManager::MaybeInitServiceWorkerShutdownProgress() const { 3520 if (!mShutdownBlocker) { 3521 return ServiceWorkerShutdownBlocker::kInvalidShutdownStateId; 3522 } 3523 3524 return mShutdownBlocker->CreateShutdownState(); 3525 } 3526 3527 void ServiceWorkerManager::ReportServiceWorkerShutdownProgress( 3528 uint32_t aShutdownStateId, 3529 ServiceWorkerShutdownState::Progress aProgress) const { 3530 MOZ_ASSERT(mShutdownBlocker); 3531 mShutdownBlocker->ReportShutdownProgress(aShutdownStateId, aProgress); 3532 } 3533 3534 nsRefPtrHashtable<nsCStringHashKey, ServiceWorkerRegistrationInfo> 3535 ServiceWorkerManager::GetRegistrations(nsIPrincipal* aPrincipal) { 3536 MOZ_ASSERT(aPrincipal); 3537 3538 nsAutoCString scopeKey; 3539 nsresult rv = PrincipalToScopeKey(aPrincipal, scopeKey); 3540 if (NS_WARN_IF(NS_FAILED(rv))) { 3541 return nsRefPtrHashtable<nsCStringHashKey, ServiceWorkerRegistrationInfo>(); 3542 } 3543 3544 RegistrationDataPerPrincipal* data; 3545 if (!mRegistrationInfos.Get(scopeKey, &data)) { 3546 return nsRefPtrHashtable<nsCStringHashKey, ServiceWorkerRegistrationInfo>(); 3547 } 3548 3549 return data->mInfos.Clone(); 3550 } 3551 3552 // ETPPermissionObserver implementation 3553 ETPPermissionObserver::ETPPermissionObserver() { RegisterObserverEvents(); } 3554 ETPPermissionObserver::~ETPPermissionObserver() { UnregisterObserverEvents(); } 3555 3556 NS_IMETHODIMP ETPPermissionObserver::Observe(nsISupports* aSubject, 3557 const char* aTopic, 3558 const char16_t* aSomeData) { 3559 nsCOMPtr<nsIPermission> perm = nullptr; 3560 perm = do_QueryInterface(aSubject); 3561 if (!perm) { 3562 return NS_OK; 3563 } 3564 3565 nsCOMPtr<nsIPrincipal> principal = nullptr; 3566 perm->GetPrincipal(getter_AddRefs(principal)); 3567 if (!principal) { 3568 return NS_OK; 3569 } 3570 3571 bool isContentPrincipal = false; 3572 principal->GetIsContentPrincipal(&isContentPrincipal); 3573 if (!isContentPrincipal) { 3574 return NS_OK; 3575 } 3576 3577 nsAutoCString type; 3578 perm->GetType(type); 3579 if (!type.EqualsLiteral("trackingprotection") && 3580 !type.EqualsLiteral("trackingprotection-pb")) { 3581 return NS_OK; 3582 } 3583 3584 RefPtr<dom::ServiceWorkerManager> swm = 3585 dom::ServiceWorkerManager::GetInstance(); 3586 auto registrations = swm->GetRegistrations(principal); 3587 3588 for (const auto& registrationInfo : registrations.Values()) { 3589 RefPtr<dom::ServiceWorkerInfo> swInfo = 3590 registrationInfo->NewestIncludingEvaluating(); 3591 if (!swInfo) { 3592 continue; 3593 } 3594 3595 bool isOnContentBlockingAllowList = ContentBlockingAllowList::Check( 3596 principal, principal->GetIsInPrivateBrowsing()); 3597 3598 swInfo->WorkerPrivate()->UpdateIsOnContentBlockingAllowList( 3599 isOnContentBlockingAllowList); 3600 } 3601 3602 return NS_OK; 3603 } 3604 3605 void ETPPermissionObserver::RegisterObserverEvents() { 3606 nsCOMPtr<nsIObserverService> observerService = 3607 mozilla::services::GetObserverService(); 3608 3609 MOZ_ASSERT(observerService); 3610 3611 if (observerService) { 3612 observerService->AddObserver(this, "perm-changed", false); 3613 } 3614 } 3615 3616 void ETPPermissionObserver::UnregisterObserverEvents() { 3617 nsCOMPtr<nsIObserverService> observerService = 3618 mozilla::services::GetObserverService(); 3619 3620 if (observerService) { 3621 observerService->RemoveObserver(this, "perm-changed"); 3622 } 3623 } 3624 3625 NS_IMPL_ISUPPORTS(ETPPermissionObserver, nsIObserver, nsISupports) 3626 3627 } // namespace mozilla::dom