tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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