Bug 1423412 P4 Add a ClientManager::GetInfoAndState() that retrieves the ClientInfo and ClientState for a given ID. r=baku
authorBen Kelly <ben@wanderview.com>
Tue, 05 Dec 2017 20:45:23 -0500
changeset 395177 bd99f4732647f01441e5ca93d11b27e98592838b
parent 395176 9a5aac891c5f144811b17168353e55641a31e10c
child 395178 7750a46652fa83acc6b8cd3e3bee8f428d24bfd5
push id98024
push userbkelly@mozilla.com
push dateWed, 06 Dec 2017 01:45:29 +0000
treeherdermozilla-inbound@7750a46652fa [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbaku
bugs1423412
milestone59.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 1423412 P4 Add a ClientManager::GetInfoAndState() that retrieves the ClientInfo and ClientState for a given ID. r=baku
dom/clients/manager/ClientIPCTypes.ipdlh
dom/clients/manager/ClientManager.cpp
dom/clients/manager/ClientManager.h
dom/clients/manager/ClientManagerOpParent.cpp
dom/clients/manager/ClientManagerOpParent.h
dom/clients/manager/ClientManagerService.cpp
dom/clients/manager/ClientManagerService.h
dom/clients/manager/ClientSource.cpp
dom/clients/manager/ClientSource.h
dom/clients/manager/ClientSourceOpChild.cpp
dom/clients/manager/ClientSourceParent.cpp
dom/clients/manager/ClientSourceParent.h
--- a/dom/clients/manager/ClientIPCTypes.ipdlh
+++ b/dom/clients/manager/ClientIPCTypes.ipdlh
@@ -45,39 +45,53 @@ struct IPCClientWorkerState
 };
 
 union IPCClientState
 {
   IPCClientWindowState;
   IPCClientWorkerState;
 };
 
+struct ClientInfoAndState
+{
+  IPCClientInfo info;
+  IPCClientState state;
+};
+
 struct ClientSourceExecutionReadyArgs
 {
   nsCString url;
   FrameType frameType;
 };
 
 struct ClientControlledArgs
 {
   IPCServiceWorkerDescriptor serviceWorker;
 };
 
+struct ClientGetInfoAndStateArgs
+{
+  nsID id;
+  PrincipalInfo principalInfo;
+};
+
 struct ClientOpenWindowArgs
 {
 };
 
 union ClientOpConstructorArgs
 {
   ClientControlledArgs;
+  ClientGetInfoAndStateArgs;
 };
 
 struct ClientNavigateOpConstructorArgs
 {
 };
 
 union ClientOpResult
 {
   nsresult;
+  ClientInfoAndState;
 };
 
 } // namespace dom
 } // namespace mozilla
--- a/dom/clients/manager/ClientManager.cpp
+++ b/dom/clients/manager/ClientManager.cpp
@@ -240,10 +240,19 @@ ClientManager::CreateSource(ClientType a
 already_AddRefed<ClientHandle>
 ClientManager::CreateHandle(const ClientInfo& aClientInfo,
                             nsISerialEventTarget* aSerialEventTarget)
 {
   RefPtr<ClientManager> mgr = GetOrCreateForCurrentThread();
   return mgr->CreateHandleInternal(aClientInfo, aSerialEventTarget);
 }
 
+// static
+RefPtr<ClientOpPromise>
+ClientManager::GetInfoAndState(const ClientGetInfoAndStateArgs& aArgs,
+                               nsISerialEventTarget* aSerialEventTarget)
+{
+  RefPtr<ClientManager> mgr = GetOrCreateForCurrentThread();
+  return mgr->StartOp(aArgs, aSerialEventTarget);
+}
+
 } // namespace dom
 } // namespace mozilla
--- a/dom/clients/manager/ClientManager.h
+++ b/dom/clients/manager/ClientManager.h
@@ -13,16 +13,17 @@ class nsIPrincipal;
 
 namespace mozilla {
 namespace ipc {
 class PBackgroundChild;
 class PrincipalInfo;
 } // namespace ipc
 namespace dom {
 
+class ClientGetInfoAndStateArgs;
 class ClientHandle;
 class ClientInfo;
 class ClientManagerChild;
 class ClientOpConstructorArgs;
 class ClientSource;
 enum class ClientType : uint8_t;
 
 namespace workers {
@@ -88,15 +89,19 @@ public:
   static UniquePtr<ClientSource>
   CreateSource(ClientType aType, nsISerialEventTarget* aEventTarget,
                const mozilla::ipc::PrincipalInfo& aPrincipal);
 
   static already_AddRefed<ClientHandle>
   CreateHandle(const ClientInfo& aClientInfo,
                nsISerialEventTarget* aSerialEventTarget);
 
+  static RefPtr<ClientOpPromise>
+  GetInfoAndState(const ClientGetInfoAndStateArgs& aArgs,
+                  nsISerialEventTarget* aSerialEventTarget);
+
   NS_INLINE_DECL_REFCOUNTING(mozilla::dom::ClientManager)
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // _mozilla_dom_ClientManager_h
--- a/dom/clients/manager/ClientManagerOpParent.cpp
+++ b/dom/clients/manager/ClientManagerOpParent.cpp
@@ -6,26 +6,61 @@
 
 #include "ClientManagerOpParent.h"
 
 #include "ClientManagerService.h"
 
 namespace mozilla {
 namespace dom {
 
+template <typename Method, typename... Args>
+void
+ClientManagerOpParent::DoServiceOp(Method aMethod, Args&&... aArgs)
+{
+  // Note, we need perfect forarding of the template type in order
+  // to allow already_AddRefed<> to be passed as an arg.
+  RefPtr<ClientOpPromise> p = (mService->*aMethod)(Forward<Args>(aArgs)...);
+
+  // Capturing `this` is safe here because we disconnect the promise in
+  // ActorDestroy() which ensures neither lambda is called if the actor
+  // is destroyed before the source operation completes.
+  p->Then(GetCurrentThreadSerialEventTarget(), __func__,
+    [this] (const mozilla::dom::ClientOpResult& aResult) {
+      mPromiseRequestHolder.Complete();
+      Unused << PClientManagerOpParent::Send__delete__(this, aResult);
+    }, [this] (nsresult aRv) {
+      mPromiseRequestHolder.Complete();
+      Unused << PClientManagerOpParent::Send__delete__(this, aRv);
+    })->Track(mPromiseRequestHolder);
+}
+
 void
 ClientManagerOpParent::ActorDestroy(ActorDestroyReason aReason)
 {
+  mPromiseRequestHolder.DisconnectIfExists();
 }
 
 ClientManagerOpParent::ClientManagerOpParent(ClientManagerService* aService)
   : mService(aService)
 {
   MOZ_DIAGNOSTIC_ASSERT(mService);
 }
 
 void
 ClientManagerOpParent::Init(const ClientOpConstructorArgs& aArgs)
 {
+  switch (aArgs.type()) {
+    case ClientOpConstructorArgs::TClientGetInfoAndStateArgs:
+    {
+      DoServiceOp(&ClientManagerService::GetInfoAndState,
+                  aArgs.get_ClientGetInfoAndStateArgs());
+      break;
+    }
+    default:
+    {
+      MOZ_ASSERT_UNREACHABLE("Unknown Client operation!");
+      break;
+    }
+  }
 }
 
 } // namespace dom
 } // namespace mozilla
--- a/dom/clients/manager/ClientManagerOpParent.h
+++ b/dom/clients/manager/ClientManagerOpParent.h
@@ -2,25 +2,31 @@
 /* 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_ClientManagerOpParent_h
 #define _mozilla_dom_ClientManagerOpParent_h
 
 #include "mozilla/dom/PClientManagerOpParent.h"
+#include "ClientOpPromise.h"
 
 namespace mozilla {
 namespace dom {
 
 class ClientManagerService;
 
 class ClientManagerOpParent final : public PClientManagerOpParent
 {
   RefPtr<ClientManagerService> mService;
+  MozPromiseRequestHolder<ClientOpPromise> mPromiseRequestHolder;
+
+  template <typename Method, typename... Args>
+  void
+  DoServiceOp(Method aMethod, Args&&... aArgs);
 
   // PClientManagerOpParent interface
   void
   ActorDestroy(ActorDestroyReason aReason) override;
 
 public:
   explicit ClientManagerOpParent(ClientManagerService* aService);
   ~ClientManagerOpParent() = default;
--- a/dom/clients/manager/ClientManagerService.cpp
+++ b/dom/clients/manager/ClientManagerService.cpp
@@ -285,10 +285,24 @@ void
 ClientManagerService::RemoveManager(ClientManagerParent* aManager)
 {
   AssertIsOnBackgroundThread();
   MOZ_DIAGNOSTIC_ASSERT(aManager);
   DebugOnly<bool> removed = mManagerList.RemoveElement(aManager);
   MOZ_ASSERT(removed);
 }
 
+RefPtr<ClientOpPromise>
+ClientManagerService::GetInfoAndState(const ClientGetInfoAndStateArgs& aArgs)
+{
+  RefPtr<ClientOpPromise> ref;
+
+  ClientSourceParent* source = FindSource(aArgs.id(), aArgs.principalInfo());
+  if (!source || !source->ExecutionReady()) {
+    ref = ClientOpPromise::CreateAndReject(NS_ERROR_FAILURE, __func__);
+    return ref.forget();
+  }
+
+  return source->StartOp(aArgs);
+}
+
 } // namespace dom
 } // namespace mozilla
--- a/dom/clients/manager/ClientManagerService.h
+++ b/dom/clients/manager/ClientManagerService.h
@@ -1,18 +1,17 @@
 /* -*- 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_ClientManagerService_h
 #define _mozilla_dom_ClientManagerService_h
 
-#include "mozilla/ipc/PBackgroundSharedTypes.h"
-#include "mozilla/MozPromise.h"
+#include "ClientOpPromise.h"
 #include "nsDataHashtable.h"
 
 namespace mozilla {
 
 namespace dom {
 
 class ClientManagerParent;
 class ClientSourceParent;
@@ -55,15 +54,18 @@ public:
              const mozilla::ipc::PrincipalInfo& aPrincipalInfo);
 
   void
   AddManager(ClientManagerParent* aManager);
 
   void
   RemoveManager(ClientManagerParent* aManager);
 
+  RefPtr<ClientOpPromise>
+  GetInfoAndState(const ClientGetInfoAndStateArgs& aArgs);
+
   NS_INLINE_DECL_REFCOUNTING(mozilla::dom::ClientManagerService)
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // _mozilla_dom_ClientManagerService_h
--- a/dom/clients/manager/ClientSource.cpp
+++ b/dom/clients/manager/ClientSource.cpp
@@ -4,16 +4,17 @@
  * 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 "ClientSource.h"
 
 #include "ClientManager.h"
 #include "ClientManagerChild.h"
 #include "ClientSourceChild.h"
+#include "ClientState.h"
 #include "ClientValidation.h"
 #include "mozilla/dom/ClientIPCTypes.h"
 #include "mozilla/dom/WorkerPrivate.h"
 #include "nsIDocShell.h"
 #include "nsPIDOMWindow.h"
 
 namespace mozilla {
 namespace dom {
@@ -49,16 +50,47 @@ ClientSource::ExecutionReady(const Clien
 
   mClientInfo.SetURL(aArgs.url());
   mClientInfo.SetFrameType(aArgs.frameType());
   MaybeExecute([aArgs](PClientSourceChild* aActor) {
     aActor->SendExecutionReady(aArgs);
   });
 }
 
+nsresult
+ClientSource::SnapshotWindowState(ClientState* aStateOut)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  nsPIDOMWindowInner* window = GetInnerWindow();
+  if (!window || !window->IsCurrentInnerWindow() ||
+      !window->HasActiveDocument()) {
+    *aStateOut = ClientState(ClientWindowState(VisibilityState::Hidden,
+                                               TimeStamp(), false));
+    return NS_OK;
+  }
+
+  nsIDocument* doc = window->GetExtantDoc();
+  if (NS_WARN_IF(!doc)) {
+    return NS_ERROR_UNEXPECTED;
+  }
+
+  ErrorResult rv;
+  bool focused = doc->HasFocus(rv);
+  if (NS_WARN_IF(rv.Failed())) {
+    rv.SuppressException();
+    return rv.StealNSResult();
+  }
+
+  *aStateOut = ClientState(ClientWindowState(doc->VisibilityState(),
+                                             doc->LastFocusTime(), focused));
+
+  return NS_OK;
+}
+
 WorkerPrivate*
 ClientSource::GetWorkerPrivate() const
 {
   NS_ASSERT_OWNINGTHREAD(ClientSource);
   if (!mOwner.is<WorkerPrivate*>()) {
     return nullptr;
   }
   return mOwner.as<WorkerPrivate*>();
@@ -69,16 +101,28 @@ ClientSource::GetDocShell() const
 {
   NS_ASSERT_OWNINGTHREAD(ClientSource);
   if (!mOwner.is<nsCOMPtr<nsIDocShell>>()) {
     return nullptr;
   }
   return mOwner.as<nsCOMPtr<nsIDocShell>>();
 }
 
+void
+ClientSource::MaybeCreateInitialDocument()
+{
+  nsIDocShell* docshell = GetDocShell();
+  if (docshell) {
+    // Force the create of the initial document if it does not exist yet.
+    Unused << docshell->GetDocument();
+
+    MOZ_DIAGNOSTIC_ASSERT(GetInnerWindow());
+  }
+}
+
 ClientSource::ClientSource(ClientManager* aManager,
                            nsISerialEventTarget* aEventTarget,
                            const ClientSourceConstructorArgs& aArgs)
   : mManager(aManager)
   , mEventTarget(aEventTarget)
   , mOwner(AsVariant(Nothing()))
   , mClientInfo(aArgs.id(), aArgs.type(), aArgs.principalInfo(), aArgs.creationTime())
 {
@@ -294,16 +338,52 @@ ClientSource::Control(const ClientContro
 }
 
 const Maybe<ServiceWorkerDescriptor>&
 ClientSource::GetController() const
 {
   return mController;
 }
 
+RefPtr<ClientOpPromise>
+ClientSource::GetInfoAndState(const ClientGetInfoAndStateArgs& aArgs)
+{
+  RefPtr<ClientOpPromise> ref;
+
+  ClientState state;
+  nsresult rv = SnapshotState(&state);
+  if (NS_FAILED(rv)) {
+    ref = ClientOpPromise::CreateAndReject(rv, __func__);
+    return ref.forget();
+  }
+
+  ref = ClientOpPromise::CreateAndResolve(ClientInfoAndState(mClientInfo.ToIPC(),
+                                                             state.ToIPC()), __func__);
+  return ref.forget();
+}
+
+nsresult
+ClientSource::SnapshotState(ClientState* aStateOut)
+{
+  NS_ASSERT_OWNINGTHREAD(ClientSource);
+  MOZ_DIAGNOSTIC_ASSERT(aStateOut);
+
+  if (mClientInfo.Type() == ClientType::Window) {
+    MaybeCreateInitialDocument();
+    nsresult rv = SnapshotWindowState(aStateOut);
+    if (NS_FAILED(rv)) {
+      return rv;
+    }
+    return NS_OK;
+  }
+
+  *aStateOut = ClientState(ClientWorkerState());
+  return NS_OK;
+}
+
 nsISerialEventTarget*
 ClientSource::EventTarget() const
 {
   return mEventTarget;
 }
 
 void
 ClientSource::Traverse(nsCycleCollectionTraversalCallback& aCallback,
--- a/dom/clients/manager/ClientSource.h
+++ b/dom/clients/manager/ClientSource.h
@@ -60,16 +60,22 @@ class ClientSource final : public Client
   ExecutionReady(const ClientSourceExecutionReadyArgs& aArgs);
 
   mozilla::dom::workers::WorkerPrivate*
   GetWorkerPrivate() const;
 
   nsIDocShell*
   GetDocShell() const;
 
+  void
+  MaybeCreateInitialDocument();
+
+  nsresult
+  SnapshotWindowState(ClientState* aStateOut);
+
   // Private methods called by ClientManager
   ClientSource(ClientManager* aManager,
                nsISerialEventTarget* aEventTarget,
                const ClientSourceConstructorArgs& aArgs);
 
   void
   Activate(PClientManagerChild* aActor);
 
@@ -120,16 +126,22 @@ public:
   RefPtr<ClientOpPromise>
   Control(const ClientControlledArgs& aArgs);
 
   // Get the ClientSource's current controlling service worker, if one has
   // been set.
   const Maybe<ServiceWorkerDescriptor>&
   GetController() const;
 
+  RefPtr<ClientOpPromise>
+  GetInfoAndState(const ClientGetInfoAndStateArgs& aArgs);
+
+  nsresult
+  SnapshotState(ClientState* aStateOut);
+
   nsISerialEventTarget*
   EventTarget() const;
 
   void
   Traverse(nsCycleCollectionTraversalCallback& aCallback,
            const char* aName,
            uint32_t aFlags);
 };
--- a/dom/clients/manager/ClientSourceOpChild.cpp
+++ b/dom/clients/manager/ClientSourceOpChild.cpp
@@ -74,16 +74,22 @@ void
 ClientSourceOpChild::Init(const ClientOpConstructorArgs& aArgs)
 {
   switch (aArgs.type()) {
     case ClientOpConstructorArgs::TClientControlledArgs:
     {
       DoSourceOp(&ClientSource::Control, aArgs.get_ClientControlledArgs());
       break;
     }
+    case ClientOpConstructorArgs::TClientGetInfoAndStateArgs:
+    {
+      DoSourceOp(&ClientSource::GetInfoAndState,
+                 aArgs.get_ClientGetInfoAndStateArgs());
+      break;
+    }
     default:
     {
       MOZ_ASSERT_UNREACHABLE("unknown client operation!");
       break;
     }
   }
 }
 
--- a/dom/clients/manager/ClientSourceParent.cpp
+++ b/dom/clients/manager/ClientSourceParent.cpp
@@ -214,16 +214,22 @@ ClientSourceParent::Info() const
 }
 
 bool
 ClientSourceParent::IsFrozen() const
 {
   return mFrozen;
 }
 
+bool
+ClientSourceParent::ExecutionReady() const
+{
+  return mExecutionReady;
+}
+
 void
 ClientSourceParent::AttachHandle(ClientHandleParent* aClientHandle)
 {
   MOZ_DIAGNOSTIC_ASSERT(aClientHandle);
   MOZ_DIAGNOSTIC_ASSERT(!mFrozen);
   MOZ_ASSERT(!mHandleList.Contains(aClientHandle));
   mHandleList.AppendElement(aClientHandle);
 }
--- a/dom/clients/manager/ClientSourceParent.h
+++ b/dom/clients/manager/ClientSourceParent.h
@@ -62,16 +62,19 @@ public:
   Init();
 
   const ClientInfo&
   Info() const;
 
   bool
   IsFrozen() const;
 
+  bool
+  ExecutionReady() const;
+
   void
   AttachHandle(ClientHandleParent* aClientSource);
 
   void
   DetachHandle(ClientHandleParent* aClientSource);
 
   RefPtr<ClientOpPromise>
   StartOp(const ClientOpConstructorArgs& aArgs);