tor-browser

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

ServiceWorkerContainer.cpp (26053B)


      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 "ServiceWorkerContainer.h"
      8 
      9 #include "mozilla/BasePrincipal.h"
     10 #include "mozilla/Components.h"
     11 #include "mozilla/SchedulerGroup.h"
     12 #include "mozilla/StaticPrefs_extensions.h"
     13 #include "mozilla/StaticPrefs_privacy.h"
     14 #include "mozilla/StoragePrincipalHelper.h"
     15 #include "mozilla/dom/ClientIPCTypes.h"
     16 #include "mozilla/dom/DOMMozPromiseRequestHolder.h"
     17 #include "mozilla/dom/MessageEvent.h"
     18 #include "mozilla/dom/MessageEventBinding.h"
     19 #include "mozilla/dom/Navigator.h"
     20 #include "mozilla/dom/Promise.h"
     21 #include "mozilla/dom/RootedDictionary.h"
     22 #include "mozilla/dom/ServiceWorker.h"
     23 #include "mozilla/dom/ServiceWorkerContainerBinding.h"
     24 #include "mozilla/dom/ServiceWorkerContainerChild.h"
     25 #include "mozilla/dom/ServiceWorkerManager.h"
     26 #include "mozilla/dom/ServiceWorkerRegistration.h"
     27 #include "mozilla/dom/ServiceWorkerUtils.h"
     28 #include "mozilla/dom/TrustedTypeUtils.h"
     29 #include "mozilla/dom/TrustedTypesConstants.h"
     30 #include "mozilla/dom/ipc/StructuredCloneData.h"
     31 #include "mozilla/ipc/BackgroundChild.h"
     32 #include "mozilla/ipc/PBackgroundChild.h"
     33 #include "nsContentSecurityManager.h"
     34 #include "nsContentUtils.h"
     35 #include "nsCycleCollectionParticipant.h"
     36 #include "nsGlobalWindowInner.h"
     37 #include "nsIScriptError.h"
     38 #include "nsIServiceWorkerManager.h"
     39 #include "nsNetUtil.h"
     40 #include "nsPIDOMWindow.h"
     41 #include "nsServiceManagerUtils.h"
     42 #include "nsThreadUtils.h"
     43 
     44 // This is defined to something else on Windows
     45 #ifdef DispatchMessage
     46 #  undef DispatchMessage
     47 #endif
     48 
     49 namespace mozilla::dom {
     50 
     51 using mozilla::ipc::BackgroundChild;
     52 using mozilla::ipc::PBackgroundChild;
     53 using mozilla::ipc::ResponseRejectReason;
     54 
     55 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ServiceWorkerContainer)
     56 NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
     57 
     58 NS_IMPL_ADDREF_INHERITED(ServiceWorkerContainer, DOMEventTargetHelper)
     59 NS_IMPL_RELEASE_INHERITED(ServiceWorkerContainer, DOMEventTargetHelper)
     60 
     61 NS_IMPL_CYCLE_COLLECTION_INHERITED(ServiceWorkerContainer, DOMEventTargetHelper,
     62                                   mControllerWorker, mReadyPromise)
     63 
     64 // static
     65 already_AddRefed<ServiceWorkerContainer> ServiceWorkerContainer::Create(
     66    nsIGlobalObject* aGlobal) {
     67  RefPtr<ServiceWorkerContainer> ref = new ServiceWorkerContainer(aGlobal);
     68  return ref.forget();
     69 }
     70 
     71 ServiceWorkerContainer::ServiceWorkerContainer(nsIGlobalObject* aGlobal)
     72    : DOMEventTargetHelper(aGlobal), mShutdown(false) {
     73  PBackgroundChild* parentActor =
     74      BackgroundChild::GetOrCreateForCurrentThread();
     75  if (NS_WARN_IF(!parentActor)) {
     76    Shutdown();
     77    return;
     78  }
     79 
     80  RefPtr<ServiceWorkerContainerChild> actor =
     81      ServiceWorkerContainerChild::Create();
     82  if (NS_WARN_IF(!actor)) {
     83    Shutdown();
     84    return;
     85  }
     86 
     87  PServiceWorkerContainerChild* sentActor =
     88      parentActor->SendPServiceWorkerContainerConstructor(actor);
     89  if (NS_WARN_IF(!sentActor)) {
     90    Shutdown();
     91    return;
     92  }
     93  MOZ_DIAGNOSTIC_ASSERT(sentActor == actor);
     94 
     95  mActor = std::move(actor);
     96  mActor->SetOwner(this);
     97 
     98  Maybe<ServiceWorkerDescriptor> controller = aGlobal->GetController();
     99  if (controller.isSome()) {
    100    mControllerWorker = aGlobal->GetOrCreateServiceWorker(controller.ref());
    101  }
    102 }
    103 
    104 ServiceWorkerContainer::~ServiceWorkerContainer() { Shutdown(); }
    105 
    106 void ServiceWorkerContainer::DisconnectFromOwner() {
    107  mControllerWorker = nullptr;
    108  mReadyPromise = nullptr;
    109  DOMEventTargetHelper::DisconnectFromOwner();
    110 }
    111 
    112 void ServiceWorkerContainer::ControllerChanged(ErrorResult& aRv) {
    113  nsCOMPtr<nsIGlobalObject> go = GetParentObject();
    114  if (!go) {
    115    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
    116    return;
    117  }
    118  mControllerWorker = go->GetOrCreateServiceWorker(go->GetController().ref());
    119  aRv = DispatchTrustedEvent(u"controllerchange"_ns);
    120 }
    121 
    122 using mozilla::dom::ipc::StructuredCloneData;
    123 
    124 // A ReceivedMessage represents a message sent via
    125 // Client.postMessage(). It is used as used both for queuing of
    126 // incoming messages and as an interface to DispatchMessage().
    127 struct MOZ_HEAP_CLASS ServiceWorkerContainer::ReceivedMessage {
    128  explicit ReceivedMessage(const ClientPostMessageArgs& aArgs)
    129      : mServiceWorker(aArgs.serviceWorker()) {
    130    mClonedData.CopyFromClonedMessageData(aArgs.clonedData());
    131  }
    132 
    133  ServiceWorkerDescriptor mServiceWorker;
    134  StructuredCloneData mClonedData;
    135 
    136  NS_INLINE_DECL_REFCOUNTING(ReceivedMessage)
    137 
    138 private:
    139  ~ReceivedMessage() = default;
    140 };
    141 
    142 void ServiceWorkerContainer::ReceiveMessage(
    143    const ClientPostMessageArgs& aArgs) {
    144  RefPtr<ReceivedMessage> message = new ReceivedMessage(aArgs);
    145  if (mMessagesStarted) {
    146    EnqueueReceivedMessageDispatch(std::move(message));
    147  } else {
    148    mPendingMessages.AppendElement(message.forget());
    149  }
    150 }
    151 
    152 void ServiceWorkerContainer::RevokeActor(ServiceWorkerContainerChild* aActor) {
    153  MOZ_DIAGNOSTIC_ASSERT(mActor);
    154  MOZ_DIAGNOSTIC_ASSERT(mActor == aActor);
    155  mActor->RevokeOwner(this);
    156  mActor = nullptr;
    157 
    158  mShutdown = true;
    159 }
    160 
    161 JSObject* ServiceWorkerContainer::WrapObject(
    162    JSContext* aCx, JS::Handle<JSObject*> aGivenProto) {
    163  return ServiceWorkerContainer_Binding::Wrap(aCx, this, aGivenProto);
    164 }
    165 
    166 already_AddRefed<Promise> ServiceWorkerContainer::Register(
    167    const TrustedScriptURLOrUSVString& aScriptURL,
    168    const RegistrationOptions& aOptions, nsIPrincipal* aSubjectPrincipal,
    169    ErrorResult& aRv) {
    170  AUTO_PROFILER_MARKER_UNTYPED("SWC Register", DOM, {});
    171 
    172  // Note, we can't use GetGlobalIfValid() from the start here.  If we
    173  // hit a storage failure we want to log a message with the final
    174  // scope string we put together below.
    175  nsCOMPtr<nsIGlobalObject> global = GetParentObject();
    176  if (!global) {
    177    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
    178    return nullptr;
    179  }
    180 
    181  Maybe<ClientInfo> clientInfo = global->GetClientInfo();
    182  if (clientInfo.isNothing()) {
    183    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
    184    return nullptr;
    185  }
    186 
    187  nsCOMPtr<nsIURI> baseURI = global->GetBaseURI();
    188  if (!baseURI) {
    189    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
    190    return nullptr;
    191  }
    192 
    193  constexpr nsLiteralString sink = u"ServiceWorkerContainer register"_ns;
    194  Maybe<nsAutoString> compliantStringHolder;
    195  const nsAString* compliantString =
    196      TrustedTypeUtils::GetTrustedTypesCompliantString(
    197          aScriptURL, sink, kTrustedTypesOnlySinkGroup, *global,
    198          aSubjectPrincipal, compliantStringHolder, aRv);
    199  if (aRv.Failed()) {
    200    return nullptr;
    201  }
    202 
    203  // Don't use NS_ConvertUTF16toUTF8 because that doesn't let us handle OOM.
    204  nsAutoCString scriptURL;
    205  if (!AppendUTF16toUTF8(*compliantString, scriptURL, fallible)) {
    206    aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
    207    return nullptr;
    208  }
    209 
    210  nsCOMPtr<nsIURI> scriptURI;
    211  nsresult rv =
    212      NS_NewURI(getter_AddRefs(scriptURI), scriptURL, nullptr, baseURI);
    213  if (NS_WARN_IF(NS_FAILED(rv))) {
    214    aRv.ThrowTypeError<MSG_INVALID_URL>(scriptURL);
    215    return nullptr;
    216  }
    217 
    218  // Never allow script URL with moz-extension scheme if support is fully
    219  // disabled by the 'extensions.background_service_worker.enabled' pref.
    220  if (scriptURI->SchemeIs("moz-extension") &&
    221      !StaticPrefs::extensions_backgroundServiceWorker_enabled_AtStartup()) {
    222    aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
    223    return nullptr;
    224  }
    225 
    226  // In ServiceWorkerContainer.register() the scope argument is parsed against
    227  // different base URLs depending on whether it was passed or not.
    228  nsCOMPtr<nsIURI> scopeURI;
    229 
    230  // Step 4. If none passed, parse against script's URL
    231  if (!aOptions.mScope.WasPassed()) {
    232    constexpr auto defaultScope = "./"_ns;
    233    rv = NS_NewURI(getter_AddRefs(scopeURI), defaultScope, nullptr, scriptURI);
    234    if (NS_WARN_IF(NS_FAILED(rv))) {
    235      nsAutoCString spec;
    236      scriptURI->GetSpec(spec);
    237      aRv.ThrowTypeError<MSG_INVALID_SCOPE>(defaultScope, spec);
    238      return nullptr;
    239    }
    240  } else {
    241    // Step 5. Parse against entry settings object's base URL.
    242    rv = NS_NewURI(getter_AddRefs(scopeURI), aOptions.mScope.Value(), nullptr,
    243                   baseURI);
    244    if (NS_WARN_IF(NS_FAILED(rv))) {
    245      nsIURI* uri = baseURI ? baseURI : scriptURI;
    246      nsAutoCString spec;
    247      uri->GetSpec(spec);
    248      aRv.ThrowTypeError<MSG_INVALID_SCOPE>(
    249          NS_ConvertUTF16toUTF8(aOptions.mScope.Value()), spec);
    250      return nullptr;
    251    }
    252  }
    253 
    254  // Strip the any ref from both the script and scope URLs.
    255  nsCOMPtr<nsIURI> cloneWithoutRef;
    256  aRv = NS_GetURIWithoutRef(scriptURI, getter_AddRefs(cloneWithoutRef));
    257  if (aRv.Failed()) {
    258    return nullptr;
    259  }
    260  scriptURI = std::move(cloneWithoutRef);
    261 
    262  aRv = NS_GetURIWithoutRef(scopeURI, getter_AddRefs(cloneWithoutRef));
    263  if (aRv.Failed()) {
    264    return nullptr;
    265  }
    266  scopeURI = std::move(cloneWithoutRef);
    267 
    268  ServiceWorkerScopeAndScriptAreValid(clientInfo.ref(), scopeURI, scriptURI,
    269                                      aRv, global);
    270  if (aRv.Failed()) {
    271    return nullptr;
    272  }
    273 
    274  // Get the string representation for both the script and scope since
    275  // we sanitized them above.
    276  nsCString cleanedScopeURL;
    277  aRv = scopeURI->GetSpec(cleanedScopeURL);
    278  if (aRv.Failed()) {
    279    return nullptr;
    280  }
    281 
    282  nsCString cleanedScriptURL;
    283  aRv = scriptURI->GetSpec(cleanedScriptURL);
    284  if (aRv.Failed()) {
    285    return nullptr;
    286  }
    287 
    288  // Verify that the global is valid and has permission to store
    289  // data.  We perform this late so that we can report the final
    290  // scope URL in any error message.
    291  (void)GetGlobalIfValid(aRv, [&](nsIGlobalObject* aGlobal) {
    292    AutoTArray<nsString, 1> param;
    293    CopyUTF8toUTF16(cleanedScopeURL, *param.AppendElement());
    294    aGlobal->ReportToConsole(nsIScriptError::errorFlag, "Service Workers"_ns,
    295                             nsContentUtils::eDOM_PROPERTIES,
    296                             "ServiceWorkerRegisterStorageError"_ns, param);
    297  });
    298 
    299  // TODO: For bug 1836707 we will move this tracking to ServiceWorkerManager
    300  // where it can establish the mapping between the job and our client info,
    301  // which will also work on workers.  For now we leave this notification for
    302  // Windows only.
    303  if (auto* window = global->GetAsInnerWindow()) {
    304    window->NoteCalledRegisterForServiceWorkerScope(cleanedScopeURL);
    305  }
    306 
    307  RefPtr<Promise> outer =
    308      Promise::Create(global, aRv, Promise::ePropagateUserInteraction);
    309  if (aRv.Failed()) {
    310    return nullptr;
    311  }
    312 
    313  RefPtr<ServiceWorkerContainer> self = this;
    314 
    315  if (!mActor) {
    316    aRv.ThrowInvalidStateError("Can't register service worker");
    317    return nullptr;
    318  }
    319 
    320  mActor->SendRegister(
    321      clientInfo.ref().ToIPC(), nsCString(cleanedScopeURL), aOptions.mType,
    322      nsCString(cleanedScriptURL), aOptions.mUpdateViaCache,
    323      [self,
    324       outer](const IPCServiceWorkerRegistrationDescriptorOrCopyableErrorResult&
    325                  aResult) {
    326        AUTO_PROFILER_MARKER_UNTYPED("SWC Register (inner)", DOM, {});
    327 
    328        if (aResult.type() ==
    329            IPCServiceWorkerRegistrationDescriptorOrCopyableErrorResult::
    330                TCopyableErrorResult) {
    331          // application layer error
    332          CopyableErrorResult rv = aResult.get_CopyableErrorResult();
    333          MOZ_DIAGNOSTIC_ASSERT(rv.Failed());
    334          outer->MaybeReject(std::move(rv));
    335          return;
    336        }
    337        // success
    338        const auto& ipcDesc =
    339            aResult.get_IPCServiceWorkerRegistrationDescriptor();
    340        ErrorResult rv;
    341        nsIGlobalObject* global = self->GetGlobalIfValid(rv);
    342        if (rv.Failed()) {
    343          outer->MaybeReject(std::move(rv));
    344          return;
    345        }
    346        RefPtr<ServiceWorkerRegistration> reg =
    347            global->GetOrCreateServiceWorkerRegistration(
    348                ServiceWorkerRegistrationDescriptor(ipcDesc));
    349        outer->MaybeResolve(reg);
    350      },
    351      [outer](ResponseRejectReason&& aReason) {
    352        // IPC layer error
    353        CopyableErrorResult rv;
    354        rv.ThrowInvalidStateError("Failed to register service worker");
    355        outer->MaybeReject(std::move(rv));
    356      });
    357 
    358  return outer.forget();
    359 }
    360 
    361 already_AddRefed<ServiceWorker> ServiceWorkerContainer::GetController() {
    362  RefPtr<ServiceWorker> ref = mControllerWorker;
    363  return ref.forget();
    364 }
    365 
    366 already_AddRefed<Promise> ServiceWorkerContainer::GetRegistrations(
    367    ErrorResult& aRv) {
    368  nsIGlobalObject* global = GetGlobalIfValid(aRv, [](nsIGlobalObject* aGlobal) {
    369    aGlobal->ReportToConsole(nsIScriptError::errorFlag, "Service Workers"_ns,
    370                             nsContentUtils::eDOM_PROPERTIES,
    371                             "ServiceWorkerGetRegistrationStorageError"_ns);
    372  });
    373  if (aRv.Failed()) {
    374    return nullptr;
    375  }
    376 
    377  Maybe<ClientInfo> clientInfo = global->GetClientInfo();
    378  if (clientInfo.isNothing()) {
    379    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
    380    return nullptr;
    381  }
    382 
    383  RefPtr<Promise> outer =
    384      Promise::Create(global, aRv, Promise::ePropagateUserInteraction);
    385  if (aRv.Failed()) {
    386    return nullptr;
    387  }
    388 
    389  RefPtr<ServiceWorkerContainer> self = this;
    390 
    391  if (!mActor) {
    392    outer->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR);
    393    return outer.forget();
    394  }
    395 
    396  mActor->SendGetRegistrations(
    397      clientInfo.ref().ToIPC(),
    398      [self, outer](
    399          const IPCServiceWorkerRegistrationDescriptorListOrCopyableErrorResult&
    400              aResult) {
    401        if (aResult.type() ==
    402            IPCServiceWorkerRegistrationDescriptorListOrCopyableErrorResult::
    403                TCopyableErrorResult) {
    404          // application layer error
    405          const auto& rv = aResult.get_CopyableErrorResult();
    406          MOZ_DIAGNOSTIC_ASSERT(rv.Failed());
    407          outer->MaybeReject(CopyableErrorResult(rv));
    408          return;
    409        }
    410        // success
    411        const auto& ipcList =
    412            aResult.get_IPCServiceWorkerRegistrationDescriptorList();
    413        nsTArray<ServiceWorkerRegistrationDescriptor> list(
    414            ipcList.values().Length());
    415        for (const auto& ipcDesc : ipcList.values()) {
    416          list.AppendElement(ServiceWorkerRegistrationDescriptor(ipcDesc));
    417        }
    418 
    419        ErrorResult rv;
    420        nsIGlobalObject* global = self->GetGlobalIfValid(rv);
    421        if (rv.Failed()) {
    422          outer->MaybeReject(std::move(rv));
    423          return;
    424        }
    425        nsTArray<RefPtr<ServiceWorkerRegistration>> regList;
    426        for (auto& desc : list) {
    427          RefPtr<ServiceWorkerRegistration> reg =
    428              global->GetOrCreateServiceWorkerRegistration(desc);
    429          if (reg) {
    430            regList.AppendElement(std::move(reg));
    431          }
    432        }
    433        outer->MaybeResolve(regList);
    434      },
    435      [outer](ResponseRejectReason&& aReason) {
    436        // IPC layer error
    437        outer->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR);
    438      });
    439 
    440  return outer.forget();
    441 }
    442 
    443 void ServiceWorkerContainer::StartMessages() {
    444  while (!mPendingMessages.IsEmpty()) {
    445    EnqueueReceivedMessageDispatch(mPendingMessages.ElementAt(0));
    446    mPendingMessages.RemoveElementAt(0);
    447  }
    448  mMessagesStarted = true;
    449 }
    450 
    451 already_AddRefed<Promise> ServiceWorkerContainer::GetRegistration(
    452    const nsAString& aURL, ErrorResult& aRv) {
    453  nsIGlobalObject* global = GetGlobalIfValid(aRv, [](nsIGlobalObject* aGlobal) {
    454    aGlobal->ReportToConsole(nsIScriptError::errorFlag, "Service Workers"_ns,
    455                             nsContentUtils::eDOM_PROPERTIES,
    456                             "ServiceWorkerGetRegistrationStorageError"_ns);
    457  });
    458  if (aRv.Failed()) {
    459    return nullptr;
    460  }
    461 
    462  Maybe<ClientInfo> clientInfo = global->GetClientInfo();
    463  if (clientInfo.isNothing()) {
    464    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
    465    return nullptr;
    466  }
    467 
    468  nsCOMPtr<nsIURI> baseURI = global->GetBaseURI();
    469  if (!baseURI) {
    470    return nullptr;
    471  }
    472 
    473  nsCOMPtr<nsIURI> uri;
    474  aRv = NS_NewURI(getter_AddRefs(uri), aURL, nullptr, baseURI);
    475  if (aRv.Failed()) {
    476    return nullptr;
    477  }
    478 
    479  nsCString spec;
    480  aRv = uri->GetSpec(spec);
    481  if (aRv.Failed()) {
    482    return nullptr;
    483  }
    484 
    485  RefPtr<Promise> outer =
    486      Promise::Create(global, aRv, Promise::ePropagateUserInteraction);
    487  if (aRv.Failed()) {
    488    return nullptr;
    489  }
    490 
    491  RefPtr<ServiceWorkerContainer> self = this;
    492 
    493  if (!mActor) {
    494    outer->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR);
    495    return outer.forget();
    496  }
    497 
    498  mActor->SendGetRegistration(
    499      clientInfo.ref().ToIPC(), spec,
    500      [self,
    501       outer](const IPCServiceWorkerRegistrationDescriptorOrCopyableErrorResult&
    502                  aResult) {
    503        if (aResult.type() ==
    504            IPCServiceWorkerRegistrationDescriptorOrCopyableErrorResult::
    505                TCopyableErrorResult) {
    506          CopyableErrorResult ipcRv(aResult.get_CopyableErrorResult());
    507          ErrorResult rv(std::move(ipcRv));
    508          if (!rv.Failed()) {
    509            // ErrorResult rv;
    510            //  If rv is a failure then this is an application layer error.
    511            //  Note, though, we also reject with NS_OK to indicate that we just
    512            //  didn't find a registration.
    513            (void)self->GetGlobalIfValid(rv);
    514            if (!rv.Failed()) {
    515              outer->MaybeResolveWithUndefined();
    516              return;
    517            }
    518          }
    519          outer->MaybeReject(std::move(rv));
    520          return;
    521        }
    522        // success
    523        const auto& ipcDesc =
    524            aResult.get_IPCServiceWorkerRegistrationDescriptor();
    525        ErrorResult rv;
    526        nsIGlobalObject* global = self->GetGlobalIfValid(rv);
    527        if (rv.Failed()) {
    528          outer->MaybeReject(std::move(rv));
    529          return;
    530        }
    531        RefPtr<ServiceWorkerRegistration> reg =
    532            global->GetOrCreateServiceWorkerRegistration(
    533                ServiceWorkerRegistrationDescriptor(ipcDesc));
    534        outer->MaybeResolve(reg);
    535      },
    536      [self, outer](ResponseRejectReason&& aReason) {
    537        // IPC layer error
    538        outer->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR);
    539      });
    540  return outer.forget();
    541 }
    542 
    543 Promise* ServiceWorkerContainer::GetReady(ErrorResult& aRv) {
    544  if (mReadyPromise) {
    545    return mReadyPromise;
    546  }
    547 
    548  nsIGlobalObject* global = GetGlobalIfValid(aRv);
    549  if (aRv.Failed()) {
    550    return nullptr;
    551  }
    552  MOZ_DIAGNOSTIC_ASSERT(global);
    553 
    554  Maybe<ClientInfo> clientInfo(global->GetClientInfo());
    555  if (clientInfo.isNothing()) {
    556    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
    557    return nullptr;
    558  }
    559 
    560  mReadyPromise =
    561      Promise::Create(global, aRv, Promise::ePropagateUserInteraction);
    562  if (aRv.Failed()) {
    563    return nullptr;
    564  }
    565 
    566  RefPtr<ServiceWorkerContainer> self = this;
    567  RefPtr<Promise> outer = mReadyPromise;
    568 
    569  if (!mActor) {
    570    mReadyPromise->MaybeReject(
    571        CopyableErrorResult(NS_ERROR_DOM_INVALID_STATE_ERR));
    572    return mReadyPromise;
    573  }
    574 
    575  mActor->SendGetReady(
    576      clientInfo.ref().ToIPC(),
    577      [self,
    578       outer](const IPCServiceWorkerRegistrationDescriptorOrCopyableErrorResult&
    579                  aResult) {
    580        if (aResult.type() ==
    581            IPCServiceWorkerRegistrationDescriptorOrCopyableErrorResult::
    582                TCopyableErrorResult) {
    583          // application layer error
    584          CopyableErrorResult rv(aResult.get_CopyableErrorResult());
    585          MOZ_DIAGNOSTIC_ASSERT(rv.Failed());
    586          outer->MaybeReject(std::move(rv));
    587          return;
    588        }
    589        // success
    590        const auto& ipcDesc =
    591            aResult.get_IPCServiceWorkerRegistrationDescriptor();
    592        ErrorResult rv;
    593        nsIGlobalObject* global = self->GetGlobalIfValid(rv);
    594        if (rv.Failed()) {
    595          outer->MaybeReject(std::move(rv));
    596          return;
    597        }
    598        RefPtr<ServiceWorkerRegistration> reg =
    599            global->GetOrCreateServiceWorkerRegistration(
    600                ServiceWorkerRegistrationDescriptor(ipcDesc));
    601        NS_ENSURE_TRUE_VOID(reg);
    602 
    603        // Don't resolve the ready promise until the registration has
    604        // reached the right version.  This ensures that the active
    605        // worker property is set correctly on the registration.
    606        reg->WhenVersionReached(ipcDesc.version(), [outer, reg](bool aResult) {
    607          outer->MaybeResolve(reg);
    608        });
    609      },
    610      [outer](ResponseRejectReason&& aReason) {
    611        // IPC layer error
    612        outer->MaybeReject(CopyableErrorResult(NS_ERROR_DOM_INVALID_STATE_ERR));
    613      });
    614 
    615  return mReadyPromise;
    616 }
    617 
    618 nsIGlobalObject* ServiceWorkerContainer::GetGlobalIfValid(
    619    ErrorResult& aRv,
    620    const std::function<void(nsIGlobalObject*)>&& aStorageFailureCB) const {
    621  nsIGlobalObject* global = GetOwnerGlobal();
    622  if (NS_WARN_IF(!global)) {
    623    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
    624    return nullptr;
    625  }
    626 
    627  if (NS_FAILED(CheckCurrentGlobalCorrectness())) {
    628    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
    629    return nullptr;
    630  }
    631 
    632  // Don't allow a service worker to access service worker registrations
    633  // from a global with storage disabled.  If these globals can access
    634  // the registration it increases the chance they can bypass the storage
    635  // block via postMessage(), etc.
    636  if (NS_WARN_IF(!ServiceWorkersStorageAllowedForGlobal(global))) {
    637    if (aStorageFailureCB) {
    638      aStorageFailureCB(global);
    639    }
    640    aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
    641    return nullptr;
    642  }
    643 
    644  // Don't allow service workers for system principals.
    645  nsIPrincipal* principal = global->PrincipalOrNull();
    646  if (NS_WARN_IF(!principal || principal->IsSystemPrincipal())) {
    647    aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
    648    return nullptr;
    649  }
    650 
    651  return global;
    652 }
    653 
    654 void ServiceWorkerContainer::EnqueueReceivedMessageDispatch(
    655    RefPtr<ReceivedMessage> aMessage) {
    656  NS_DispatchToCurrentThread(NewRunnableMethod<RefPtr<ReceivedMessage>>(
    657      "ServiceWorkerContainer::DispatchMessage", this,
    658      &ServiceWorkerContainer::DispatchMessage, std::move(aMessage)));
    659 }
    660 
    661 template <typename F>
    662 void ServiceWorkerContainer::RunWithJSContext(F&& aCallable) {
    663  nsCOMPtr<nsIGlobalObject> globalObject = GetOwnerGlobal();
    664 
    665  // If AutoJSAPI::Init() fails then either global is nullptr or not
    666  // in a usable state.
    667  AutoJSAPI jsapi;
    668  if (!jsapi.Init(globalObject)) {
    669    return;
    670  }
    671 
    672  aCallable(jsapi.cx(), globalObject);
    673 }
    674 
    675 void ServiceWorkerContainer::DispatchMessage(RefPtr<ReceivedMessage> aMessage) {
    676  nsresult rv = CheckCurrentGlobalCorrectness();
    677  if (NS_FAILED(rv)) {
    678    return;
    679  }
    680 
    681  // When dispatching a message, either DOMContentLoaded has already
    682  // been fired, or someone called startMessages() or set onmessage.
    683  // Either way, a global object is supposed to be present. If it's
    684  // not, we'd fail to initialize the JS API and exit.
    685  RunWithJSContext([this, message = std::move(aMessage)](
    686                       JSContext* const aCx, nsIGlobalObject* const aGlobal) {
    687    ErrorResult result;
    688    bool deserializationFailed = false;
    689    RootedDictionary<MessageEventInit> init(aCx);
    690    auto res = FillInMessageEventInit(aCx, aGlobal, *message, init, result);
    691    if (res.isErr()) {
    692      deserializationFailed = res.unwrapErr();
    693      MOZ_ASSERT_IF(deserializationFailed, init.mData.isNull());
    694      MOZ_ASSERT_IF(deserializationFailed, init.mPorts.IsEmpty());
    695      MOZ_ASSERT_IF(deserializationFailed, !init.mOrigin.IsEmpty());
    696      MOZ_ASSERT_IF(deserializationFailed, !init.mSource.IsNull());
    697      result.SuppressException();
    698 
    699      if (!deserializationFailed && result.MaybeSetPendingException(aCx)) {
    700        return;
    701      }
    702    }
    703 
    704    RefPtr<MessageEvent> event = MessageEvent::Constructor(
    705        this, deserializationFailed ? u"messageerror"_ns : u"message"_ns, init);
    706    event->SetTrusted(true);
    707 
    708    result = NS_OK;
    709    DispatchEvent(*event, result);
    710    if (result.Failed()) {
    711      result.SuppressException();
    712    }
    713  });
    714 }
    715 
    716 namespace {
    717 
    718 nsresult FillInOriginNoSuffix(const ServiceWorkerDescriptor& aServiceWorker,
    719                              nsString& aOrigin) {
    720  using mozilla::ipc::PrincipalInfoToPrincipal;
    721 
    722  nsresult rv;
    723 
    724  auto principalOrErr =
    725      PrincipalInfoToPrincipal(aServiceWorker.PrincipalInfo());
    726  if (NS_WARN_IF(principalOrErr.isErr())) {
    727    return principalOrErr.unwrapErr();
    728  }
    729 
    730  nsAutoCString originUTF8;
    731  rv = principalOrErr.unwrap()->GetOriginNoSuffix(originUTF8);
    732  if (NS_FAILED(rv)) {
    733    return rv;
    734  }
    735 
    736  CopyUTF8toUTF16(originUTF8, aOrigin);
    737  return NS_OK;
    738 }
    739 
    740 }  // namespace
    741 
    742 Result<Ok, bool> ServiceWorkerContainer::FillInMessageEventInit(
    743    JSContext* const aCx, nsIGlobalObject* const aGlobal,
    744    ReceivedMessage& aMessage, MessageEventInit& aInit, ErrorResult& aRv) {
    745  // Determining the source and origin should preceed attempting deserialization
    746  // because on a "messageerror" event (i.e. when deserialization fails), the
    747  // dispatched message needs to contain such an origin and source, per spec:
    748  //
    749  // "If this throws an exception, catch it, fire an event named messageerror
    750  //  at destination, using MessageEvent, with the origin attribute initialized
    751  //  to origin and the source attribute initialized to source, and then abort
    752  //  these steps." - 6.4 of postMessage
    753  //  See: https://w3c.github.io/ServiceWorker/#service-worker-postmessage
    754  const RefPtr<ServiceWorker> serviceWorkerInstance =
    755      aGlobal->GetOrCreateServiceWorker(aMessage.mServiceWorker);
    756  if (serviceWorkerInstance) {
    757    aInit.mSource.SetValue().SetAsServiceWorker() = serviceWorkerInstance;
    758  }
    759 
    760  const nsresult rv =
    761      FillInOriginNoSuffix(aMessage.mServiceWorker, aInit.mOrigin);
    762  if (NS_FAILED(rv)) {
    763    return Err(false);
    764  }
    765 
    766  JS::Rooted<JS::Value> messageData(aCx);
    767  aMessage.mClonedData.Read(aCx, &messageData, aRv);
    768  if (aRv.Failed()) {
    769    return Err(true);
    770  }
    771 
    772  aInit.mData = messageData;
    773 
    774  if (!aMessage.mClonedData.TakeTransferredPortsAsSequence(aInit.mPorts)) {
    775    xpc::Throw(aCx, NS_ERROR_OUT_OF_MEMORY);
    776    return Err(false);
    777  }
    778 
    779  return Ok();
    780 }
    781 
    782 void ServiceWorkerContainer::Shutdown() {
    783  if (mShutdown) {
    784    return;
    785  }
    786  mShutdown = true;
    787 
    788  if (mActor) {
    789    mActor->RevokeOwner(this);
    790    mActor->MaybeStartTeardown();
    791    mActor = nullptr;
    792  }
    793 }
    794 
    795 }  // namespace mozilla::dom