tor-browser

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

ClientManagerService.cpp (24163B)


      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 "ClientManagerService.h"
      8 
      9 #include "ClientHandleParent.h"
     10 #include "ClientManagerParent.h"
     11 #include "ClientNavigateOpParent.h"
     12 #include "ClientOpenWindowUtils.h"
     13 #include "ClientPrincipalUtils.h"
     14 #include "ClientSourceParent.h"
     15 #include "jsfriendapi.h"
     16 #include "mozilla/ClearOnShutdown.h"
     17 #include "mozilla/MozPromise.h"
     18 #include "mozilla/SchedulerGroup.h"
     19 #include "mozilla/ScopeExit.h"
     20 #include "mozilla/dom/ContentParent.h"
     21 #include "mozilla/dom/ServiceWorkerManager.h"
     22 #include "mozilla/dom/ServiceWorkerUtils.h"
     23 #include "mozilla/ipc/BackgroundParent.h"
     24 #include "mozilla/ipc/PBackgroundSharedTypes.h"
     25 #include "nsIAsyncShutdown.h"
     26 #include "nsIXULRuntime.h"
     27 #include "nsProxyRelease.h"
     28 
     29 namespace mozilla::dom {
     30 
     31 using mozilla::ipc::AssertIsOnBackgroundThread;
     32 using mozilla::ipc::PrincipalInfo;
     33 
     34 namespace {
     35 
     36 ClientManagerService* sClientManagerServiceInstance = nullptr;
     37 bool sClientManagerServiceShutdownRegistered = false;
     38 
     39 class ClientShutdownBlocker final : public nsIAsyncShutdownBlocker {
     40  RefPtr<GenericPromise::Private> mPromise;
     41 
     42  ~ClientShutdownBlocker() = default;
     43 
     44 public:
     45  explicit ClientShutdownBlocker(GenericPromise::Private* aPromise)
     46      : mPromise(aPromise) {
     47    MOZ_DIAGNOSTIC_ASSERT(mPromise);
     48  }
     49 
     50  NS_IMETHOD
     51  GetName(nsAString& aNameOut) override {
     52    aNameOut = nsLiteralString(
     53        u"ClientManagerService: start destroying IPC actors early");
     54    return NS_OK;
     55  }
     56 
     57  NS_IMETHOD
     58  BlockShutdown(nsIAsyncShutdownClient* aClient) override {
     59    mPromise->Resolve(true, __func__);
     60    aClient->RemoveBlocker(this);
     61    return NS_OK;
     62  }
     63 
     64  NS_IMETHOD
     65  GetState(nsIPropertyBag**) override { return NS_OK; }
     66 
     67  NS_DECL_ISUPPORTS
     68 };
     69 
     70 NS_IMPL_ISUPPORTS(ClientShutdownBlocker, nsIAsyncShutdownBlocker)
     71 
     72 // Helper function the resolves a MozPromise when we detect that the browser
     73 // has begun to shutdown.
     74 RefPtr<GenericPromise> OnShutdown() {
     75  RefPtr<GenericPromise::Private> ref = new GenericPromise::Private(__func__);
     76 
     77  nsCOMPtr<nsIRunnable> r =
     78      NS_NewRunnableFunction("ClientManagerServer::OnShutdown", [ref]() {
     79        nsCOMPtr<nsIAsyncShutdownService> svc =
     80            services::GetAsyncShutdownService();
     81        if (!svc) {
     82          ref->Resolve(true, __func__);
     83          return;
     84        }
     85 
     86        nsCOMPtr<nsIAsyncShutdownClient> phase;
     87        MOZ_ALWAYS_SUCCEEDS(svc->GetXpcomWillShutdown(getter_AddRefs(phase)));
     88        if (!phase) {
     89          ref->Resolve(true, __func__);
     90          return;
     91        }
     92 
     93        nsCOMPtr<nsIAsyncShutdownBlocker> blocker =
     94            new ClientShutdownBlocker(ref);
     95        nsresult rv =
     96            phase->AddBlocker(blocker, NS_LITERAL_STRING_FROM_CSTRING(__FILE__),
     97                              __LINE__, u"ClientManagerService shutdown"_ns);
     98 
     99        if (NS_FAILED(rv)) {
    100          ref->Resolve(true, __func__);
    101          return;
    102        }
    103      });
    104 
    105  MOZ_ALWAYS_SUCCEEDS(SchedulerGroup::Dispatch(r.forget()));
    106 
    107  return ref;
    108 }
    109 
    110 }  // anonymous namespace
    111 
    112 ClientManagerService::FutureClientSourceParent::FutureClientSourceParent(
    113    const IPCClientInfo& aClientInfo)
    114    : mPrincipalInfo(aClientInfo.principalInfo()), mAssociated(false) {}
    115 
    116 ClientManagerService::ClientManagerService() : mShutdown(false) {
    117  AssertIsOnBackgroundThread();
    118 
    119  // Only register one shutdown handler at a time.  If a previous service
    120  // instance did this, but shutdown has not come, then we can avoid
    121  // doing it again.
    122  if (!sClientManagerServiceShutdownRegistered) {
    123    sClientManagerServiceShutdownRegistered = true;
    124 
    125    // While the ClientManagerService will be gracefully terminated as windows
    126    // and workers are naturally killed, this can cause us to do extra work
    127    // relatively late in the shutdown process.  To avoid this we eagerly begin
    128    // shutdown at the first sign it has begun.  Since we handle normal shutdown
    129    // gracefully we don't really need to block anything here.  We just begin
    130    // destroying our IPC actors immediately.
    131    OnShutdown()->Then(GetCurrentSerialEventTarget(), __func__, []() {
    132      // Look up the latest service instance, if it exists.  This may
    133      // be different from the instance that registered the shutdown
    134      // handler.
    135      RefPtr<ClientManagerService> svc = ClientManagerService::GetInstance();
    136      if (svc) {
    137        svc->Shutdown();
    138      }
    139    });
    140  }
    141 }
    142 
    143 ClientManagerService::~ClientManagerService() {
    144  AssertIsOnBackgroundThread();
    145  MOZ_DIAGNOSTIC_ASSERT(mSourceTable.Count() == 0);
    146  MOZ_DIAGNOSTIC_ASSERT(mManagerList.IsEmpty());
    147 
    148  MOZ_DIAGNOSTIC_ASSERT(sClientManagerServiceInstance == this);
    149  sClientManagerServiceInstance = nullptr;
    150 }
    151 
    152 void ClientManagerService::Shutdown() {
    153  AssertIsOnBackgroundThread();
    154  MOZ_DIAGNOSTIC_ASSERT(sClientManagerServiceShutdownRegistered);
    155 
    156  // If many ClientManagerService are created and destroyed quickly we can
    157  // in theory get more than one shutdown listener calling us.
    158  if (mShutdown) {
    159    return;
    160  }
    161  mShutdown = true;
    162 
    163  // Begin destroying our various manager actors which will in turn destroy
    164  // all source, handle, and operation actors.
    165  for (auto actor :
    166       CopyableAutoTArray<ClientManagerParent*, 16>(mManagerList)) {
    167    (void)PClientManagerParent::Send__delete__(actor);
    168  }
    169 
    170  // Destroying manager actors should've also destroyed all source actors, so
    171  // the only sources left will be future sources, which need to be aborted.
    172  for (auto& entry : mSourceTable) {
    173    MOZ_RELEASE_ASSERT(entry.GetData().is<FutureClientSourceParent>());
    174    CopyableErrorResult rv;
    175    rv.ThrowInvalidStateError("Client creation aborted.");
    176    entry.GetModifiableData()
    177        ->as<FutureClientSourceParent>()
    178        .RejectPromiseIfExists(rv);
    179  }
    180  mSourceTable.Clear();
    181 }
    182 
    183 ClientSourceParent* ClientManagerService::MaybeUnwrapAsExistingSource(
    184    const SourceTableEntry& aEntry) const {
    185  AssertIsOnBackgroundThread();
    186 
    187  if (aEntry.is<FutureClientSourceParent>()) {
    188    return nullptr;
    189  }
    190 
    191  return aEntry.as<ClientSourceParent*>();
    192 }
    193 
    194 ClientSourceParent* ClientManagerService::FindExistingSource(
    195    const nsID& aID, const PrincipalInfo& aPrincipalInfo) const {
    196  AssertIsOnBackgroundThread();
    197 
    198  auto entry = mSourceTable.Lookup(aID);
    199 
    200  if (!entry) {
    201    return nullptr;
    202  }
    203 
    204  ClientSourceParent* source = MaybeUnwrapAsExistingSource(entry.Data());
    205 
    206  if (!source || NS_WARN_IF(!ClientMatchPrincipalInfo(
    207                     source->Info().PrincipalInfo(), aPrincipalInfo))) {
    208    return nullptr;
    209  }
    210  return source;
    211 }
    212 
    213 // static
    214 already_AddRefed<ClientManagerService>
    215 ClientManagerService::GetOrCreateInstance() {
    216  AssertIsOnBackgroundThread();
    217 
    218  if (!sClientManagerServiceInstance) {
    219    sClientManagerServiceInstance = new ClientManagerService();
    220  }
    221 
    222  RefPtr<ClientManagerService> ref(sClientManagerServiceInstance);
    223  return ref.forget();
    224 }
    225 
    226 // static
    227 already_AddRefed<ClientManagerService> ClientManagerService::GetInstance() {
    228  AssertIsOnBackgroundThread();
    229 
    230  if (!sClientManagerServiceInstance) {
    231    return nullptr;
    232  }
    233 
    234  RefPtr<ClientManagerService> ref(sClientManagerServiceInstance);
    235  return ref.forget();
    236 }
    237 
    238 namespace {
    239 
    240 bool IsNullPrincipalInfo(const PrincipalInfo& aPrincipalInfo) {
    241  return aPrincipalInfo.type() == PrincipalInfo::TNullPrincipalInfo;
    242 }
    243 
    244 bool AreBothNullPrincipals(const PrincipalInfo& aPrincipalInfo1,
    245                           const PrincipalInfo& aPrincipalInfo2) {
    246  return IsNullPrincipalInfo(aPrincipalInfo1) &&
    247         IsNullPrincipalInfo(aPrincipalInfo2);
    248 }
    249 
    250 }  // anonymous namespace
    251 
    252 bool ClientManagerService::AddSource(ClientSourceParent* aSource) {
    253  AssertIsOnBackgroundThread();
    254  MOZ_ASSERT(aSource);
    255 
    256  auto entry = mSourceTable.Lookup(aSource->Info().Id());
    257  if (entry) {
    258    // Do not permit overwriting an existing ClientSource with the same
    259    // UUID.  This would allow a spoofed ClientParentSource actor to
    260    // intercept postMessage() intended for the real actor.
    261    if (entry.Data().is<ClientSourceParent*>()) {
    262      return false;
    263    }
    264    FutureClientSourceParent& placeHolder =
    265        entry.Data().as<FutureClientSourceParent>();
    266 
    267    const PrincipalInfo& placeHolderPrincipalInfo = placeHolder.PrincipalInfo();
    268    const PrincipalInfo& sourcePrincipalInfo = aSource->Info().PrincipalInfo();
    269 
    270    // The placeholder FutureClientSourceParent's PrincipalInfo must match the
    271    // real ClientSourceParent's PrincipalInfo. The only exception is if both
    272    // are null principals (two null principals are considered unequal).
    273    if (!AreBothNullPrincipals(placeHolderPrincipalInfo, sourcePrincipalInfo) &&
    274        NS_WARN_IF(!ClientMatchPrincipalInfo(placeHolderPrincipalInfo,
    275                                             sourcePrincipalInfo))) {
    276      return false;
    277    }
    278 
    279    placeHolder.ResolvePromiseIfExists();
    280    *entry = AsVariant(aSource);
    281    return true;
    282  }
    283  if (!mSourceTable.WithEntryHandle(aSource->Info().Id(),
    284                                    [&aSource](auto&& entry) {
    285                                      if (NS_WARN_IF(entry.HasEntry())) {
    286                                        return false;
    287                                      }
    288                                      entry.Insert(AsVariant(aSource));
    289                                      return true;
    290                                    })) {
    291    return false;
    292  }
    293  return true;
    294 }
    295 
    296 bool ClientManagerService::RemoveSource(ClientSourceParent* aSource) {
    297  AssertIsOnBackgroundThread();
    298  MOZ_ASSERT(aSource);
    299  auto entry = mSourceTable.Lookup(aSource->Info().Id());
    300  if (NS_WARN_IF(!entry)) {
    301    return false;
    302  }
    303  entry.Remove();
    304  return true;
    305 }
    306 
    307 bool ClientManagerService::ExpectFutureSource(
    308    const IPCClientInfo& aClientInfo) {
    309  AssertIsOnBackgroundThread();
    310 
    311  if (!mSourceTable.WithEntryHandle(
    312          aClientInfo.id(), [&aClientInfo](auto&& entry) {
    313            // Prevent overwrites.
    314            if (entry.HasEntry()) {
    315              return false;
    316            }
    317            entry.Insert(SourceTableEntry(
    318                VariantIndex<0>(), FutureClientSourceParent(aClientInfo)));
    319            return true;
    320          })) {
    321    return false;
    322  }
    323 
    324  return true;
    325 }
    326 
    327 void ClientManagerService::ForgetFutureSource(
    328    const IPCClientInfo& aClientInfo) {
    329  AssertIsOnBackgroundThread();
    330 
    331  auto entry = mSourceTable.Lookup(aClientInfo.id());
    332 
    333  if (entry) {
    334    if (entry.Data().is<ClientSourceParent*>()) {
    335      return;
    336    }
    337 
    338    // For non-e10s case, ClientChannelHelperParent will be freed before real
    339    // ClientSourceParnet be created. In the end this methoed will be called to
    340    // release the FutureClientSourceParent. That means a ClientHandle operation
    341    // which waits for the FutureClientSourceParent will have no chance to
    342    // connect to the ClientSourceParent. So the FutureClientSourceParent should
    343    // be keep in this case.
    344    // IsAssociated() makes sure there is a ClientHandle operation associated
    345    // with it.
    346    // More details please refer
    347    // https://bugzilla.mozilla.org/show_bug.cgi?id=1730350#c2
    348    if (!XRE_IsE10sParentProcess() &&
    349        entry.Data().as<FutureClientSourceParent>().IsAssociated()) {
    350      return;
    351    }
    352 
    353    CopyableErrorResult rv;
    354    rv.ThrowInvalidStateError("Client creation aborted.");
    355    entry.Data().as<FutureClientSourceParent>().RejectPromiseIfExists(rv);
    356 
    357    entry.Remove();
    358  }
    359 }
    360 
    361 RefPtr<SourcePromise> ClientManagerService::FindSource(
    362    const nsID& aID, const PrincipalInfo& aPrincipalInfo) {
    363  AssertIsOnBackgroundThread();
    364 
    365  auto entry = mSourceTable.Lookup(aID);
    366  if (!entry) {
    367    CopyableErrorResult rv;
    368    rv.ThrowInvalidStateError("Unknown client.");
    369    return SourcePromise::CreateAndReject(rv, __func__);
    370  }
    371 
    372  if (entry.Data().is<FutureClientSourceParent>()) {
    373    entry.Data().as<FutureClientSourceParent>().SetAsAssociated();
    374    return entry.Data().as<FutureClientSourceParent>().Promise();
    375  }
    376 
    377  ClientSourceParent* source = entry.Data().as<ClientSourceParent*>();
    378  if (NS_WARN_IF(!ClientMatchPrincipalInfo(source->Info().PrincipalInfo(),
    379                                           aPrincipalInfo))) {
    380    CopyableErrorResult rv;
    381    rv.ThrowInvalidStateError("Unknown client.");
    382    return SourcePromise::CreateAndReject(rv, __func__);
    383  }
    384 
    385  return SourcePromise::CreateAndResolve(true, __func__);
    386 }
    387 
    388 void ClientManagerService::AddManager(ClientManagerParent* aManager) {
    389  AssertIsOnBackgroundThread();
    390  MOZ_DIAGNOSTIC_ASSERT(aManager);
    391  MOZ_ASSERT(!mManagerList.Contains(aManager));
    392  mManagerList.AppendElement(aManager);
    393 
    394  // If shutdown has already begun then immediately destroy the actor.
    395  if (mShutdown) {
    396    (void)PClientManagerParent::Send__delete__(aManager);
    397  }
    398 }
    399 
    400 void ClientManagerService::RemoveManager(ClientManagerParent* aManager) {
    401  AssertIsOnBackgroundThread();
    402  MOZ_DIAGNOSTIC_ASSERT(aManager);
    403  DebugOnly<bool> removed = mManagerList.RemoveElement(aManager);
    404  MOZ_ASSERT(removed);
    405 }
    406 
    407 RefPtr<ClientOpPromise> ClientManagerService::Navigate(
    408    ThreadsafeContentParentHandle* aOriginContent,
    409    const ClientNavigateArgs& aArgs) {
    410  ClientSourceParent* source =
    411      FindExistingSource(aArgs.target().id(), aArgs.target().principalInfo());
    412  if (!source) {
    413    CopyableErrorResult rv;
    414    rv.ThrowInvalidStateError("Unknown client");
    415    return ClientOpPromise::CreateAndReject(rv, __func__);
    416  }
    417 
    418  const IPCServiceWorkerDescriptor& serviceWorker = aArgs.serviceWorker();
    419 
    420  // Per https://w3c.github.io/ServiceWorker/#dom-windowclient-navigate step 4,
    421  // if the service worker does not control the client, reject with a TypeError.
    422  const Maybe<ServiceWorkerDescriptor>& controller = source->GetController();
    423  if (controller.isNothing() ||
    424      controller.ref().Scope() != serviceWorker.scope() ||
    425      controller.ref().Id() != serviceWorker.id()) {
    426    CopyableErrorResult rv;
    427    rv.ThrowTypeError("Client is not controlled by this Service Worker");
    428    return ClientOpPromise::CreateAndReject(rv, __func__);
    429  }
    430 
    431  PClientManagerParent* manager = source->Manager();
    432  MOZ_DIAGNOSTIC_ASSERT(manager);
    433 
    434  // This is safe to do because the ClientSourceChild cannot directly delete
    435  // itself.  Instead it sends a Teardown message to the parent which then
    436  // calls delete.  That means we can be sure that we are not racing with
    437  // source destruction here.
    438  ClientNavigateOpConstructorArgs args(WrapNotNull(source), aArgs.url(),
    439                                       aArgs.baseURL());
    440 
    441  RefPtr<ClientOpPromise::Private> promise =
    442      new ClientOpPromise::Private(__func__);
    443 
    444  ClientNavigateOpParent* op = new ClientNavigateOpParent(args, promise);
    445  PClientNavigateOpParent* result =
    446      manager->SendPClientNavigateOpConstructor(op, args);
    447  if (!result) {
    448    CopyableErrorResult rv;
    449    rv.ThrowInvalidStateError("Client is aborted");
    450    promise->Reject(rv, __func__);
    451  }
    452 
    453  return promise;
    454 }
    455 
    456 namespace {
    457 
    458 class PromiseListHolder final {
    459  RefPtr<ClientOpPromise::Private> mResultPromise;
    460  nsTArray<RefPtr<ClientOpPromise>> mPromiseList;
    461  nsTArray<ClientInfoAndState> mResultList;
    462  uint32_t mOutstandingPromiseCount;
    463 
    464  void ProcessSuccess(const ClientInfoAndState& aResult) {
    465    mResultList.AppendElement(aResult);
    466    ProcessCompletion();
    467  }
    468 
    469  void ProcessCompletion() {
    470    MOZ_DIAGNOSTIC_ASSERT(mOutstandingPromiseCount > 0);
    471    mOutstandingPromiseCount -= 1;
    472    MaybeFinish();
    473  }
    474 
    475  ~PromiseListHolder() = default;
    476 
    477 public:
    478  PromiseListHolder()
    479      : mResultPromise(new ClientOpPromise::Private(__func__)),
    480        mOutstandingPromiseCount(0) {}
    481 
    482  RefPtr<ClientOpPromise> GetResultPromise() {
    483    RefPtr<PromiseListHolder> kungFuDeathGrip = this;
    484    return mResultPromise->Then(
    485        GetCurrentSerialEventTarget(), __func__,
    486        [kungFuDeathGrip](const ClientOpPromise::ResolveOrRejectValue& aValue) {
    487          return ClientOpPromise::CreateAndResolveOrReject(aValue, __func__);
    488        });
    489  }
    490 
    491  void AddPromise(RefPtr<ClientOpPromise>&& aPromise) {
    492    mPromiseList.AppendElement(std::move(aPromise));
    493    MOZ_DIAGNOSTIC_ASSERT(mPromiseList.LastElement());
    494    mOutstandingPromiseCount += 1;
    495 
    496    RefPtr<PromiseListHolder> self(this);
    497    mPromiseList.LastElement()->Then(
    498        GetCurrentSerialEventTarget(), __func__,
    499        [self](const ClientOpResult& aResult) {
    500          // TODO: This is pretty clunky.  Try to figure out a better
    501          //       wait for MatchAll() and Claim() to share this code
    502          //       even though they expect different return values.
    503          if (aResult.type() == ClientOpResult::TClientInfoAndState) {
    504            self->ProcessSuccess(aResult.get_ClientInfoAndState());
    505          } else {
    506            self->ProcessCompletion();
    507          }
    508        },
    509        [self](const CopyableErrorResult& aResult) {
    510          self->ProcessCompletion();
    511        });
    512  }
    513 
    514  void MaybeFinish() {
    515    if (!mOutstandingPromiseCount) {
    516      mResultPromise->Resolve(CopyableTArray(mResultList.Clone()), __func__);
    517    }
    518  }
    519 
    520  NS_INLINE_DECL_REFCOUNTING(PromiseListHolder)
    521 };
    522 
    523 }  // anonymous namespace
    524 
    525 RefPtr<ClientOpPromise> ClientManagerService::MatchAll(
    526    ThreadsafeContentParentHandle* aOriginContent,
    527    const ClientMatchAllArgs& aArgs) {
    528  AssertIsOnBackgroundThread();
    529 
    530  ServiceWorkerDescriptor swd(aArgs.serviceWorker());
    531  const PrincipalInfo& principalInfo = swd.PrincipalInfo();
    532 
    533  RefPtr<PromiseListHolder> promiseList = new PromiseListHolder();
    534 
    535  for (const auto& entry : mSourceTable) {
    536    ClientSourceParent* source = MaybeUnwrapAsExistingSource(entry.GetData());
    537 
    538    if (!source || source->IsFrozen() || !source->ExecutionReady()) {
    539      continue;
    540    }
    541 
    542    if (aArgs.type() != ClientType::All &&
    543        source->Info().Type() != aArgs.type()) {
    544      continue;
    545    }
    546 
    547    if (!ClientMatchPrincipalInfo(source->Info().PrincipalInfo(),
    548                                  principalInfo)) {
    549      continue;
    550    }
    551 
    552    if (!aArgs.includeUncontrolled()) {
    553      const Maybe<ServiceWorkerDescriptor>& controller =
    554          source->GetController();
    555      if (controller.isNothing()) {
    556        continue;
    557      }
    558 
    559      if (controller.ref().Id() != swd.Id() ||
    560          controller.ref().Scope() != swd.Scope()) {
    561        continue;
    562      }
    563    }
    564 
    565    promiseList->AddPromise(source->StartOp(ClientGetInfoAndStateArgs(
    566        source->Info().Id(), source->Info().PrincipalInfo())));
    567  }
    568 
    569  // Maybe finish the promise now in case we didn't find any matching clients.
    570  promiseList->MaybeFinish();
    571 
    572  return promiseList->GetResultPromise();
    573 }
    574 
    575 namespace {
    576 
    577 RefPtr<ClientOpPromise> ClaimOnMainThread(
    578    const ClientInfo& aClientInfo, const ServiceWorkerDescriptor& aDescriptor) {
    579  RefPtr<ClientOpPromise::Private> promise =
    580      new ClientOpPromise::Private(__func__);
    581 
    582  nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(
    583      __func__, [promise, clientInfo = std::move(aClientInfo),
    584                 desc = std::move(aDescriptor)]() {
    585        auto scopeExit = MakeScopeExit([&] {
    586          // This will truncate the URLs if they have embedded nulls, if that
    587          // can happen, but for // our purposes here that's OK.
    588          nsPrintfCString err(
    589              "Service worker at <%s> can't claim Client at <%s>",
    590              desc.ScriptURL().get(), clientInfo.URL().get());
    591          CopyableErrorResult rv;
    592          rv.ThrowInvalidStateError(err);
    593          promise->Reject(rv, __func__);
    594        });
    595 
    596        RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
    597        NS_ENSURE_TRUE_VOID(swm);
    598 
    599        RefPtr<GenericErrorResultPromise> inner =
    600            swm->MaybeClaimClient(clientInfo, desc);
    601        inner->Then(
    602            GetMainThreadSerialEventTarget(), __func__,
    603            [promise](bool aResult) {
    604              promise->Resolve(CopyableErrorResult(), __func__);
    605            },
    606            [promise](const CopyableErrorResult& aRv) {
    607              promise->Reject(aRv, __func__);
    608            });
    609 
    610        scopeExit.release();
    611      });
    612 
    613  MOZ_ALWAYS_SUCCEEDS(SchedulerGroup::Dispatch(r.forget()));
    614 
    615  return promise;
    616 }
    617 
    618 }  // anonymous namespace
    619 
    620 RefPtr<ClientOpPromise> ClientManagerService::Claim(
    621    ThreadsafeContentParentHandle* aOriginContent,
    622    const ClientClaimArgs& aArgs) {
    623  AssertIsOnBackgroundThread();
    624 
    625  const IPCServiceWorkerDescriptor& serviceWorker = aArgs.serviceWorker();
    626  const PrincipalInfo& principalInfo = serviceWorker.principalInfo();
    627 
    628  RefPtr<PromiseListHolder> promiseList = new PromiseListHolder();
    629 
    630  for (const auto& entry : mSourceTable) {
    631    ClientSourceParent* source = MaybeUnwrapAsExistingSource(entry.GetData());
    632 
    633    if (!source) {
    634      continue;
    635    }
    636 
    637    if (!ClientMatchPrincipalInfo(source->Info().PrincipalInfo(),
    638                                  principalInfo)) {
    639      continue;
    640    }
    641 
    642    const Maybe<ServiceWorkerDescriptor>& controller = source->GetController();
    643    if (controller.isSome() &&
    644        controller.ref().Scope() == serviceWorker.scope() &&
    645        controller.ref().Id() == serviceWorker.id()) {
    646      continue;
    647    }
    648 
    649    // TODO: This logic to determine if a service worker should control
    650    //       a particular client should be moved to the ServiceWorkerManager.
    651    //       This can't happen until the SWM is moved to the parent process,
    652    //       though.
    653    if (!source->ExecutionReady() ||
    654        source->Info().Type() == ClientType::Serviceworker ||
    655        source->Info().URL().Find(serviceWorker.scope()) != 0) {
    656      continue;
    657    }
    658 
    659    if (source->IsFrozen()) {
    660      (void)source->SendEvictFromBFCache();
    661      continue;
    662    }
    663 
    664    promiseList->AddPromise(ClaimOnMainThread(
    665        source->Info(), ServiceWorkerDescriptor(serviceWorker)));
    666  }
    667 
    668  // Maybe finish the promise now in case we didn't find any matching clients.
    669  promiseList->MaybeFinish();
    670 
    671  return promiseList->GetResultPromise();
    672 }
    673 
    674 RefPtr<ClientOpPromise> ClientManagerService::GetInfoAndState(
    675    ThreadsafeContentParentHandle* aOriginContent,
    676    const ClientGetInfoAndStateArgs& aArgs) {
    677  ClientSourceParent* source =
    678      FindExistingSource(aArgs.id(), aArgs.principalInfo());
    679 
    680  if (!source) {
    681    CopyableErrorResult rv;
    682    rv.ThrowInvalidStateError("Unknown client");
    683    return ClientOpPromise::CreateAndReject(rv, __func__);
    684  }
    685 
    686  if (!source->ExecutionReady()) {
    687    RefPtr<ClientManagerService> self = this;
    688 
    689    // rejection ultimately converted to `undefined` in Clients::Get
    690    return source->ExecutionReadyPromise()->Then(
    691        GetCurrentSerialEventTarget(), __func__,
    692        [self = std::move(self), aArgs]() -> RefPtr<ClientOpPromise> {
    693          ClientSourceParent* source =
    694              self->FindExistingSource(aArgs.id(), aArgs.principalInfo());
    695 
    696          if (!source) {
    697            CopyableErrorResult rv;
    698            rv.ThrowInvalidStateError("Unknown client");
    699            return ClientOpPromise::CreateAndReject(rv, __func__);
    700          }
    701 
    702          return source->StartOp(aArgs);
    703        });
    704  }
    705 
    706  return source->StartOp(aArgs);
    707 }
    708 
    709 RefPtr<ClientOpPromise> ClientManagerService::OpenWindow(
    710    ThreadsafeContentParentHandle* aOriginContent,
    711    const ClientOpenWindowArgs& aArgs) {
    712  return InvokeAsync(GetMainThreadSerialEventTarget(), __func__,
    713                     [originContent = RefPtr{aOriginContent}, aArgs]() {
    714                       return ClientOpenWindow(originContent, aArgs);
    715                     });
    716 }
    717 
    718 bool ClientManagerService::HasWindow(
    719    const Maybe<ContentParentId>& aContentParentId,
    720    const PrincipalInfo& aPrincipalInfo, const nsID& aClientId) {
    721  AssertIsOnBackgroundThread();
    722 
    723  ClientSourceParent* source = FindExistingSource(aClientId, aPrincipalInfo);
    724  if (!source) {
    725    return false;
    726  }
    727 
    728  if (!source->ExecutionReady()) {
    729    return false;
    730  }
    731 
    732  if (source->Info().Type() != ClientType::Window) {
    733    return false;
    734  }
    735 
    736  if (aContentParentId && !source->IsOwnedByProcess(aContentParentId.value())) {
    737    return false;
    738  }
    739 
    740  return true;
    741 }
    742 
    743 }  // namespace mozilla::dom