Bug 1315850 - Add GMPService::GetContentChild() with unresolved NodeId. r=gerald draft
authorChris Pearce <cpearce@mozilla.com>
Fri, 25 Nov 2016 10:30:38 +1300
changeset 503969 cf20a165048f777f34dab01fce984018ad641b85
parent 503968 b3c63c76f9eda144e6ae2c8801ac8637444f6c48
child 503970 59dc672686c574b690a833eb7f30c916a466e209
child 504153 c7f5c395a719b9f3f13d398f8ca976b09f25ce49
push id50727
push userbmo:cpearce@mozilla.com
push dateThu, 23 Mar 2017 22:10:26 +0000
reviewersgerald
bugs1315850
milestone55.0a1
Bug 1315850 - Add GMPService::GetContentChild() with unresolved NodeId. r=gerald We currently do two sync IPCs to launch a GMP; one from content to main process to get the nodeId and a second to get a GMPContentParent for that nodeId. We use the nodeIds to ensure that the GMPVideoDecoder and GMPDecryptor actors correspond to the same CDM instance/process. However once we switch to having one protocol that encompasses both decryption and decoding, we don't need to worry about making sure our decoder and decryptor actors match up, as we only have one underlying connection to the CDM instance. So we can merge the get nodeId and get GMPContentParent operations into a single operation that does both. To do this, we just need to pass the parameters used to calculate the nodeId in the LaunchGMP message. Once we've switched EME over to using the CDM via a single actor, we can remove the nodeId nsCString from our media code and from GMPVideoDecoder and GMPVideoEncoder. MozReview-Commit-ID: 7GXlJ37fOTZ
dom/media/gmp/GMPService.h
dom/media/gmp/GMPServiceChild.cpp
dom/media/gmp/GMPServiceChild.h
dom/media/gmp/GMPServiceParent.cpp
dom/media/gmp/GMPServiceParent.h
dom/media/gmp/GMPTypes.ipdlh
dom/media/gmp/PGMPService.ipdl
--- a/dom/media/gmp/GMPService.h
+++ b/dom/media/gmp/GMPService.h
@@ -30,16 +30,23 @@ template <class> struct already_AddRefed
 namespace mozilla {
 
 class GMPCrashHelper;
 
 extern LogModule* GetGMPLog();
 
 namespace gmp {
 
+struct NodeId
+{
+  nsString mOrigin;
+  nsString mTopLevelOrigin;
+  nsString mGMPName;
+};
+
 typedef MozPromise<RefPtr<GMPContentParent::CloseBlocker>, nsresult, /* IsExclusive = */ true> GetGMPContentParentPromise;
 
 class GeckoMediaPluginService : public mozIGeckoMediaPluginService
                               , public nsIObserver
 {
 public:
   static already_AddRefed<GeckoMediaPluginService> GetGeckoMediaPluginService();
 
@@ -85,21 +92,27 @@ public:
   void DisconnectCrashHelper(GMPCrashHelper* aHelper);
 
 protected:
   GeckoMediaPluginService();
   virtual ~GeckoMediaPluginService();
 
   virtual void InitializePlugins(AbstractThread* aAbstractGMPThread) = 0;
 
-  virtual RefPtr<GetGMPContentParentPromise>
-  GetContentParent(GMPCrashHelper* aHelper,
-                   const nsACString& aNodeId,
-                   const nsCString& aAPI,
-                   const nsTArray<nsCString>& aTags) = 0;
+  virtual RefPtr<GetGMPContentParentPromise> GetContentParent(
+    GMPCrashHelper* aHelper,
+    const nsACString& aNodeIdString,
+    const nsCString& aAPI,
+    const nsTArray<nsCString>& aTags) = 0;
+
+  virtual RefPtr<GetGMPContentParentPromise> GetContentParent(
+    GMPCrashHelper* aHelper,
+    const NodeId& aNodeId,
+    const nsCString& aAPI,
+    const nsTArray<nsCString>& aTags) = 0;
 
   nsresult GMPDispatch(nsIRunnable* event, uint32_t flags = NS_DISPATCH_NORMAL);
   nsresult GMPDispatch(already_AddRefed<nsIRunnable> event, uint32_t flags = NS_DISPATCH_NORMAL);
   void ShutdownGMPThread();
 
   Mutex mMutex; // Protects mGMPThread, mAbstractGMPThread, mPluginCrashHelpers,
                 // mGMPThreadShutdown and some members in derived classes.
   nsCOMPtr<nsIThread> mGMPThread;
--- a/dom/media/gmp/GMPServiceChild.cpp
+++ b/dom/media/gmp/GMPServiceChild.cpp
@@ -49,27 +49,103 @@ GeckoMediaPluginServiceChild::GetSinglet
     MOZ_ASSERT(!chromeService);
   }
 #endif
   return service.forget().downcast<GeckoMediaPluginServiceChild>();
 }
 
 RefPtr<GetGMPContentParentPromise>
 GeckoMediaPluginServiceChild::GetContentParent(GMPCrashHelper* aHelper,
-                                               const nsACString& aNodeId,
+                                               const nsACString& aNodeIdString,
                                                const nsCString& aAPI,
                                                const nsTArray<nsCString>& aTags)
 {
   MOZ_ASSERT(NS_GetCurrentThread() == mGMPThread);
 
   MozPromiseHolder<GetGMPContentParentPromise>* rawHolder = new MozPromiseHolder<GetGMPContentParentPromise>();
   RefPtr<GetGMPContentParentPromise> promise = rawHolder->Ensure(__func__);
   RefPtr<AbstractThread> thread(GetAbstractGMPThread());
 
-  nsCString nodeId(aNodeId);
+  nsCString nodeIdString(aNodeIdString);
+  nsCString api(aAPI);
+  nsTArray<nsCString> tags(aTags);
+  RefPtr<GMPCrashHelper> helper(aHelper);
+  RefPtr<GeckoMediaPluginServiceChild> self(this);
+  GetServiceChild()->Then(
+    thread,
+    __func__,
+    [self, nodeIdString, api, tags, helper, rawHolder](GMPServiceChild* child) {
+      UniquePtr<MozPromiseHolder<GetGMPContentParentPromise>> holder(rawHolder);
+      nsresult rv;
+
+      nsTArray<base::ProcessId> alreadyBridgedTo;
+      child->GetAlreadyBridgedTo(alreadyBridgedTo);
+
+      base::ProcessId otherProcess;
+      nsCString displayName;
+      uint32_t pluginId = 0;
+      ipc::Endpoint<PGMPContentParent> endpoint;
+      bool ok = child->SendLaunchGMP(nodeIdString,
+                                     api,
+                                     tags,
+                                     alreadyBridgedTo,
+                                     &pluginId,
+                                     &otherProcess,
+                                     &displayName,
+                                     &endpoint,
+                                     &rv);
+      if (helper && pluginId) {
+        // Note: Even if the launch failed, we need to connect the crash
+        // helper so that if the launch failed due to the plugin crashing,
+        // we can report the crash via the crash reporter. The crash
+        // handling notification will arrive shortly if the launch failed
+        // due to the plugin crashing.
+        self->ConnectCrashHelper(pluginId, helper);
+      }
+
+      if (!ok || NS_FAILED(rv)) {
+        LOGD(("GeckoMediaPluginServiceChild::GetContentParent SendLaunchGMP "
+              "failed rv=0x%x",
+              static_cast<uint32_t>(rv)));
+        holder->Reject(rv, __func__);
+        return;
+      }
+
+      RefPtr<GMPContentParent> parent =
+        child->GetBridgedGMPContentParent(otherProcess, Move(endpoint));
+      if (!alreadyBridgedTo.Contains(otherProcess)) {
+        parent->SetDisplayName(displayName);
+        parent->SetPluginId(pluginId);
+      }
+      RefPtr<GMPContentParent::CloseBlocker> blocker(
+        new GMPContentParent::CloseBlocker(parent));
+      holder->Resolve(blocker, __func__);
+    },
+    [rawHolder](nsresult rv) {
+      UniquePtr<MozPromiseHolder<GetGMPContentParentPromise>> holder(rawHolder);
+      holder->Reject(rv, __func__);
+    });
+
+  return promise;
+}
+
+RefPtr<GetGMPContentParentPromise>
+GeckoMediaPluginServiceChild::GetContentParent(GMPCrashHelper* aHelper,
+                                               const NodeId& aNodeId,
+                                               const nsCString& aAPI,
+                                               const nsTArray<nsCString>& aTags)
+{
+  MOZ_ASSERT(NS_GetCurrentThread() == mGMPThread);
+
+  MozPromiseHolder<GetGMPContentParentPromise>* rawHolder =
+    new MozPromiseHolder<GetGMPContentParentPromise>();
+  RefPtr<GetGMPContentParentPromise> promise = rawHolder->Ensure(__func__);
+  RefPtr<AbstractThread> thread(GetAbstractGMPThread());
+
+  NodeIdData nodeId(aNodeId.mOrigin, aNodeId.mTopLevelOrigin, aNodeId.mGMPName);
   nsCString api(aAPI);
   nsTArray<nsCString> tags(aTags);
   RefPtr<GMPCrashHelper> helper(aHelper);
   RefPtr<GeckoMediaPluginServiceChild> self(this);
   GetServiceChild()->Then(thread, __func__,
     [self, nodeId, api, tags, helper, rawHolder](GMPServiceChild* child) {
       UniquePtr<MozPromiseHolder<GetGMPContentParentPromise>> holder(rawHolder);
       nsresult rv;
@@ -77,25 +153,25 @@ GeckoMediaPluginServiceChild::GetContent
       nsTArray<base::ProcessId> alreadyBridgedTo;
       child->GetAlreadyBridgedTo(alreadyBridgedTo);
 
       base::ProcessId otherProcess;
       nsCString displayName;
       uint32_t pluginId = 0;
       ipc::Endpoint<PGMPContentParent> endpoint;
 
-      bool ok = child->SendLaunchGMP(nodeId,
-                                     api,
-                                     tags,
-                                     alreadyBridgedTo,
-                                     &pluginId,
-                                     &otherProcess,
-                                     &displayName,
-                                     &endpoint,
-                                     &rv);
+      bool ok = child->SendLaunchGMPForNodeId(nodeId,
+                                              api,
+                                              tags,
+                                              alreadyBridgedTo,
+                                              &pluginId,
+                                              &otherProcess,
+                                              &displayName,
+                                              &endpoint,
+                                              &rv);
 
       if (helper && pluginId) {
         // Note: Even if the launch failed, we need to connect the crash
         // helper so that if the launch failed due to the plugin crashing,
         // we can report the crash via the crash reporter. The crash
         // handling notification will arrive shortly if the launch failed
         // due to the plugin crashing.
         self->ConnectCrashHelper(pluginId, helper);
--- a/dom/media/gmp/GMPServiceChild.h
+++ b/dom/media/gmp/GMPServiceChild.h
@@ -43,21 +43,27 @@ public:
   static void UpdateGMPCapabilities(nsTArray<mozilla::dom::GMPCapabilityData>&& aCapabilities);
 
 protected:
   void InitializePlugins(AbstractThread*) override
   {
     // Nothing to do here.
   }
 
-  virtual RefPtr<GetGMPContentParentPromise>
-  GetContentParent(GMPCrashHelper* aHelper,
-                   const nsACString& aNodeId,
-                   const nsCString& aAPI,
-                   const nsTArray<nsCString>& aTags) override;
+  virtual RefPtr<GetGMPContentParentPromise> GetContentParent(
+    GMPCrashHelper* aHelper,
+    const nsACString& aNodeIdString,
+    const nsCString& aAPI,
+    const nsTArray<nsCString>& aTags) override;
+
+  RefPtr<GetGMPContentParentPromise> GetContentParent(
+    GMPCrashHelper* aHelper,
+    const NodeId& aNodeId,
+    const nsCString& aAPI,
+    const nsTArray<nsCString>& aTags) override;
 
 private:
   friend class OpenPGMPServiceChild;
 
   typedef MozPromise<GMPServiceChild*, nsresult, /* IsExclusive = */ true> GetServiceChildPromise;
   RefPtr<GetServiceChildPromise> GetServiceChild();
 
   nsTArray<MozPromiseHolder<GetServiceChildPromise>> mGetServiceChildPromises;
--- a/dom/media/gmp/GMPServiceParent.cpp
+++ b/dom/media/gmp/GMPServiceParent.cpp
@@ -348,38 +348,41 @@ GeckoMediaPluginServiceParent::EnsureIni
     return GenericPromise::CreateAndResolve(true, __func__);
   }
   // We should have an init promise in flight.
   MOZ_ASSERT(!mInitPromise.IsEmpty());
   return mInitPromise.Ensure(__func__);
 }
 
 RefPtr<GetGMPContentParentPromise>
-GeckoMediaPluginServiceParent::GetContentParent(GMPCrashHelper* aHelper,
-                                               const nsACString& aNodeId,
-                                               const nsCString& aAPI,
-                                               const nsTArray<nsCString>& aTags)
+GeckoMediaPluginServiceParent::GetContentParent(
+  GMPCrashHelper* aHelper,
+  const nsACString& aNodeIdString,
+  const nsCString& aAPI,
+  const nsTArray<nsCString>& aTags)
 {
   RefPtr<AbstractThread> thread(GetAbstractGMPThread());
   if (!thread) {
     return GetGMPContentParentPromise::CreateAndReject(NS_ERROR_FAILURE, __func__);
   }
 
   typedef MozPromiseHolder<GetGMPContentParentPromise> PromiseHolder;
   PromiseHolder* rawHolder = new PromiseHolder();
   RefPtr<GeckoMediaPluginServiceParent> self(this);
   RefPtr<GetGMPContentParentPromise> promise = rawHolder->Ensure(__func__);
-  nsCString nodeId(aNodeId);
+  nsCString nodeIdString(aNodeIdString);
   nsTArray<nsCString> tags(aTags);
   nsCString api(aAPI);
   RefPtr<GMPCrashHelper> helper(aHelper);
-  EnsureInitialized()->Then(thread, __func__,
-    [self, tags, api, nodeId, helper, rawHolder]() -> void {
+  EnsureInitialized()->Then(
+    thread,
+    __func__,
+    [self, tags, api, nodeIdString, helper, rawHolder]() -> void {
       UniquePtr<PromiseHolder> holder(rawHolder);
-      RefPtr<GMPParent> gmp = self->SelectPluginForAPI(nodeId, api, tags);
+      RefPtr<GMPParent> gmp = self->SelectPluginForAPI(nodeIdString, api, tags);
       LOGD(("%s: %p returning %p for api %s", __FUNCTION__, (void *)self, (void *)gmp, api.get()));
       if (!gmp) {
         NS_WARNING("GeckoMediaPluginServiceParent::GetContentParentFrom failed");
         holder->Reject(NS_ERROR_FAILURE, __func__);
         return;
       }
       self->ConnectCrashHelper(gmp->GetPluginId(), helper);
       gmp->GetGMPContentParent(Move(holder));
@@ -388,16 +391,35 @@ GeckoMediaPluginServiceParent::GetConten
       UniquePtr<PromiseHolder> holder(rawHolder);
       NS_WARNING("GMPService::EnsureInitialized failed.");
       holder->Reject(NS_ERROR_FAILURE, __func__);
     });
 
   return promise;
 }
 
+RefPtr<GetGMPContentParentPromise>
+GeckoMediaPluginServiceParent::GetContentParent(
+  GMPCrashHelper* aHelper,
+  const NodeId& aNodeId,
+  const nsCString& aAPI,
+  const nsTArray<nsCString>& aTags)
+{
+  MOZ_ASSERT(NS_GetCurrentThread() == mGMPThread);
+
+  nsCString nodeIdString;
+  nsresult rv = GetNodeId(
+    aNodeId.mOrigin, aNodeId.mTopLevelOrigin, aNodeId.mGMPName, nodeIdString);
+  if (NS_FAILED(rv)) {
+    return GetGMPContentParentPromise::CreateAndReject(NS_ERROR_FAILURE,
+                                                       __func__);
+  }
+  return GetContentParent(aHelper, nodeIdString, aAPI, aTags);
+}
+
 void
 GeckoMediaPluginServiceParent::InitializePlugins(
   AbstractThread* aAbstractGMPThread)
 {
   MOZ_ASSERT(aAbstractGMPThread);
   MonitorAutoLock lock(mInitPromiseMonitor);
   if (mLoadPluginsFromDiskComplete) {
     return;
@@ -1732,16 +1754,46 @@ GMPServiceParent::RecvLaunchGMP(const ns
 
   gmp->IncrementGMPContentChildCount();
 
   *aOutRv = NS_OK;
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
+GMPServiceParent::RecvLaunchGMPForNodeId(
+  const NodeIdData& aNodeId,
+  const nsCString& aApi,
+  nsTArray<nsCString>&& aTags,
+  nsTArray<ProcessId>&& aAlreadyBridgedTo,
+  uint32_t* aOutPluginId,
+  ProcessId* aOutId,
+  nsCString* aOutDisplayName,
+  Endpoint<PGMPContentParent>* aOutEndpoint,
+  nsresult* aOutRv)
+{
+  nsCString nodeId;
+  nsresult rv = mService->GetNodeId(
+    aNodeId.mOrigin(), aNodeId.mTopLevelOrigin(), aNodeId.mGMPName(), nodeId);
+  if (!NS_SUCCEEDED(rv)) {
+    *aOutRv = rv;
+    return IPC_OK();
+  }
+  return RecvLaunchGMP(nodeId,
+                       aApi,
+                       Move(aTags),
+                       Move(aAlreadyBridgedTo),
+                       aOutPluginId,
+                       aOutId,
+                       aOutDisplayName,
+                       aOutEndpoint,
+                       aOutRv);
+}
+
+mozilla::ipc::IPCResult
 GMPServiceParent::RecvGetGMPNodeId(const nsString& aOrigin,
                                    const nsString& aTopLevelOrigin,
                                    const nsString& aGMPName,
                                    nsCString* aID)
 {
   nsresult rv = mService->GetNodeId(aOrigin, aTopLevelOrigin, aGMPName, *aID);
   if (!NS_SUCCEEDED(rv)) {
     return IPC_FAIL_NO_REASON(this);
--- a/dom/media/gmp/GMPServiceParent.h
+++ b/dom/media/gmp/GMPServiceParent.h
@@ -112,21 +112,27 @@ private:
 protected:
   friend class GMPParent;
   void ReAddOnGMPThread(const RefPtr<GMPParent>& aOld);
   void PluginTerminated(const RefPtr<GMPParent>& aOld);
   void InitializePlugins(AbstractThread* aAbstractGMPThread) override;
   RefPtr<GenericPromise::AllPromiseType> LoadFromEnvironment();
   RefPtr<GenericPromise> AddOnGMPThread(nsString aDirectory);
 
-  virtual RefPtr<GetGMPContentParentPromise>
-  GetContentParent(GMPCrashHelper* aHelper,
-                   const nsACString& aNodeId,
-                   const nsCString& aAPI,
-                   const nsTArray<nsCString>& aTags) override;
+  virtual RefPtr<GetGMPContentParentPromise> GetContentParent(
+    GMPCrashHelper* aHelper,
+    const nsACString& aNodeIdString,
+    const nsCString& aAPI,
+    const nsTArray<nsCString>& aTags) override;
+
+  RefPtr<GetGMPContentParentPromise> GetContentParent(
+    GMPCrashHelper* aHelper,
+    const NodeId& aNodeId,
+    const nsCString& aAPI,
+    const nsTArray<nsCString>& aTags) override;
 
 private:
   // Creates a copy of aOriginal. Note that the caller is responsible for
   // adding this to GeckoMediaPluginServiceParent::mPlugins.
   already_AddRefed<GMPParent> ClonePlugin(const GMPParent* aOriginal);
   nsresult EnsurePluginsOnDiskScanned();
   nsresult InitStorage();
 
@@ -218,33 +224,44 @@ class GMPServiceParent final : public PG
 public:
   explicit GMPServiceParent(GeckoMediaPluginServiceParent* aService)
     : mService(aService)
   {
     mService->ServiceUserCreated();
   }
   virtual ~GMPServiceParent();
 
-  mozilla::ipc::IPCResult RecvGetGMPNodeId(const nsString& aOrigin,
-                                           const nsString& aTopLevelOrigin,
-                                           const nsString& aGMPName,
-                                           nsCString* aID) override;
+  ipc::IPCResult RecvGetGMPNodeId(const nsString& aOrigin,
+                                  const nsString& aTopLevelOrigin,
+                                  const nsString& aGMPName,
+                                  nsCString* aID) override;
   void ActorDestroy(ActorDestroyReason aWhy) override;
 
   static bool Create(Endpoint<PGMPServiceParent>&& aGMPService);
 
-  mozilla::ipc::IPCResult RecvLaunchGMP(const nsCString& aNodeId,
-                                        const nsCString& aAPI,
-                                        nsTArray<nsCString>&& aTags,
-                                        nsTArray<ProcessId>&& aAlreadyBridgedTo,
-                                        uint32_t* aOutPluginId,
-                                        ProcessId* aOutID,
-                                        nsCString* aOutDisplayName,
-                                        Endpoint<PGMPContentParent>* aOutEndpoint,
-                                        nsresult* aOutRv) override;
+  ipc::IPCResult RecvLaunchGMP(const nsCString& aNodeId,
+                               const nsCString& aAPI,
+                               nsTArray<nsCString>&& aTags,
+                               nsTArray<ProcessId>&& aAlreadyBridgedTo,
+                               uint32_t* aOutPluginId,
+                               ProcessId* aOutID,
+                               nsCString* aOutDisplayName,
+                               Endpoint<PGMPContentParent>* aOutEndpoint,
+                               nsresult* aOutRv) override;
+
+  ipc::IPCResult RecvLaunchGMPForNodeId(
+    const NodeIdData& nodeId,
+    const nsCString& aAPI,
+    nsTArray<nsCString>&& aTags,
+    nsTArray<ProcessId>&& aAlreadyBridgedTo,
+    uint32_t* aOutPluginId,
+    ProcessId* aOutID,
+    nsCString* aOutDisplayName,
+    Endpoint<PGMPContentParent>* aOutEndpoint,
+    nsresult* aOutRv) override;
 
 private:
   void CloseTransport(Monitor* aSyncMonitor, bool* aCompleted);
 
   RefPtr<GeckoMediaPluginServiceParent> mService;
 };
 
 } // namespace gmp
--- a/dom/media/gmp/GMPTypes.ipdlh
+++ b/dom/media/gmp/GMPTypes.ipdlh
@@ -4,16 +4,22 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 using GMPBufferType from "gmp-video-codec.h";
 using GMPMediaKeyStatus from "gmp-decryption.h";
 
 namespace mozilla {
 namespace gmp {
 
+struct NodeIdData {
+  nsString mOrigin;
+  nsString mTopLevelOrigin;
+  nsString mGMPName;
+};
+
 struct GMPDecryptionData {
   uint8_t[] mKeyId;
   uint8_t[] mIV;
   uint16_t[] mClearBytes;
   uint32_t[] mCipherBytes;
   nsCString[] mSessionIds;
 };
 
--- a/dom/media/gmp/PGMPService.ipdl
+++ b/dom/media/gmp/PGMPService.ipdl
@@ -1,14 +1,15 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* 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 protocol PGMPContent;
+include GMPTypes;
 
 using base::ProcessId from "base/process.h";
 
 namespace mozilla {
 namespace gmp {
 
 sync protocol PGMPService
 {
@@ -18,14 +19,24 @@ parent:
                  nsCString[] tags,
                  ProcessId[] alreadyBridgedTo)
     returns (uint32_t pluginId,
              ProcessId id,
              nsCString displayName,
              Endpoint<PGMPContentParent> endpoint,
              nsresult aResult);
 
+  sync LaunchGMPForNodeId(NodeIdData nodeId,
+                          nsCString api,
+                          nsCString[] tags,
+                          ProcessId[] alreadyBridgedTo)
+    returns (uint32_t pluginId,
+             ProcessId id,
+             nsCString displayName,
+             Endpoint<PGMPContentParent> endpoint,
+             nsresult aResult);
+
   sync GetGMPNodeId(nsString origin, nsString topLevelOrigin, nsString gmpName)
     returns (nsCString id);
 };
 
 } // namespace gmp
 } // namespace mozilla