tor-browser

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

ClientSource.cpp (23170B)


      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 "ClientSource.h"
      8 
      9 #include "ClientManager.h"
     10 #include "ClientManagerChild.h"
     11 #include "ClientPrincipalUtils.h"
     12 #include "ClientSourceChild.h"
     13 #include "ClientState.h"
     14 #include "ClientValidation.h"
     15 #include "mozilla/SchedulerGroup.h"
     16 #include "mozilla/StaticPrefs_privacy.h"
     17 #include "mozilla/StorageAccess.h"
     18 #include "mozilla/Try.h"
     19 #include "mozilla/dom/BlobURLProtocolHandler.h"
     20 #include "mozilla/dom/ClientIPCTypes.h"
     21 #include "mozilla/dom/DOMMozPromiseRequestHolder.h"
     22 #include "mozilla/dom/JSExecutionManager.h"
     23 #include "mozilla/dom/MessageEvent.h"
     24 #include "mozilla/dom/MessageEventBinding.h"
     25 #include "mozilla/dom/Navigator.h"
     26 #include "mozilla/dom/PolicyContainer.h"
     27 #include "mozilla/dom/ServiceWorker.h"
     28 #include "mozilla/dom/ServiceWorkerContainer.h"
     29 #include "mozilla/dom/ServiceWorkerManager.h"
     30 #include "mozilla/dom/ServiceWorkerUtils.h"
     31 #include "mozilla/dom/WorkerRef.h"
     32 #include "mozilla/dom/WorkerScope.h"
     33 #include "mozilla/dom/ipc/StructuredCloneData.h"
     34 #include "mozilla/ipc/BackgroundUtils.h"
     35 #include "nsContentUtils.h"
     36 #include "nsFocusManager.h"
     37 #include "nsIContentSecurityPolicy.h"
     38 #include "nsIDocShell.h"
     39 #include "nsPIDOMWindow.h"
     40 
     41 namespace mozilla::dom {
     42 
     43 using mozilla::dom::ipc::StructuredCloneData;
     44 using mozilla::ipc::CSPInfo;
     45 using mozilla::ipc::CSPToCSPInfo;
     46 using mozilla::ipc::PrincipalInfo;
     47 using mozilla::ipc::PrincipalInfoToPrincipal;
     48 
     49 void ClientSource::Shutdown() {
     50  NS_ASSERT_OWNINGTHREAD(ClientSource);
     51  if (IsShutdown()) {
     52    return;
     53  }
     54 
     55  ShutdownThing();
     56 
     57  mManager = nullptr;
     58 }
     59 
     60 void ClientSource::ExecutionReady(const ClientSourceExecutionReadyArgs& aArgs) {
     61  // Fast fail if we don't understand this particular principal/URL combination.
     62  // This can happen since we use MozURL for validation which does not handle
     63  // some of the more obscure internal principal/url combinations.  Normal
     64  // content pages will pass this check.
     65  if (NS_WARN_IF(!ClientIsValidCreationURL(mClientInfo.PrincipalInfo(),
     66                                           aArgs.url()))) {
     67    Shutdown();
     68    return;
     69  }
     70 
     71  mClientInfo.SetURL(aArgs.url());
     72  mClientInfo.SetFrameType(aArgs.frameType());
     73  MaybeExecute([aArgs](PClientSourceChild* aActor) {
     74    aActor->SendExecutionReady(aArgs);
     75  });
     76 }
     77 
     78 Result<ClientState, ErrorResult> ClientSource::SnapshotWindowState() {
     79  MOZ_ASSERT(NS_IsMainThread());
     80 
     81  nsPIDOMWindowInner* window = GetInnerWindow();
     82  if (!window || !window->IsCurrentInnerWindow() ||
     83      !window->HasActiveDocument()) {
     84    return ClientState(ClientWindowState(VisibilityState::Hidden, TimeStamp(),
     85                                         StorageAccess::eDeny, false));
     86  }
     87 
     88  Document* doc = window->GetExtantDoc();
     89  ErrorResult rv;
     90  if (NS_WARN_IF(!doc)) {
     91    rv.ThrowInvalidStateError("Document not active");
     92    return Err(std::move(rv));
     93  }
     94 
     95  bool focused = doc->HasFocus(rv);
     96  if (NS_WARN_IF(rv.Failed())) {
     97    return Err(std::move(rv));
     98  }
     99 
    100  StorageAccess storage = StorageAllowedForDocument(doc);
    101 
    102  return ClientState(ClientWindowState(doc->VisibilityState(),
    103                                       doc->LastFocusTime(), storage, focused));
    104 }
    105 
    106 WorkerPrivate* ClientSource::GetWorkerPrivate() const {
    107  NS_ASSERT_OWNINGTHREAD(ClientSource);
    108  if (!mOwner.is<WorkerPrivate*>()) {
    109    return nullptr;
    110  }
    111  return mOwner.as<WorkerPrivate*>();
    112 }
    113 
    114 nsIDocShell* ClientSource::GetDocShell() const {
    115  NS_ASSERT_OWNINGTHREAD(ClientSource);
    116  if (!mOwner.is<nsCOMPtr<nsIDocShell>>()) {
    117    return nullptr;
    118  }
    119  return mOwner.as<nsCOMPtr<nsIDocShell>>();
    120 }
    121 
    122 nsIGlobalObject* ClientSource::GetGlobal() const {
    123  NS_ASSERT_OWNINGTHREAD(ClientSource);
    124  nsPIDOMWindowInner* win = GetInnerWindow();
    125  if (win) {
    126    return win->AsGlobal();
    127  }
    128 
    129  WorkerPrivate* wp = GetWorkerPrivate();
    130  if (wp) {
    131    return wp->GlobalScope();
    132  }
    133 
    134  // Note, ClientSource objects attached to docshell for conceptual
    135  // initial about:blank will get nullptr here.  The caller should
    136  // use MaybeCreateIntitialDocument() to create the window before
    137  // GetGlobal() if it wants this before.
    138 
    139  return nullptr;
    140 }
    141 
    142 // We want to be explicit about possible invalid states and
    143 // return them as errors.
    144 Result<bool, ErrorResult> ClientSource::MaybeCreateInitialDocument() {
    145  // If there is not even a docshell, we do not expect to have a document
    146  nsIDocShell* docshell = GetDocShell();
    147  if (!docshell) {
    148    return false;
    149  }
    150 
    151  // Force the creation of the initial document if it does not yet exist.
    152  if (!docshell->GetDocument()) {
    153    ErrorResult rv;
    154    rv.ThrowInvalidStateError("No document available.");
    155    return Err(std::move(rv));
    156  }
    157 
    158  return true;
    159 }
    160 
    161 ClientSource::ClientSource(ClientManager* aManager,
    162                           nsISerialEventTarget* aEventTarget,
    163                           const ClientSourceConstructorArgs& aArgs)
    164    : mManager(aManager),
    165      mEventTarget(aEventTarget),
    166      mOwner(AsVariant(Nothing())),
    167      mClientInfo(aArgs.id(), aArgs.agentClusterId(), aArgs.type(),
    168                  aArgs.principalInfo(), aArgs.creationTime(), aArgs.url(),
    169                  aArgs.frameType()) {
    170  MOZ_ASSERT(mManager);
    171  MOZ_ASSERT(mEventTarget);
    172 }
    173 
    174 void ClientSource::Activate(PClientManagerChild* aActor) {
    175  NS_ASSERT_OWNINGTHREAD(ClientSource);
    176  MOZ_ASSERT(!GetActor());
    177 
    178  if (IsShutdown()) {
    179    return;
    180  }
    181 
    182  // Fast fail if we don't understand this particular kind of PrincipalInfo.
    183  // This can happen since we use MozURL for validation which does not handle
    184  // some of the more obscure internal principal/url combinations.  Normal
    185  // content pages will pass this check.
    186  if (NS_WARN_IF(!ClientIsValidPrincipalInfo(mClientInfo.PrincipalInfo()))) {
    187    Shutdown();
    188    return;
    189  }
    190 
    191  ClientSourceConstructorArgs args(
    192      mClientInfo.Id(), Nothing(), mClientInfo.Type(),
    193      mClientInfo.PrincipalInfo(), mClientInfo.CreationTime(), VoidCString(),
    194      FrameType::None);
    195  RefPtr<ClientSourceChild> actor = new ClientSourceChild(args);
    196  if (!aActor->SendPClientSourceConstructor(actor, args)) {
    197    Shutdown();
    198    return;
    199  }
    200 
    201  ActivateThing(actor);
    202 }
    203 
    204 ClientSource::~ClientSource() { Shutdown(); }
    205 
    206 nsPIDOMWindowInner* ClientSource::GetInnerWindow() const {
    207  NS_ASSERT_OWNINGTHREAD(ClientSource);
    208  if (!mOwner.is<RefPtr<nsPIDOMWindowInner>>()) {
    209    return nullptr;
    210  }
    211  return mOwner.as<RefPtr<nsPIDOMWindowInner>>();
    212 }
    213 
    214 void ClientSource::WorkerExecutionReady(WorkerPrivate* aWorkerPrivate) {
    215  MOZ_DIAGNOSTIC_ASSERT(aWorkerPrivate);
    216  aWorkerPrivate->AssertIsOnWorkerThread();
    217 
    218  if (IsShutdown()) {
    219    return;
    220  }
    221 
    222  // Its safe to store the WorkerPrivate* here because the ClientSource
    223  // is explicitly destroyed by WorkerPrivate before exiting its run loop.
    224  //
    225  // We need to initialize our owner before the call to
    226  // ServiceWorkersStorageAllowedForGlobal below which will use SnapshotState to
    227  // retrieve our StorageAccess.
    228  MOZ_DIAGNOSTIC_ASSERT(mOwner.is<Nothing>());
    229  mOwner = AsVariant(aWorkerPrivate);
    230 
    231  // A client without access to storage should never be controlled by
    232  // a service worker.  Check this here in case we were controlled before
    233  // execution ready.  We can't reliably determine what our storage policy
    234  // is before execution ready, unfortunately.
    235  if (mController.isSome()) {
    236    MOZ_DIAGNOSTIC_ASSERT(
    237        ServiceWorkersStorageAllowedForGlobal(aWorkerPrivate->GlobalScope()) ||
    238        StringBeginsWith(aWorkerPrivate->ScriptURL(), u"blob:"_ns));
    239  }
    240 
    241  ClientSourceExecutionReadyArgs args(aWorkerPrivate->GetLocationInfo().mHref,
    242                                      FrameType::None);
    243 
    244  ExecutionReady(args);
    245 }
    246 
    247 nsresult ClientSource::WindowExecutionReady(nsPIDOMWindowInner* aInnerWindow) {
    248  MOZ_ASSERT(NS_IsMainThread());
    249  MOZ_DIAGNOSTIC_ASSERT(aInnerWindow);
    250  MOZ_ASSERT(aInnerWindow->IsCurrentInnerWindow());
    251  MOZ_ASSERT(aInnerWindow->HasActiveDocument());
    252 
    253  if (IsShutdown()) {
    254    return NS_OK;
    255  }
    256 
    257  Document* doc = aInnerWindow->GetExtantDoc();
    258  NS_ENSURE_TRUE(doc, NS_ERROR_UNEXPECTED);
    259 
    260  nsIURI* uri = doc->GetOriginalURI();
    261  NS_ENSURE_TRUE(uri, NS_ERROR_UNEXPECTED);
    262 
    263  // Don't use nsAutoCString here since IPC requires a full nsCString anyway.
    264  nsCString spec;
    265  nsresult rv = uri->GetSpec(spec);
    266  NS_ENSURE_SUCCESS(rv, rv);
    267 
    268  // A client without access to storage should never be controlled by
    269  // a service worker.  Check this here in case we were controlled before
    270  // execution ready.  We can't reliably determine what our storage policy
    271  // is before execution ready, unfortunately.
    272  //
    273  // Note, explicitly avoid checking storage policy for windows that inherit
    274  // service workers from their parent.  If a user opens a controlled window
    275  // and then blocks storage, that window will continue to be controlled by
    276  // the SW until the window is closed.  Any about:blank or blob URL should
    277  // continue to inherit the SW as well.  We need to avoid triggering the
    278  // assertion in this corner case.
    279 #ifdef DEBUG
    280  if (mController.isSome()) {
    281    bool isAboutBlankURL = spec.LowerCaseEqualsLiteral("about:blank");
    282    bool isBlobURL = StringBeginsWith(spec, "blob:"_ns);
    283    bool isStorageAllowed =
    284        ServiceWorkersStorageAllowedForGlobal(aInnerWindow->AsGlobal());
    285    MOZ_ASSERT(isAboutBlankURL || isBlobURL || isStorageAllowed);
    286  }
    287 #endif
    288 
    289  nsPIDOMWindowOuter* outer = aInnerWindow->GetOuterWindow();
    290  NS_ENSURE_TRUE(outer, NS_ERROR_UNEXPECTED);
    291 
    292  FrameType frameType = FrameType::Top_level;
    293  if (!outer->GetBrowsingContext()->IsTop()) {
    294    frameType = FrameType::Nested;
    295  } else if (outer->GetBrowsingContext()->HadOriginalOpener()) {
    296    frameType = FrameType::Auxiliary;
    297  }
    298 
    299  // We should either be setting a window execution ready for the
    300  // first time or setting the same window execution ready again.
    301  // The secondary calls are due to initial about:blank replacement.
    302  MOZ_DIAGNOSTIC_ASSERT(mOwner.is<Nothing>() ||
    303                        mOwner.is<nsCOMPtr<nsIDocShell>>() ||
    304                        GetInnerWindow() == aInnerWindow);
    305 
    306  // This creates a cycle with the window.  It is broken when
    307  // nsGlobalWindow::FreeInnerObjects() deletes the ClientSource.
    308  mOwner = AsVariant(RefPtr<nsPIDOMWindowInner>(aInnerWindow));
    309 
    310  ClientSourceExecutionReadyArgs args(spec, frameType);
    311  ExecutionReady(args);
    312 
    313  return NS_OK;
    314 }
    315 
    316 nsresult ClientSource::DocShellExecutionReady(nsIDocShell* aDocShell) {
    317  MOZ_ASSERT(NS_IsMainThread());
    318  MOZ_DIAGNOSTIC_ASSERT(aDocShell);
    319 
    320  if (IsShutdown()) {
    321    return NS_OK;
    322  }
    323 
    324  nsPIDOMWindowOuter* outer = aDocShell->GetWindow();
    325  if (NS_WARN_IF(!outer)) {
    326    return NS_ERROR_UNEXPECTED;
    327  }
    328 
    329  // Note: We don't assert storage access for a controlled client.  If
    330  // the about:blank actually gets used then WindowExecutionReady() will
    331  // get called which asserts storage access.
    332 
    333  // TODO: dedupe this with WindowExecutionReady
    334  FrameType frameType = FrameType::Top_level;
    335  if (!outer->GetBrowsingContext()->IsTop()) {
    336    frameType = FrameType::Nested;
    337  } else if (outer->GetBrowsingContext()->HadOriginalOpener()) {
    338    frameType = FrameType::Auxiliary;
    339  }
    340 
    341  MOZ_DIAGNOSTIC_ASSERT(mOwner.is<Nothing>());
    342 
    343  // This creates a cycle with the docshell.  It is broken when
    344  // nsDocShell::Destroy() deletes the ClientSource.
    345  mOwner = AsVariant(nsCOMPtr<nsIDocShell>(aDocShell));
    346 
    347  ClientSourceExecutionReadyArgs args("about:blank"_ns, frameType);
    348  ExecutionReady(args);
    349 
    350  return NS_OK;
    351 }
    352 
    353 void ClientSource::Freeze() {
    354  MaybeExecute([](PClientSourceChild* aActor) { aActor->SendFreeze(); });
    355 }
    356 
    357 void ClientSource::Thaw() {
    358  MaybeExecute([](PClientSourceChild* aActor) { aActor->SendThaw(); });
    359 }
    360 
    361 void ClientSource::EvictFromBFCache() {
    362  if (nsCOMPtr<nsPIDOMWindowInner> win = GetInnerWindow()) {
    363    win->RemoveFromBFCacheSync();
    364  } else if (WorkerPrivate* vp = GetWorkerPrivate()) {
    365    vp->EvictFromBFCache();
    366  }
    367 }
    368 
    369 RefPtr<ClientOpPromise> ClientSource::EvictFromBFCacheOp() {
    370  EvictFromBFCache();
    371  return ClientOpPromise::CreateAndResolve(CopyableErrorResult(), __func__);
    372 }
    373 
    374 const ClientInfo& ClientSource::Info() const { return mClientInfo; }
    375 
    376 void ClientSource::WorkerSyncPing(WorkerPrivate* aWorkerPrivate) {
    377  NS_ASSERT_OWNINGTHREAD(ClientSource);
    378  MOZ_DIAGNOSTIC_ASSERT(aWorkerPrivate);
    379 
    380  if (IsShutdown()) {
    381    return;
    382  }
    383 
    384  // We need to make sure the mainthread is unblocked.
    385  AutoYieldJSThreadExecution yield;
    386 
    387  MOZ_DIAGNOSTIC_ASSERT(aWorkerPrivate == mManager->GetWorkerPrivate());
    388  aWorkerPrivate->AssertIsOnWorkerThread();
    389  MOZ_DIAGNOSTIC_ASSERT(GetActor());
    390 
    391  GetActor()->SendWorkerSyncPing();
    392 }
    393 
    394 void ClientSource::SetController(
    395    const ServiceWorkerDescriptor& aServiceWorker) {
    396  NS_ASSERT_OWNINGTHREAD(ClientSource);
    397 
    398  // We should never have a cross-origin controller.  Since this would be
    399  // same-origin policy violation we do a full release assertion here.
    400  MOZ_RELEASE_ASSERT(ClientMatchPrincipalInfo(mClientInfo.PrincipalInfo(),
    401                                              aServiceWorker.PrincipalInfo()));
    402 
    403  // A client without access to storage should never be controlled a
    404  // a service worker.  If we are already execution ready with a real
    405  // window or worker, then verify assert the storage policy is correct.
    406  //
    407  // Note, explicitly avoid checking storage policy for clients that inherit
    408  // service workers from their parent.  This basically means blob: URLs
    409  // and about:blank windows.
    410 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
    411  if (GetInnerWindow()) {
    412    bool IsAboutBlankURL = Info().URL().LowerCaseEqualsLiteral("about:blank");
    413    bool IsBlobURL = StringBeginsWith(Info().URL(), "blob:"_ns);
    414    bool IsStorageAllowed = ServiceWorkersStorageAllowedForGlobal(GetGlobal());
    415    MOZ_DIAGNOSTIC_ASSERT(IsAboutBlankURL || IsBlobURL || IsStorageAllowed);
    416  } else if (GetWorkerPrivate()) {
    417    MOZ_DIAGNOSTIC_ASSERT(
    418        ServiceWorkersStorageAllowedForGlobal(GetGlobal()) ||
    419        StringBeginsWith(GetWorkerPrivate()->ScriptURL(), u"blob:"_ns));
    420  }
    421 #endif
    422 
    423  if (mController.isSome() && mController.ref() == aServiceWorker) {
    424    return;
    425  }
    426 
    427  mController.reset();
    428  mController.emplace(aServiceWorker);
    429 
    430  RefPtr<ServiceWorkerContainer> swc;
    431  nsPIDOMWindowInner* window = GetInnerWindow();
    432  if (window) {
    433    swc = window->Navigator()->ServiceWorker();
    434  }
    435 
    436  // TODO: Also self.navigator.serviceWorker on workers when its exposed there
    437 
    438  if (swc && nsContentUtils::IsSafeToRunScript()) {
    439    swc->ControllerChanged(IgnoreErrors());
    440  }
    441 }
    442 
    443 RefPtr<ClientOpPromise> ClientSource::Control(
    444    const ClientControlledArgs& aArgs) {
    445  NS_ASSERT_OWNINGTHREAD(ClientSource);
    446 
    447  // Determine if the client is allowed to be controlled.  Currently we
    448  // prevent service workers from controlling clients that cannot access
    449  // storage.  We exempt this restriction for local URL clients, like
    450  // about:blank and blob:, since access to service workers is dictated by their
    451  // parent.
    452  //
    453  // Note, we default to allowing the client to be controlled in the case
    454  // where we are not execution ready yet.  This can only happen if the
    455  // the non-subresource load is intercepted by a service worker.  Since
    456  // ServiceWorkerInterceptController() uses StorageAllowedForChannel()
    457  // it should be fine to accept these control messages.
    458  //
    459  // Its also fine to default to allowing ClientSource attached to a docshell
    460  // to be controlled.  These clients represent inital about:blank windows
    461  // that do not have an inner window created yet.  We explicitly allow initial
    462  // about:blank.
    463  bool controlAllowed = true;
    464  if (GetInnerWindow()) {
    465    // Local URL windows and windows with access to storage can be controlled.
    466    bool isAboutBlankURL = Info().URL().LowerCaseEqualsLiteral("about:blank");
    467    bool isBlobURL = StringBeginsWith(Info().URL(), "blob:"_ns);
    468    bool isStorageAllowed = ServiceWorkersStorageAllowedForGlobal(GetGlobal());
    469    controlAllowed = isAboutBlankURL || isBlobURL || isStorageAllowed;
    470  } else if (GetWorkerPrivate()) {
    471    // Local URL workers and workers with access to storage can be controlled.
    472    controlAllowed =
    473        ServiceWorkersStorageAllowedForGlobal(GetGlobal()) ||
    474        StringBeginsWith(GetWorkerPrivate()->ScriptURL(), u"blob:"_ns);
    475  }
    476 
    477  if (NS_WARN_IF(!controlAllowed)) {
    478    CopyableErrorResult rv;
    479    rv.ThrowInvalidStateError("Client cannot be controlled");
    480    return ClientOpPromise::CreateAndReject(rv, __func__);
    481  }
    482 
    483  SetController(ServiceWorkerDescriptor(aArgs.serviceWorker()));
    484 
    485  return ClientOpPromise::CreateAndResolve(CopyableErrorResult(), __func__);
    486 }
    487 
    488 void ClientSource::InheritController(
    489    const ServiceWorkerDescriptor& aServiceWorker) {
    490  NS_ASSERT_OWNINGTHREAD(ClientSource);
    491 
    492  // Tell the parent-side ClientManagerService that the controller was
    493  // inherited.  This is necessary for clients.matchAll() to work properly.
    494  // In parent-side intercept mode this will also note the inheritance in
    495  // the parent-side SWM.
    496  MaybeExecute([aServiceWorker](PClientSourceChild* aActor) {
    497    aActor->SendInheritController(ClientControlledArgs(aServiceWorker.ToIPC()));
    498  });
    499 
    500  // Finally, record the new controller in our local ClientSource for any
    501  // immediate synchronous access.
    502  SetController(aServiceWorker);
    503 }
    504 
    505 const Maybe<ServiceWorkerDescriptor>& ClientSource::GetController() const {
    506  return mController;
    507 }
    508 
    509 void ClientSource::NoteDOMContentLoaded() {
    510  MaybeExecute(
    511      [](PClientSourceChild* aActor) { aActor->SendNoteDOMContentLoaded(); });
    512 }
    513 
    514 RefPtr<ClientOpPromise> ClientSource::Focus(const ClientFocusArgs& aArgs) {
    515  NS_ASSERT_OWNINGTHREAD(ClientSource);
    516 
    517  if (mClientInfo.Type() != ClientType::Window) {
    518    CopyableErrorResult rv;
    519    rv.ThrowNotSupportedError("Not a Window client");
    520    return ClientOpPromise::CreateAndReject(rv, __func__);
    521  }
    522  nsCOMPtr<nsPIDOMWindowOuter> outer;
    523  nsPIDOMWindowInner* inner = GetInnerWindow();
    524  if (inner) {
    525    outer = inner->GetOuterWindow();
    526  } else {
    527    nsIDocShell* docshell = GetDocShell();
    528    if (docshell) {
    529      outer = docshell->GetWindow();
    530    }
    531  }
    532 
    533  if (!outer) {
    534    CopyableErrorResult rv;
    535    rv.ThrowInvalidStateError("Browsing context discarded");
    536    return ClientOpPromise::CreateAndReject(rv, __func__);
    537  }
    538 
    539  MOZ_ASSERT(NS_IsMainThread());
    540  nsFocusManager::FocusWindow(outer, aArgs.callerType());
    541 
    542  Result<ClientState, ErrorResult> state = SnapshotState();
    543  if (state.isErr()) {
    544    return ClientOpPromise::CreateAndReject(
    545        CopyableErrorResult(state.unwrapErr()), __func__);
    546  }
    547 
    548  return ClientOpPromise::CreateAndResolve(state.inspect().ToIPC(), __func__);
    549 }
    550 
    551 RefPtr<ClientOpPromise> ClientSource::PostMessage(
    552    const ClientPostMessageArgs& aArgs) {
    553  NS_ASSERT_OWNINGTHREAD(ClientSource);
    554 
    555  // (This only returns a global for windows or workers.)
    556  nsIGlobalObject* global = GetGlobal();
    557  if (global) {
    558    const RefPtr<ServiceWorkerContainer> container =
    559        global->GetServiceWorkerContainer();
    560    MOZ_ASSERT_DEBUG_OR_FUZZING(container);
    561 
    562    // Note, EvictFromBFCache() may delete the ClientSource object
    563    // when bfcache lives in the child process.
    564    EvictFromBFCache();
    565    container->ReceiveMessage(aArgs);
    566    return ClientOpPromise::CreateAndResolve(CopyableErrorResult(), __func__);
    567  }
    568 
    569  CopyableErrorResult rv;
    570  rv.ThrowInvalidStateError("Global discarded");
    571  return ClientOpPromise::CreateAndReject(rv, __func__);
    572 }
    573 
    574 RefPtr<ClientOpPromise> ClientSource::GetInfoAndState(
    575    const ClientGetInfoAndStateArgs& aArgs) {
    576  Result<ClientState, ErrorResult> state = SnapshotState();
    577  if (state.isErr()) {
    578    return ClientOpPromise::CreateAndReject(
    579        CopyableErrorResult(state.unwrapErr()), __func__);
    580  }
    581 
    582  return ClientOpPromise::CreateAndResolve(
    583      ClientInfoAndState(mClientInfo.ToIPC(), state.inspect().ToIPC()),
    584      __func__);
    585 }
    586 
    587 Result<ClientState, ErrorResult> ClientSource::SnapshotState() {
    588  NS_ASSERT_OWNINGTHREAD(ClientSource);
    589 
    590  if (mClientInfo.Type() == ClientType::Window) {
    591    // If there is a docshell, try to create a document, too.
    592    MOZ_TRY(MaybeCreateInitialDocument());
    593    // SnapshotWindowState can deal with a missing inner window
    594    return SnapshotWindowState();
    595  }
    596 
    597  WorkerPrivate* workerPrivate = GetWorkerPrivate();
    598  if (!workerPrivate) {
    599    ErrorResult rv;
    600    rv.ThrowInvalidStateError("Worker terminated");
    601    return Err(std::move(rv));
    602  }
    603 
    604  return ClientState(ClientWorkerState(workerPrivate->StorageAccess()));
    605 }
    606 
    607 nsISerialEventTarget* ClientSource::EventTarget() const { return mEventTarget; }
    608 
    609 void ClientSource::SetPolicyContainer(nsIPolicyContainer* aPolicyContainer) {
    610  NS_ASSERT_OWNINGTHREAD(ClientSource);
    611  if (!aPolicyContainer) {
    612    return;
    613  }
    614 
    615  mozilla::ipc::PolicyContainerArgs policyContainerArgs;
    616  PolicyContainer::ToArgs(PolicyContainer::Cast(aPolicyContainer),
    617                          policyContainerArgs);
    618 
    619  SetPolicyContainerArgs(policyContainerArgs);
    620 }
    621 
    622 void ClientSource::SetPreloadCsp(nsIContentSecurityPolicy* aPreloadCsp) {
    623  NS_ASSERT_OWNINGTHREAD(ClientSource);
    624  if (!aPreloadCsp) {
    625    return;
    626  }
    627 
    628  CSPInfo cspPreloadInfo;
    629  nsresult rv = CSPToCSPInfo(aPreloadCsp, &cspPreloadInfo);
    630  if (NS_WARN_IF(NS_FAILED(rv))) {
    631    return;
    632  }
    633  mClientInfo.SetPreloadCspInfo(cspPreloadInfo);
    634 }
    635 
    636 void ClientSource::SetPolicyContainerArgs(
    637    const mozilla::ipc::PolicyContainerArgs& aPolicyContainer) {
    638  NS_ASSERT_OWNINGTHREAD(ClientSource);
    639  mClientInfo.SetPolicyContainerArgs(aPolicyContainer);
    640 }
    641 
    642 const Maybe<mozilla::ipc::PolicyContainerArgs>&
    643 ClientSource::GetPolicyContainerArgs() {
    644  NS_ASSERT_OWNINGTHREAD(ClientSource);
    645  return mClientInfo.GetPolicyContainerArgs();
    646 }
    647 
    648 void ClientSource::Traverse(nsCycleCollectionTraversalCallback& aCallback,
    649                            const char* aName, uint32_t aFlags) {
    650  if (mOwner.is<RefPtr<nsPIDOMWindowInner>>()) {
    651    ImplCycleCollectionTraverse(
    652        aCallback, mOwner.as<RefPtr<nsPIDOMWindowInner>>(), aName, aFlags);
    653  } else if (mOwner.is<nsCOMPtr<nsIDocShell>>()) {
    654    ImplCycleCollectionTraverse(aCallback, mOwner.as<nsCOMPtr<nsIDocShell>>(),
    655                                aName, aFlags);
    656  }
    657 }
    658 
    659 void ClientSource::NoteCalledRegisterForServiceWorkerScope(
    660    const nsACString& aScope) {
    661  if (mRegisteringScopeList.Contains(aScope)) {
    662    return;
    663  }
    664  mRegisteringScopeList.AppendElement(aScope);
    665 }
    666 
    667 bool ClientSource::CalledRegisterForServiceWorkerScope(
    668    const nsACString& aScope) {
    669  return mRegisteringScopeList.Contains(aScope);
    670 }
    671 
    672 nsIPrincipal* ClientSource::GetPrincipal() {
    673  MOZ_ASSERT(NS_IsMainThread());
    674 
    675  // We only create the principal if necessary because creating a principal is
    676  // expensive.
    677  if (!mPrincipal) {
    678    auto principalOrErr = Info().GetPrincipal();
    679    nsCOMPtr<nsIPrincipal> prin =
    680        principalOrErr.isOk() ? principalOrErr.unwrap() : nullptr;
    681 
    682    mPrincipal.emplace(prin);
    683  }
    684 
    685  return mPrincipal.ref();
    686 }
    687 
    688 }  // namespace mozilla::dom