tor-browser

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

ServiceWorkerRegistrar.cpp (55515B)


      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 "ServiceWorkerRegistrar.h"
      8 
      9 #include "MainThreadUtils.h"
     10 #include "ServiceWorkerUtils.h"
     11 #include "mozilla/ClearOnShutdown.h"
     12 #include "mozilla/CycleCollectedJSContext.h"
     13 #include "mozilla/ErrorNames.h"
     14 #include "mozilla/ModuleUtils.h"
     15 #include "mozilla/Services.h"
     16 #include "mozilla/StaticPrefs_dom.h"
     17 #include "mozilla/StaticPtr.h"
     18 #include "mozilla/dom/CookieStoreSubscriptionService.h"
     19 #include "mozilla/dom/DOMException.h"
     20 #include "mozilla/dom/StorageActivityService.h"
     21 #include "mozilla/glean/DomServiceworkersMetrics.h"
     22 #include "mozilla/ipc/BackgroundChild.h"
     23 #include "mozilla/ipc/BackgroundParent.h"
     24 #include "mozilla/ipc/PBackgroundChild.h"
     25 #include "nsAppDirectoryServiceDefs.h"
     26 #include "nsComponentManagerUtils.h"
     27 #include "nsContentUtils.h"
     28 #include "nsDirectoryServiceUtils.h"
     29 #include "nsIEventTarget.h"
     30 #include "nsIInputStream.h"
     31 #include "nsILineInputStream.h"
     32 #include "nsIObserverService.h"
     33 #include "nsIOutputStream.h"
     34 #include "nsISafeOutputStream.h"
     35 #include "nsIServiceWorkerManager.h"
     36 #include "nsIURI.h"
     37 #include "nsIWritablePropertyBag2.h"
     38 #include "nsNetCID.h"
     39 #include "nsNetUtil.h"
     40 #include "nsServiceManagerUtils.h"
     41 #include "nsThreadUtils.h"
     42 #include "nsXULAppAPI.h"
     43 
     44 using namespace mozilla::ipc;
     45 
     46 namespace mozilla::dom {
     47 
     48 namespace {
     49 
     50 static const uint32_t gSupportedRegistrarVersions[] = {
     51    SERVICEWORKERREGISTRAR_VERSION, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2};
     52 
     53 static const uint32_t kInvalidGeneration = static_cast<uint32_t>(-1);
     54 
     55 StaticRefPtr<ServiceWorkerRegistrar> gServiceWorkerRegistrar;
     56 
     57 nsresult GetOriginAndBaseDomain(const nsACString& aURL, nsACString& aOrigin,
     58                                nsACString& aBaseDomain) {
     59  nsCOMPtr<nsIURI> url;
     60  nsresult rv = NS_NewURI(getter_AddRefs(url), aURL);
     61  if (NS_WARN_IF(NS_FAILED(rv))) {
     62    return rv;
     63  }
     64 
     65  OriginAttributes attrs;
     66  nsCOMPtr<nsIPrincipal> principal =
     67      BasePrincipal::CreateContentPrincipal(url, attrs);
     68  if (!principal) {
     69    return NS_ERROR_NULL_POINTER;
     70  }
     71 
     72  rv = principal->GetOriginNoSuffix(aOrigin);
     73  if (NS_WARN_IF(NS_FAILED(rv))) {
     74    return rv;
     75  }
     76 
     77  rv = principal->GetBaseDomain(aBaseDomain);
     78  if (NS_WARN_IF(NS_FAILED(rv))) {
     79    return rv;
     80  }
     81 
     82  return NS_OK;
     83 }
     84 
     85 nsresult ReadLine(nsILineInputStream* aStream, nsACString& aValue) {
     86  bool hasMoreLines;
     87  nsresult rv = aStream->ReadLine(aValue, &hasMoreLines);
     88  if (NS_WARN_IF(NS_FAILED(rv))) {
     89    return rv;
     90  }
     91 
     92  if (NS_WARN_IF(!hasMoreLines)) {
     93    return NS_ERROR_FAILURE;
     94  }
     95 
     96  return NS_OK;
     97 }
     98 
     99 nsresult CreatePrincipalInfo(nsILineInputStream* aStream,
    100                             ServiceWorkerRegistrationData& aEntry,
    101                             bool aSkipSpec = false) {
    102  nsAutoCString suffix;
    103  nsresult rv = ReadLine(aStream, suffix);
    104  if (NS_WARN_IF(NS_FAILED(rv))) {
    105    return rv;
    106  }
    107 
    108  OriginAttributes attrs;
    109  if (!attrs.PopulateFromSuffix(suffix)) {
    110    return NS_ERROR_INVALID_ARG;
    111  }
    112 
    113  if (aSkipSpec) {
    114    nsAutoCString unused;
    115    nsresult rv = ReadLine(aStream, unused);
    116    if (NS_WARN_IF(NS_FAILED(rv))) {
    117      return rv;
    118    }
    119  }
    120 
    121  rv = ReadLine(aStream, aEntry.scope());
    122  if (NS_WARN_IF(NS_FAILED(rv))) {
    123    return rv;
    124  }
    125 
    126  nsCString origin;
    127  nsCString baseDomain;
    128  rv = GetOriginAndBaseDomain(aEntry.scope(), origin, baseDomain);
    129  if (NS_WARN_IF(NS_FAILED(rv))) {
    130    return rv;
    131  }
    132 
    133  aEntry.principal() = mozilla::ipc::ContentPrincipalInfo(
    134      attrs, origin, aEntry.scope(), Nothing(), baseDomain);
    135 
    136  return NS_OK;
    137 }
    138 
    139 MOZ_RUNINIT const IPCNavigationPreloadState
    140    gDefaultNavigationPreloadState(false, "true"_ns);
    141 
    142 }  // namespace
    143 
    144 NS_IMPL_ISUPPORTS(ServiceWorkerRegistrar, nsIObserver, nsIAsyncShutdownBlocker)
    145 
    146 void ServiceWorkerRegistrar::Initialize() {
    147  MOZ_ASSERT(!gServiceWorkerRegistrar);
    148 
    149  if (!XRE_IsParentProcess()) {
    150    return;
    151  }
    152 
    153  gServiceWorkerRegistrar = new ServiceWorkerRegistrar();
    154  ClearOnShutdown(&gServiceWorkerRegistrar);
    155 
    156  nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
    157  if (obs) {
    158    DebugOnly<nsresult> rv = obs->AddObserver(gServiceWorkerRegistrar,
    159                                              "profile-after-change", false);
    160    MOZ_ASSERT(NS_SUCCEEDED(rv));
    161  }
    162 }
    163 
    164 /* static */
    165 already_AddRefed<ServiceWorkerRegistrar> ServiceWorkerRegistrar::Get() {
    166  MOZ_ASSERT(XRE_IsParentProcess());
    167 
    168  MOZ_ASSERT(gServiceWorkerRegistrar);
    169  RefPtr<ServiceWorkerRegistrar> service = gServiceWorkerRegistrar.get();
    170  return service.forget();
    171 }
    172 
    173 ServiceWorkerRegistrar::ServiceWorkerRegistrar()
    174    : mMonitor("ServiceWorkerRegistrar.mMonitor"),
    175      mDataLoaded(false),
    176      mDataGeneration(kInvalidGeneration),
    177      mFileGeneration(kInvalidGeneration),
    178      mRetryCount(0),
    179      mShuttingDown(false),
    180      mSaveDataRunnableDispatched(false) {
    181  MOZ_ASSERT(NS_IsMainThread());
    182 
    183  mExpandoHandlers.AppendElement(ExpandoHandler{
    184      nsCString("cookie-store"),
    185      CookieStoreSubscriptionService::ServiceWorkerLoaded,
    186      CookieStoreSubscriptionService::ServiceWorkerUpdated,
    187      CookieStoreSubscriptionService::ServiceWorkerUnregistered});
    188 }
    189 
    190 ServiceWorkerRegistrar::~ServiceWorkerRegistrar() {
    191  MOZ_ASSERT(!mSaveDataRunnableDispatched);
    192 }
    193 
    194 void ServiceWorkerRegistrar::GetRegistrations(
    195    nsTArray<ServiceWorkerRegistrationData>& aValues) {
    196  MOZ_ASSERT(NS_IsMainThread());
    197  MOZ_ASSERT(aValues.IsEmpty());
    198 
    199  MonitorAutoLock lock(mMonitor);
    200 
    201  // If we don't have the profile directory, profile is not started yet (and
    202  // probably we are in a utest).
    203  if (!mProfileDir) {
    204    return;
    205  }
    206 
    207  // We care just about the first execution because this can be blocked by
    208  // loading data from disk.
    209  static bool firstTime = true;
    210  TimeStamp startTime;
    211 
    212  if (firstTime) {
    213    startTime = TimeStamp::NowLoRes();
    214  }
    215 
    216  // Waiting for data loaded.
    217  mMonitor.AssertCurrentThreadOwns();
    218  while (!mDataLoaded) {
    219    mMonitor.Wait();
    220  }
    221 
    222  for (const ServiceWorkerData& data : mData) {
    223    aValues.AppendElement(data.mRegistration);
    224  }
    225 
    226  MaybeResetGeneration();
    227  MOZ_DIAGNOSTIC_ASSERT(mDataGeneration != kInvalidGeneration);
    228  MOZ_DIAGNOSTIC_ASSERT(mFileGeneration != kInvalidGeneration);
    229 
    230  if (firstTime) {
    231    firstTime = false;
    232    glean::service_worker::registration_loading.AccumulateRawDuration(
    233        TimeStamp::Now() - startTime);
    234  }
    235 }
    236 
    237 namespace {
    238 
    239 bool Equivalent(const ServiceWorkerRegistrationData& aLeft,
    240                const ServiceWorkerRegistrationData& aRight) {
    241  MOZ_ASSERT(aLeft.principal().type() ==
    242             mozilla::ipc::PrincipalInfo::TContentPrincipalInfo);
    243  MOZ_ASSERT(aRight.principal().type() ==
    244             mozilla::ipc::PrincipalInfo::TContentPrincipalInfo);
    245 
    246  const auto& leftPrincipal = aLeft.principal().get_ContentPrincipalInfo();
    247  const auto& rightPrincipal = aRight.principal().get_ContentPrincipalInfo();
    248 
    249  // Only compare the attributes, not the spec part of the principal.
    250  // The scope comparison above already covers the origin and codebase
    251  // principals include the full path in their spec which is not what
    252  // we want here.
    253  return aLeft.scope() == aRight.scope() &&
    254         leftPrincipal.attrs() == rightPrincipal.attrs();
    255 }
    256 
    257 }  // anonymous namespace
    258 
    259 void ServiceWorkerRegistrar::RegisterServiceWorker(
    260    const ServiceWorkerRegistrationData& aData) {
    261  AssertIsOnBackgroundThread();
    262 
    263  if (mShuttingDown) {
    264    NS_WARNING("Failed to register a serviceWorker during shutting down.");
    265    return;
    266  }
    267 
    268  {
    269    MonitorAutoLock lock(mMonitor);
    270    MOZ_ASSERT(mDataLoaded);
    271    RegisterServiceWorkerInternal(aData);
    272  }
    273 
    274  MaybeScheduleSaveData();
    275  StorageActivityService::SendActivity(aData.principal());
    276 }
    277 
    278 void ServiceWorkerRegistrar::UnregisterServiceWorker(
    279    const PrincipalInfo& aPrincipalInfo, const nsACString& aScope) {
    280  AssertIsOnBackgroundThread();
    281 
    282  if (mShuttingDown) {
    283    NS_WARNING("Failed to unregister a serviceWorker during shutting down.");
    284    return;
    285  }
    286 
    287  bool deleted = false;
    288 
    289  {
    290    MonitorAutoLock lock(mMonitor);
    291    MOZ_ASSERT(mDataLoaded);
    292 
    293    ServiceWorkerRegistrationData tmp;
    294    tmp.principal() = aPrincipalInfo;
    295    tmp.scope() = aScope;
    296 
    297    for (uint32_t i = 0; i < mData.Length(); ++i) {
    298      if (Equivalent(tmp, mData[i].mRegistration)) {
    299        UnregisterExpandoCallbacks(CopyableTArray<ServiceWorkerData>{mData[i]});
    300 
    301        mData.RemoveElementAt(i);
    302        mDataGeneration = GetNextGeneration();
    303        deleted = true;
    304        break;
    305      }
    306    }
    307  }
    308 
    309  if (deleted) {
    310    MaybeScheduleSaveData();
    311    StorageActivityService::SendActivity(aPrincipalInfo);
    312  }
    313 }
    314 
    315 void ServiceWorkerRegistrar::StoreServiceWorkerExpandoOnMainThread(
    316    const PrincipalInfo& aPrincipalInfo, const nsACString& aScope,
    317    const nsACString& aKey, const nsACString& aValue) {
    318  MOZ_ASSERT(NS_IsMainThread());
    319  MOZ_ASSERT(!aValue.Contains('\n'), "Invalid chars in the value");
    320 
    321  nsCOMPtr<nsISerialEventTarget> backgroundThread =
    322      BackgroundParent::GetBackgroundThread();
    323  if (NS_WARN_IF(!backgroundThread)) {
    324    // Probably we are shutting down. Unfortunately this expando data will not
    325    // be stored.
    326    return;
    327  }
    328 
    329  backgroundThread->Dispatch(NS_NewRunnableFunction(
    330      __func__,
    331      [self = RefPtr(this), aPrincipalInfo, aScope = nsCString(aScope),
    332       aKey = nsCString(aKey), aValue = nsCString(aValue)]() {
    333        if (self->mShuttingDown) {
    334          NS_WARNING(
    335              "Failed to store an expando to a serviceWorker during shutting "
    336              "down.");
    337          return;
    338        }
    339 
    340        const ExpandoHandler* expandoHandler = nullptr;
    341 
    342        for (const ExpandoHandler& handler : self->mExpandoHandlers) {
    343          if (handler.mKey == aKey) {
    344            expandoHandler = &handler;
    345            break;
    346          }
    347        }
    348 
    349        if (!expandoHandler) {
    350          NS_WARNING("Unsupported handler");
    351          return;
    352        }
    353 
    354        bool saveNeeded = false;
    355 
    356        {
    357          MonitorAutoLock lock(self->mMonitor);
    358          MOZ_ASSERT(self->mDataLoaded);
    359 
    360          ServiceWorkerRegistrationData tmp;
    361          tmp.principal() = aPrincipalInfo;
    362          tmp.scope() = aScope;
    363 
    364          for (uint32_t i = 0; i < self->mData.Length(); ++i) {
    365            if (Equivalent(tmp, self->mData[i].mRegistration)) {
    366              bool found = false;
    367              for (ExpandoData& expando : self->mData[i].mExpandos) {
    368                if (expando.mKey == aKey) {
    369                  MOZ_ASSERT(expando.mHandler == expandoHandler);
    370                  expando.mValue = aValue;
    371                  found = true;
    372                  break;
    373                }
    374              }
    375 
    376              if (!found) {
    377                self->mData[i].mExpandos.AppendElement(ExpandoData{
    378                    nsCString(aKey), nsCString(aValue), expandoHandler});
    379              }
    380 
    381              self->mDataGeneration = self->GetNextGeneration();
    382              saveNeeded = true;
    383              break;
    384            }
    385          }
    386        }
    387 
    388        if (saveNeeded) {
    389          self->MaybeScheduleSaveData();
    390          StorageActivityService::SendActivity(aPrincipalInfo);
    391        }
    392      }));
    393 }
    394 
    395 void ServiceWorkerRegistrar::UnstoreServiceWorkerExpandoOnMainThread(
    396    const PrincipalInfo& aPrincipalInfo, const nsACString& aScope,
    397    const nsACString& aKey) {
    398  MOZ_ASSERT(NS_IsMainThread());
    399 
    400  nsCOMPtr<nsISerialEventTarget> backgroundThread =
    401      BackgroundParent::GetBackgroundThread();
    402  if (NS_WARN_IF(!backgroundThread)) {
    403    // Probably we are shutting down. Unfortunately this expando data will not
    404    // be stored.
    405    return;
    406  }
    407 
    408  backgroundThread->Dispatch(NS_NewRunnableFunction(
    409      __func__, [self = RefPtr(this), aPrincipalInfo,
    410                 aScope = nsCString(aScope), aKey = nsCString(aKey)]() {
    411        if (self->mShuttingDown) {
    412          NS_WARNING(
    413              "Failed to unstore an expando from a serviceWorker during "
    414              "shutting down.");
    415          return;
    416        }
    417 
    418        bool saveNeeded = false;
    419 
    420        {
    421          MonitorAutoLock lock(self->mMonitor);
    422          MOZ_ASSERT(self->mDataLoaded);
    423 
    424          ServiceWorkerRegistrationData tmp;
    425          tmp.principal() = aPrincipalInfo;
    426          tmp.scope() = aScope;
    427 
    428          for (ServiceWorkerData& data : self->mData) {
    429            if (Equivalent(tmp, data.mRegistration)) {
    430              for (uint32_t i = 0; i < data.mExpandos.Length(); ++i) {
    431                if (data.mExpandos[i].mKey == aKey) {
    432                  data.mExpandos.RemoveElementAt(i);
    433                  self->mDataGeneration = self->GetNextGeneration();
    434                  saveNeeded = true;
    435                  break;
    436                }
    437              }
    438 
    439              break;
    440            }
    441          }
    442        }
    443 
    444        if (saveNeeded) {
    445          self->MaybeScheduleSaveData();
    446          StorageActivityService::SendActivity(aPrincipalInfo);
    447        }
    448      }));
    449 }
    450 
    451 void ServiceWorkerRegistrar::RemoveAll() {
    452  AssertIsOnBackgroundThread();
    453 
    454  if (mShuttingDown) {
    455    NS_WARNING("Failed to remove all the serviceWorkers during shutting down.");
    456    return;
    457  }
    458 
    459  bool deleted = false;
    460 
    461  nsTArray<ServiceWorkerRegistrationData> data;
    462  nsTArray<ServiceWorkerData> registrationsWithExpandos;
    463  {
    464    MonitorAutoLock lock(mMonitor);
    465    MOZ_ASSERT(mDataLoaded);
    466 
    467    // Let's take a copy in order to inform StorageActivityService.
    468    for (const ServiceWorkerData& i : mData) {
    469      data.AppendElement(i.mRegistration);
    470 
    471      if (!i.mExpandos.IsEmpty()) {
    472        registrationsWithExpandos.AppendElement(i);
    473      }
    474    }
    475 
    476    deleted = !mData.IsEmpty();
    477    mData.Clear();
    478 
    479    mDataGeneration = GetNextGeneration();
    480  }
    481 
    482  if (!deleted) {
    483    return;
    484  }
    485 
    486  if (!registrationsWithExpandos.IsEmpty()) {
    487    UnregisterExpandoCallbacks(registrationsWithExpandos);
    488  }
    489 
    490  MaybeScheduleSaveData();
    491 
    492  for (uint32_t i = 0, len = data.Length(); i < len; ++i) {
    493    StorageActivityService::SendActivity(data[i].principal());
    494  }
    495 }
    496 
    497 void ServiceWorkerRegistrar::LoadData() {
    498  MOZ_ASSERT(!NS_IsMainThread());
    499 #ifdef DEBUG
    500  {
    501    MonitorAutoLock lock(mMonitor);
    502    MOZ_ASSERT(!mDataLoaded);
    503  }
    504 #endif
    505 
    506  nsresult rv = ReadData();
    507 
    508  if (NS_WARN_IF(NS_FAILED(rv))) {
    509    DeleteData();
    510    // Also if the reading failed we have to notify what is waiting for data.
    511  }
    512 
    513  MonitorAutoLock lock(mMonitor);
    514  MOZ_ASSERT(!mDataLoaded);
    515  mDataLoaded = true;
    516  mMonitor.Notify();
    517 }
    518 
    519 bool ServiceWorkerRegistrar::ReloadDataForTest() {
    520  if (NS_WARN_IF(!StaticPrefs::dom_serviceWorkers_testing_enabled())) {
    521    return false;
    522  }
    523 
    524  MOZ_ASSERT(NS_IsMainThread());
    525  MonitorAutoLock lock(mMonitor);
    526  mData.Clear();
    527  mDataLoaded = false;
    528 
    529  nsCOMPtr<nsIEventTarget> target =
    530      do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID);
    531  MOZ_ASSERT(target, "Must have stream transport service");
    532 
    533  nsCOMPtr<nsIRunnable> runnable =
    534      NewRunnableMethod("dom::ServiceWorkerRegistrar::LoadData", this,
    535                        &ServiceWorkerRegistrar::LoadData);
    536  nsresult rv = target->Dispatch(runnable.forget(), NS_DISPATCH_NORMAL);
    537  if (NS_FAILED(rv)) {
    538    NS_WARNING("Failed to dispatch the LoadDataRunnable.");
    539    return false;
    540  }
    541 
    542  mMonitor.AssertCurrentThreadOwns();
    543  while (!mDataLoaded) {
    544    mMonitor.Wait();
    545  }
    546 
    547  return mDataLoaded;
    548 }
    549 
    550 nsresult ServiceWorkerRegistrar::ReadData() {
    551  // We cannot assert about the correct thread because normally this method
    552  // runs on a IO thread, but in gTests we call it from the main-thread.
    553 
    554  nsCOMPtr<nsIFile> file;
    555 
    556  {
    557    MonitorAutoLock lock(mMonitor);
    558 
    559    if (!mProfileDir) {
    560      return NS_ERROR_FAILURE;
    561    }
    562 
    563    nsresult rv = mProfileDir->Clone(getter_AddRefs(file));
    564    if (NS_WARN_IF(NS_FAILED(rv))) {
    565      return rv;
    566    }
    567  }
    568 
    569  nsresult rv = file->Append(nsLiteralString(SERVICEWORKERREGISTRAR_FILE));
    570  if (NS_WARN_IF(NS_FAILED(rv))) {
    571    return rv;
    572  }
    573 
    574  bool exists;
    575  rv = file->Exists(&exists);
    576  if (NS_WARN_IF(NS_FAILED(rv))) {
    577    return rv;
    578  }
    579 
    580  if (!exists) {
    581    return NS_OK;
    582  }
    583 
    584  nsCOMPtr<nsIInputStream> stream;
    585  rv = NS_NewLocalFileInputStream(getter_AddRefs(stream), file);
    586  if (NS_WARN_IF(NS_FAILED(rv))) {
    587    return rv;
    588  }
    589 
    590  nsCOMPtr<nsILineInputStream> lineInputStream = do_QueryInterface(stream);
    591  MOZ_ASSERT(lineInputStream);
    592 
    593  nsAutoCString versionStr;
    594  bool hasMoreLines;
    595  rv = lineInputStream->ReadLine(versionStr, &hasMoreLines);
    596  if (NS_WARN_IF(NS_FAILED(rv))) {
    597    return rv;
    598  }
    599 
    600  uint32_t version = versionStr.ToUnsignedInteger(&rv);
    601  if (NS_WARN_IF(NS_FAILED(rv))) {
    602    return rv;
    603  }
    604 
    605  if (!IsSupportedVersion(version)) {
    606    nsContentUtils::LogMessageToConsole(
    607        nsPrintfCString("Unsupported service worker registrar version: %s",
    608                        versionStr.get())
    609            .get());
    610    return NS_ERROR_FAILURE;
    611  }
    612 
    613  nsTArray<ServiceWorkerData> tmpData;
    614 
    615  bool overwrite = false;
    616  bool dedupe = false;
    617  while (hasMoreLines) {
    618    ServiceWorkerData* entry = tmpData.AppendElement();
    619 
    620 #define GET_LINE(x)                                 \
    621  rv = lineInputStream->ReadLine(x, &hasMoreLines); \
    622  if (NS_WARN_IF(NS_FAILED(rv))) {                  \
    623    return rv;                                      \
    624  }                                                 \
    625  if (NS_WARN_IF(!hasMoreLines)) {                  \
    626    return NS_ERROR_FAILURE;                        \
    627  }
    628 
    629    // baseSchemaVersion represents the version where major schema changes
    630    // happened and requires a different reading strategy as done below in the
    631    // switch statement. Version 9 is the latest major schema version, versions
    632    // 10 and 11 are just extensions to version 9 and that's why gets processed
    633    // under the same block.
    634    auto baseSchemaVersion = version >= 9 ? 9 : version;
    635 
    636    nsAutoCString line;
    637    switch (baseSchemaVersion) {
    638      case 9: {
    639        rv = CreatePrincipalInfo(lineInputStream, entry->mRegistration);
    640        if (NS_WARN_IF(NS_FAILED(rv))) {
    641          return rv;
    642        }
    643 
    644        GET_LINE(entry->mRegistration.currentWorkerURL());
    645 
    646        nsAutoCString fetchFlag;
    647        GET_LINE(fetchFlag);
    648        if (!fetchFlag.EqualsLiteral(SERVICEWORKERREGISTRAR_TRUE) &&
    649            !fetchFlag.EqualsLiteral(SERVICEWORKERREGISTRAR_FALSE)) {
    650          return NS_ERROR_INVALID_ARG;
    651        }
    652        entry->mRegistration.currentWorkerHandlesFetch() =
    653            fetchFlag.EqualsLiteral(SERVICEWORKERREGISTRAR_TRUE);
    654 
    655        nsAutoCString cacheName;
    656        GET_LINE(cacheName);
    657        CopyUTF8toUTF16(cacheName, entry->mRegistration.cacheName());
    658 
    659        nsAutoCString updateViaCache;
    660        GET_LINE(updateViaCache);
    661        entry->mRegistration.updateViaCache() =
    662            updateViaCache.ToInteger(&rv, 16);
    663        if (NS_WARN_IF(NS_FAILED(rv))) {
    664          return rv;
    665        }
    666        if (entry->mRegistration.updateViaCache() >
    667            nsIServiceWorkerRegistrationInfo::UPDATE_VIA_CACHE_NONE) {
    668          return NS_ERROR_INVALID_ARG;
    669        }
    670 
    671        nsAutoCString installedTimeStr;
    672        GET_LINE(installedTimeStr);
    673        int64_t installedTime = installedTimeStr.ToInteger64(&rv);
    674        if (NS_WARN_IF(NS_FAILED(rv))) {
    675          return rv;
    676        }
    677        entry->mRegistration.currentWorkerInstalledTime() = installedTime;
    678 
    679        nsAutoCString activatedTimeStr;
    680        GET_LINE(activatedTimeStr);
    681        int64_t activatedTime = activatedTimeStr.ToInteger64(&rv);
    682        if (NS_WARN_IF(NS_FAILED(rv))) {
    683          return rv;
    684        }
    685        entry->mRegistration.currentWorkerActivatedTime() = activatedTime;
    686 
    687        nsAutoCString lastUpdateTimeStr;
    688        GET_LINE(lastUpdateTimeStr);
    689        int64_t lastUpdateTime = lastUpdateTimeStr.ToInteger64(&rv);
    690        if (NS_WARN_IF(NS_FAILED(rv))) {
    691          return rv;
    692        }
    693        entry->mRegistration.lastUpdateTime() = lastUpdateTime;
    694 
    695        nsAutoCString navigationPreloadEnabledStr;
    696        GET_LINE(navigationPreloadEnabledStr);
    697        bool navigationPreloadEnabled =
    698            navigationPreloadEnabledStr.ToInteger(&rv);
    699        if (NS_WARN_IF(NS_FAILED(rv))) {
    700          return rv;
    701        }
    702        entry->mRegistration.navigationPreloadState().enabled() =
    703            navigationPreloadEnabled;
    704 
    705        GET_LINE(entry->mRegistration.navigationPreloadState().headerValue());
    706 
    707        if (version >= 10) {
    708          nsAutoCString expandoCountStr;
    709          GET_LINE(expandoCountStr);
    710          uint32_t expandoCount = expandoCountStr.ToInteger(&rv, 16);
    711          if (NS_WARN_IF(NS_FAILED(rv))) {
    712            return rv;
    713          }
    714 
    715          for (uint32_t expandoId = 0; expandoId < expandoCount; ++expandoId) {
    716            nsAutoCString key;
    717            GET_LINE(key);
    718 
    719            nsAutoCString value;
    720            GET_LINE(value);
    721 
    722            for (const ExpandoHandler& handler : mExpandoHandlers) {
    723              if (handler.mKey == key) {
    724                entry->mExpandos.AppendElement(
    725                    ExpandoData{key, value, &handler});
    726                break;
    727              }
    728            }
    729          }
    730        }
    731 
    732        if (version >= 11) {
    733          nsAutoCString numberOfAttemptedActivationsStr;
    734          GET_LINE(numberOfAttemptedActivationsStr);
    735          int64_t numberOfAttemptedActivations =
    736              numberOfAttemptedActivationsStr.ToInteger64(&rv);
    737          if (NS_WARN_IF(NS_FAILED(rv))) {
    738            return rv;
    739          }
    740          entry->mRegistration.numberOfAttemptedActivations() =
    741              numberOfAttemptedActivations;
    742          nsAutoCString isRegistrationBrokenStr;
    743          GET_LINE(isRegistrationBrokenStr);
    744          int64_t isBroken = isRegistrationBrokenStr.ToInteger64(&rv);
    745          if (NS_WARN_IF(NS_FAILED(rv))) {
    746            return rv;
    747          }
    748          entry->mRegistration.isBroken() = (isBroken != 0);
    749          nsAutoCString cacheAPIIdStr;
    750          GET_LINE(cacheAPIIdStr);
    751          int64_t cacheAPIId = cacheAPIIdStr.ToInteger64(&rv);
    752          if (NS_WARN_IF(NS_FAILED(rv))) {
    753            return rv;
    754          }
    755          entry->mRegistration.cacheAPIId() = cacheAPIId;
    756        }
    757 
    758        // if we are on latest version, get service worker type
    759        if (version == SERVICEWORKERREGISTRAR_VERSION) {
    760          nsAutoCString serviceWorkerTypeStr;
    761          GET_LINE(serviceWorkerTypeStr);
    762          uint32_t serviceWorkerType =
    763              serviceWorkerTypeStr.ToUnsignedInteger(&rv);
    764          if (NS_WARN_IF(NS_FAILED(rv))) {
    765            return rv;
    766          }
    767          if (serviceWorkerType > static_cast<uint32_t>(WorkerType::Module)) {
    768            return NS_ERROR_INVALID_ARG;
    769          }
    770          entry->mRegistration.type() =
    771              static_cast<WorkerType>(serviceWorkerType);
    772        }
    773        break;
    774      }
    775 
    776      case 8: {
    777        rv = CreatePrincipalInfo(lineInputStream, entry->mRegistration);
    778        if (NS_WARN_IF(NS_FAILED(rv))) {
    779          return rv;
    780        }
    781 
    782        GET_LINE(entry->mRegistration.currentWorkerURL());
    783 
    784        nsAutoCString fetchFlag;
    785        GET_LINE(fetchFlag);
    786        if (!fetchFlag.EqualsLiteral(SERVICEWORKERREGISTRAR_TRUE) &&
    787            !fetchFlag.EqualsLiteral(SERVICEWORKERREGISTRAR_FALSE)) {
    788          return NS_ERROR_INVALID_ARG;
    789        }
    790        entry->mRegistration.currentWorkerHandlesFetch() =
    791            fetchFlag.EqualsLiteral(SERVICEWORKERREGISTRAR_TRUE);
    792 
    793        nsAutoCString cacheName;
    794        GET_LINE(cacheName);
    795        CopyUTF8toUTF16(cacheName, entry->mRegistration.cacheName());
    796 
    797        nsAutoCString updateViaCache;
    798        GET_LINE(updateViaCache);
    799        entry->mRegistration.updateViaCache() =
    800            updateViaCache.ToInteger(&rv, 16);
    801        if (NS_WARN_IF(NS_FAILED(rv))) {
    802          return rv;
    803        }
    804        if (entry->mRegistration.updateViaCache() >
    805            nsIServiceWorkerRegistrationInfo::UPDATE_VIA_CACHE_NONE) {
    806          return NS_ERROR_INVALID_ARG;
    807        }
    808 
    809        nsAutoCString installedTimeStr;
    810        GET_LINE(installedTimeStr);
    811        int64_t installedTime = installedTimeStr.ToInteger64(&rv);
    812        if (NS_WARN_IF(NS_FAILED(rv))) {
    813          return rv;
    814        }
    815        entry->mRegistration.currentWorkerInstalledTime() = installedTime;
    816 
    817        nsAutoCString activatedTimeStr;
    818        GET_LINE(activatedTimeStr);
    819        int64_t activatedTime = activatedTimeStr.ToInteger64(&rv);
    820        if (NS_WARN_IF(NS_FAILED(rv))) {
    821          return rv;
    822        }
    823        entry->mRegistration.currentWorkerActivatedTime() = activatedTime;
    824 
    825        nsAutoCString lastUpdateTimeStr;
    826        GET_LINE(lastUpdateTimeStr);
    827        int64_t lastUpdateTime = lastUpdateTimeStr.ToInteger64(&rv);
    828        if (NS_WARN_IF(NS_FAILED(rv))) {
    829          return rv;
    830        }
    831        entry->mRegistration.lastUpdateTime() = lastUpdateTime;
    832 
    833        entry->mRegistration.navigationPreloadState() =
    834            gDefaultNavigationPreloadState;
    835        break;
    836      }
    837 
    838      case 7: {
    839        rv = CreatePrincipalInfo(lineInputStream, entry->mRegistration);
    840        if (NS_WARN_IF(NS_FAILED(rv))) {
    841          return rv;
    842        }
    843 
    844        GET_LINE(entry->mRegistration.currentWorkerURL());
    845 
    846        nsAutoCString fetchFlag;
    847        GET_LINE(fetchFlag);
    848        if (!fetchFlag.EqualsLiteral(SERVICEWORKERREGISTRAR_TRUE) &&
    849            !fetchFlag.EqualsLiteral(SERVICEWORKERREGISTRAR_FALSE)) {
    850          return NS_ERROR_INVALID_ARG;
    851        }
    852        entry->mRegistration.currentWorkerHandlesFetch() =
    853            fetchFlag.EqualsLiteral(SERVICEWORKERREGISTRAR_TRUE);
    854 
    855        nsAutoCString cacheName;
    856        GET_LINE(cacheName);
    857        CopyUTF8toUTF16(cacheName, entry->mRegistration.cacheName());
    858 
    859        nsAutoCString loadFlags;
    860        GET_LINE(loadFlags);
    861        entry->mRegistration.updateViaCache() =
    862            loadFlags.ToInteger(&rv, 16) == nsIRequest::LOAD_NORMAL
    863                ? nsIServiceWorkerRegistrationInfo::UPDATE_VIA_CACHE_ALL
    864                : nsIServiceWorkerRegistrationInfo::UPDATE_VIA_CACHE_IMPORTS;
    865 
    866        if (NS_WARN_IF(NS_FAILED(rv))) {
    867          return rv;
    868        }
    869 
    870        nsAutoCString installedTimeStr;
    871        GET_LINE(installedTimeStr);
    872        int64_t installedTime = installedTimeStr.ToInteger64(&rv);
    873        if (NS_WARN_IF(NS_FAILED(rv))) {
    874          return rv;
    875        }
    876        entry->mRegistration.currentWorkerInstalledTime() = installedTime;
    877 
    878        nsAutoCString activatedTimeStr;
    879        GET_LINE(activatedTimeStr);
    880        int64_t activatedTime = activatedTimeStr.ToInteger64(&rv);
    881        if (NS_WARN_IF(NS_FAILED(rv))) {
    882          return rv;
    883        }
    884        entry->mRegistration.currentWorkerActivatedTime() = activatedTime;
    885 
    886        nsAutoCString lastUpdateTimeStr;
    887        GET_LINE(lastUpdateTimeStr);
    888        int64_t lastUpdateTime = lastUpdateTimeStr.ToInteger64(&rv);
    889        if (NS_WARN_IF(NS_FAILED(rv))) {
    890          return rv;
    891        }
    892        entry->mRegistration.lastUpdateTime() = lastUpdateTime;
    893 
    894        entry->mRegistration.navigationPreloadState() =
    895            gDefaultNavigationPreloadState;
    896        break;
    897      }
    898 
    899      case 6: {
    900        rv = CreatePrincipalInfo(lineInputStream, entry->mRegistration);
    901        if (NS_WARN_IF(NS_FAILED(rv))) {
    902          return rv;
    903        }
    904 
    905        GET_LINE(entry->mRegistration.currentWorkerURL());
    906 
    907        nsAutoCString fetchFlag;
    908        GET_LINE(fetchFlag);
    909        if (!fetchFlag.EqualsLiteral(SERVICEWORKERREGISTRAR_TRUE) &&
    910            !fetchFlag.EqualsLiteral(SERVICEWORKERREGISTRAR_FALSE)) {
    911          return NS_ERROR_INVALID_ARG;
    912        }
    913        entry->mRegistration.currentWorkerHandlesFetch() =
    914            fetchFlag.EqualsLiteral(SERVICEWORKERREGISTRAR_TRUE);
    915 
    916        nsAutoCString cacheName;
    917        GET_LINE(cacheName);
    918        CopyUTF8toUTF16(cacheName, entry->mRegistration.cacheName());
    919 
    920        nsAutoCString loadFlags;
    921        GET_LINE(loadFlags);
    922        entry->mRegistration.updateViaCache() =
    923            loadFlags.ToInteger(&rv, 16) == nsIRequest::LOAD_NORMAL
    924                ? nsIServiceWorkerRegistrationInfo::UPDATE_VIA_CACHE_ALL
    925                : nsIServiceWorkerRegistrationInfo::UPDATE_VIA_CACHE_IMPORTS;
    926 
    927        if (NS_WARN_IF(NS_FAILED(rv))) {
    928          return rv;
    929        }
    930 
    931        entry->mRegistration.currentWorkerInstalledTime() = 0;
    932        entry->mRegistration.currentWorkerActivatedTime() = 0;
    933        entry->mRegistration.lastUpdateTime() = 0;
    934 
    935        entry->mRegistration.navigationPreloadState() =
    936            gDefaultNavigationPreloadState;
    937        break;
    938      }
    939 
    940      case 5: {
    941        overwrite = true;
    942        dedupe = true;
    943 
    944        rv = CreatePrincipalInfo(lineInputStream, entry->mRegistration);
    945        if (NS_WARN_IF(NS_FAILED(rv))) {
    946          return rv;
    947        }
    948 
    949        GET_LINE(entry->mRegistration.currentWorkerURL());
    950 
    951        nsAutoCString fetchFlag;
    952        GET_LINE(fetchFlag);
    953        if (!fetchFlag.EqualsLiteral(SERVICEWORKERREGISTRAR_TRUE) &&
    954            !fetchFlag.EqualsLiteral(SERVICEWORKERREGISTRAR_FALSE)) {
    955          return NS_ERROR_INVALID_ARG;
    956        }
    957        entry->mRegistration.currentWorkerHandlesFetch() =
    958            fetchFlag.EqualsLiteral(SERVICEWORKERREGISTRAR_TRUE);
    959 
    960        nsAutoCString cacheName;
    961        GET_LINE(cacheName);
    962        CopyUTF8toUTF16(cacheName, entry->mRegistration.cacheName());
    963 
    964        entry->mRegistration.updateViaCache() =
    965            nsIServiceWorkerRegistrationInfo::UPDATE_VIA_CACHE_IMPORTS;
    966 
    967        entry->mRegistration.currentWorkerInstalledTime() = 0;
    968        entry->mRegistration.currentWorkerActivatedTime() = 0;
    969        entry->mRegistration.lastUpdateTime() = 0;
    970 
    971        entry->mRegistration.navigationPreloadState() =
    972            gDefaultNavigationPreloadState;
    973        break;
    974      }
    975 
    976      case 4: {
    977        overwrite = true;
    978        dedupe = true;
    979 
    980        rv = CreatePrincipalInfo(lineInputStream, entry->mRegistration);
    981        if (NS_WARN_IF(NS_FAILED(rv))) {
    982          return rv;
    983        }
    984 
    985        GET_LINE(entry->mRegistration.currentWorkerURL());
    986 
    987        // default handlesFetch flag to Enabled
    988        entry->mRegistration.currentWorkerHandlesFetch() = true;
    989 
    990        nsAutoCString cacheName;
    991        GET_LINE(cacheName);
    992        CopyUTF8toUTF16(cacheName, entry->mRegistration.cacheName());
    993 
    994        entry->mRegistration.updateViaCache() =
    995            nsIServiceWorkerRegistrationInfo::UPDATE_VIA_CACHE_IMPORTS;
    996 
    997        entry->mRegistration.currentWorkerInstalledTime() = 0;
    998        entry->mRegistration.currentWorkerActivatedTime() = 0;
    999        entry->mRegistration.lastUpdateTime() = 0;
   1000 
   1001        entry->mRegistration.navigationPreloadState() =
   1002            gDefaultNavigationPreloadState;
   1003        break;
   1004      }
   1005 
   1006      case 3: {
   1007        overwrite = true;
   1008        dedupe = true;
   1009 
   1010        rv = CreatePrincipalInfo(lineInputStream, entry->mRegistration, true);
   1011        if (NS_WARN_IF(NS_FAILED(rv))) {
   1012          return rv;
   1013        }
   1014 
   1015        GET_LINE(entry->mRegistration.currentWorkerURL());
   1016 
   1017        // default handlesFetch flag to Enabled
   1018        entry->mRegistration.currentWorkerHandlesFetch() = true;
   1019 
   1020        nsAutoCString cacheName;
   1021        GET_LINE(cacheName);
   1022        CopyUTF8toUTF16(cacheName, entry->mRegistration.cacheName());
   1023 
   1024        entry->mRegistration.updateViaCache() =
   1025            nsIServiceWorkerRegistrationInfo::UPDATE_VIA_CACHE_IMPORTS;
   1026 
   1027        entry->mRegistration.currentWorkerInstalledTime() = 0;
   1028        entry->mRegistration.currentWorkerActivatedTime() = 0;
   1029        entry->mRegistration.lastUpdateTime() = 0;
   1030 
   1031        entry->mRegistration.navigationPreloadState() =
   1032            gDefaultNavigationPreloadState;
   1033        break;
   1034      }
   1035 
   1036      case 2: {
   1037        overwrite = true;
   1038        dedupe = true;
   1039 
   1040        rv = CreatePrincipalInfo(lineInputStream, entry->mRegistration, true);
   1041        if (NS_WARN_IF(NS_FAILED(rv))) {
   1042          return rv;
   1043        }
   1044 
   1045        // scriptSpec is no more used in latest version.
   1046        nsAutoCString unused;
   1047        GET_LINE(unused);
   1048 
   1049        GET_LINE(entry->mRegistration.currentWorkerURL());
   1050 
   1051        // default handlesFetch flag to Enabled
   1052        entry->mRegistration.currentWorkerHandlesFetch() = true;
   1053 
   1054        nsAutoCString cacheName;
   1055        GET_LINE(cacheName);
   1056        CopyUTF8toUTF16(cacheName, entry->mRegistration.cacheName());
   1057 
   1058        // waitingCacheName is no more used in latest version.
   1059        GET_LINE(unused);
   1060 
   1061        entry->mRegistration.updateViaCache() =
   1062            nsIServiceWorkerRegistrationInfo::UPDATE_VIA_CACHE_IMPORTS;
   1063 
   1064        entry->mRegistration.currentWorkerInstalledTime() = 0;
   1065        entry->mRegistration.currentWorkerActivatedTime() = 0;
   1066        entry->mRegistration.lastUpdateTime() = 0;
   1067 
   1068        entry->mRegistration.navigationPreloadState() =
   1069            gDefaultNavigationPreloadState;
   1070        break;
   1071      }
   1072 
   1073      default:
   1074        MOZ_ASSERT_UNREACHABLE("Should never get here!");
   1075    }
   1076 
   1077 #undef GET_LINE
   1078 
   1079    rv = lineInputStream->ReadLine(line, &hasMoreLines);
   1080    if (NS_WARN_IF(NS_FAILED(rv))) {
   1081      return rv;
   1082    }
   1083 
   1084    if (!line.EqualsLiteral(SERVICEWORKERREGISTRAR_TERMINATOR)) {
   1085      return NS_ERROR_FAILURE;
   1086    }
   1087  }
   1088 
   1089  stream->Close();
   1090 
   1091  // We currently only call this at startup where we block the main thread
   1092  // preventing further operation until it completes, however take the lock
   1093  // in case that changes
   1094 
   1095  nsTArray<ServiceWorkerData> registrationsWithExpandos;
   1096 
   1097  {
   1098    MonitorAutoLock lock(mMonitor);
   1099    // Copy data over to mData.
   1100    for (uint32_t i = 0; i < tmpData.Length(); ++i) {
   1101      // Older versions could sometimes write out empty, useless entries.
   1102      // Prune those here.
   1103      if (!ServiceWorkerRegistrationDataIsValid(tmpData[i].mRegistration)) {
   1104        continue;
   1105      }
   1106 
   1107      bool match = false;
   1108      if (dedupe) {
   1109        MOZ_ASSERT(overwrite);
   1110        // If this is an old profile, then we might need to deduplicate.  In
   1111        // theory this can be removed in the future (Bug 1248449)
   1112        for (uint32_t j = 0; j < mData.Length(); ++j) {
   1113          // Use same comparison as RegisterServiceWorker. Scope contains
   1114          // basic origin information.  Combine with any principal attributes.
   1115          if (Equivalent(tmpData[i].mRegistration, mData[j].mRegistration)) {
   1116            // Last match wins, just like legacy loading used to do in
   1117            // the ServiceWorkerManager.
   1118            mData[j].mRegistration = tmpData[i].mRegistration;
   1119            mData[j].mExpandos.Clear();
   1120            // Dupe found, so overwrite file with reduced list.
   1121            match = true;
   1122            break;
   1123          }
   1124        }
   1125      } else {
   1126 #ifdef DEBUG
   1127        // Otherwise assert no duplications in debug builds.
   1128        for (uint32_t j = 0; j < mData.Length(); ++j) {
   1129          MOZ_ASSERT(
   1130              !Equivalent(tmpData[i].mRegistration, mData[j].mRegistration));
   1131        }
   1132 #endif
   1133      }
   1134      if (!match) {
   1135        mData.AppendElement(tmpData[i]);
   1136 
   1137        if (!tmpData[i].mExpandos.IsEmpty()) {
   1138          registrationsWithExpandos.AppendElement(tmpData[i]);
   1139        }
   1140      }
   1141    }
   1142  }
   1143 
   1144  if (!registrationsWithExpandos.IsEmpty()) {
   1145    LoadExpandoCallbacks(registrationsWithExpandos);
   1146  }
   1147 
   1148  // Overwrite previous version.
   1149  // Cannot call SaveData directly because gtest uses main-thread.
   1150 
   1151  // XXX NOTE: if we could be accessed multi-threaded here, we would need to
   1152  // find a way to lock around access to mData.  Since we can't, suppress the
   1153  // thread-safety warnings.
   1154  MOZ_PUSH_IGNORE_THREAD_SAFETY
   1155  if (overwrite && NS_FAILED(WriteData(mData))) {
   1156    NS_WARNING("Failed to write data for the ServiceWorker Registations.");
   1157    DeleteData();
   1158  }
   1159  MOZ_POP_THREAD_SAFETY
   1160 
   1161  return NS_OK;
   1162 }
   1163 
   1164 void ServiceWorkerRegistrar::DeleteData() {
   1165  // We cannot assert about the correct thread because normally this method
   1166  // runs on a IO thread, but in gTests we call it from the main-thread.
   1167 
   1168  nsCOMPtr<nsIFile> file;
   1169 
   1170  {
   1171    MonitorAutoLock lock(mMonitor);
   1172    mData.Clear();
   1173 
   1174    if (!mProfileDir) {
   1175      return;
   1176    }
   1177 
   1178    nsresult rv = mProfileDir->Clone(getter_AddRefs(file));
   1179    if (NS_WARN_IF(NS_FAILED(rv))) {
   1180      return;
   1181    }
   1182  }
   1183 
   1184  nsresult rv = file->Append(nsLiteralString(SERVICEWORKERREGISTRAR_FILE));
   1185  if (NS_WARN_IF(NS_FAILED(rv))) {
   1186    return;
   1187  }
   1188 
   1189  rv = file->Remove(false);
   1190  if (rv == NS_ERROR_FILE_NOT_FOUND) {
   1191    return;
   1192  }
   1193 
   1194  if (NS_WARN_IF(NS_FAILED(rv))) {
   1195    return;
   1196  }
   1197 }
   1198 
   1199 void ServiceWorkerRegistrar::RegisterServiceWorkerInternal(
   1200    const ServiceWorkerRegistrationData& aData) {
   1201  bool found = false;
   1202  for (uint32_t i = 0, len = mData.Length(); i < len; ++i) {
   1203    if (Equivalent(aData, mData[i].mRegistration)) {
   1204      UpdateExpandoCallbacks(mData[i]);
   1205 
   1206      found = true;
   1207      mData[i].mRegistration = aData;
   1208      mData[i].mExpandos.Clear();
   1209      break;
   1210    }
   1211  }
   1212 
   1213  if (!found) {
   1214    MOZ_ASSERT(ServiceWorkerRegistrationDataIsValid(aData));
   1215    mData.AppendElement(ServiceWorkerData{aData, nsTArray<ExpandoData>()});
   1216  }
   1217 
   1218  mDataGeneration = GetNextGeneration();
   1219 }
   1220 
   1221 class ServiceWorkerRegistrarSaveDataRunnable final : public Runnable {
   1222  nsCOMPtr<nsIEventTarget> mEventTarget;
   1223  const nsTArray<ServiceWorkerRegistrar::ServiceWorkerData> mData;
   1224  const uint32_t mGeneration;
   1225 
   1226 public:
   1227  ServiceWorkerRegistrarSaveDataRunnable(
   1228      nsTArray<ServiceWorkerRegistrar::ServiceWorkerData>&& aData,
   1229      uint32_t aGeneration)
   1230      : Runnable("dom::ServiceWorkerRegistrarSaveDataRunnable"),
   1231        mEventTarget(GetCurrentSerialEventTarget()),
   1232        mData(std::move(aData)),
   1233        mGeneration(aGeneration) {
   1234    AssertIsOnBackgroundThread();
   1235    MOZ_DIAGNOSTIC_ASSERT(mGeneration != kInvalidGeneration);
   1236  }
   1237 
   1238  NS_IMETHOD
   1239  Run() override {
   1240    RefPtr<ServiceWorkerRegistrar> service = ServiceWorkerRegistrar::Get();
   1241    MOZ_ASSERT(service);
   1242 
   1243    uint32_t fileGeneration = kInvalidGeneration;
   1244 
   1245    if (NS_SUCCEEDED(service->SaveData(mData))) {
   1246      fileGeneration = mGeneration;
   1247    }
   1248 
   1249    RefPtr<Runnable> runnable = NewRunnableMethod<uint32_t>(
   1250        "ServiceWorkerRegistrar::DataSaved", service,
   1251        &ServiceWorkerRegistrar::DataSaved, fileGeneration);
   1252    MOZ_ALWAYS_SUCCEEDS(
   1253        mEventTarget->Dispatch(runnable.forget(), NS_DISPATCH_NORMAL));
   1254 
   1255    return NS_OK;
   1256  }
   1257 };
   1258 
   1259 void ServiceWorkerRegistrar::MaybeScheduleSaveData() {
   1260  AssertIsOnBackgroundThread();
   1261  MOZ_ASSERT(!mShuttingDown);
   1262 
   1263  if (mShuttingDown || mSaveDataRunnableDispatched ||
   1264      mDataGeneration <= mFileGeneration) {
   1265    return;
   1266  }
   1267 
   1268  nsCOMPtr<nsIEventTarget> target =
   1269      do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID);
   1270  MOZ_ASSERT(target, "Must have stream transport service");
   1271 
   1272  uint32_t generation = kInvalidGeneration;
   1273  nsTArray<ServiceWorkerData> data;
   1274 
   1275  {
   1276    MonitorAutoLock lock(mMonitor);
   1277    generation = mDataGeneration;
   1278    data.AppendElements(mData);
   1279  }
   1280 
   1281  RefPtr<Runnable> runnable =
   1282      new ServiceWorkerRegistrarSaveDataRunnable(std::move(data), generation);
   1283  nsresult rv = target->Dispatch(runnable.forget(), NS_DISPATCH_NORMAL);
   1284  NS_ENSURE_SUCCESS_VOID(rv);
   1285 
   1286  mSaveDataRunnableDispatched = true;
   1287 }
   1288 
   1289 void ServiceWorkerRegistrar::ShutdownCompleted() {
   1290  MOZ_ASSERT(NS_IsMainThread());
   1291 
   1292  DebugOnly<nsresult> rv = GetShutdownPhase()->RemoveBlocker(this);
   1293  MOZ_ASSERT(NS_SUCCEEDED(rv));
   1294 }
   1295 
   1296 nsresult ServiceWorkerRegistrar::SaveData(
   1297    const nsTArray<ServiceWorkerData>& aData) {
   1298  MOZ_ASSERT(!NS_IsMainThread());
   1299 
   1300  nsresult rv = WriteData(aData);
   1301  if (NS_FAILED(rv)) {
   1302    NS_WARNING("Failed to write data for the ServiceWorker Registations.");
   1303    // Don't touch the file or in-memory state.  Writing files can
   1304    // sometimes fail due to virus scanning, etc.  We should just leave
   1305    // things as is so the next save operation can pick up any changes
   1306    // without losing data.
   1307  }
   1308  return rv;
   1309 }
   1310 
   1311 void ServiceWorkerRegistrar::DataSaved(uint32_t aFileGeneration) {
   1312  AssertIsOnBackgroundThread();
   1313  MOZ_ASSERT(mSaveDataRunnableDispatched);
   1314 
   1315  mSaveDataRunnableDispatched = false;
   1316 
   1317  // Check for shutdown before possibly triggering any more saves
   1318  // runnables.
   1319  MaybeScheduleShutdownCompleted();
   1320  if (mShuttingDown) {
   1321    return;
   1322  }
   1323 
   1324  // If we got a valid generation, then the save was successful.
   1325  if (aFileGeneration != kInvalidGeneration) {
   1326    // Update the file generation.  We also check to see if we
   1327    // can reset the generation back to zero if the file and data
   1328    // are now in sync.  This allows us to avoid dealing with wrap
   1329    // around of the generation count.
   1330    mFileGeneration = aFileGeneration;
   1331    MaybeResetGeneration();
   1332 
   1333    // Successful write resets the retry count.
   1334    mRetryCount = 0;
   1335 
   1336    // Possibly schedule another save operation if more data
   1337    // has come in while processing this one.
   1338    MaybeScheduleSaveData();
   1339 
   1340    return;
   1341  }
   1342 
   1343  // Otherwise, the save failed since the generation is invalid.  We
   1344  // want to retry the save, but only a limited number of times.
   1345  static const uint32_t kMaxRetryCount = 2;
   1346  if (mRetryCount >= kMaxRetryCount) {
   1347    return;
   1348  }
   1349 
   1350  mRetryCount += 1;
   1351  MaybeScheduleSaveData();
   1352 }
   1353 
   1354 void ServiceWorkerRegistrar::MaybeScheduleShutdownCompleted() {
   1355  AssertIsOnBackgroundThread();
   1356 
   1357  if (mSaveDataRunnableDispatched || !mShuttingDown) {
   1358    return;
   1359  }
   1360 
   1361  RefPtr<Runnable> runnable =
   1362      NewRunnableMethod("dom::ServiceWorkerRegistrar::ShutdownCompleted", this,
   1363                        &ServiceWorkerRegistrar::ShutdownCompleted);
   1364  MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(runnable.forget()));
   1365 }
   1366 
   1367 uint32_t ServiceWorkerRegistrar::GetNextGeneration() {
   1368  uint32_t ret = mDataGeneration + 1;
   1369  if (ret == kInvalidGeneration) {
   1370    ret += 1;
   1371  }
   1372  return ret;
   1373 }
   1374 
   1375 void ServiceWorkerRegistrar::MaybeResetGeneration() {
   1376  if (mDataGeneration != mFileGeneration) {
   1377    return;
   1378  }
   1379  mDataGeneration = mFileGeneration = 0;
   1380 }
   1381 
   1382 bool ServiceWorkerRegistrar::IsSupportedVersion(uint32_t aVersion) const {
   1383  uint32_t numVersions = std::size(gSupportedRegistrarVersions);
   1384  for (uint32_t i = 0; i < numVersions; i++) {
   1385    if (aVersion == gSupportedRegistrarVersions[i]) {
   1386      return true;
   1387    }
   1388  }
   1389  return false;
   1390 }
   1391 
   1392 nsresult ServiceWorkerRegistrar::WriteData(
   1393    const nsTArray<ServiceWorkerData>& aData) {
   1394  // We cannot assert about the correct thread because normally this method
   1395  // runs on a IO thread, but in gTests we call it from the main-thread.
   1396 
   1397  nsCOMPtr<nsIFile> file;
   1398 
   1399  {
   1400    MonitorAutoLock lock(mMonitor);
   1401 
   1402    if (!mProfileDir) {
   1403      return NS_ERROR_FAILURE;
   1404    }
   1405 
   1406    nsresult rv = mProfileDir->Clone(getter_AddRefs(file));
   1407    if (NS_WARN_IF(NS_FAILED(rv))) {
   1408      return rv;
   1409    }
   1410  }
   1411 
   1412  nsresult rv = file->Append(nsLiteralString(SERVICEWORKERREGISTRAR_FILE));
   1413  if (NS_WARN_IF(NS_FAILED(rv))) {
   1414    return rv;
   1415  }
   1416 
   1417  nsCOMPtr<nsIOutputStream> stream;
   1418  rv = NS_NewSafeLocalFileOutputStream(getter_AddRefs(stream), file);
   1419  if (NS_WARN_IF(NS_FAILED(rv))) {
   1420    return rv;
   1421  }
   1422 
   1423  nsAutoCString buffer;
   1424  buffer.AppendInt(static_cast<uint32_t>(SERVICEWORKERREGISTRAR_VERSION));
   1425  buffer.Append('\n');
   1426 
   1427  uint32_t count;
   1428  rv = stream->Write(buffer.Data(), buffer.Length(), &count);
   1429  if (NS_WARN_IF(NS_FAILED(rv))) {
   1430    return rv;
   1431  }
   1432 
   1433  if (count != buffer.Length()) {
   1434    return NS_ERROR_UNEXPECTED;
   1435  }
   1436 
   1437  for (const ServiceWorkerData& data : aData) {
   1438    // We have an assertion further up the stack, but as a last
   1439    // resort avoid writing out broken entries here.
   1440    if (!ServiceWorkerRegistrationDataIsValid(data.mRegistration)) {
   1441      continue;
   1442    }
   1443 
   1444    const mozilla::ipc::PrincipalInfo& info = data.mRegistration.principal();
   1445 
   1446    MOZ_ASSERT(info.type() ==
   1447               mozilla::ipc::PrincipalInfo::TContentPrincipalInfo);
   1448 
   1449    const mozilla::ipc::ContentPrincipalInfo& cInfo =
   1450        info.get_ContentPrincipalInfo();
   1451 
   1452    nsAutoCString suffix;
   1453    cInfo.attrs().CreateSuffix(suffix);
   1454 
   1455    buffer.Truncate();
   1456 
   1457    buffer.Append(suffix.get());
   1458    buffer.Append('\n');
   1459 
   1460    buffer.Append(data.mRegistration.scope());
   1461    buffer.Append('\n');
   1462 
   1463    buffer.Append(data.mRegistration.currentWorkerURL());
   1464    buffer.Append('\n');
   1465 
   1466    buffer.Append(data.mRegistration.currentWorkerHandlesFetch()
   1467                      ? SERVICEWORKERREGISTRAR_TRUE
   1468                      : SERVICEWORKERREGISTRAR_FALSE);
   1469    buffer.Append('\n');
   1470 
   1471    buffer.Append(NS_ConvertUTF16toUTF8(data.mRegistration.cacheName()));
   1472    buffer.Append('\n');
   1473 
   1474    buffer.AppendInt(data.mRegistration.updateViaCache(), 16);
   1475    buffer.Append('\n');
   1476    MOZ_DIAGNOSTIC_ASSERT(
   1477        data.mRegistration.updateViaCache() ==
   1478            nsIServiceWorkerRegistrationInfo::UPDATE_VIA_CACHE_IMPORTS ||
   1479        data.mRegistration.updateViaCache() ==
   1480            nsIServiceWorkerRegistrationInfo::UPDATE_VIA_CACHE_ALL ||
   1481        data.mRegistration.updateViaCache() ==
   1482            nsIServiceWorkerRegistrationInfo::UPDATE_VIA_CACHE_NONE);
   1483 
   1484    static_assert(nsIRequest::LOAD_NORMAL == 0,
   1485                  "LOAD_NORMAL matches serialized value.");
   1486    static_assert(nsIRequest::VALIDATE_ALWAYS == (1 << 11),
   1487                  "VALIDATE_ALWAYS matches serialized value");
   1488 
   1489    buffer.AppendInt(data.mRegistration.currentWorkerInstalledTime());
   1490    buffer.Append('\n');
   1491 
   1492    buffer.AppendInt(data.mRegistration.currentWorkerActivatedTime());
   1493    buffer.Append('\n');
   1494 
   1495    buffer.AppendInt(data.mRegistration.lastUpdateTime());
   1496    buffer.Append('\n');
   1497 
   1498    buffer.AppendInt(static_cast<int32_t>(
   1499        data.mRegistration.navigationPreloadState().enabled()));
   1500    buffer.Append('\n');
   1501 
   1502    buffer.Append(data.mRegistration.navigationPreloadState().headerValue());
   1503    buffer.Append('\n');
   1504 
   1505    buffer.AppendInt(static_cast<uint32_t>(data.mExpandos.Length()), 16);
   1506    buffer.Append('\n');
   1507 
   1508    for (const ExpandoData& expando : data.mExpandos) {
   1509      buffer.Append(expando.mKey);
   1510      buffer.Append('\n');
   1511      buffer.Append(expando.mValue);
   1512      buffer.Append('\n');
   1513    }
   1514 
   1515    buffer.AppendInt(static_cast<int32_t>(
   1516        data.mRegistration.numberOfAttemptedActivations()));
   1517    buffer.Append('\n');
   1518 
   1519    buffer.AppendInt(static_cast<int32_t>(data.mRegistration.isBroken()));
   1520    buffer.Append('\n');
   1521 
   1522    buffer.AppendInt(static_cast<int32_t>(data.mRegistration.cacheAPIId()));
   1523    buffer.Append('\n');
   1524 
   1525    buffer.AppendInt(static_cast<uint32_t>(data.mRegistration.type()));
   1526    buffer.Append('\n');
   1527 
   1528    buffer.AppendLiteral(SERVICEWORKERREGISTRAR_TERMINATOR);
   1529    buffer.Append('\n');
   1530 
   1531    rv = stream->Write(buffer.Data(), buffer.Length(), &count);
   1532    if (NS_WARN_IF(NS_FAILED(rv))) {
   1533      return rv;
   1534    }
   1535 
   1536    if (count != buffer.Length()) {
   1537      return NS_ERROR_UNEXPECTED;
   1538    }
   1539  }
   1540 
   1541  nsCOMPtr<nsISafeOutputStream> safeStream = do_QueryInterface(stream);
   1542  MOZ_ASSERT(safeStream);
   1543 
   1544  rv = safeStream->Finish();
   1545  if (NS_WARN_IF(NS_FAILED(rv))) {
   1546    return rv;
   1547  }
   1548 
   1549  return NS_OK;
   1550 }
   1551 
   1552 void ServiceWorkerRegistrar::ProfileStarted() {
   1553  MOZ_ASSERT(NS_IsMainThread());
   1554 
   1555  MonitorAutoLock lock(mMonitor);
   1556  MOZ_DIAGNOSTIC_ASSERT(!mProfileDir);
   1557 
   1558  nsresult rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR,
   1559                                       getter_AddRefs(mProfileDir));
   1560  if (NS_WARN_IF(NS_FAILED(rv))) {
   1561    return;
   1562  }
   1563 
   1564  nsAutoString blockerName;
   1565  MOZ_ALWAYS_SUCCEEDS(GetName(blockerName));
   1566 
   1567  rv = GetShutdownPhase()->AddBlocker(
   1568      this, NS_LITERAL_STRING_FROM_CSTRING(__FILE__), __LINE__, blockerName);
   1569  if (NS_WARN_IF(NS_FAILED(rv))) {
   1570    return;
   1571  }
   1572 
   1573  nsCOMPtr<nsIEventTarget> target =
   1574      do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID);
   1575  MOZ_ASSERT(target, "Must have stream transport service");
   1576 
   1577  nsCOMPtr<nsIRunnable> runnable =
   1578      NewRunnableMethod("dom::ServiceWorkerRegistrar::LoadData", this,
   1579                        &ServiceWorkerRegistrar::LoadData);
   1580  rv = target->Dispatch(runnable.forget(), NS_DISPATCH_NORMAL);
   1581  if (NS_FAILED(rv)) {
   1582    NS_WARNING("Failed to dispatch the LoadDataRunnable.");
   1583  }
   1584 }
   1585 
   1586 void ServiceWorkerRegistrar::ProfileStopped() {
   1587  MOZ_ASSERT(NS_IsMainThread());
   1588 
   1589  MonitorAutoLock lock(mMonitor);
   1590 
   1591  if (!mProfileDir) {
   1592    nsresult rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR,
   1593                                         getter_AddRefs(mProfileDir));
   1594    if (NS_WARN_IF(NS_FAILED(rv))) {
   1595      // If we do not have a profile directory, we are somehow screwed.
   1596      MOZ_DIAGNOSTIC_ASSERT(
   1597          false,
   1598          "NS_GetSpecialDirectory for NS_APP_USER_PROFILE_50_DIR failed!");
   1599    }
   1600  }
   1601 
   1602  // Mutations to the ServiceWorkerRegistrar happen on the PBackground thread,
   1603  // issued by the ServiceWorkerManagerService, so the appropriate place to
   1604  // trigger shutdown is on that thread.
   1605  //
   1606  // However, it's quite possible that the PBackground thread was not brought
   1607  // into existence for xpcshell tests.  We don't cause it to be created
   1608  // ourselves for any reason, for example.
   1609  //
   1610  // In this scenario, we know that:
   1611  // - We will receive exactly one call to ourself from BlockShutdown() and
   1612  //   BlockShutdown() will be called (at most) once.
   1613  // - The only way our Shutdown() method gets called is via
   1614  //   BackgroundParentImpl::RecvShutdownServiceWorkerRegistrar() being
   1615  //   invoked, which only happens if we get to that send below here that we
   1616  //   can't get to.
   1617  // - All Shutdown() does is set mShuttingDown=true (essential for
   1618  //   invariants) and invoke MaybeScheduleShutdownCompleted().
   1619  // - Since there is no PBackground thread, mSaveDataRunnableDispatched must
   1620  //   be false because only MaybeScheduleSaveData() set it and it only runs
   1621  //   on the background thread, so it cannot have run.  And so we would
   1622  //   expect MaybeScheduleShutdownCompleted() to schedule an invocation of
   1623  //   ShutdownCompleted on the main thread.
   1624  PBackgroundChild* child = BackgroundChild::GetForCurrentThread();
   1625  if (mProfileDir && child) {
   1626    if (child->SendShutdownServiceWorkerRegistrar()) {
   1627      // Normal shutdown sequence has been initiated, go home.
   1628      return;
   1629    }
   1630    // If we get here, the PBackground thread has probably gone nuts and we
   1631    // want to know it.
   1632    MOZ_DIAGNOSTIC_ASSERT(
   1633        false, "Unable to send the ShutdownServiceWorkerRegistrar message.");
   1634  }
   1635 
   1636  // On any error it's appropriate to set mShuttingDown=true (as Shutdown
   1637  // would do) and directly invoke ShutdownCompleted() (as Shutdown would
   1638  // indirectly do via MaybeScheduleShutdownCompleted) in order to unblock
   1639  // shutdown.
   1640  mShuttingDown = true;
   1641  ShutdownCompleted();
   1642 }
   1643 
   1644 // Async shutdown blocker methods
   1645 
   1646 NS_IMETHODIMP
   1647 ServiceWorkerRegistrar::BlockShutdown(nsIAsyncShutdownClient* aClient) {
   1648  ProfileStopped();
   1649  return NS_OK;
   1650 }
   1651 
   1652 NS_IMETHODIMP
   1653 ServiceWorkerRegistrar::GetName(nsAString& aName) {
   1654  aName = u"ServiceWorkerRegistrar: Flushing data"_ns;
   1655  return NS_OK;
   1656 }
   1657 
   1658 NS_IMETHODIMP
   1659 ServiceWorkerRegistrar::GetState(nsIPropertyBag** aBagOut) {
   1660  nsCOMPtr<nsIWritablePropertyBag2> propertyBag =
   1661      do_CreateInstance("@mozilla.org/hash-property-bag;1");
   1662 
   1663  MOZ_TRY(propertyBag->SetPropertyAsBool(u"shuttingDown"_ns, mShuttingDown));
   1664 
   1665  MOZ_TRY(propertyBag->SetPropertyAsBool(u"saveDataRunnableDispatched"_ns,
   1666                                         mSaveDataRunnableDispatched));
   1667 
   1668  propertyBag.forget(aBagOut);
   1669 
   1670  return NS_OK;
   1671 }
   1672 
   1673 #define RELEASE_ASSERT_SUCCEEDED(rv, name)                                    \
   1674  do {                                                                        \
   1675    if (NS_FAILED(rv)) {                                                      \
   1676      if ((rv) == NS_ERROR_XPC_JAVASCRIPT_ERROR_WITH_DETAILS) {               \
   1677        if (auto* context = CycleCollectedJSContext::Get()) {                 \
   1678          if (RefPtr<Exception> exn = context->GetPendingException()) {       \
   1679            MOZ_CRASH_UNSAFE_PRINTF("Failed to get " name ": %s",             \
   1680                                    exn->GetMessageMoz().get());              \
   1681          }                                                                   \
   1682        }                                                                     \
   1683      }                                                                       \
   1684                                                                              \
   1685      nsAutoCString errorName;                                                \
   1686      GetErrorName(rv, errorName);                                            \
   1687      MOZ_CRASH_UNSAFE_PRINTF("Failed to get " name ": %s", errorName.get()); \
   1688    }                                                                         \
   1689  } while (0)
   1690 
   1691 nsCOMPtr<nsIAsyncShutdownClient> ServiceWorkerRegistrar::GetShutdownPhase()
   1692    const {
   1693  nsresult rv;
   1694  nsCOMPtr<nsIAsyncShutdownService> svc =
   1695      do_GetService("@mozilla.org/async-shutdown-service;1", &rv);
   1696  // If this fails, something is very wrong on the JS side (or we're out of
   1697  // memory), and there's no point in continuing startup. Include as much
   1698  // information as possible in the crash report.
   1699  RELEASE_ASSERT_SUCCEEDED(rv, "async shutdown service");
   1700 
   1701  nsCOMPtr<nsIAsyncShutdownClient> client;
   1702  rv = svc->GetProfileBeforeChange(getter_AddRefs(client));
   1703  RELEASE_ASSERT_SUCCEEDED(rv, "profileBeforeChange shutdown blocker");
   1704  return client;
   1705 }
   1706 
   1707 #undef RELEASE_ASSERT_SUCCEEDED
   1708 
   1709 void ServiceWorkerRegistrar::Shutdown() {
   1710  AssertIsOnBackgroundThread();
   1711  MOZ_ASSERT(!mShuttingDown);
   1712 
   1713  mShuttingDown = true;
   1714  MaybeScheduleShutdownCompleted();
   1715 }
   1716 
   1717 NS_IMETHODIMP
   1718 ServiceWorkerRegistrar::Observe(nsISupports* aSubject, const char* aTopic,
   1719                                const char16_t* aData) {
   1720  MOZ_ASSERT(NS_IsMainThread());
   1721 
   1722  if (!strcmp(aTopic, "profile-after-change")) {
   1723    nsCOMPtr<nsIObserverService> observerService =
   1724        services::GetObserverService();
   1725    observerService->RemoveObserver(this, "profile-after-change");
   1726 
   1727    // The profile is fully loaded, now we can proceed with the loading of data
   1728    // from disk.
   1729    ProfileStarted();
   1730 
   1731    return NS_OK;
   1732  }
   1733 
   1734  MOZ_ASSERT(false, "ServiceWorkerRegistrar got unexpected topic!");
   1735  return NS_ERROR_UNEXPECTED;
   1736 }
   1737 
   1738 void ServiceWorkerRegistrar::LoadExpandoCallbacks(
   1739    const CopyableTArray<ServiceWorkerData>& aData) {
   1740  if (NS_IsMainThread()) {
   1741    for (const ServiceWorkerData& data : aData) {
   1742      for (const ExpandoData& expando : data.mExpandos) {
   1743        MOZ_ASSERT(expando.mHandler);
   1744        expando.mHandler->mServiceWorkerLoaded(data.mRegistration,
   1745                                               expando.mValue);
   1746      }
   1747    }
   1748 
   1749    return;
   1750  }
   1751 
   1752  NS_DispatchToMainThread(NS_NewRunnableFunction(
   1753      __func__,
   1754      [self = RefPtr{this}, aData] { self->LoadExpandoCallbacks(aData); }));
   1755 }
   1756 
   1757 void ServiceWorkerRegistrar::UpdateExpandoCallbacks(
   1758    const ServiceWorkerData& aData) {
   1759  if (NS_IsMainThread()) {
   1760    for (const ExpandoData& expando : aData.mExpandos) {
   1761      MOZ_ASSERT(expando.mHandler);
   1762      expando.mHandler->mServiceWorkerUpdated(aData.mRegistration);
   1763    }
   1764 
   1765    return;
   1766  }
   1767 
   1768  NS_DispatchToMainThread(NS_NewRunnableFunction(
   1769      __func__,
   1770      [self = RefPtr{this}, aData] { self->UpdateExpandoCallbacks(aData); }));
   1771 }
   1772 
   1773 void ServiceWorkerRegistrar::UnregisterExpandoCallbacks(
   1774    const CopyableTArray<ServiceWorkerData>& aData) {
   1775  if (NS_IsMainThread()) {
   1776    for (const ServiceWorkerData& data : aData) {
   1777      for (const ExpandoData& expando : data.mExpandos) {
   1778        MOZ_ASSERT(expando.mHandler);
   1779        expando.mHandler->mServiceWorkerUnregistered(data.mRegistration);
   1780      }
   1781    }
   1782 
   1783    return;
   1784  }
   1785 
   1786  NS_DispatchToMainThread(
   1787      NS_NewRunnableFunction(__func__, [self = RefPtr{this}, aData] {
   1788        self->UnregisterExpandoCallbacks(aData);
   1789      }));
   1790 }
   1791 
   1792 }  // namespace mozilla::dom