ServiceWorkerRegistrationInfo.cpp (29244B)
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 "ServiceWorkerRegistrationInfo.h" 8 9 #include "ServiceWorkerManager.h" 10 #include "ServiceWorkerPrivate.h" 11 #include "ServiceWorkerRegistrationListener.h" 12 #include "mozilla/Preferences.h" 13 #include "mozilla/SchedulerGroup.h" 14 #include "mozilla/StaticPrefs_dom.h" 15 16 namespace mozilla::dom { 17 18 namespace { 19 20 class ContinueActivateRunnable final : public LifeCycleEventCallback { 21 nsMainThreadPtrHandle<ServiceWorkerRegistrationInfo> mRegistration; 22 bool mSuccess; 23 24 public: 25 explicit ContinueActivateRunnable( 26 const nsMainThreadPtrHandle<ServiceWorkerRegistrationInfo>& aRegistration) 27 : mRegistration(aRegistration), mSuccess(false) { 28 MOZ_ASSERT(NS_IsMainThread()); 29 } 30 31 void SetResult(bool aResult) override { mSuccess = aResult; } 32 33 NS_IMETHOD 34 Run() override { 35 MOZ_ASSERT(NS_IsMainThread()); 36 mRegistration->FinishActivate(mSuccess); 37 mRegistration = nullptr; 38 return NS_OK; 39 } 40 }; 41 42 } // anonymous namespace 43 44 void ServiceWorkerRegistrationInfo::ShutdownWorkers() { 45 ForEachWorker([](RefPtr<ServiceWorkerInfo>& aWorker) { 46 aWorker->WorkerPrivate()->NoteDeadServiceWorkerInfo(); 47 aWorker = nullptr; 48 }); 49 } 50 51 void ServiceWorkerRegistrationInfo::Clear() { 52 ForEachWorker([](RefPtr<ServiceWorkerInfo>& aWorker) { 53 aWorker->UpdateState(ServiceWorkerState::Redundant); 54 aWorker->UpdateRedundantTime(); 55 }); 56 57 // FIXME: Abort any inflight requests from installing worker. 58 59 ShutdownWorkers(); 60 UpdateRegistrationState(); 61 NotifyChromeRegistrationListeners(); 62 NotifyCleared(); 63 } 64 65 void ServiceWorkerRegistrationInfo::ClearAsCorrupt() { 66 mCorrupt = true; 67 Clear(); 68 } 69 70 bool ServiceWorkerRegistrationInfo::IsCorrupt() const { return mCorrupt; } 71 72 ServiceWorkerRegistrationInfo::ServiceWorkerRegistrationInfo( 73 const nsACString& aScope, WorkerType aType, nsIPrincipal* aPrincipal, 74 ServiceWorkerUpdateViaCache aUpdateViaCache, 75 IPCNavigationPreloadState&& aNavigationPreloadState) 76 : mPrincipal(aPrincipal), 77 mDescriptor(GetNextId(), GetNextVersion(), aPrincipal, aScope, aType, 78 aUpdateViaCache), 79 mControlledClientsCounter(0), 80 mDelayMultiplier(0), 81 mUpdateState(NoUpdate), 82 mCreationTime(PR_Now()), 83 mCreationTimeStamp(TimeStamp::Now()), 84 mLastUpdateTime(0), 85 mUnregistered(false), 86 mCorrupt(false), 87 mNavigationPreloadState(std::move(aNavigationPreloadState)) { 88 MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default); 89 } 90 91 ServiceWorkerRegistrationInfo::~ServiceWorkerRegistrationInfo() { 92 MOZ_DIAGNOSTIC_ASSERT(!IsControllingClients()); 93 } 94 95 void ServiceWorkerRegistrationInfo::AddInstance( 96 ServiceWorkerRegistrationListener* aInstance, 97 const ServiceWorkerRegistrationDescriptor& aDescriptor) { 98 MOZ_DIAGNOSTIC_ASSERT(aInstance); 99 MOZ_ASSERT(!mInstanceList.Contains(aInstance)); 100 MOZ_DIAGNOSTIC_ASSERT(aDescriptor.Id() == mDescriptor.Id()); 101 MOZ_DIAGNOSTIC_ASSERT(aDescriptor.PrincipalInfo() == 102 mDescriptor.PrincipalInfo()); 103 MOZ_DIAGNOSTIC_ASSERT(aDescriptor.Scope() == mDescriptor.Scope()); 104 MOZ_DIAGNOSTIC_ASSERT(aDescriptor.Version() <= mDescriptor.Version()); 105 uint64_t lastVersion = aDescriptor.Version(); 106 for (auto& entry : mVersionList) { 107 if (lastVersion > entry->mDescriptor.Version()) { 108 continue; 109 } 110 lastVersion = entry->mDescriptor.Version(); 111 aInstance->UpdateState(entry->mDescriptor); 112 } 113 // Note, the mDescriptor may be contained in the version list. Since the 114 // version list is aged out, though, it may also not be in the version list. 115 // So always check for the mDescriptor update here. 116 if (lastVersion < mDescriptor.Version()) { 117 aInstance->UpdateState(mDescriptor); 118 } 119 mInstanceList.AppendElement(aInstance); 120 } 121 122 void ServiceWorkerRegistrationInfo::RemoveInstance( 123 ServiceWorkerRegistrationListener* aInstance) { 124 MOZ_DIAGNOSTIC_ASSERT(aInstance); 125 DebugOnly<bool> removed = mInstanceList.RemoveElement(aInstance); 126 MOZ_ASSERT(removed); 127 } 128 129 const nsCString& ServiceWorkerRegistrationInfo::Scope() const { 130 return mDescriptor.Scope(); 131 } 132 133 WorkerType ServiceWorkerRegistrationInfo::Type() const { 134 return mDescriptor.Type(); 135 } 136 137 nsIPrincipal* ServiceWorkerRegistrationInfo::Principal() const { 138 return mPrincipal; 139 } 140 141 bool ServiceWorkerRegistrationInfo::IsUnregistered() const { 142 return mUnregistered; 143 } 144 145 void ServiceWorkerRegistrationInfo::SetUnregistered() { 146 #ifdef DEBUG 147 MOZ_ASSERT(!mUnregistered); 148 149 RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance(); 150 MOZ_ASSERT(swm); 151 152 RefPtr<ServiceWorkerRegistrationInfo> registration = 153 swm->GetRegistration(Principal(), Scope()); 154 MOZ_ASSERT(registration != this); 155 #endif 156 157 mUnregistered = true; 158 NotifyChromeRegistrationListeners(); 159 } 160 161 NS_IMPL_ISUPPORTS(ServiceWorkerRegistrationInfo, 162 nsIServiceWorkerRegistrationInfo) 163 164 NS_IMETHODIMP 165 ServiceWorkerRegistrationInfo::GetPrincipal(nsIPrincipal** aPrincipal) { 166 MOZ_ASSERT(NS_IsMainThread()); 167 NS_ADDREF(*aPrincipal = mPrincipal); 168 return NS_OK; 169 } 170 171 NS_IMETHODIMP ServiceWorkerRegistrationInfo::GetUnregistered( 172 bool* aUnregistered) { 173 MOZ_ASSERT(NS_IsMainThread()); 174 MOZ_ASSERT(aUnregistered); 175 *aUnregistered = mUnregistered; 176 return NS_OK; 177 } 178 179 NS_IMETHODIMP 180 ServiceWorkerRegistrationInfo::GetScope(nsAString& aScope) { 181 MOZ_ASSERT(NS_IsMainThread()); 182 CopyUTF8toUTF16(Scope(), aScope); 183 return NS_OK; 184 } 185 186 NS_IMETHODIMP 187 ServiceWorkerRegistrationInfo::GetScriptSpec(nsAString& aScriptSpec) { 188 MOZ_ASSERT(NS_IsMainThread()); 189 RefPtr<ServiceWorkerInfo> newest = NewestIncludingEvaluating(); 190 if (newest) { 191 CopyUTF8toUTF16(newest->ScriptSpec(), aScriptSpec); 192 } 193 return NS_OK; 194 } 195 196 NS_IMETHODIMP 197 ServiceWorkerRegistrationInfo::GetUpdateViaCache(uint16_t* aUpdateViaCache) { 198 *aUpdateViaCache = static_cast<uint16_t>(GetUpdateViaCache()); 199 return NS_OK; 200 } 201 202 NS_IMETHODIMP 203 ServiceWorkerRegistrationInfo::GetLastUpdateTime(PRTime* _retval) { 204 MOZ_ASSERT(NS_IsMainThread()); 205 MOZ_ASSERT(_retval); 206 *_retval = mLastUpdateTime; 207 return NS_OK; 208 } 209 210 NS_IMETHODIMP 211 ServiceWorkerRegistrationInfo::GetEvaluatingWorker( 212 nsIServiceWorkerInfo** aResult) { 213 MOZ_ASSERT(NS_IsMainThread()); 214 RefPtr<ServiceWorkerInfo> info = mEvaluatingWorker; 215 info.forget(aResult); 216 return NS_OK; 217 } 218 219 NS_IMETHODIMP 220 ServiceWorkerRegistrationInfo::GetInstallingWorker( 221 nsIServiceWorkerInfo** aResult) { 222 MOZ_ASSERT(NS_IsMainThread()); 223 RefPtr<ServiceWorkerInfo> info = mInstallingWorker; 224 info.forget(aResult); 225 return NS_OK; 226 } 227 228 NS_IMETHODIMP 229 ServiceWorkerRegistrationInfo::GetWaitingWorker( 230 nsIServiceWorkerInfo** aResult) { 231 MOZ_ASSERT(NS_IsMainThread()); 232 RefPtr<ServiceWorkerInfo> info = mWaitingWorker; 233 info.forget(aResult); 234 return NS_OK; 235 } 236 237 NS_IMETHODIMP 238 ServiceWorkerRegistrationInfo::GetActiveWorker(nsIServiceWorkerInfo** aResult) { 239 MOZ_ASSERT(NS_IsMainThread()); 240 RefPtr<ServiceWorkerInfo> info = mActiveWorker; 241 info.forget(aResult); 242 return NS_OK; 243 } 244 245 NS_IMETHODIMP 246 ServiceWorkerRegistrationInfo::GetQuotaUsageCheckCount( 247 int32_t* aQuotaUsageCheckCount) { 248 MOZ_ASSERT(NS_IsMainThread()); 249 MOZ_ASSERT(aQuotaUsageCheckCount); 250 251 // This value is actually stored on SWM's internal-only 252 // RegistrationDataPerPrincipal structure, but we expose it here for 253 // simplicity for our consumers, so we have to ask SWM to look it up for us. 254 RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance(); 255 MOZ_ASSERT(swm); 256 257 *aQuotaUsageCheckCount = swm->GetPrincipalQuotaUsageCheckCount(mPrincipal); 258 259 return NS_OK; 260 } 261 262 NS_IMETHODIMP 263 ServiceWorkerRegistrationInfo::GetWorkerByID(uint64_t aID, 264 nsIServiceWorkerInfo** aResult) { 265 MOZ_ASSERT(NS_IsMainThread()); 266 MOZ_ASSERT(aResult); 267 268 RefPtr<ServiceWorkerInfo> info = GetServiceWorkerInfoById(aID); 269 // It is ok to return null for a missing service worker info. 270 info.forget(aResult); 271 return NS_OK; 272 } 273 274 NS_IMETHODIMP 275 ServiceWorkerRegistrationInfo::AddListener( 276 nsIServiceWorkerRegistrationInfoListener* aListener) { 277 MOZ_ASSERT(NS_IsMainThread()); 278 279 if (!aListener || mListeners.Contains(aListener)) { 280 return NS_ERROR_INVALID_ARG; 281 } 282 283 mListeners.AppendElement(aListener); 284 285 return NS_OK; 286 } 287 288 NS_IMETHODIMP 289 ServiceWorkerRegistrationInfo::RemoveListener( 290 nsIServiceWorkerRegistrationInfoListener* aListener) { 291 MOZ_ASSERT(NS_IsMainThread()); 292 293 if (!aListener || !mListeners.Contains(aListener)) { 294 return NS_ERROR_INVALID_ARG; 295 } 296 297 mListeners.RemoveElement(aListener); 298 299 return NS_OK; 300 } 301 302 NS_IMETHODIMP 303 ServiceWorkerRegistrationInfo::ForceShutdown() { 304 ClearInstalling(); 305 ShutdownWorkers(); 306 return NS_OK; 307 } 308 309 already_AddRefed<ServiceWorkerInfo> 310 ServiceWorkerRegistrationInfo::GetServiceWorkerInfoById(uint64_t aId) { 311 MOZ_ASSERT(NS_IsMainThread()); 312 313 RefPtr<ServiceWorkerInfo> serviceWorker; 314 if (mEvaluatingWorker && mEvaluatingWorker->ID() == aId) { 315 serviceWorker = mEvaluatingWorker; 316 } else if (mInstallingWorker && mInstallingWorker->ID() == aId) { 317 serviceWorker = mInstallingWorker; 318 } else if (mWaitingWorker && mWaitingWorker->ID() == aId) { 319 serviceWorker = mWaitingWorker; 320 } else if (mActiveWorker && mActiveWorker->ID() == aId) { 321 serviceWorker = mActiveWorker; 322 } 323 324 return serviceWorker.forget(); 325 } 326 327 void ServiceWorkerRegistrationInfo::TryToActivateAsync( 328 const ServiceWorkerLifetimeExtension& aLifetimeExtension, 329 TryToActivateCallback&& aCallback) { 330 MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread( 331 NewRunnableMethod<StoreCopyPassByRRef<ServiceWorkerLifetimeExtension>, 332 StoreCopyPassByRRef<TryToActivateCallback>>( 333 "ServiceWorkerRegistrationInfo::TryToActivate", this, 334 &ServiceWorkerRegistrationInfo::TryToActivate, aLifetimeExtension, 335 std::move(aCallback)))); 336 } 337 338 /* 339 * TryToActivate should not be called directly, use TryToActivateAsync instead. 340 */ 341 void ServiceWorkerRegistrationInfo::TryToActivate( 342 ServiceWorkerLifetimeExtension&& aLifetimeExtension, 343 TryToActivateCallback&& aCallback) { 344 MOZ_ASSERT(NS_IsMainThread()); 345 ++mNumberOfAttemptedActivations; 346 bool controlling = IsControllingClients(); 347 bool skipWaiting = mWaitingWorker && mWaitingWorker->SkipWaitingFlag(); 348 bool idle = IsIdle(); 349 if (idle && (!controlling || skipWaiting)) { 350 if (controlling) { 351 aLifetimeExtension.emplace<FullLifetimeExtension>(); 352 } 353 Activate(aLifetimeExtension); 354 } 355 356 if (aCallback) { 357 aCallback(); 358 } 359 } 360 361 void ServiceWorkerRegistrationInfo::Activate( 362 const ServiceWorkerLifetimeExtension& aLifetimeExtension) { 363 if (!mWaitingWorker) { 364 return; 365 } 366 367 RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance(); 368 if (!swm) { 369 // browser shutdown began during async activation step 370 return; 371 } 372 373 TransitionWaitingToActive(); 374 375 // FIXME(nsm): Unlink appcache if there is one. 376 377 // "Queue a task to fire a simple event named controllerchange..." 378 MOZ_DIAGNOSTIC_ASSERT(mActiveWorker); 379 swm->UpdateClientControllers(this); 380 381 nsMainThreadPtrHandle<ServiceWorkerRegistrationInfo> handle( 382 new nsMainThreadPtrHolder<ServiceWorkerRegistrationInfo>( 383 "ServiceWorkerRegistrationInfoProxy", this)); 384 RefPtr<LifeCycleEventCallback> callback = 385 new ContinueActivateRunnable(handle); 386 387 ServiceWorkerPrivate* workerPrivate = mActiveWorker->WorkerPrivate(); 388 MOZ_ASSERT(workerPrivate); 389 nsresult rv = workerPrivate->SendLifeCycleEvent(u"activate"_ns, 390 aLifetimeExtension, callback); 391 if (NS_WARN_IF(NS_FAILED(rv))) { 392 nsCOMPtr<nsIRunnable> failRunnable = NewRunnableMethod<bool>( 393 "dom::ServiceWorkerRegistrationInfo::FinishActivate", this, 394 &ServiceWorkerRegistrationInfo::FinishActivate, false /* success */); 395 MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(failRunnable.forget())); 396 return; 397 } 398 } 399 400 void ServiceWorkerRegistrationInfo::FinishActivate(bool aSuccess) { 401 if (mUnregistered || !mActiveWorker || 402 mActiveWorker->State() != ServiceWorkerState::Activating) { 403 return; 404 } 405 406 // Activation never fails, so aSuccess is ignored. 407 mActiveWorker->UpdateState(ServiceWorkerState::Activated); 408 mActiveWorker->UpdateActivatedTime(); 409 410 UpdateRegistrationState(); 411 NotifyChromeRegistrationListeners(); 412 413 RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance(); 414 if (!swm) { 415 // browser shutdown started during async activation completion step 416 return; 417 } 418 swm->StoreRegistration(mPrincipal, this); 419 } 420 421 void ServiceWorkerRegistrationInfo::RefreshLastUpdateCheckTime() { 422 MOZ_ASSERT(NS_IsMainThread()); 423 424 mLastUpdateTime = 425 mCreationTime + 426 static_cast<PRTime>( 427 (TimeStamp::Now() - mCreationTimeStamp).ToMicroseconds()); 428 NotifyChromeRegistrationListeners(); 429 } 430 431 bool ServiceWorkerRegistrationInfo::IsLastUpdateCheckTimeOverOneDay() const { 432 MOZ_ASSERT(NS_IsMainThread()); 433 434 // For testing. 435 if (Preferences::GetBool("dom.serviceWorkers.testUpdateOverOneDay")) { 436 return true; 437 } 438 439 const int64_t kSecondsPerDay = 86400; 440 const int64_t nowMicros = 441 mCreationTime + 442 static_cast<PRTime>( 443 (TimeStamp::Now() - mCreationTimeStamp).ToMicroseconds()); 444 445 // now < mLastUpdateTime if the system time is reset between storing 446 // and loading mLastUpdateTime from ServiceWorkerRegistrar. 447 if (nowMicros < mLastUpdateTime || 448 (nowMicros - mLastUpdateTime) / PR_USEC_PER_SEC > kSecondsPerDay) { 449 return true; 450 } 451 return false; 452 } 453 454 void ServiceWorkerRegistrationInfo::UpdateRegistrationState() { 455 UpdateRegistrationState(mDescriptor.UpdateViaCache(), mDescriptor.Type()); 456 } 457 458 void ServiceWorkerRegistrationInfo::UpdateRegistrationState( 459 ServiceWorkerUpdateViaCache aUpdateViaCache, WorkerType aType) { 460 MOZ_ASSERT(NS_IsMainThread()); 461 462 TimeStamp oldest = TimeStamp::Now() - TimeDuration::FromSeconds(30); 463 if (!mVersionList.IsEmpty() && mVersionList[0]->mTimeStamp < oldest) { 464 nsTArray<UniquePtr<VersionEntry>> list = std::move(mVersionList); 465 for (auto& entry : list) { 466 if (entry->mTimeStamp >= oldest) { 467 mVersionList.AppendElement(std::move(entry)); 468 } 469 } 470 } 471 mVersionList.AppendElement(MakeUnique<VersionEntry>(mDescriptor)); 472 473 // We are going to modify the descriptor, so increase its version number. 474 mDescriptor.SetVersion(GetNextVersion()); 475 476 // Note, this also sets the new version number on the ServiceWorkerInfo 477 // objects before we copy over their updated descriptors. 478 mDescriptor.SetWorkers(mInstallingWorker, mWaitingWorker, mActiveWorker); 479 480 mDescriptor.SetUpdateViaCache(aUpdateViaCache); 481 mDescriptor.SetWorkerType(aType); 482 483 for (RefPtr<ServiceWorkerRegistrationListener> pinnedTarget : 484 mInstanceList.ForwardRange()) { 485 pinnedTarget->UpdateState(mDescriptor); 486 } 487 } 488 489 void ServiceWorkerRegistrationInfo::NotifyChromeRegistrationListeners() { 490 nsTArray<nsCOMPtr<nsIServiceWorkerRegistrationInfoListener>> listeners( 491 mListeners.Clone()); 492 for (size_t index = 0; index < listeners.Length(); ++index) { 493 listeners[index]->OnChange(); 494 } 495 } 496 497 void ServiceWorkerRegistrationInfo::MaybeScheduleTimeCheckAndUpdate() { 498 MOZ_ASSERT(NS_IsMainThread()); 499 500 RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance(); 501 if (!swm) { 502 // shutting down, do nothing 503 return; 504 } 505 506 if (mUpdateState == NoUpdate) { 507 mUpdateState = NeedTimeCheckAndUpdate; 508 } 509 510 swm->ScheduleUpdateTimer(mPrincipal, Scope()); 511 } 512 513 void ServiceWorkerRegistrationInfo::MaybeScheduleUpdate() { 514 MOZ_ASSERT(NS_IsMainThread()); 515 516 RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance(); 517 if (!swm) { 518 // shutting down, do nothing 519 return; 520 } 521 522 // When reach the navigation fault threshold, calling unregister instead of 523 // scheduling update. 524 if (mActiveWorker && !mUnregistered) { 525 uint32_t navigationFaultCount; 526 mActiveWorker->GetNavigationFaultCount(&navigationFaultCount); 527 const auto navigationFaultThreshold = StaticPrefs:: 528 dom_serviceWorkers_mitigations_navigation_fault_threshold(); 529 // Disable unregister mitigation when navigation fault threshold is 0. 530 if (navigationFaultThreshold <= navigationFaultCount && 531 navigationFaultThreshold != 0) { 532 CheckQuotaUsage(); 533 swm->Unregister(mPrincipal, nullptr, NS_ConvertUTF8toUTF16(Scope())); 534 return; 535 } 536 } 537 538 mUpdateState = NeedUpdate; 539 540 swm->ScheduleUpdateTimer(mPrincipal, Scope()); 541 } 542 543 bool ServiceWorkerRegistrationInfo::CheckAndClearIfUpdateNeeded() { 544 MOZ_ASSERT(NS_IsMainThread()); 545 546 bool result = 547 mUpdateState == NeedUpdate || (mUpdateState == NeedTimeCheckAndUpdate && 548 IsLastUpdateCheckTimeOverOneDay()); 549 550 mUpdateState = NoUpdate; 551 552 return result; 553 } 554 555 ServiceWorkerInfo* ServiceWorkerRegistrationInfo::GetEvaluating() const { 556 MOZ_ASSERT(NS_IsMainThread()); 557 return mEvaluatingWorker; 558 } 559 560 ServiceWorkerInfo* ServiceWorkerRegistrationInfo::GetInstalling() const { 561 MOZ_ASSERT(NS_IsMainThread()); 562 return mInstallingWorker; 563 } 564 565 ServiceWorkerInfo* ServiceWorkerRegistrationInfo::GetWaiting() const { 566 MOZ_ASSERT(NS_IsMainThread()); 567 return mWaitingWorker; 568 } 569 570 ServiceWorkerInfo* ServiceWorkerRegistrationInfo::GetActive() const { 571 MOZ_ASSERT(NS_IsMainThread()); 572 return mActiveWorker; 573 } 574 575 ServiceWorkerInfo* ServiceWorkerRegistrationInfo::GetByDescriptor( 576 const ServiceWorkerDescriptor& aDescriptor) const { 577 if (mActiveWorker && mActiveWorker->Descriptor().Matches(aDescriptor)) { 578 return mActiveWorker; 579 } 580 if (mWaitingWorker && mWaitingWorker->Descriptor().Matches(aDescriptor)) { 581 return mWaitingWorker; 582 } 583 if (mInstallingWorker && 584 mInstallingWorker->Descriptor().Matches(aDescriptor)) { 585 return mInstallingWorker; 586 } 587 if (mEvaluatingWorker && 588 mEvaluatingWorker->Descriptor().Matches(aDescriptor)) { 589 return mEvaluatingWorker; 590 } 591 return nullptr; 592 } 593 594 ServiceWorkerInfo* ServiceWorkerRegistrationInfo::GetByClientInfo( 595 const ClientInfo& aClientInfo) const { 596 if (mActiveWorker) { 597 auto clientRef = mActiveWorker->GetClientInfo(); 598 if (clientRef && *clientRef == aClientInfo) { 599 return mActiveWorker; 600 } 601 } 602 if (mWaitingWorker) { 603 auto clientRef = mWaitingWorker->GetClientInfo(); 604 if (clientRef && *clientRef == aClientInfo) { 605 return mWaitingWorker; 606 } 607 } 608 if (mInstallingWorker) { 609 auto clientRef = mInstallingWorker->GetClientInfo(); 610 if (clientRef && *clientRef == aClientInfo) { 611 return mInstallingWorker; 612 } 613 } 614 if (mEvaluatingWorker) { 615 auto clientRef = mEvaluatingWorker->GetClientInfo(); 616 if (clientRef && *clientRef == aClientInfo) { 617 return mEvaluatingWorker; 618 } 619 } 620 return nullptr; 621 } 622 623 void ServiceWorkerRegistrationInfo::SetEvaluating( 624 ServiceWorkerInfo* aServiceWorker) { 625 MOZ_ASSERT(NS_IsMainThread()); 626 MOZ_ASSERT(aServiceWorker); 627 MOZ_ASSERT(!mEvaluatingWorker); 628 MOZ_ASSERT(!mInstallingWorker); 629 MOZ_ASSERT(mWaitingWorker != aServiceWorker); 630 MOZ_ASSERT(mActiveWorker != aServiceWorker); 631 632 mEvaluatingWorker = aServiceWorker; 633 634 // We don't call UpdateRegistrationState() here because the evaluating worker 635 // is currently not exposed to content on the registration, so calling it here 636 // would produce redundant IPC traffic. 637 NotifyChromeRegistrationListeners(); 638 } 639 640 void ServiceWorkerRegistrationInfo::ClearEvaluating() { 641 MOZ_ASSERT(NS_IsMainThread()); 642 643 if (!mEvaluatingWorker) { 644 return; 645 } 646 647 mEvaluatingWorker->UpdateState(ServiceWorkerState::Redundant); 648 // We don't update the redundant time for the sw here, since we've not expose 649 // evalutingWorker yet. 650 mEvaluatingWorker = nullptr; 651 652 // As for SetEvaluating, UpdateRegistrationState() does not need to be called. 653 NotifyChromeRegistrationListeners(); 654 } 655 656 void ServiceWorkerRegistrationInfo::ClearInstalling() { 657 MOZ_ASSERT(NS_IsMainThread()); 658 659 if (!mInstallingWorker) { 660 return; 661 } 662 663 RefPtr<ServiceWorkerInfo> installing = std::move(mInstallingWorker); 664 installing->UpdateState(ServiceWorkerState::Redundant); 665 installing->UpdateRedundantTime(); 666 667 UpdateRegistrationState(); 668 NotifyChromeRegistrationListeners(); 669 } 670 671 void ServiceWorkerRegistrationInfo::TransitionEvaluatingToInstalling() { 672 MOZ_ASSERT(NS_IsMainThread()); 673 MOZ_ASSERT(mEvaluatingWorker); 674 MOZ_ASSERT(!mInstallingWorker); 675 676 mInstallingWorker = std::move(mEvaluatingWorker); 677 mInstallingWorker->UpdateState(ServiceWorkerState::Installing); 678 679 UpdateRegistrationState(); 680 NotifyChromeRegistrationListeners(); 681 } 682 683 void ServiceWorkerRegistrationInfo::TransitionInstallingToWaiting() { 684 MOZ_ASSERT(NS_IsMainThread()); 685 MOZ_ASSERT(mInstallingWorker); 686 687 if (mWaitingWorker) { 688 MOZ_ASSERT(mInstallingWorker->CacheName() != mWaitingWorker->CacheName()); 689 mWaitingWorker->UpdateState(ServiceWorkerState::Redundant); 690 mWaitingWorker->UpdateRedundantTime(); 691 } 692 693 mWaitingWorker = std::move(mInstallingWorker); 694 mWaitingWorker->UpdateState(ServiceWorkerState::Installed); 695 mWaitingWorker->UpdateInstalledTime(); 696 697 UpdateRegistrationState(); 698 NotifyChromeRegistrationListeners(); 699 700 // TODO: When bug 1426401 is implemented we will need to call 701 // StoreRegistration() here to persist the waiting worker. 702 } 703 704 void ServiceWorkerRegistrationInfo::SetActive( 705 ServiceWorkerInfo* aServiceWorker) { 706 MOZ_ASSERT(NS_IsMainThread()); 707 MOZ_ASSERT(aServiceWorker); 708 709 // TODO: Assert installing, waiting, and active are nullptr once the SWM 710 // moves to the parent process. After that happens this code will 711 // only run for browser initialization and not for cross-process 712 // overrides. 713 MOZ_ASSERT(mInstallingWorker != aServiceWorker); 714 MOZ_ASSERT(mWaitingWorker != aServiceWorker); 715 MOZ_ASSERT(mActiveWorker != aServiceWorker); 716 717 if (mActiveWorker) { 718 MOZ_ASSERT(aServiceWorker->CacheName() != mActiveWorker->CacheName()); 719 mActiveWorker->UpdateState(ServiceWorkerState::Redundant); 720 mActiveWorker->UpdateRedundantTime(); 721 } 722 723 // The active worker is being overriden due to initial load or 724 // another process activating a worker. Move straight to the 725 // Activated state. 726 mActiveWorker = aServiceWorker; 727 mActiveWorker->SetActivateStateUncheckedWithoutEvent( 728 ServiceWorkerState::Activated); 729 730 // We don't need to update activated time when we load registration from 731 // registrar. 732 UpdateRegistrationState(); 733 NotifyChromeRegistrationListeners(); 734 } 735 736 void ServiceWorkerRegistrationInfo::TransitionWaitingToActive() { 737 MOZ_ASSERT(NS_IsMainThread()); 738 MOZ_ASSERT(mWaitingWorker); 739 740 if (mActiveWorker) { 741 MOZ_ASSERT(mWaitingWorker->CacheName() != mActiveWorker->CacheName()); 742 mActiveWorker->UpdateState(ServiceWorkerState::Redundant); 743 mActiveWorker->UpdateRedundantTime(); 744 } 745 746 // We are transitioning from waiting to active normally, so go to 747 // the activating state. 748 mActiveWorker = std::move(mWaitingWorker); 749 mActiveWorker->UpdateState(ServiceWorkerState::Activating); 750 751 nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction( 752 "ServiceWorkerRegistrationInfo::TransitionWaitingToActive", [] { 753 RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance(); 754 if (swm) { 755 swm->CheckPendingReadyPromises(); 756 } 757 }); 758 MOZ_ALWAYS_SUCCEEDS(SchedulerGroup::Dispatch(r.forget())); 759 UpdateRegistrationState(); 760 NotifyChromeRegistrationListeners(); 761 } 762 763 bool ServiceWorkerRegistrationInfo::IsIdle() const { 764 return !mActiveWorker || mActiveWorker->WorkerPrivate()->IsIdle(); 765 } 766 767 ServiceWorkerUpdateViaCache ServiceWorkerRegistrationInfo::GetUpdateViaCache() 768 const { 769 return mDescriptor.UpdateViaCache(); 770 } 771 772 void ServiceWorkerRegistrationInfo::SetOptions( 773 ServiceWorkerUpdateViaCache aUpdateViaCache, WorkerType aType) { 774 UpdateRegistrationState(aUpdateViaCache, aType); 775 } 776 777 int64_t ServiceWorkerRegistrationInfo::GetLastUpdateTime() const { 778 return mLastUpdateTime; 779 } 780 781 void ServiceWorkerRegistrationInfo::SetLastUpdateTime(const int64_t aTime) { 782 if (aTime == 0) { 783 return; 784 } 785 786 mLastUpdateTime = aTime; 787 } 788 789 const ServiceWorkerRegistrationDescriptor& 790 ServiceWorkerRegistrationInfo::Descriptor() const { 791 return mDescriptor; 792 } 793 794 uint64_t ServiceWorkerRegistrationInfo::Id() const { return mDescriptor.Id(); } 795 796 uint64_t ServiceWorkerRegistrationInfo::Version() const { 797 return mDescriptor.Version(); 798 } 799 800 uint32_t ServiceWorkerRegistrationInfo::GetUpdateDelay( 801 const bool aWithMultiplier) { 802 uint32_t delay = Preferences::GetInt("dom.serviceWorkers.update_delay", 1000); 803 804 if (!aWithMultiplier) { 805 return delay; 806 } 807 808 // This can potentially happen if you spam registration->Update(). We don't 809 // want to wrap to a lower value. 810 if (mDelayMultiplier >= INT_MAX / (delay ? delay : 1)) { 811 return INT_MAX; 812 } 813 814 delay *= mDelayMultiplier; 815 816 if (!mControlledClientsCounter && mDelayMultiplier < (INT_MAX / 30)) { 817 mDelayMultiplier = (mDelayMultiplier ? mDelayMultiplier : 1) * 30; 818 } 819 820 return delay; 821 } 822 823 void ServiceWorkerRegistrationInfo::FireUpdateFound() { 824 for (RefPtr<ServiceWorkerRegistrationListener> pinnedTarget : 825 mInstanceList.ForwardRange()) { 826 pinnedTarget->FireUpdateFound(); 827 } 828 } 829 830 void ServiceWorkerRegistrationInfo::NotifyCleared() { 831 for (RefPtr<ServiceWorkerRegistrationListener> pinnedTarget : 832 mInstanceList.ForwardRange()) { 833 pinnedTarget->RegistrationCleared(); 834 } 835 } 836 837 void ServiceWorkerRegistrationInfo::ClearWhenIdle() { 838 MOZ_ASSERT(NS_IsMainThread()); 839 MOZ_ASSERT(IsUnregistered()); 840 MOZ_ASSERT(!IsControllingClients()); 841 MOZ_ASSERT(!IsIdle(), "Already idle!"); 842 843 RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance(); 844 MOZ_ASSERT(swm); 845 846 swm->AddOrphanedRegistration(this); 847 848 /** 849 * Although a Service Worker will transition to idle many times during its 850 * lifetime, the promise is only resolved once `GetIdlePromise` has been 851 * called, populating the `MozPromiseHolder`. Additionally, this is the only 852 * time this method will be called for the given ServiceWorker. This means we 853 * will be notified to the transition we are interested in, and there are no 854 * other callers to get confused. 855 * 856 * Note that because we are using `MozPromise`, our callback will be invoked 857 * as a separate task, so there is a small potential for races in the event 858 * code if things are still holding onto the ServiceWorker binding and using 859 * `postMessage()` or other mechanisms to schedule new events on it, which 860 * would make it non-idle. However, this is a race inherent in the spec which 861 * does not deal with the reality of multiple threads in "Try Clear 862 * Registration". 863 */ 864 GetActive()->WorkerPrivate()->GetIdlePromise()->Then( 865 GetCurrentSerialEventTarget(), __func__, 866 [self = RefPtr<ServiceWorkerRegistrationInfo>(this)]( 867 const GenericPromise::ResolveOrRejectValue& aResult) { 868 MOZ_ASSERT(aResult.IsResolve()); 869 // This registration was already unregistered and not controlling 870 // clients when `ClearWhenIdle` was called, so there should be no way 871 // that more clients were acquired. 872 MOZ_ASSERT(!self->IsControllingClients()); 873 MOZ_ASSERT(self->IsIdle()); 874 self->Clear(); 875 876 RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance(); 877 if (swm) { 878 swm->RemoveOrphanedRegistration(self); 879 } 880 }); 881 } 882 883 const nsID& ServiceWorkerRegistrationInfo::AgentClusterId() const { 884 return mAgentClusterId; 885 } 886 887 void ServiceWorkerRegistrationInfo::SetNavigationPreloadEnabled( 888 const bool& aEnabled) { 889 MOZ_ASSERT(NS_IsMainThread()); 890 mNavigationPreloadState.enabled() = aEnabled; 891 } 892 893 void ServiceWorkerRegistrationInfo::SetNavigationPreloadHeader( 894 const nsCString& aHeader) { 895 MOZ_ASSERT(NS_IsMainThread()); 896 mNavigationPreloadState.headerValue() = aHeader; 897 } 898 899 IPCNavigationPreloadState 900 ServiceWorkerRegistrationInfo::GetNavigationPreloadState() const { 901 MOZ_ASSERT(NS_IsMainThread()); 902 return mNavigationPreloadState; 903 } 904 905 // static 906 uint64_t ServiceWorkerRegistrationInfo::GetNextId() { 907 MOZ_ASSERT(NS_IsMainThread()); 908 static uint64_t sNextId = 0; 909 return ++sNextId; 910 } 911 912 // static 913 uint64_t ServiceWorkerRegistrationInfo::GetNextVersion() { 914 MOZ_ASSERT(NS_IsMainThread()); 915 static uint64_t sNextVersion = 0; 916 return ++sNextVersion; 917 } 918 919 void ServiceWorkerRegistrationInfo::ForEachWorker( 920 void (*aFunc)(RefPtr<ServiceWorkerInfo>&)) { 921 if (mEvaluatingWorker) { 922 aFunc(mEvaluatingWorker); 923 } 924 925 if (mInstallingWorker) { 926 aFunc(mInstallingWorker); 927 } 928 929 if (mWaitingWorker) { 930 aFunc(mWaitingWorker); 931 } 932 933 if (mActiveWorker) { 934 aFunc(mActiveWorker); 935 } 936 } 937 938 void ServiceWorkerRegistrationInfo::CheckQuotaUsage() { 939 MOZ_ASSERT(NS_IsMainThread()); 940 941 RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance(); 942 MOZ_ASSERT(swm); 943 944 swm->CheckPrincipalQuotaUsage(mPrincipal, Scope()); 945 } 946 947 } // namespace mozilla::dom