Backed out changeset daecd36223c9 (bug 1584007) for ClientManagerService.cpp failures CLOSED TREE
authorBogdan Tara <btara@mozilla.com>
Wed, 02 Oct 2019 14:36:27 +0300
changeset 495995 d227b30b9940b2b3aeefdaf85c84af8e3f0e7863
parent 495994 e03401a358a6940b5402d82f210981e9d0027e71
child 495996 4e984f54259d60d6bdb6dab40c2ffd6d263e2737
push id114140
push userdvarga@mozilla.com
push dateWed, 02 Oct 2019 18:04:51 +0000
treeherdermozilla-inbound@32eb0ea893f3 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs1584007
milestone71.0a1
backs outdaecd36223c955d8f2dfcada32034c11af62641e
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Backed out changeset daecd36223c9 (bug 1584007) for ClientManagerService.cpp failures CLOSED TREE
dom/clients/manager/ClientChannelHelper.cpp
dom/clients/manager/ClientHandleParent.cpp
dom/clients/manager/ClientHandleParent.h
dom/clients/manager/ClientManager.cpp
dom/clients/manager/ClientManager.h
dom/clients/manager/ClientManagerParent.cpp
dom/clients/manager/ClientManagerParent.h
dom/clients/manager/ClientManagerService.cpp
dom/clients/manager/ClientManagerService.h
dom/clients/manager/PClientManager.ipdl
--- a/dom/clients/manager/ClientChannelHelper.cpp
+++ b/dom/clients/manager/ClientChannelHelper.cpp
@@ -4,18 +4,16 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "ClientChannelHelper.h"
 
 #include "ClientManager.h"
 #include "ClientSource.h"
 #include "MainThreadUtils.h"
-#include "mozilla/Result.h"
-#include "mozilla/ResultExtensions.h"
 #include "mozilla/dom/ServiceWorkerDescriptor.h"
 #include "mozilla/ipc/BackgroundUtils.h"
 #include "nsContentUtils.h"
 #include "nsIAsyncVerifyRedirectCallback.h"
 #include "nsIChannel.h"
 #include "nsIChannelEventSink.h"
 #include "nsIDocShell.h"
 #include "nsIHttpChannelInternal.h"
@@ -136,27 +134,20 @@ class ClientChannelHelper final : public
       NS_ENSURE_SUCCESS(rv, rv);
 
       // If we're managing redirects in the parent, then we don't want
       // to create a new ClientSource (since those need to live with
       // the global), so just allocate a new ClientInfo/id and we can
       // create a ClientSource when the final channel propagates back
       // to the child.
       if (mMode == Mode::Mode_Parent) {
-        const Maybe<ClientInfo>& oldReservedInfo =
-            oldLoadInfo->GetReservedClientInfo();
-        if (oldReservedInfo) {
-          MOZ_TRY(ClientManager::ForgetFutureClientSource(*oldReservedInfo));
-        }
-
-        Maybe<ClientInfo> newReservedInfo =
+        Maybe<ClientInfo> reservedInfo =
             ClientManager::CreateInfo(ClientType::Window, principal);
-        if (newReservedInfo) {
-          MOZ_TRY(ClientManager::ExpectFutureClientSource(*newReservedInfo));
-          newLoadInfo->SetReservedClientInfo(*newReservedInfo);
+        if (reservedInfo) {
+          newLoadInfo->SetReservedClientInfo(*reservedInfo);
         }
       } else {
         reservedClient.reset();
 
         const Maybe<ClientInfo>& reservedClientInfo =
             newLoadInfo->GetReservedClientInfo();
         // If we're in the child, but the parent managed redirects for
         // us then it might have allocated an id for us already. If
--- a/dom/clients/manager/ClientHandleParent.cpp
+++ b/dom/clients/manager/ClientHandleParent.cpp
@@ -3,17 +3,16 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "ClientHandleParent.h"
 
 #include "ClientHandleOpParent.h"
 #include "ClientManagerService.h"
-#include "ClientPrincipalUtils.h"
 #include "ClientSourceParent.h"
 #include "mozilla/dom/ClientIPCTypes.h"
 #include "mozilla/Unused.h"
 
 namespace mozilla {
 namespace dom {
 
 using mozilla::ipc::IPCResult;
@@ -23,20 +22,22 @@ IPCResult ClientHandleParent::RecvTeardo
   return IPC_OK();
 }
 
 void ClientHandleParent::ActorDestroy(ActorDestroyReason aReason) {
   if (mSource) {
     mSource->DetachHandle(this);
     mSource = nullptr;
   } else {
-    mSourcePromiseRequestHolder.DisconnectIfExists();
+    mService->StopWaitingForSource(this, mClientId);
   }
 
-  mSourcePromiseHolder.RejectIfExists(NS_ERROR_FAILURE, __func__);
+  if (mSourcePromise) {
+    mSourcePromise->Reject(NS_ERROR_FAILURE, __func__);
+  }
 }
 
 PClientHandleOpParent* ClientHandleParent::AllocPClientHandleOpParent(
     const ClientOpConstructorArgs& aArgs) {
   return new ClientHandleOpParent();
 }
 
 bool ClientHandleParent::DeallocPClientHandleOpParent(
@@ -55,51 +56,50 @@ IPCResult ClientHandleParent::RecvPClien
 ClientHandleParent::ClientHandleParent()
     : mService(ClientManagerService::GetOrCreateInstance()), mSource(nullptr) {}
 
 ClientHandleParent::~ClientHandleParent() { MOZ_DIAGNOSTIC_ASSERT(!mSource); }
 
 void ClientHandleParent::Init(const IPCClientInfo& aClientInfo) {
   mClientId = aClientInfo.id();
   mPrincipalInfo = aClientInfo.principalInfo();
+  mSource = mService->FindSource(aClientInfo.id(), aClientInfo.principalInfo());
+  if (!mSource) {
+    mService->WaitForSource(this, aClientInfo.id());
+    return;
+  }
 
-  // Capturing `this` is okay because the callbacks are disconnected from the
-  // promise returned by `FindSource` in `ActorDestroy`, so the dangling
-  // pointers will never be used.
-  mService->FindSource(aClientInfo.id(), aClientInfo.principalInfo())
-      ->Then(
-          GetCurrentThreadSerialEventTarget(), __func__,
-          [this](ClientSourceParent* aSource) {
-            mSourcePromiseRequestHolder.Complete();
-            FoundSource(aSource);
-          },
-          [this](nsresult) {
-            mSourcePromiseRequestHolder.Complete();
-            Unused << Send__delete__(this);
-          })
-      ->Track(mSourcePromiseRequestHolder);
+  mSource->AttachHandle(this);
 }
 
 ClientSourceParent* ClientHandleParent::GetSource() const { return mSource; }
 
 RefPtr<SourcePromise> ClientHandleParent::EnsureSource() {
   if (mSource) {
     return SourcePromise::CreateAndResolve(mSource, __func__);
   }
 
-  return mSourcePromiseHolder.Ensure(__func__);
+  if (!mSourcePromise) {
+    mSourcePromise = new SourcePromise::Private(__func__);
+  }
+  return mSourcePromise;
 }
 
 void ClientHandleParent::FoundSource(ClientSourceParent* aSource) {
   MOZ_ASSERT(aSource->Info().Id() == mClientId);
   if (!ClientMatchPrincipalInfo(aSource->Info().PrincipalInfo(),
                                 mPrincipalInfo)) {
+    if (mSourcePromise) {
+      mSourcePromise->Reject(NS_ERROR_FAILURE, __func__);
+    }
     Unused << Send__delete__(this);
     return;
   }
 
   mSource = aSource;
   mSource->AttachHandle(this);
-  mSourcePromiseHolder.ResolveIfExists(aSource, __func__);
+  if (mSourcePromise) {
+    mSourcePromise->Resolve(aSource, __func__);
+  }
 }
 
 }  // namespace dom
 }  // namespace mozilla
--- a/dom/clients/manager/ClientHandleParent.h
+++ b/dom/clients/manager/ClientHandleParent.h
@@ -1,36 +1,30 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 #ifndef _mozilla_dom_ClientHandleParent_h
 #define _mozilla_dom_ClientHandleParent_h
 
-#include "ClientManagerService.h"
-#include "mozilla/MozPromise.h"
-#include "mozilla/RefPtr.h"
-#include "mozilla/dom/PClientHandleOpParent.h"
 #include "mozilla/dom/PClientHandleParent.h"
 
 namespace mozilla {
 namespace dom {
 
+class ClientManagerService;
 class ClientSourceParent;
 
+typedef MozPromise<ClientSourceParent*, nsresult, /* IsExclusive = */ false>
+    SourcePromise;
+
 class ClientHandleParent final : public PClientHandleParent {
   RefPtr<ClientManagerService> mService;
-
-  // `mSource` and (`mSourcePromiseHolder`, `mSourcePromiseRequestHolder`) are
-  // mutually exclusive, so they could be combined in a `mozilla::Variant`.
   ClientSourceParent* mSource;
-  MozPromiseHolder<SourcePromise> mSourcePromiseHolder;
-
-  MozPromiseRequestHolder<SourcePromise> mSourcePromiseRequestHolder;
 
   nsID mClientId;
   PrincipalInfo mPrincipalInfo;
 
   // A promise for HandleOps that want to access our ClientSourceParent.
   // Resolved once FoundSource is called and we have a ClientSourceParent
   // available.
   RefPtr<SourcePromise::Private> mSourcePromise;
--- a/dom/clients/manager/ClientManager.cpp
+++ b/dom/clients/manager/ClientManager.cpp
@@ -275,47 +275,16 @@ UniquePtr<ClientSource> ClientManager::C
 
 // static
 UniquePtr<ClientSource> ClientManager::CreateSourceFromInfo(
     const ClientInfo& aClientInfo, nsISerialEventTarget* aEventTarget) {
   RefPtr<ClientManager> mgr = GetOrCreateForCurrentThread();
   return mgr->CreateSourceInternal(aClientInfo, aEventTarget);
 }
 
-// static
-nsresult ClientManager::ExpectOrForgetFutureClientSource(
-    bool aExpect, const ClientInfo& aClientInfo) {
-  nsresult rv = NS_OK;
-
-  RefPtr<ClientManager> mgr = GetOrCreateForCurrentThread();
-  mgr->MaybeExecute(
-      [&](ClientManagerChild* aActor) {
-        if (!aActor->SendExpectOrForgetFutureClientSource(
-                aExpect, aClientInfo.ToIPC())) {
-          rv = NS_ERROR_DOM_INVALID_STATE_ERR;
-        }
-      },
-      [&] { rv = NS_ERROR_DOM_INVALID_STATE_ERR; });
-
-  return rv;
-}
-
-// static
-nsresult ClientManager::ExpectFutureClientSource(
-    const ClientInfo& aClientInfo) {
-  return ExpectOrForgetFutureClientSource(true, aClientInfo);
-}
-
-// static
-nsresult ClientManager::ForgetFutureClientSource(
-    const ClientInfo& aClientInfo) {
-  return ExpectOrForgetFutureClientSource(false, aClientInfo);
-}
-
-// static
 Maybe<ClientInfo> ClientManager::CreateInfo(ClientType aType,
                                             nsIPrincipal* aPrincipal) {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(aPrincipal);
 
   PrincipalInfo principalInfo;
   nsresult rv = PrincipalToPrincipalInfo(aPrincipal, &principalInfo);
   if (NS_WARN_IF(NS_FAILED(rv))) {
--- a/dom/clients/manager/ClientManager.h
+++ b/dom/clients/manager/ClientManager.h
@@ -24,17 +24,16 @@ class ClientHandle;
 class ClientInfo;
 class ClientManagerChild;
 class ClientMatchAllArgs;
 class ClientNavigateArgs;
 class ClientOpConstructorArgs;
 class ClientOpenWindowArgs;
 class ClientSource;
 enum class ClientType : uint8_t;
-class PClientManagerChild;
 class WorkerPrivate;
 
 // The ClientManager provides a per-thread singleton interface workering
 // with the client subsystem.  It allows globals to create ClientSource
 // objects.  It allows other parts of the system to attach to this globals
 // by creating ClientHandle objects.  The ClientManager also provides
 // methods for querying the list of clients active in the system.
 class ClientManager final : public ClientThing<ClientManagerChild> {
@@ -69,19 +68,16 @@ class ClientManager final : public Clien
   // Get or create the TLS singleton.  Currently this is only used
   // internally and external code indirectly calls it by invoking
   // static methods.
   static already_AddRefed<ClientManager> GetOrCreateForCurrentThread();
 
   // Private methods called by ClientSource
   mozilla::dom::WorkerPrivate* GetWorkerPrivate() const;
 
-  static nsresult ExpectOrForgetFutureClientSource(
-      bool aExpect, const ClientInfo& aClientInfo);
-
  public:
   // Initialize the ClientManager at process start.  This
   // does book-keeping like creating a TLS identifier, etc.
   // This should only be called by process startup code.
   static void Startup();
 
   static UniquePtr<ClientSource> CreateSource(
       ClientType aType, nsISerialEventTarget* aEventTarget,
@@ -91,38 +87,19 @@ class ClientManager final : public Clien
       ClientType aType, nsISerialEventTarget* aEventTarget,
       const mozilla::ipc::PrincipalInfo& aPrincipal);
 
   // Construct a new ClientSource from an existing ClientInfo (and id) rather
   // than allocating a new id.
   static UniquePtr<ClientSource> CreateSourceFromInfo(
       const ClientInfo& aClientInfo, nsISerialEventTarget* aSerialEventTarget);
 
-  // Asynchronously declare that a `ClientSource` will _possibly_ be constructed
-  // from an equivalent `ClientInfo` in the future. This must be called _before_
-  // any `ClientHandle`s are created with the `ClientInfo` to avoid race
-  // conditions when `ClientHandle`s query the `ClientManagerService`.
-  //
-  // This method exists so that the `ClientManagerService` can determine if a
-  // particular `ClientSource` can be expected to exist in the future or has
-  // already existed and been destroyed.
-  //
-  // If it's later known that the expected `ClientSource` will _not_ be
-  // constructed, `ForgetFutureClientSource` _must_ be called.
-  static nsresult ExpectFutureClientSource(const ClientInfo& aClientInfo);
-
-  // Negates a prior call to `ExpectFutureClientSource`.
-  static nsresult ForgetFutureClientSource(const ClientInfo& aClientInfo);
-
   // Allocate a new ClientInfo and id without creating a ClientSource. Used
   // when we have a redirect that isn't exposed to the process that owns
   // the global/ClientSource.
-  //
-  // NOTE: callers should consider whether a call to `ExpectFutureClientSource`
-  // should be made when calling this method.
   static Maybe<ClientInfo> CreateInfo(ClientType aType,
                                       nsIPrincipal* aPrincipal);
 
   static already_AddRefed<ClientHandle> CreateHandle(
       const ClientInfo& aClientInfo, nsISerialEventTarget* aSerialEventTarget);
 
   static RefPtr<ClientOpPromise> MatchAll(const ClientMatchAllArgs& aArgs,
                                           nsISerialEventTarget* aTarget);
--- a/dom/clients/manager/ClientManagerParent.cpp
+++ b/dom/clients/manager/ClientManagerParent.cpp
@@ -5,17 +5,16 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "ClientManagerParent.h"
 
 #include "ClientHandleParent.h"
 #include "ClientManagerOpParent.h"
 #include "ClientManagerService.h"
 #include "ClientSourceParent.h"
-#include "ClientValidation.h"
 #include "mozilla/dom/PClientNavigateOpParent.h"
 #include "mozilla/Unused.h"
 
 namespace mozilla {
 namespace dom {
 
 using mozilla::ipc::IPCResult;
 
@@ -95,35 +94,16 @@ bool ClientManagerParent::DeallocPClient
 
 IPCResult ClientManagerParent::RecvPClientSourceConstructor(
     PClientSourceParent* aActor, const ClientSourceConstructorArgs& aArgs) {
   ClientSourceParent* actor = static_cast<ClientSourceParent*>(aActor);
   actor->Init();
   return IPC_OK();
 }
 
-mozilla::ipc::IPCResult
-ClientManagerParent::RecvExpectOrForgetFutureClientSource(
-    const bool& aExpect, const IPCClientInfo& aClientInfo) {
-  if (NS_WARN_IF(!ClientIsValidPrincipalInfo(aClientInfo.principalInfo()))) {
-    return IPC_FAIL(this, "Invalid PrincipalInfo.");
-  }
-
-  RefPtr<ClientManagerService> svc =
-      ClientManagerService::GetOrCreateInstance();
-
-  if (aExpect) {
-    svc->ExpectFutureSource(aClientInfo);
-  } else {
-    svc->ForgetFutureSource(aClientInfo);
-  }
-
-  return IPC_OK();
-}
-
 ClientManagerParent::ClientManagerParent()
     : mService(ClientManagerService::GetOrCreateInstance()) {}
 
 ClientManagerParent::~ClientManagerParent() { mService->RemoveManager(this); }
 
 void ClientManagerParent::Init() { mService->AddManager(this); }
 
 }  // namespace dom
--- a/dom/clients/manager/ClientManagerParent.h
+++ b/dom/clients/manager/ClientManagerParent.h
@@ -47,19 +47,16 @@ class ClientManagerParent final : public
       const ClientSourceConstructorArgs& aArgs) override;
 
   bool DeallocPClientSourceParent(PClientSourceParent* aActor) override;
 
   mozilla::ipc::IPCResult RecvPClientSourceConstructor(
       PClientSourceParent* aActor,
       const ClientSourceConstructorArgs& aArgs) override;
 
-  mozilla::ipc::IPCResult RecvExpectOrForgetFutureClientSource(
-      const bool& aExpect, const IPCClientInfo& aClientInfo) override;
-
  public:
   ClientManagerParent();
   ~ClientManagerParent();
 
   void Init();
 };
 
 }  // namespace dom
--- a/dom/clients/manager/ClientManagerService.cpp
+++ b/dom/clients/manager/ClientManagerService.cpp
@@ -7,17 +7,16 @@
 #include "ClientManagerService.h"
 
 #include "ClientManagerParent.h"
 #include "ClientNavigateOpParent.h"
 #include "ClientOpenWindowOpParent.h"
 #include "ClientOpenWindowUtils.h"
 #include "ClientPrincipalUtils.h"
 #include "ClientSourceParent.h"
-#include "mozilla/dom/ClientIPCTypes.h"
 #include "mozilla/dom/ContentParent.h"
 #include "mozilla/dom/ServiceWorkerManager.h"
 #include "mozilla/dom/ServiceWorkerUtils.h"
 #include "mozilla/ipc/BackgroundParent.h"
 #include "mozilla/ipc/PBackgroundSharedTypes.h"
 #include "mozilla/ClearOnShutdown.h"
 #include "mozilla/MozPromise.h"
 #include "mozilla/SystemGroup.h"
@@ -133,17 +132,17 @@ ClientManagerService::ClientManagerServi
         svc->Shutdown();
       }
     });
   }
 }
 
 ClientManagerService::~ClientManagerService() {
   AssertIsOnBackgroundThread();
-  MOZ_DIAGNOSTIC_ASSERT(mSourceTable.count() == 0);
+  MOZ_DIAGNOSTIC_ASSERT(mSourceTable.Count() == 0);
   MOZ_DIAGNOSTIC_ASSERT(mManagerList.IsEmpty());
 
   MOZ_DIAGNOSTIC_ASSERT(sClientManagerServiceInstance == this);
   sClientManagerServiceInstance = nullptr;
 }
 
 void ClientManagerService::Shutdown() {
   AssertIsOnBackgroundThread();
@@ -184,173 +183,84 @@ already_AddRefed<ClientManagerService> C
   if (!sClientManagerServiceInstance) {
     return nullptr;
   }
 
   RefPtr<ClientManagerService> ref(sClientManagerServiceInstance);
   return ref.forget();
 }
 
-ClientManagerService::FutureClientSourceParent::FutureClientSourceParent(
-    const IPCClientInfo& aClientInfo)
-    : mPrincipalInfo(aClientInfo.principalInfo()) {}
-
-// static
 bool ClientManagerService::AddSource(ClientSourceParent* aSource) {
   AssertIsOnBackgroundThread();
   MOZ_ASSERT(aSource);
-
-  const nsID& id = aSource->Info().Id();
-  auto entryPtr = mSourceTable.lookupForAdd(id);
-
-  if (entryPtr) {
-    SourceTableEntry& entry = entryPtr->value();
-
-    // Do not permit overwriting an existing ClientSource with the same
-    // UUID.  This would allow a spoofed ClientParentSource actor to
-    // intercept postMessage() intended for the real actor.
-    if (NS_WARN_IF(entry.is<ClientSourceParent*>())) {
-      return false;
-    }
+  auto entry = mSourceTable.LookupForAdd(aSource->Info().Id());
+  // Do not permit overwriting an existing ClientSource with the same
+  // UUID.  This would allow a spoofed ClientParentSource actor to
+  // intercept postMessage() intended for the real actor.
+  if (NS_WARN_IF(!!entry)) {
+    return false;
+  }
+  entry.OrInsert([&] { return aSource; });
 
-    {
-      FutureClientSourceParent& placeHolder =
-          entry.as<FutureClientSourceParent>();
-
-      if (NS_WARN_IF(!ClientMatchPrincipalInfo(
-              placeHolder.PrincipalInfo(), aSource->Info().PrincipalInfo()))) {
-        return false;
-      }
-
-      placeHolder.ResolvePromiseIfExists(aSource);
+  // Now that we've been created, notify any handles that were
+  // waiting on us.
+  auto* handles = mPendingHandles.GetValue(aSource->Info().Id());
+  if (handles) {
+    for (auto handle : *handles) {
+      handle->FoundSource(aSource);
     }
-
-    entry = AsVariant(aSource);
-    return true;
   }
-
-  return mSourceTable.add(entryPtr, id, AsVariant(aSource));
+  mPendingHandles.Remove(aSource->Info().Id());
+  return true;
 }
 
 bool ClientManagerService::RemoveSource(ClientSourceParent* aSource) {
   AssertIsOnBackgroundThread();
   MOZ_ASSERT(aSource);
-
-  auto entryPtr = mSourceTable.lookup(aSource->Info().Id());
-
-  if (NS_WARN_IF(!entryPtr) ||
-      NS_WARN_IF(entryPtr->value().is<FutureClientSourceParent>())) {
-    return false;
-  }
-
-  mSourceTable.remove(entryPtr);
-  return true;
-}
-
-bool ClientManagerService::ExpectFutureSource(
-    const IPCClientInfo& aClientInfo) {
-  AssertIsOnBackgroundThread();
-
-  const nsID& id = aClientInfo.id();
-  auto entryPtr = mSourceTable.lookupForAdd(id);
-
-  // Prevent overwrites.
-  if (NS_WARN_IF(static_cast<bool>(entryPtr))) {
+  auto entry = mSourceTable.Lookup(aSource->Info().Id());
+  if (NS_WARN_IF(!entry)) {
     return false;
   }
-
-  return mSourceTable.add(
-      entryPtr, id,
-      SourceTableEntry(VariantIndex<0>(),
-                       FutureClientSourceParent(aClientInfo)));
-}
-
-bool ClientManagerService::ForgetFutureSource(
-    const IPCClientInfo& aClientInfo) {
-  AssertIsOnBackgroundThread();
-
-  auto entryPtr = mSourceTable.lookup(aClientInfo.id());
-
-  if (entryPtr) {
-    SourceTableEntry& entry = entryPtr->value();
-
-    if (NS_WARN_IF(entry.is<ClientSourceParent*>())) {
-      return false;
-    }
-
-    entry.as<FutureClientSourceParent>().RejectPromiseIfExists(
-        NS_ERROR_NOT_AVAILABLE);
-  }
-
-  mSourceTable.remove(entryPtr);
+  entry.Remove();
   return true;
 }
 
-RefPtr<SourcePromise> ClientManagerService::FindSource(
-    const nsID& aID, const PrincipalInfo& aPrincipalInfo) const {
+ClientSourceParent* ClientManagerService::FindSource(
+    const nsID& aID, const PrincipalInfo& aPrincipalInfo) {
   AssertIsOnBackgroundThread();
 
-  auto entryPtr = mSourceTable.lookup(aID);
-
-  if (!entryPtr) {
-    return SourcePromise::CreateAndReject(NS_ERROR_NOT_AVAILABLE, __func__);
-  }
-
-  SourceTableEntry& entry = entryPtr->value();
-
-  if (entry.is<FutureClientSourceParent>()) {
-    return entry.as<FutureClientSourceParent>().Promise();
-  }
-
-  ClientSourceParent* source = entry.as<ClientSourceParent*>();
-  MOZ_DIAGNOSTIC_ASSERT(source);
-
-  if (source->IsFrozen() ||
-      NS_WARN_IF(!ClientMatchPrincipalInfo(source->Info().PrincipalInfo(),
-                                           aPrincipalInfo))) {
-    return SourcePromise::CreateAndReject(NS_ERROR_DOM_INVALID_STATE_ERR,
-                                          __func__);
-  }
-
-  return SourcePromise::CreateAndResolve(source, __func__);
-}
-
-inline ClientSourceParent* MaybeUnwrapAsExistingSource(
-    const ClientManagerService::SourceTableEntry& aEntry) {
-  AssertIsOnBackgroundThread();
-
-  if (aEntry.is<ClientManagerService::FutureClientSourceParent>()) {
+  auto entry = mSourceTable.Lookup(aID);
+  if (!entry) {
     return nullptr;
   }
 
-  MOZ_DIAGNOSTIC_ASSERT(aEntry.as<ClientSourceParent*>());
-  return aEntry.as<ClientSourceParent*>();
-}
-
-ClientSourceParent* ClientManagerService::FindExistingSource(
-    const nsID& aID, const PrincipalInfo& aPrincipalInfo) const {
-  AssertIsOnBackgroundThread();
-
-  auto entryPtr = mSourceTable.lookup(aID);
-
-  if (!entryPtr) {
-    return nullptr;
-  }
-
-  ClientSourceParent* source = MaybeUnwrapAsExistingSource(entryPtr->value());
-
-  if (!source || source->IsFrozen() ||
+  ClientSourceParent* source = entry.Data();
+  if (source->IsFrozen() ||
       !ClientMatchPrincipalInfo(source->Info().PrincipalInfo(),
                                 aPrincipalInfo)) {
     return nullptr;
   }
 
   return source;
 }
 
+void ClientManagerService::WaitForSource(ClientHandleParent* aHandle,
+                                         const nsID& aID) {
+  auto& entry = mPendingHandles.GetOrInsert(aID);
+  entry.AppendElement(aHandle);
+}
+
+void ClientManagerService::StopWaitingForSource(ClientHandleParent* aHandle,
+                                                const nsID& aID) {
+  auto* entry = mPendingHandles.GetValue(aID);
+  if (entry) {
+    entry->RemoveElement(aHandle);
+  }
+}
+
 void ClientManagerService::AddManager(ClientManagerParent* aManager) {
   AssertIsOnBackgroundThread();
   MOZ_DIAGNOSTIC_ASSERT(aManager);
   MOZ_ASSERT(!mManagerList.Contains(aManager));
   mManagerList.AppendElement(aManager);
 
   // If shutdown has already begun then immediately destroy the actor.
   if (mShutdown) {
@@ -363,17 +273,17 @@ void ClientManagerService::RemoveManager
   MOZ_DIAGNOSTIC_ASSERT(aManager);
   DebugOnly<bool> removed = mManagerList.RemoveElement(aManager);
   MOZ_ASSERT(removed);
 }
 
 RefPtr<ClientOpPromise> ClientManagerService::Navigate(
     const ClientNavigateArgs& aArgs) {
   ClientSourceParent* source =
-      FindExistingSource(aArgs.target().id(), aArgs.target().principalInfo());
+      FindSource(aArgs.target().id(), aArgs.target().principalInfo());
   if (!source) {
     return ClientOpPromise::CreateAndReject(NS_ERROR_FAILURE, __func__);
   }
 
   PClientManagerParent* manager = source->Manager();
   MOZ_DIAGNOSTIC_ASSERT(manager);
 
   ClientNavigateOpConstructorArgs args;
@@ -470,21 +380,21 @@ RefPtr<ClientOpPromise> ClientManagerSer
     const ClientMatchAllArgs& aArgs) {
   AssertIsOnBackgroundThread();
 
   ServiceWorkerDescriptor swd(aArgs.serviceWorker());
   const PrincipalInfo& principalInfo = swd.PrincipalInfo();
 
   RefPtr<PromiseListHolder> promiseList = new PromiseListHolder();
 
-  for (auto iter = mSourceTable.iter(); !iter.done(); iter.next()) {
-    ClientSourceParent* source =
-        MaybeUnwrapAsExistingSource(iter.get().value());
+  for (auto iter = mSourceTable.Iter(); !iter.Done(); iter.Next()) {
+    ClientSourceParent* source = iter.UserData();
+    MOZ_DIAGNOSTIC_ASSERT(source);
 
-    if (!source || source->IsFrozen() || !source->ExecutionReady()) {
+    if (source->IsFrozen() || !source->ExecutionReady()) {
       continue;
     }
 
     if (aArgs.type() != ClientType::All &&
         source->Info().Type() != aArgs.type()) {
       continue;
     }
 
@@ -552,21 +462,21 @@ RefPtr<ClientOpPromise> ClientManagerSer
     const ClientClaimArgs& aArgs) {
   AssertIsOnBackgroundThread();
 
   const IPCServiceWorkerDescriptor& serviceWorker = aArgs.serviceWorker();
   const PrincipalInfo& principalInfo = serviceWorker.principalInfo();
 
   RefPtr<PromiseListHolder> promiseList = new PromiseListHolder();
 
-  for (auto iter = mSourceTable.iter(); !iter.done(); iter.next()) {
-    ClientSourceParent* source =
-        MaybeUnwrapAsExistingSource(iter.get().value());
+  for (auto iter = mSourceTable.Iter(); !iter.Done(); iter.Next()) {
+    ClientSourceParent* source = iter.UserData();
+    MOZ_DIAGNOSTIC_ASSERT(source);
 
-    if (!source || source->IsFrozen()) {
+    if (source->IsFrozen()) {
       continue;
     }
 
     if (!ClientMatchPrincipalInfo(source->Info().PrincipalInfo(),
                                   principalInfo)) {
       continue;
     }
 
@@ -598,33 +508,31 @@ RefPtr<ClientOpPromise> ClientManagerSer
   // Maybe finish the promise now in case we didn't find any matching clients.
   promiseList->MaybeFinish();
 
   return promiseList->GetResultPromise();
 }
 
 RefPtr<ClientOpPromise> ClientManagerService::GetInfoAndState(
     const ClientGetInfoAndStateArgs& aArgs) {
-  ClientSourceParent* source =
-      FindExistingSource(aArgs.id(), aArgs.principalInfo());
+  ClientSourceParent* source = FindSource(aArgs.id(), aArgs.principalInfo());
 
   if (!source) {
     return ClientOpPromise::CreateAndReject(NS_ERROR_FAILURE, __func__);
   }
 
   if (!source->ExecutionReady()) {
     RefPtr<ClientManagerService> self = this;
 
     // rejection ultimately converted to `undefined` in Clients::Get
     return source->ExecutionReadyPromise()->Then(
         GetCurrentThreadSerialEventTarget(), __func__,
-        [self = std::move(self), aArgs] {
-          // Execution ready implies the source had registered itself.
+        [self, aArgs]() -> RefPtr<ClientOpPromise> {
           ClientSourceParent* source =
-              self->FindExistingSource(aArgs.id(), aArgs.principalInfo());
+              self->FindSource(aArgs.id(), aArgs.principalInfo());
 
           if (!source) {
             return ClientOpPromise::CreateAndReject(NS_ERROR_FAILURE, __func__);
           }
 
           return source->StartOp(aArgs);
         });
   }
@@ -727,17 +635,17 @@ RefPtr<ClientOpPromise> ClientManagerSer
   return promise.forget();
 }
 
 bool ClientManagerService::HasWindow(
     const Maybe<ContentParentId>& aContentParentId,
     const PrincipalInfo& aPrincipalInfo, const nsID& aClientId) {
   AssertIsOnBackgroundThread();
 
-  ClientSourceParent* source = FindExistingSource(aClientId, aPrincipalInfo);
+  ClientSourceParent* source = FindSource(aClientId, aPrincipalInfo);
   if (!source) {
     return false;
   }
 
   if (!source->ExecutionReady()) {
     return false;
   }
 
--- a/dom/clients/manager/ClientManagerService.h
+++ b/dom/clients/manager/ClientManagerService.h
@@ -2,66 +2,72 @@
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 #ifndef _mozilla_dom_ClientManagerService_h
 #define _mozilla_dom_ClientManagerService_h
 
 #include "ClientOpPromise.h"
-#include "mozilla/HashTable.h"
-#include "mozilla/MozPromise.h"
-#include "mozilla/Variant.h"
-#include "mozilla/dom/ipc/IdType.h"
-#include "mozilla/ipc/PBackgroundSharedTypes.h"
 #include "nsDataHashtable.h"
 
 namespace mozilla {
 
 namespace ipc {
 
 class PrincipalInfo;
 
 }  // namespace ipc
 
 namespace dom {
 
 class ClientManagerParent;
 class ClientSourceParent;
 class ClientHandleParent;
-
-typedef MozPromise<ClientSourceParent*, nsresult, /* IsExclusive = */ false>
-    SourcePromise;
+class ContentParent;
 
 // Define a singleton service to manage client activity throughout the
 // browser.  This service runs on the PBackground thread.  To interact
 // it with it please use the ClientManager and ClientHandle classes.
 class ClientManagerService final {
+  // Store the ClientSourceParent objects in a hash table.  We want to
+  // optimize for insertion, removal, and lookup by UUID.
+  nsDataHashtable<nsIDHashKey, ClientSourceParent*> mSourceTable;
+
+  // The set of handles waiting for their corresponding ClientSourceParent
+  // to be created.
+  nsDataHashtable<nsIDHashKey, nsTArray<ClientHandleParent*>> mPendingHandles;
+
+  nsTArray<ClientManagerParent*> mManagerList;
+
+  bool mShutdown;
+
+  ClientManagerService();
+  ~ClientManagerService();
+
+  void Shutdown();
+
  public:
   static already_AddRefed<ClientManagerService> GetOrCreateInstance();
 
   // Returns nullptr if the service is not already created.
   static already_AddRefed<ClientManagerService> GetInstance();
 
   bool AddSource(ClientSourceParent* aSource);
 
   bool RemoveSource(ClientSourceParent* aSource);
 
-  bool ExpectFutureSource(const IPCClientInfo& aClientInfo);
-
-  bool ForgetFutureSource(const IPCClientInfo& aClientInfo);
+  ClientSourceParent* FindSource(
+      const nsID& aID, const mozilla::ipc::PrincipalInfo& aPrincipalInfo);
 
-  // The returned promise rejects if:
-  //  - the corresponding `ClientSourceParent` has already removed itself from
-  //  the `ClientManagerService` (i.e. the corresponding `ClientSource` has been
-  //  detroyed) or if
-  //  - it's known that the corresponding `ClientSourceParent` will not exist
-  //  (i.e. the corresponding `ClientSource` will not be created).
-  RefPtr<SourcePromise> FindSource(
-      const nsID& aID, const mozilla::ipc::PrincipalInfo& aPrincipalInfo) const;
+  // Called when a ClientHandle is created before the corresponding
+  // ClientSource. Will call FoundSource on the ClientHandleParent when it
+  // becomes available.
+  void WaitForSource(ClientHandleParent* aHandle, const nsID& aID);
+  void StopWaitingForSource(ClientHandleParent* aHandle, const nsID& aID);
 
   void AddManager(ClientManagerParent* aManager);
 
   void RemoveManager(ClientManagerParent* aManager);
 
   RefPtr<ClientOpPromise> Navigate(const ClientNavigateArgs& aArgs);
 
   RefPtr<ClientOpPromise> MatchAll(const ClientMatchAllArgs& aArgs);
@@ -75,79 +81,14 @@ class ClientManagerService final {
       const ClientOpenWindowArgs& aArgs,
       already_AddRefed<ContentParent> aSourceProcess);
 
   bool HasWindow(const Maybe<ContentParentId>& aContentParentId,
                  const mozilla::ipc::PrincipalInfo& aPrincipalInfo,
                  const nsID& aClientId);
 
   NS_INLINE_DECL_REFCOUNTING(mozilla::dom::ClientManagerService)
-
- private:
-  ClientManagerService();
-  ~ClientManagerService();
-
-  void Shutdown();
-
-  // Returns `nullptr` if the `ClientSourceParent*` doesn't exist.
-  ClientSourceParent* FindExistingSource(
-      const nsID& aID, const mozilla::ipc::PrincipalInfo& aPrincipalInfo) const;
-
-  // Represents a `ClientSourceParent` that may possibly be created and add
-  // itself in the future.
-  class FutureClientSourceParent {
-   public:
-    explicit FutureClientSourceParent(const IPCClientInfo& aClientInfo);
-
-    const mozilla::ipc::PrincipalInfo& PrincipalInfo() const {
-      return mPrincipalInfo;
-    }
-
-    already_AddRefed<SourcePromise> Promise() {
-      return mPromiseHolder.Ensure(__func__);
-    }
-
-    void ResolvePromiseIfExists(ClientSourceParent* aSource) {
-      mPromiseHolder.ResolveIfExists(aSource, __func__);
-    }
-
-    void RejectPromiseIfExists(nsresult aRv) {
-      mPromiseHolder.RejectIfExists(aRv, __func__);
-    }
-
-   private:
-    const mozilla::ipc::PrincipalInfo mPrincipalInfo;
-    MozPromiseHolder<SourcePromise> mPromiseHolder;
-  };
-
-  using SourceTableEntry =
-      Variant<FutureClientSourceParent, ClientSourceParent*>;
-
-  // Returns `nullptr` if `aEntry` isn't a `ClientSourceParent*`.
-  friend inline ClientSourceParent* MaybeUnwrapAsExistingSource(
-      const SourceTableEntry& aEntry);
-
-  struct nsIDHasher {
-    using Key = nsID;
-    using Lookup = Key;
-
-    static HashNumber hash(const Lookup& aLookup) {
-      return HashBytes(&aLookup, sizeof(Lookup));
-    }
-
-    static bool match(const Key& aKey, const Lookup& aLookup) {
-      return aKey.Equals(aLookup);
-    }
-  };
-
-  // Store the possible ClientSourceParent objects in a hash table.  We want to
-  // optimize for insertion, removal, and lookup by UUID.
-  HashMap<nsID, SourceTableEntry, nsIDHasher> mSourceTable;
-
-  nsTArray<ClientManagerParent*> mManagerList;
-
-  bool mShutdown;
 };
 
 }  // namespace dom
 }  // namespace mozilla
 
 #endif  // _mozilla_dom_ClientManagerService_h
--- a/dom/clients/manager/PClientManager.ipdl
+++ b/dom/clients/manager/PClientManager.ipdl
@@ -27,19 +27,16 @@ sync protocol PClientManager
 
 parent:
   async Teardown();
 
   async PClientHandle(IPCClientInfo aClientInfo);
   async PClientManagerOp(ClientOpConstructorArgs aArgs);
   async PClientSource(ClientSourceConstructorArgs aArgs);
 
-  async ExpectOrForgetFutureClientSource(bool aExpect,
-                                         IPCClientInfo aClientInfo);
-
 child:
   async PClientNavigateOp(ClientNavigateOpConstructorArgs aArgs);
 
   async __delete__();
 };
 
 } // namespace dom
 } // namespace mozilla