tor-browser

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

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