Bug 1584007 - allow ClientManager to register "future" ClientSources r=dom-workers-and-storage-reviewers,asuth
authorPerry Jiang <perry@mozilla.com>
Tue, 24 Mar 2020 15:18:29 +0000
changeset 520467 8ca43f80bdd229df6d76e9c6598ac2200567bb0a
parent 520466 00eda7b39a1316ec75ac112535a02ac80ab99307
child 520468 0d130a64b0f5c16db4995d3f27f2e6b21c656977
push id37251
push usermalexandru@mozilla.com
push dateThu, 26 Mar 2020 09:33:08 +0000
treeherdermozilla-central@3e5a7430c8d7 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdom-workers-and-storage-reviewers, asuth
bugs1584007
milestone76.0a1
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
Bug 1584007 - allow ClientManager to register "future" ClientSources r=dom-workers-and-storage-reviewers,asuth Differential Revision: https://phabricator.services.mozilla.com/D66520
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/ClientManager.cpp
+++ b/dom/clients/manager/ClientManager.cpp
@@ -222,16 +222,46 @@ already_AddRefed<ClientManager> ClientMa
 }
 
 WorkerPrivate* ClientManager::GetWorkerPrivate() const {
   NS_ASSERT_OWNINGTHREAD(ClientManager);
   MOZ_DIAGNOSTIC_ASSERT(GetActor());
   return GetActor()->GetWorkerPrivate();
 }
 
+// Used to share logic between ExpectFutureSource and ForgetFutureSource.
+/* static */ bool ClientManager::ExpectOrForgetFutureSource(
+    const ClientInfo& aClientInfo,
+    bool (PClientManagerChild::*aMethod)(const IPCClientInfo&)) {
+  bool rv = true;
+
+  RefPtr<ClientManager> mgr = ClientManager::GetOrCreateForCurrentThread();
+  mgr->MaybeExecute(
+      [&](ClientManagerChild* aActor) {
+        if (!(aActor->*aMethod)(aClientInfo.ToIPC())) {
+          rv = false;
+        }
+      },
+      [&] { rv = false; });
+
+  return rv;
+}
+
+/* static */ bool ClientManager::ExpectFutureSource(
+    const ClientInfo& aClientInfo) {
+  return ExpectOrForgetFutureSource(
+      aClientInfo, &PClientManagerChild::SendExpectFutureClientSource);
+}
+
+/* static */ bool ClientManager::ForgetFutureSource(
+    const ClientInfo& aClientInfo) {
+  return ExpectOrForgetFutureSource(
+      aClientInfo, &PClientManagerChild::SendForgetFutureClientSource);
+}
+
 // static
 void ClientManager::Startup() {
   MOZ_ASSERT(NS_IsMainThread());
 
   MOZ_DIAGNOSTIC_ASSERT(sClientManagerThreadLocalMagic1 == kThreadLocalMagic1);
   MOZ_DIAGNOSTIC_ASSERT(sClientManagerThreadLocalMagic2 == kThreadLocalMagic2);
   MOZ_DIAGNOSTIC_ASSERT(sClientManagerThreadLocalIndex == kBadThreadLocalIndex);
   MOZ_DIAGNOSTIC_ASSERT(sClientManagerThreadLocalIndex ==
--- a/dom/clients/manager/ClientManager.h
+++ b/dom/clients/manager/ClientManager.h
@@ -3,16 +3,17 @@
 /* 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_ClientManager_h
 #define _mozilla_dom_ClientManager_h
 
 #include "mozilla/dom/ClientOpPromise.h"
 #include "mozilla/dom/ClientThing.h"
+#include "mozilla/dom/PClientManagerChild.h"
 
 class nsIPrincipal;
 
 namespace mozilla {
 namespace ipc {
 class PBackgroundChild;
 class PrincipalInfo;
 }  // namespace ipc
@@ -68,17 +69,39 @@ 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;
 
+  // Don't use - use {Expect,Forget}FutureSource instead.
+  static bool ExpectOrForgetFutureSource(
+      const ClientInfo& aClientInfo,
+      bool (PClientManagerChild::*aMethod)(const IPCClientInfo&));
+
  public:
+  // Asynchronously declare that a ClientSource will possibly be constructed
+  // from an equivalent ClientInfo in the future. This must be called before any
+  // any ClientHandles are created with the ClientInfo to avoid race conditions
+  // when ClientHandles 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, ForgetFutureSource must be called.
+  static bool ExpectFutureSource(const ClientInfo& aClientInfo);
+
+  // May also be called even when the "future" source has become a "real"
+  // source, in which case this is a no-op.
+  static bool ForgetFutureSource(const ClientInfo& aClientInfo);
+
   // 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,
       nsIPrincipal* aPrincipal);
--- a/dom/clients/manager/ClientManagerParent.cpp
+++ b/dom/clients/manager/ClientManagerParent.cpp
@@ -5,16 +5,17 @@
  * 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;
 
@@ -101,10 +102,33 @@ IPCResult ClientManagerParent::RecvPClie
 
 ClientManagerParent::ClientManagerParent()
     : mService(ClientManagerService::GetOrCreateInstance()) {}
 
 ClientManagerParent::~ClientManagerParent() { mService->RemoveManager(this); }
 
 void ClientManagerParent::Init() { mService->AddManager(this); }
 
+IPCResult ClientManagerParent::RecvExpectFutureClientSource(
+    const IPCClientInfo& aClientInfo) {
+  if (NS_WARN_IF(!ClientIsValidPrincipalInfo(aClientInfo.principalInfo()))) {
+    return IPC_FAIL(this, "Invalid PrincipalInfo.");
+  }
+
+  RefPtr<ClientManagerService> cms =
+      ClientManagerService::GetOrCreateInstance();
+  Unused << NS_WARN_IF(cms->ExpectFutureSource(aClientInfo));
+  return IPC_OK();
+}
+
+IPCResult ClientManagerParent::RecvForgetFutureClientSource(
+    const IPCClientInfo& aClientInfo) {
+  if (NS_WARN_IF(!ClientIsValidPrincipalInfo(aClientInfo.principalInfo()))) {
+    return IPC_FAIL(this, "Invalid PrincipalInfo.");
+  }
+
+  RefPtr<ClientManagerService> cms = ClientManagerService::GetInstance();
+  cms->ForgetFutureSource(aClientInfo);
+  return IPC_OK();
+}
+
 }  // namespace dom
 }  // namespace mozilla
--- a/dom/clients/manager/ClientManagerParent.h
+++ b/dom/clients/manager/ClientManagerParent.h
@@ -47,16 +47,22 @@ 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 RecvExpectFutureClientSource(
+      const IPCClientInfo& aClientInfo) override;
+
+  mozilla::ipc::IPCResult RecvForgetFutureClientSource(
+      const IPCClientInfo& aClientInfo) override;
+
  public:
   ClientManagerParent();
   ~ClientManagerParent();
 
   void Init();
 };
 
 }  // namespace dom
--- a/dom/clients/manager/ClientManagerService.cpp
+++ b/dom/clients/manager/ClientManagerService.cpp
@@ -288,16 +288,55 @@ bool ClientManagerService::RemoveSource(
   if (NS_WARN_IF(!entryPtr)) {
     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))) {
+    return false;
+  }
+
+  return mSourceTable.add(
+      entryPtr, id,
+      SourceTableEntry(VariantIndex<0>(),
+                       FutureClientSourceParent(aClientInfo)));
+}
+
+void ClientManagerService::ForgetFutureSource(
+    const IPCClientInfo& aClientInfo) {
+  AssertIsOnBackgroundThread();
+
+  auto entryPtr = mSourceTable.lookup(aClientInfo.id());
+
+  if (entryPtr) {
+    SourceTableEntry& entry = entryPtr->value();
+
+    if (entry.is<ClientSourceParent*>()) {
+      return;
+    }
+
+    CopyableErrorResult rv;
+    rv.ThrowInvalidStateError("Client creation aborted.");
+    entry.as<FutureClientSourceParent>().RejectPromiseIfExists(rv);
+
+    mSourceTable.remove(entryPtr);
+  }
+}
+
 RefPtr<SourcePromise> ClientManagerService::FindSource(
     const nsID& aID, const PrincipalInfo& aPrincipalInfo) {
   AssertIsOnBackgroundThread();
 
   auto entryPtr = mSourceTable.lookup(aID);
 
   if (!entryPtr) {
     CopyableErrorResult rv;
--- a/dom/clients/manager/ClientManagerService.h
+++ b/dom/clients/manager/ClientManagerService.h
@@ -110,16 +110,23 @@ class ClientManagerService final {
 
   // Returns nullptr if the service is not already created.
   static already_AddRefed<ClientManagerService> GetInstance();
 
   bool AddSource(ClientSourceParent* aSource);
 
   bool RemoveSource(ClientSourceParent* aSource);
 
+  // Returns true when a FutureClientSourceParent is successfully added.
+  bool ExpectFutureSource(const IPCClientInfo& aClientInfo);
+
+  // May still be called if it's possible that the FutureClientSourceParent
+  // no longer exists.
+  void ForgetFutureSource(const IPCClientInfo& aClientInfo);
+
   RefPtr<SourcePromise> FindSource(
       const nsID& aID, const mozilla::ipc::PrincipalInfo& aPrincipalInfo);
 
   void AddManager(ClientManagerParent* aManager);
 
   void RemoveManager(ClientManagerParent* aManager);
 
   RefPtr<ClientOpPromise> Navigate(const ClientNavigateArgs& aArgs);
--- a/dom/clients/manager/PClientManager.ipdl
+++ b/dom/clients/manager/PClientManager.ipdl
@@ -27,16 +27,19 @@ sync protocol PClientManager
 
 parent:
   async Teardown();
 
   async PClientHandle(IPCClientInfo aClientInfo);
   async PClientManagerOp(ClientOpConstructorArgs aArgs);
   async PClientSource(ClientSourceConstructorArgs aArgs);
 
+  async ExpectFutureClientSource(IPCClientInfo aClientInfo);
+  async ForgetFutureClientSource(IPCClientInfo aClientInfo);
+
 child:
   async PClientNavigateOp(ClientNavigateOpConstructorArgs aArgs);
 
   async __delete__();
 };
 
 } // namespace dom
 } // namespace mozilla