tor-browser

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

ServiceWorkerRegistration.cpp (28470B)


      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 "ServiceWorkerRegistration.h"
      8 
      9 #include "ServiceWorkerRegistrationChild.h"
     10 #include "mozilla/ScopeExit.h"
     11 #include "mozilla/dom/CookieStoreManager.h"
     12 #include "mozilla/dom/DOMMozPromiseRequestHolder.h"
     13 #include "mozilla/dom/NavigationPreloadManager.h"
     14 #include "mozilla/dom/NavigationPreloadManagerBinding.h"
     15 #include "mozilla/dom/Notification.h"
     16 #include "mozilla/dom/Promise.h"
     17 #include "mozilla/dom/PushManager.h"
     18 #include "mozilla/dom/ServiceWorker.h"
     19 #include "mozilla/dom/ServiceWorkerUtils.h"
     20 #include "mozilla/dom/WorkerPrivate.h"
     21 #include "mozilla/ipc/BackgroundChild.h"
     22 #include "mozilla/ipc/PBackgroundChild.h"
     23 #include "mozilla/ipc/PBackgroundSharedTypes.h"
     24 #include "nsCycleCollectionParticipant.h"
     25 #include "nsPIDOMWindow.h"
     26 
     27 using mozilla::ipc::ResponseRejectReason;
     28 
     29 namespace mozilla::dom {
     30 
     31 NS_IMPL_CYCLE_COLLECTION_INHERITED(ServiceWorkerRegistration,
     32                                   DOMEventTargetHelper, mInstallingWorker,
     33                                   mWaitingWorker, mActiveWorker,
     34                                   mNavigationPreloadManager, mPushManager,
     35                                   mCookieStoreManager);
     36 
     37 NS_IMPL_ADDREF_INHERITED(ServiceWorkerRegistration, DOMEventTargetHelper)
     38 NS_IMPL_RELEASE_INHERITED(ServiceWorkerRegistration, DOMEventTargetHelper)
     39 
     40 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ServiceWorkerRegistration)
     41  NS_INTERFACE_MAP_ENTRY_CONCRETE(ServiceWorkerRegistration)
     42 NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
     43 
     44 namespace {
     45 const uint64_t kInvalidUpdateFoundId = 0;
     46 }  // anonymous namespace
     47 
     48 ServiceWorkerRegistration::ServiceWorkerRegistration(
     49    nsIGlobalObject* aGlobal,
     50    const ServiceWorkerRegistrationDescriptor& aDescriptor)
     51    : DOMEventTargetHelper(aGlobal),
     52      mDescriptor(aDescriptor),
     53      mShutdown(false),
     54      mScheduledUpdateFoundId(kInvalidUpdateFoundId),
     55      mDispatchedUpdateFoundId(kInvalidUpdateFoundId) {
     56  ::mozilla::ipc::PBackgroundChild* parentActor =
     57      ::mozilla::ipc::BackgroundChild::GetOrCreateForCurrentThread();
     58  if (NS_WARN_IF(!parentActor)) {
     59    Shutdown();
     60    return;
     61  }
     62 
     63  auto actor = ServiceWorkerRegistrationChild::Create();
     64  if (NS_WARN_IF(!actor)) {
     65    Shutdown();
     66    return;
     67  }
     68 
     69  Maybe<ClientInfo> clientInfo = aGlobal->GetClientInfo();
     70  if (clientInfo.isNothing()) {
     71    Shutdown();
     72    return;
     73  }
     74 
     75  PServiceWorkerRegistrationChild* sentActor =
     76      parentActor->SendPServiceWorkerRegistrationConstructor(
     77          actor, aDescriptor.ToIPC(), clientInfo.ref().ToIPC());
     78  if (NS_WARN_IF(!sentActor)) {
     79    Shutdown();
     80    return;
     81  }
     82  MOZ_DIAGNOSTIC_ASSERT(sentActor == actor);
     83 
     84  mActor = std::move(actor);
     85  mActor->SetOwner(this);
     86 
     87  KeepAliveIfHasListenersFor(nsGkAtoms::onupdatefound);
     88 }
     89 
     90 ServiceWorkerRegistration::~ServiceWorkerRegistration() { Shutdown(); }
     91 
     92 JSObject* ServiceWorkerRegistration::WrapObject(
     93    JSContext* aCx, JS::Handle<JSObject*> aGivenProto) {
     94  return ServiceWorkerRegistration_Binding::Wrap(aCx, this, aGivenProto);
     95 }
     96 
     97 /* static */
     98 already_AddRefed<ServiceWorkerRegistration>
     99 ServiceWorkerRegistration::CreateForMainThread(
    100    nsPIDOMWindowInner* aWindow,
    101    const ServiceWorkerRegistrationDescriptor& aDescriptor) {
    102  MOZ_ASSERT(aWindow);
    103  MOZ_ASSERT(NS_IsMainThread());
    104 
    105  RefPtr<ServiceWorkerRegistration> registration =
    106      new ServiceWorkerRegistration(aWindow->AsGlobal(), aDescriptor);
    107  // This is not called from within the constructor, as it may call content code
    108  // which can cause the deletion of the registration, so we need to keep a
    109  // strong reference while calling it.
    110  registration->UpdateState(aDescriptor);
    111 
    112  return registration.forget();
    113 }
    114 
    115 /* static */
    116 already_AddRefed<ServiceWorkerRegistration>
    117 ServiceWorkerRegistration::CreateForWorker(
    118    WorkerPrivate* aWorkerPrivate, nsIGlobalObject* aGlobal,
    119    const ServiceWorkerRegistrationDescriptor& aDescriptor) {
    120  MOZ_DIAGNOSTIC_ASSERT(aWorkerPrivate);
    121  MOZ_DIAGNOSTIC_ASSERT(aGlobal);
    122  aWorkerPrivate->AssertIsOnWorkerThread();
    123 
    124  RefPtr<ServiceWorkerRegistration> registration =
    125      new ServiceWorkerRegistration(aGlobal, aDescriptor);
    126  // This is not called from within the constructor, as it may call content code
    127  // which can cause the deletion of the registration, so we need to keep a
    128  // strong reference while calling it.
    129  registration->UpdateState(aDescriptor);
    130 
    131  return registration.forget();
    132 }
    133 
    134 void ServiceWorkerRegistration::DisconnectFromOwner() {
    135  DOMEventTargetHelper::DisconnectFromOwner();
    136  Shutdown();
    137 }
    138 
    139 void ServiceWorkerRegistration::RegistrationCleared() {
    140  // Its possible that the registration will fail to install and be
    141  // immediately removed.  In that case we may never receive the
    142  // UpdateState() call if the actor was too slow to connect, etc.
    143  // Ensure that we force all our known actors to redundant so that
    144  // the appropriate statechange events are fired.  If we got the
    145  // UpdateState() already then this will be a no-op.
    146  UpdateStateInternal(Maybe<ServiceWorkerDescriptor>(),
    147                      Maybe<ServiceWorkerDescriptor>(),
    148                      Maybe<ServiceWorkerDescriptor>());
    149 
    150  // Our underlying registration was removed from SWM, so we
    151  // will never get an updatefound event again.  We can let
    152  // the object GC if content is not holding it alive.
    153  IgnoreKeepAliveIfHasListenersFor(nsGkAtoms::onupdatefound);
    154 }
    155 
    156 already_AddRefed<ServiceWorker> ServiceWorkerRegistration::GetInstalling()
    157    const {
    158  RefPtr<ServiceWorker> ref = mInstallingWorker;
    159  return ref.forget();
    160 }
    161 
    162 already_AddRefed<ServiceWorker> ServiceWorkerRegistration::GetWaiting() const {
    163  RefPtr<ServiceWorker> ref = mWaitingWorker;
    164  return ref.forget();
    165 }
    166 
    167 already_AddRefed<ServiceWorker> ServiceWorkerRegistration::GetActive() const {
    168  RefPtr<ServiceWorker> ref = mActiveWorker;
    169  return ref.forget();
    170 }
    171 
    172 already_AddRefed<NavigationPreloadManager>
    173 ServiceWorkerRegistration::NavigationPreload() {
    174  RefPtr<ServiceWorkerRegistration> reg = this;
    175  if (!mNavigationPreloadManager) {
    176    mNavigationPreloadManager = MakeRefPtr<NavigationPreloadManager>(reg);
    177  }
    178  RefPtr<NavigationPreloadManager> ref = mNavigationPreloadManager;
    179  return ref.forget();
    180 }
    181 
    182 CookieStoreManager* ServiceWorkerRegistration::GetCookies(ErrorResult& aRv) {
    183  if (!mCookieStoreManager) {
    184    nsIGlobalObject* globalObject = GetParentObject();
    185    if (!globalObject) {
    186      aRv.ThrowInvalidStateError("No global");
    187      return nullptr;
    188    }
    189 
    190    mCookieStoreManager =
    191        new CookieStoreManager(globalObject, mDescriptor.Scope());
    192  }
    193 
    194  return mCookieStoreManager;
    195 }
    196 
    197 void ServiceWorkerRegistration::UpdateState(
    198    const ServiceWorkerRegistrationDescriptor& aDescriptor) {
    199  MOZ_DIAGNOSTIC_ASSERT(MatchesDescriptor(aDescriptor));
    200 
    201  mDescriptor = aDescriptor;
    202 
    203  UpdateStateInternal(aDescriptor.GetInstalling(), aDescriptor.GetWaiting(),
    204                      aDescriptor.GetActive());
    205 
    206  nsTArray<UniquePtr<VersionCallback>> callbackList =
    207      std::move(mVersionCallbackList);
    208  for (auto& cb : callbackList) {
    209    if (cb->mVersion > mDescriptor.Version()) {
    210      mVersionCallbackList.AppendElement(std::move(cb));
    211      continue;
    212    }
    213 
    214    cb->mFunc(cb->mVersion == mDescriptor.Version());
    215  }
    216 }
    217 
    218 bool ServiceWorkerRegistration::MatchesDescriptor(
    219    const ServiceWorkerRegistrationDescriptor& aDescriptor) const {
    220  return aDescriptor.Id() == mDescriptor.Id() &&
    221         aDescriptor.PrincipalInfo() == mDescriptor.PrincipalInfo() &&
    222         aDescriptor.Scope() == mDescriptor.Scope();
    223 }
    224 
    225 void ServiceWorkerRegistration::GetScope(nsAString& aScope) const {
    226  CopyUTF8toUTF16(mDescriptor.Scope(), aScope);
    227 }
    228 
    229 ServiceWorkerUpdateViaCache ServiceWorkerRegistration::GetUpdateViaCache(
    230    ErrorResult& aRv) const {
    231  return mDescriptor.UpdateViaCache();
    232 }
    233 
    234 already_AddRefed<Promise> ServiceWorkerRegistration::Update(ErrorResult& aRv) {
    235  AUTO_PROFILER_MARKER_UNTYPED("ServiceWorkerRegistration::Update", DOM, {});
    236 
    237  nsIGlobalObject* global = GetParentObject();
    238  if (!global) {
    239    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
    240    return nullptr;
    241  }
    242 
    243  RefPtr<Promise> outer = Promise::Create(global, aRv);
    244  if (NS_WARN_IF(aRv.Failed())) {
    245    return nullptr;
    246  }
    247 
    248  // `ServiceWorker` objects are not exposed on worker threads yet, so calling
    249  // `ServiceWorkerRegistration::Get{Installing,Waiting,Active}` won't work.
    250  const Maybe<ServiceWorkerDescriptor> newestWorkerDescriptor =
    251      mDescriptor.Newest();
    252 
    253  // "If newestWorker is null, return a promise rejected with an
    254  // "InvalidStateError" DOMException and abort these steps."
    255  if (newestWorkerDescriptor.isNothing()) {
    256    outer->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR);
    257    return outer.forget();
    258  }
    259 
    260  // "If the context object’s relevant settings object’s global object
    261  // globalObject is a ServiceWorkerGlobalScope object, and globalObject’s
    262  // associated service worker's state is "installing", return a promise
    263  // rejected with an "InvalidStateError" DOMException and abort these steps."
    264  if (!NS_IsMainThread()) {
    265    WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
    266    MOZ_ASSERT(workerPrivate);
    267 
    268    if (workerPrivate->IsServiceWorker() &&
    269        (workerPrivate->GetServiceWorkerDescriptor().State() ==
    270         ServiceWorkerState::Installing)) {
    271      outer->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR);
    272      return outer.forget();
    273    }
    274  }
    275 
    276  // Keep the SWR and thereby its actor live throughout the IPC call (unless
    277  // the global is torn down and DisconnectFromOwner is called which will cause
    278  // us to call Shutdown() which will shutdown the actor and reject the IPC
    279  // calls).
    280  RefPtr<ServiceWorkerRegistration> self = this;
    281 
    282  if (!mActor) {
    283    outer->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR);
    284    return outer.forget();
    285  }
    286 
    287  mActor->SendUpdate(
    288      newestWorkerDescriptor.ref().ScriptURL(),
    289      [outer, self = std::move(self)](
    290          const IPCServiceWorkerRegistrationDescriptorOrCopyableErrorResult&
    291              aResult) {
    292        AUTO_PROFILER_MARKER_UNTYPED(
    293            "ServiceWorkerRegistration::Update (inner)", DOM, {});
    294 
    295        if (aResult.type() ==
    296            IPCServiceWorkerRegistrationDescriptorOrCopyableErrorResult::
    297                TCopyableErrorResult) {
    298          // application layer error
    299          const auto& rv = aResult.get_CopyableErrorResult();
    300          MOZ_DIAGNOSTIC_ASSERT(rv.Failed());
    301          outer->MaybeReject(CopyableErrorResult(rv));
    302          return;
    303        }
    304        // success
    305        const auto& ipcDesc =
    306            aResult.get_IPCServiceWorkerRegistrationDescriptor();
    307        nsIGlobalObject* global = self->GetParentObject();
    308        // Given that we destroy the actor on DisconnectFromOwner, it should be
    309        // impossible for global to be null here since we should only process
    310        // the reject case below in that case.  (And in the event there is an
    311        // in-flight IPC message, it will be discarded.)  This assertion will
    312        // help validate this without inconveniencing users.
    313        MOZ_ASSERT_DEBUG_OR_FUZZING(global);
    314        if (!global) {
    315          outer->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR);
    316          return;
    317        }
    318        // TODO: Given that we are keeping this registration alive through the
    319        // call, it's not clear how `ref` could be anything but this instance.
    320        // Consider just returning `self` after doing the code archaeology to
    321        // ensure there isn't some still-valid reason.
    322        RefPtr<ServiceWorkerRegistration> ref =
    323            global->GetOrCreateServiceWorkerRegistration(
    324                ServiceWorkerRegistrationDescriptor(ipcDesc));
    325        if (!ref) {
    326          outer->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR);
    327          return;
    328        }
    329        outer->MaybeResolve(ref);
    330      },
    331      [outer](ResponseRejectReason&& aReason) {
    332        // IPC layer error
    333        outer->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR);
    334      });
    335 
    336  return outer.forget();
    337 }
    338 
    339 already_AddRefed<Promise> ServiceWorkerRegistration::Unregister(
    340    ErrorResult& aRv) {
    341  nsIGlobalObject* global = GetParentObject();
    342  if (NS_WARN_IF(!global)) {
    343    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
    344    return nullptr;
    345  }
    346 
    347  RefPtr<Promise> outer = Promise::Create(global, aRv);
    348  if (NS_WARN_IF(aRv.Failed())) {
    349    return nullptr;
    350  }
    351 
    352  if (!mActor) {
    353    outer->MaybeResolve(false);
    354    return outer.forget();
    355  }
    356 
    357  // Keep the SWR and thereby its actor live throughout the IPC call (unless
    358  // the global is torn down and DisconnectFromOwner is called which will cause
    359  // us to call Shutdown() which will shutdown the actor and reject the IPC
    360  // calls).
    361  RefPtr<ServiceWorkerRegistration> self = this;
    362  mActor->SendUnregister(
    363      [self = std::move(self),
    364       outer](std::tuple<bool, CopyableErrorResult>&& aResult) {
    365        if (std::get<1>(aResult).Failed()) {
    366          // application layer error
    367          // register() should be resilient and resolve false instead of
    368          // rejecting in most cases.
    369          std::get<1>(aResult).SuppressException();
    370          outer->MaybeResolve(false);
    371          return;
    372        }
    373        // success
    374        outer->MaybeResolve(std::get<0>(aResult));
    375      },
    376      [outer](ResponseRejectReason&& aReason) {
    377        // IPC layer error
    378        outer->MaybeResolve(false);
    379      });
    380 
    381  return outer.forget();
    382 }
    383 
    384 already_AddRefed<PushManager> ServiceWorkerRegistration::GetPushManager(
    385    JSContext* aCx, ErrorResult& aRv) {
    386  if (!mPushManager) {
    387    nsCOMPtr<nsIGlobalObject> globalObject = GetParentObject();
    388 
    389    if (!globalObject) {
    390      aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
    391      return nullptr;
    392    }
    393 
    394    GlobalObject global(aCx, globalObject->GetGlobalJSObject());
    395    mPushManager = PushManager::Constructor(
    396        global, NS_ConvertUTF8toUTF16(mDescriptor.Scope()), aRv);
    397    if (aRv.Failed()) {
    398      return nullptr;
    399    }
    400  }
    401 
    402  RefPtr<PushManager> ret = mPushManager;
    403  return ret.forget();
    404 }
    405 
    406 // https://notifications.spec.whatwg.org/#dom-serviceworkerregistration-shownotification
    407 already_AddRefed<Promise> ServiceWorkerRegistration::ShowNotification(
    408    JSContext* aCx, const nsAString& aTitle,
    409    const NotificationOptions& aOptions, ErrorResult& aRv) {
    410  // Step 1: Let global be this’s relevant global object.
    411  nsIGlobalObject* global = GetParentObject();
    412  if (!global) {
    413    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
    414    return nullptr;
    415  }
    416 
    417  // Step 3: If this’s active worker is null, then reject promise with a
    418  // TypeError and return promise.
    419  //
    420  // Until we ship ServiceWorker objects on worker threads the active
    421  // worker will always be nullptr.  So limit this check to main
    422  // thread for now.
    423  if (mDescriptor.GetActive().isNothing() && NS_IsMainThread()) {
    424    aRv.ThrowTypeError<MSG_NO_ACTIVE_WORKER>(mDescriptor.Scope());
    425    return nullptr;
    426  }
    427 
    428  NS_ConvertUTF8toUTF16 scope(mDescriptor.Scope());
    429 
    430  // Step 2, 5, 6
    431  RefPtr<Promise> p = Notification::ShowPersistentNotification(
    432      aCx, global, scope, aTitle, aOptions, mDescriptor, aRv);
    433  if (NS_WARN_IF(aRv.Failed())) {
    434    return nullptr;
    435  }
    436 
    437  // Step 7: Return promise.
    438  return p.forget();
    439 }
    440 
    441 // https://notifications.spec.whatwg.org/#dom-serviceworkerregistration-getnotifications
    442 already_AddRefed<Promise> ServiceWorkerRegistration::GetNotifications(
    443    const GetNotificationOptions& aOptions, ErrorResult& aRv) {
    444  // Step 1: Let global be this’s relevant global object.
    445  // Step 2: Let realm be this’s relevant Realm.
    446  nsCOMPtr<nsIGlobalObject> global = GetParentObject();
    447  if (!global) {
    448    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
    449    return nullptr;
    450  }
    451 
    452  // Step 3: Let origin be this’s relevant settings object’s origin.
    453  // (Done in ServiceWorkerRegistrationProxy::GetNotifications)
    454 
    455  // Step 4: Let promise be a new promise in realm.
    456  RefPtr<Promise> promise = Promise::CreateInfallible(global);
    457 
    458  // Step 5: Run these steps in parallel:
    459  // Step 5.1: Let tag be filter["tag"].
    460  // Step 5.2: Let notifications be a list of all notifications in the list of
    461  // notifications whose origin is same origin with origin, whose service worker
    462  // registration is this, and whose tag, if tag is not the empty string, is
    463  // tag.
    464 
    465  if (!mActor) {
    466    // While it's not clear from the current spec, it's fair to say that
    467    // unregistered registrations cannot have a match in the step 5.2. See also
    468    // bug 1881812.
    469    // One could also say we should throw here, but no browsers throw.
    470    promise->MaybeResolve(nsTArray<RefPtr<Notification>>());
    471    return promise.forget();
    472  }
    473 
    474  // Keep the SWR and thereby its actor live throughout the IPC call (unless
    475  // the global is torn down and DisconnectFromOwner is called which will cause
    476  // us to call Shutdown() which will shutdown the actor and reject the IPC
    477  // calls).
    478  RefPtr<ServiceWorkerRegistration> self = this;
    479 
    480  // Step 5.3: Queue a global task on the DOM manipulation task source
    481  // given global to run these steps:
    482  mActor->SendGetNotifications(aOptions.mTag)
    483      ->Then(GetCurrentSerialEventTarget(), __func__,
    484             [self = std::move(self), promise,
    485              scope = NS_ConvertUTF8toUTF16(mDescriptor.Scope())](
    486                 const PServiceWorkerRegistrationChild::
    487                     GetNotificationsPromise::ResolveOrRejectValue&& aValue) {
    488               if (aValue.IsReject()) {
    489                 // An unregistered registration
    490                 promise->MaybeResolve(nsTArray<RefPtr<Notification>>());
    491                 return;
    492               }
    493 
    494               if (aValue.ResolveValue().type() ==
    495                   IPCNotificationsOrError::Tnsresult) {
    496                 // An active registration but had some internal error
    497                 promise->MaybeRejectWithInvalidStateError(
    498                     "Could not retrieve notifications"_ns);
    499                 return;
    500               }
    501 
    502               const nsTArray<IPCNotification>& notifications =
    503                   aValue.ResolveValue().get_ArrayOfIPCNotification();
    504 
    505               // Step 5.3.1: Let objects be a list.
    506               nsTArray<RefPtr<Notification>> objects(notifications.Length());
    507 
    508               // Step 5.3.2: For each notification in notifications, in
    509               // creation order, create a new Notification object with realm
    510               // representing notification, and append it to objects.
    511               for (const IPCNotification& ipcNotification : notifications) {
    512                 auto result = Notification::ConstructFromIPC(
    513                     promise->GetParentObject(), ipcNotification, scope);
    514                 if (result.isErr()) {
    515                   continue;
    516                 }
    517                 RefPtr<Notification> n = result.unwrap();
    518                 objects.AppendElement(n.forget());
    519               }
    520 
    521               // Step 5.3.3: Resolve promise with objects.
    522               promise->MaybeResolve(std::move(objects));
    523             });
    524 
    525  // Step 6: Return promise.
    526  return promise.forget();
    527 }
    528 
    529 void ServiceWorkerRegistration::SetNavigationPreloadEnabled(
    530    bool aEnabled, ServiceWorkerBoolCallback&& aSuccessCB,
    531    ServiceWorkerFailureCallback&& aFailureCB) {
    532  if (!mActor) {
    533    aFailureCB(CopyableErrorResult(NS_ERROR_DOM_INVALID_STATE_ERR));
    534    return;
    535  }
    536 
    537  // Keep the SWR and thereby its actor live throughout the IPC call (unless
    538  // the global is torn down and DisconnectFromOwner is called which will cause
    539  // us to call Shutdown() which will shutdown the actor and reject the IPC
    540  // calls).
    541  RefPtr<ServiceWorkerRegistration> self = this;
    542 
    543  mActor->SendSetNavigationPreloadEnabled(
    544      aEnabled,
    545      [self = std::move(self), successCB = std::move(aSuccessCB),
    546       aFailureCB](bool aResult) {
    547        if (!aResult) {
    548          aFailureCB(CopyableErrorResult(NS_ERROR_DOM_INVALID_STATE_ERR));
    549          return;
    550        }
    551        successCB(aResult);
    552      },
    553      [aFailureCB](ResponseRejectReason&& aReason) {
    554        aFailureCB(CopyableErrorResult(NS_ERROR_DOM_INVALID_STATE_ERR));
    555      });
    556 }
    557 
    558 void ServiceWorkerRegistration::SetNavigationPreloadHeader(
    559    const nsCString& aHeader, ServiceWorkerBoolCallback&& aSuccessCB,
    560    ServiceWorkerFailureCallback&& aFailureCB) {
    561  if (!mActor) {
    562    aFailureCB(CopyableErrorResult(NS_ERROR_DOM_INVALID_STATE_ERR));
    563    return;
    564  }
    565 
    566  // Keep the SWR and thereby its actor live throughout the IPC call (unless
    567  // the global is torn down and DisconnectFromOwner is called which will cause
    568  // us to call Shutdown() which will shutdown the actor and reject the IPC
    569  // calls).
    570  RefPtr<ServiceWorkerRegistration> self = this;
    571 
    572  mActor->SendSetNavigationPreloadHeader(
    573      aHeader,
    574      [self = std::move(self), successCB = std::move(aSuccessCB),
    575       aFailureCB](bool aResult) {
    576        if (!aResult) {
    577          aFailureCB(CopyableErrorResult(NS_ERROR_DOM_INVALID_STATE_ERR));
    578          return;
    579        }
    580        successCB(aResult);
    581      },
    582      [aFailureCB](ResponseRejectReason&& aReason) {
    583        aFailureCB(CopyableErrorResult(NS_ERROR_DOM_INVALID_STATE_ERR));
    584      });
    585 }
    586 
    587 void ServiceWorkerRegistration::GetNavigationPreloadState(
    588    NavigationPreloadGetStateCallback&& aSuccessCB,
    589    ServiceWorkerFailureCallback&& aFailureCB) {
    590  if (!mActor) {
    591    aFailureCB(CopyableErrorResult(NS_ERROR_DOM_INVALID_STATE_ERR));
    592    return;
    593  }
    594 
    595  // Keep the SWR and thereby its actor live throughout the IPC call (unless
    596  // the global is torn down and DisconnectFromOwner is called which will cause
    597  // us to call Shutdown() which will shutdown the actor and reject the IPC
    598  // calls).
    599  RefPtr<ServiceWorkerRegistration> self = this;
    600 
    601  mActor->SendGetNavigationPreloadState(
    602      [self = std::move(self), successCB = std::move(aSuccessCB),
    603       aFailureCB](Maybe<IPCNavigationPreloadState>&& aState) {
    604        if (NS_WARN_IF(!aState)) {
    605          aFailureCB(CopyableErrorResult(NS_ERROR_DOM_INVALID_STATE_ERR));
    606          return;
    607        }
    608 
    609        NavigationPreloadState state;
    610        state.mEnabled = aState.ref().enabled();
    611        state.mHeaderValue.Construct(std::move(aState.ref().headerValue()));
    612        successCB(std::move(state));
    613      },
    614      [aFailureCB](ResponseRejectReason&& aReason) {
    615        aFailureCB(CopyableErrorResult(NS_ERROR_DOM_INVALID_STATE_ERR));
    616      });
    617 }
    618 
    619 const ServiceWorkerRegistrationDescriptor&
    620 ServiceWorkerRegistration::Descriptor() const {
    621  return mDescriptor;
    622 }
    623 
    624 void ServiceWorkerRegistration::WhenVersionReached(
    625    uint64_t aVersion, ServiceWorkerBoolCallback&& aCallback) {
    626  if (aVersion <= mDescriptor.Version()) {
    627    aCallback(aVersion == mDescriptor.Version());
    628    return;
    629  }
    630 
    631  mVersionCallbackList.AppendElement(
    632      MakeUnique<VersionCallback>(aVersion, std::move(aCallback)));
    633 }
    634 
    635 void ServiceWorkerRegistration::MaybeScheduleUpdateFound(
    636    const Maybe<ServiceWorkerDescriptor>& aInstallingDescriptor) {
    637  // This function sets mScheduledUpdateFoundId to note when we were told about
    638  // a new installing worker. We rely on a call to MaybeDispatchUpdateFound via
    639  // ServiceWorkerRegistrationChild::RecvFireUpdateFound to trigger the properly
    640  // timed notification...
    641  uint64_t newId = aInstallingDescriptor.isSome()
    642                       ? aInstallingDescriptor.ref().Id()
    643                       : kInvalidUpdateFoundId;
    644 
    645  // ...but the whole reason this logic exists is because SWRegistrations are
    646  // bootstrapped off of inherently stale descriptor snapshots and receive
    647  // catch-up updates once the actor is created and registered in the parent.
    648  // To handle the catch-up case where we need to generate a synthetic
    649  // updatefound that would otherwise be lost, we immediately flush here.
    650  if (mScheduledUpdateFoundId != kInvalidUpdateFoundId) {
    651    if (mScheduledUpdateFoundId == newId) {
    652      return;
    653    }
    654    MaybeDispatchUpdateFound();
    655    MOZ_DIAGNOSTIC_ASSERT(mScheduledUpdateFoundId == kInvalidUpdateFoundId);
    656  }
    657 
    658  bool updateFound =
    659      newId != kInvalidUpdateFoundId && mDispatchedUpdateFoundId != newId;
    660 
    661  if (!updateFound) {
    662    return;
    663  }
    664 
    665  mScheduledUpdateFoundId = newId;
    666 }
    667 
    668 void ServiceWorkerRegistration::MaybeDispatchUpdateFound() {
    669  uint64_t scheduledId = mScheduledUpdateFoundId;
    670  mScheduledUpdateFoundId = kInvalidUpdateFoundId;
    671 
    672  if (scheduledId == kInvalidUpdateFoundId ||
    673      scheduledId == mDispatchedUpdateFoundId) {
    674    return;
    675  }
    676 
    677  mDispatchedUpdateFoundId = scheduledId;
    678  DispatchTrustedEvent(u"updatefound"_ns);
    679 }
    680 
    681 void ServiceWorkerRegistration::UpdateStateInternal(
    682    const Maybe<ServiceWorkerDescriptor>& aInstalling,
    683    const Maybe<ServiceWorkerDescriptor>& aWaiting,
    684    const Maybe<ServiceWorkerDescriptor>& aActive) {
    685  // Do this immediately as it may flush an already pending updatefound
    686  // event.  In that case we want to fire the pending event before
    687  // modifying any of the registration properties.
    688  MaybeScheduleUpdateFound(aInstalling);
    689 
    690  // Move the currently exposed workers into a separate list
    691  // of "old" workers.  We will then potentially add them
    692  // back to the registration properties below based on the
    693  // given descriptor.  Any that are not restored will need
    694  // to be moved to the redundant state.
    695  AutoTArray<RefPtr<ServiceWorker>, 3> oldWorkerList({
    696      std::move(mInstallingWorker),
    697      std::move(mWaitingWorker),
    698      std::move(mActiveWorker),
    699  });
    700 
    701  // Its important that all state changes are actually applied before
    702  // dispatching any statechange events.  Each ServiceWorker object
    703  // should be in the correct state and the ServiceWorkerRegistration
    704  // properties need to be set correctly as well.  To accomplish this
    705  // we use a ScopeExit to dispatch any statechange events.
    706  auto scopeExit = MakeScopeExit([&] {
    707    // Check to see if any of the "old" workers was completely discarded.
    708    // Set these workers to the redundant state.
    709    for (auto& oldWorker : oldWorkerList) {
    710      if (!oldWorker || oldWorker == mInstallingWorker ||
    711          oldWorker == mWaitingWorker || oldWorker == mActiveWorker) {
    712        continue;
    713      }
    714 
    715      oldWorker->SetState(ServiceWorkerState::Redundant);
    716    }
    717 
    718    // Check each worker to see if it needs a statechange event dispatched.
    719    if (mInstallingWorker) {
    720      mInstallingWorker->MaybeDispatchStateChangeEvent();
    721    }
    722    if (mWaitingWorker) {
    723      mWaitingWorker->MaybeDispatchStateChangeEvent();
    724    }
    725    if (mActiveWorker) {
    726      mActiveWorker->MaybeDispatchStateChangeEvent();
    727    }
    728 
    729    // We also check the "old" workers to see if they need a statechange
    730    // event as well.  Note, these may overlap with the known worker properties
    731    // above, but MaybeDispatchStateChangeEvent() will ignore duplicated calls.
    732    for (auto& oldWorker : oldWorkerList) {
    733      if (!oldWorker) {
    734        continue;
    735      }
    736 
    737      oldWorker->MaybeDispatchStateChangeEvent();
    738    }
    739  });
    740 
    741  // Clear all workers if the registration has been detached from the global.
    742  nsCOMPtr<nsIGlobalObject> global = GetParentObject();
    743  if (!global) {
    744    return;
    745  }
    746 
    747  if (aActive.isSome()) {
    748    if ((mActiveWorker = global->GetOrCreateServiceWorker(aActive.ref()))) {
    749      mActiveWorker->SetState(aActive.ref().State());
    750    }
    751  } else {
    752    mActiveWorker = nullptr;
    753  }
    754 
    755  if (aWaiting.isSome()) {
    756    if ((mWaitingWorker = global->GetOrCreateServiceWorker(aWaiting.ref()))) {
    757      mWaitingWorker->SetState(aWaiting.ref().State());
    758    }
    759  } else {
    760    mWaitingWorker = nullptr;
    761  }
    762 
    763  if (aInstalling.isSome()) {
    764    if ((mInstallingWorker =
    765             global->GetOrCreateServiceWorker(aInstalling.ref()))) {
    766      mInstallingWorker->SetState(aInstalling.ref().State());
    767    }
    768  } else {
    769    mInstallingWorker = nullptr;
    770  }
    771 }
    772 
    773 void ServiceWorkerRegistration::RevokeActor(
    774    ServiceWorkerRegistrationChild* aActor) {
    775  MOZ_DIAGNOSTIC_ASSERT(mActor);
    776  MOZ_DIAGNOSTIC_ASSERT(mActor == aActor);
    777  mActor->RevokeOwner(this);
    778  mActor = nullptr;
    779 
    780  mShutdown = true;
    781 
    782  RegistrationCleared();
    783 }
    784 
    785 void ServiceWorkerRegistration::Shutdown() {
    786  if (mShutdown) {
    787    return;
    788  }
    789  mShutdown = true;
    790 
    791  if (mActor) {
    792    mActor->RevokeOwner(this);
    793    mActor->Shutdown();
    794    mActor = nullptr;
    795  }
    796 }
    797 
    798 }  // namespace mozilla::dom