Bug 1306314 - Add an ID to GMPDecryptor instances, reflect that on CDMProxy. r=gerald draft
authorChris Pearce <cpearce@mozilla.com>
Fri, 11 Nov 2016 12:10:43 +1300
changeset 438364 da14d9a8a7313a609e30649af1a23e79b3e401fe
parent 438112 47e0584afe0ab0b867412189c610b302b6ba0ea7
child 438365 d678628dec67a059aec06918f07ea93ecc54a5f9
push id35680
push usercpearce@mozilla.com
push dateMon, 14 Nov 2016 09:37:10 +0000
reviewersgerald
bugs1306314
milestone52.0a1
Bug 1306314 - Add an ID to GMPDecryptor instances, reflect that on CDMProxy. r=gerald This enables us to identify GMPDecryptor instances in the child process, so that in a later patch when we create a GMPVideoDecoder instance, we can associate it with a GMPDecryptor. Then the cdm::ContentDecryptionModule8 instance that these two actors are adapted to can know whom it's supposed to respond to. We use the IPDL PGMPDecryptorChild actor ID as the GMPDecryptor's ID. This is unique per GMP process, which is sufficient. MozReview-Commit-ID: 7NKND9VjPUW
dom/media/eme/CDMProxy.h
dom/media/eme/DecryptorProxyCallback.h
dom/media/eme/mediadrm/MediaDrmCDMCallbackProxy.h
dom/media/gmp/GMPCDMCallbackProxy.cpp
dom/media/gmp/GMPCDMCallbackProxy.h
dom/media/gmp/GMPCDMProxy.cpp
dom/media/gmp/GMPCDMProxy.h
dom/media/gmp/GMPCrashHelperHolder.h
dom/media/gmp/GMPDecryptorChild.cpp
dom/media/gmp/GMPDecryptorParent.cpp
dom/media/gmp/GMPDecryptorParent.h
dom/media/gmp/PGMPDecryptor.ipdl
dom/media/gtest/TestGMPCrossOrigin.cpp
--- a/dom/media/eme/CDMProxy.h
+++ b/dom/media/eme/CDMProxy.h
@@ -93,16 +93,18 @@ public:
   // Loads the CDM corresponding to mKeySystem.
   // Calls MediaKeys::OnCDMCreated() when the CDM is created.
   virtual void Init(PromiseId aPromiseId,
                     const nsAString& aOrigin,
                     const nsAString& aTopLevelOrigin,
                     const nsAString& aName,
                     bool aInPrivateBrowsing) = 0;
 
+  virtual void OnSetDecryptorId(uint32_t aId) {}
+
   // Main thread only.
   // Uses the CDM to create a key session.
   // Calls MediaKeys::OnSessionActivated() when session is created.
   // Assumes ownership of (Move()s) aInitData's contents.
   virtual void CreateSession(uint32_t aCreateSessionToken,
                              MediaKeySessionType aSessionType,
                              PromiseId aPromiseId,
                              const nsAString& aInitDataType,
@@ -214,16 +216,18 @@ public:
 
   virtual void GetSessionIdsForKeyId(const nsTArray<uint8_t>& aKeyId,
                                      nsTArray<nsCString>& aSessionIds) = 0;
 
 #ifdef DEBUG
   virtual bool IsOnOwnerThread() = 0;
 #endif
 
+  virtual uint32_t GetDecryptorId() { return 0; }
+
 protected:
   virtual ~CDMProxy() {}
 
   // Helper to enforce that a raw pointer is only accessed on the main thread.
   template<class Type>
   class MainThreadOnlyRawPtr {
   public:
     explicit MainThreadOnlyRawPtr(Type* aPtr)
--- a/dom/media/eme/DecryptorProxyCallback.h
+++ b/dom/media/eme/DecryptorProxyCallback.h
@@ -10,16 +10,18 @@
 #include "mozilla/dom/MediaKeyMessageEventBinding.h" // For MediaKeyMessageType
 #include "mozilla/CDMProxy.h"
 
 class DecryptorProxyCallback {
 public:
 
   virtual ~DecryptorProxyCallback() {}
 
+  virtual void SetDecryptorId(uint32_t aId) = 0;
+
   virtual void SetSessionId(uint32_t aCreateSessionId,
                             const nsCString& aSessionId) = 0;
 
   virtual void ResolveLoadSessionPromise(uint32_t aPromiseId,
                                          bool aSuccess) = 0;
 
   virtual void ResolvePromise(uint32_t aPromiseId) = 0;
 
--- a/dom/media/eme/mediadrm/MediaDrmCDMCallbackProxy.h
+++ b/dom/media/eme/mediadrm/MediaDrmCDMCallbackProxy.h
@@ -12,16 +12,19 @@
 
 namespace mozilla {
 class CDMProxy;
 // Proxies call backs from the MediaDrmProxy -> MediaDrmProxySupport back to the MediaKeys
 // object on the main thread.
 // We used annotation calledFrom = "gecko" to ensure running on main thread.
 class MediaDrmCDMCallbackProxy : public DecryptorProxyCallback {
 public:
+
+  void SetDecryptorId(uint32_t aId) override {}
+
   void SetSessionId(uint32_t aCreateSessionToken,
                     const nsCString& aSessionId) override;
 
   void ResolveLoadSessionPromise(uint32_t aPromiseId,
                                  bool aSuccess) override;
 
   void ResolvePromise(uint32_t aPromiseId) override;
 
--- a/dom/media/gmp/GMPCDMCallbackProxy.cpp
+++ b/dom/media/gmp/GMPCDMCallbackProxy.cpp
@@ -17,16 +17,29 @@
 
 namespace mozilla {
 
 GMPCDMCallbackProxy::GMPCDMCallbackProxy(CDMProxy* aProxy)
   : mProxy(aProxy)
 {}
 
 void
+GMPCDMCallbackProxy::SetDecryptorId(uint32_t aId)
+{
+  MOZ_ASSERT(mProxy->IsOnOwnerThread());
+
+  RefPtr<CDMProxy> proxy = mProxy;
+  NS_DispatchToMainThread(
+    NS_NewRunnableFunction([proxy, aId] ()
+    {
+      proxy->OnSetDecryptorId(aId);
+    })
+  );}
+
+void
 GMPCDMCallbackProxy::SetSessionId(uint32_t aToken,
                                   const nsCString& aSessionId)
 {
   MOZ_ASSERT(mProxy->IsOnOwnerThread());
 
   RefPtr<CDMProxy> proxy = mProxy;
   auto sid = NS_ConvertUTF8toUTF16(aSessionId);
   NS_DispatchToMainThread(
--- a/dom/media/gmp/GMPCDMCallbackProxy.h
+++ b/dom/media/gmp/GMPCDMCallbackProxy.h
@@ -12,16 +12,19 @@
 #include "GMPDecryptorProxy.h"
 
 namespace mozilla {
 
 // Proxies call backs from the CDM on the GMP thread back to the MediaKeys
 // object on the main thread.
 class GMPCDMCallbackProxy : public GMPDecryptorProxyCallback {
 public:
+
+  void SetDecryptorId(uint32_t aId) override;
+
   void SetSessionId(uint32_t aCreateSessionToken,
                     const nsCString& aSessionId) override;
 
   void ResolveLoadSessionPromise(uint32_t aPromiseId,
                                  bool aSuccess) override;
 
   void ResolvePromise(uint32_t aPromiseId) override;
 
--- a/dom/media/gmp/GMPCDMProxy.cpp
+++ b/dom/media/gmp/GMPCDMProxy.cpp
@@ -33,16 +33,18 @@ GMPCDMProxy::GMPCDMProxy(dom::MediaKeys*
   : CDMProxy(aKeys,
              aKeySystem,
              aDistinctiveIdentifierRequired,
              aPersistentStateRequired)
   , mCrashHelper(aCrashHelper)
   , mCDM(nullptr)
   , mDecryptionJobCount(0)
   , mShutdownCalled(false)
+  , mDecryptorId(0)
+  , mCreatePromiseId(0)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_COUNT_CTOR(GMPCDMProxy);
 }
 
 GMPCDMProxy::~GMPCDMProxy()
 {
   MOZ_COUNT_DTOR(GMPCDMProxy);
@@ -126,20 +128,29 @@ GMPCDMProxy::gmp_InitDone(GMPDecryptorPr
     return;
   }
 
   mCDM = aCDM;
   mCallback = new GMPCDMCallbackProxy(this);
   mCDM->Init(mCallback,
              mDistinctiveIdentifierRequired,
              mPersistentStateRequired);
+
+  // Await the OnSetDecryptorId callback.
+  mCreatePromiseId = aData->mPromiseId;
+}
+
+void GMPCDMProxy::OnSetDecryptorId(uint32_t aId)
+{
+  MOZ_ASSERT(mCreatePromiseId);
+  mDecryptorId = aId;
   nsCOMPtr<nsIRunnable> task(
     NewRunnableMethod<uint32_t>(this,
                                 &GMPCDMProxy::OnCDMCreated,
-                                aData->mPromiseId));
+                                mCreatePromiseId));
   NS_DispatchToMainThread(task);
 }
 
 class gmp_InitDoneCallback : public GetGMPDecryptorCallback
 {
 public:
   gmp_InitDoneCallback(GMPCDMProxy* aGMPCDMProxy,
                        nsAutoPtr<GMPCDMProxy::InitData>&& aData)
@@ -763,14 +774,26 @@ GMPCDMProxy::GetSessionIdsForKeyId(const
   caps.GetSessionIdsForKeyId(aKeyId, aSessionIds);
 }
 
 void
 GMPCDMProxy::Terminated()
 {
   MOZ_ASSERT(NS_IsMainThread());
   NS_WARNING("CDM terminated");
+  if (mCreatePromiseId) {
+    RejectPromise(mCreatePromiseId,
+                  NS_ERROR_DOM_MEDIA_FATAL_ERR,
+                  NS_LITERAL_CSTRING("Crashed waiting for CDM to initialize"));
+    mCreatePromiseId = 0;
+  }
   if (!mKeys.IsNull()) {
     mKeys->Terminated();
   }
 }
 
+uint32_t
+GMPCDMProxy::GetDecryptorId()
+{
+  return mDecryptorId;
+}
+
 } // namespace mozilla
--- a/dom/media/gmp/GMPCDMProxy.h
+++ b/dom/media/gmp/GMPCDMProxy.h
@@ -29,16 +29,18 @@ public:
               bool aPersistentStateRequired);
 
   void Init(PromiseId aPromiseId,
             const nsAString& aOrigin,
             const nsAString& aTopLevelOrigin,
             const nsAString& aGMPName,
             bool aInPrivateBrowsing) override;
 
+  void OnSetDecryptorId(uint32_t aId) override;
+
   void CreateSession(uint32_t aCreateSessionToken,
                      dom::MediaKeySessionType aSessionType,
                      PromiseId aPromiseId,
                      const nsAString& aInitDataType,
                      nsTArray<uint8_t>& aInitData) override;
 
   void LoadSession(PromiseId aPromiseId,
                    const nsAString& aSessionId) override;
@@ -104,16 +106,18 @@ public:
 
   void GetSessionIdsForKeyId(const nsTArray<uint8_t>& aKeyId,
                              nsTArray<nsCString>& aSessionIds) override;
 
 #ifdef DEBUG
   bool IsOnOwnerThread() override;
 #endif
 
+  uint32_t GetDecryptorId() override;
+
 private:
   friend class gmp_InitDoneCallback;
   friend class gmp_InitGetGMPDecryptorCallback;
 
   struct InitData {
     uint32_t mPromiseId;
     nsString mOrigin;
     nsString mTopLevelOrigin;
@@ -245,14 +249,18 @@ private:
   // mDecryptionJobs as that shrinks as jobs are completed and removed
   // from it.
   // GMP thread only.
   uint32_t mDecryptionJobCount;
 
   // True if GMPCDMProxy::gmp_Shutdown was called.
   // GMP thread only.
   bool mShutdownCalled;
+
+  uint32_t mDecryptorId;
+
+  PromiseId mCreatePromiseId;
 };
 
 
 } // namespace mozilla
 
 #endif // GMPCDMProxy_h_
--- a/dom/media/gmp/GMPCrashHelperHolder.h
+++ b/dom/media/gmp/GMPCrashHelperHolder.h
@@ -48,22 +48,22 @@ namespace mozilla {
 // In the crashing case, the GMPCrashHelpers are deallocated when the crash
 // report is processed in GeckoMediaPluginService::RunPluginCrashCallbacks().
 //
 // It's a bit yuck that we have to have two paths for disconnecting the crash
 // helpers, but there aren't really any better options.
 class GMPCrashHelperHolder
 {
 public:
-  
+
   void SetCrashHelper(GMPCrashHelper* aHelper)
   {
     mCrashHelper = aHelper;
   }
-  
+
   GMPCrashHelper* GetCrashHelper()
   {
     return mCrashHelper;
   }
 
   void MaybeDisconnect(bool aAbnormalShutdown)
   {
     if (!aAbnormalShutdown) {
--- a/dom/media/gmp/GMPDecryptorChild.cpp
+++ b/dom/media/gmp/GMPDecryptorChild.cpp
@@ -68,16 +68,21 @@ GMPDecryptorChild::CallOnGMPThread(Metho
   }
 }
 
 void
 GMPDecryptorChild::Init(GMPDecryptor* aSession)
 {
   MOZ_ASSERT(aSession);
   mSession = aSession;
+  // The ID of this decryptor is the IPDL actor ID. Note it's unique inside
+  // the child process, but not necessarily across all gecko processes. However,
+  // since GMPDecryptors are segregated by node ID/origin, we shouldn't end up
+  // with clashes in the content process.
+  SendSetDecryptorId(Id());
 }
 
 void
 GMPDecryptorChild::SetSessionId(uint32_t aCreateSessionToken,
                                 const char* aSessionId,
                                 uint32_t aSessionIdLength)
 {
   CALL_ON_GMP_THREAD(SendSetSessionId,
--- a/dom/media/gmp/GMPDecryptorParent.cpp
+++ b/dom/media/gmp/GMPDecryptorParent.cpp
@@ -35,16 +35,27 @@ GMPDecryptorParent::GMPDecryptorParent(G
 {
   MOZ_ASSERT(mPlugin && mGMPThread);
 }
 
 GMPDecryptorParent::~GMPDecryptorParent()
 {
 }
 
+bool
+GMPDecryptorParent::RecvSetDecryptorId(const uint32_t& aId)
+{
+  if (!mIsOpen) {
+    NS_WARNING("Trying to use a dead GMP decrypter!");
+    return false;
+  }
+  mCallback->SetDecryptorId(aId);
+  return true;
+}
+
 nsresult
 GMPDecryptorParent::Init(GMPDecryptorProxyCallback* aCallback,
                          bool aDistinctiveIdentifierRequired,
                          bool aPersistentStateRequired)
 {
   LOGD(("GMPDecryptorParent[%p]::Init()", this));
 
   if (mIsOpen) {
--- a/dom/media/gmp/GMPDecryptorParent.h
+++ b/dom/media/gmp/GMPDecryptorParent.h
@@ -67,16 +67,18 @@ public:
 
   void Shutdown();
 
 private:
   ~GMPDecryptorParent();
 
   // PGMPDecryptorParent
 
+  bool RecvSetDecryptorId(const uint32_t& aId) override;
+
   bool RecvSetSessionId(const uint32_t& aCreateSessionToken,
                         const nsCString& aSessionId) override;
 
   bool RecvResolveLoadSessionPromise(const uint32_t& aPromiseId,
                                      const bool& aSuccess) override;
 
   bool RecvResolvePromise(const uint32_t& aPromiseId) override;
 
--- a/dom/media/gmp/PGMPDecryptor.ipdl
+++ b/dom/media/gmp/PGMPDecryptor.ipdl
@@ -48,16 +48,18 @@ child:
                 uint8_t[] aBuffer,
                 GMPDecryptionData aMetadata);
 
   async DecryptingComplete();
 
 parent:
   async __delete__();
 
+  async SetDecryptorId(uint32_t aId);
+
   async SetSessionId(uint32_t aCreateSessionToken,
                      nsCString aSessionId);
 
   async ResolveLoadSessionPromise(uint32_t aPromiseId,
                                   bool aSuccess);
 
   async ResolvePromise(uint32_t aPromiseId);
 
--- a/dom/media/gtest/TestGMPCrossOrigin.cpp
+++ b/dom/media/gtest/TestGMPCrossOrigin.cpp
@@ -591,37 +591,33 @@ class GMPStorageTest : public GMPDecrypt
     EXPECT_TRUE(!aNodeId1.Equals(nodeId3));
 
     SetFinished();
   }
 
   class CreateDecryptorDone : public GetGMPDecryptorCallback
   {
   public:
-    CreateDecryptorDone(GMPStorageTest* aRunner, nsIRunnable* aContinuation)
-      : mRunner(aRunner),
-        mContinuation(aContinuation)
+    explicit CreateDecryptorDone(GMPStorageTest* aRunner)
+      : mRunner(aRunner)
     {
     }
 
     void Done(GMPDecryptorProxy* aDecryptor) override
     {
       mRunner->mDecryptor = aDecryptor;
       EXPECT_TRUE(!!mRunner->mDecryptor);
 
       if (mRunner->mDecryptor) {
         mRunner->mDecryptor->Init(mRunner, false, true);
       }
-      nsCOMPtr<nsIThread> thread(GetGMPThread());
-      thread->Dispatch(mContinuation, NS_DISPATCH_NORMAL);
     }
 
   private:
     RefPtr<GMPStorageTest> mRunner;
-    nsCOMPtr<nsIRunnable> mContinuation;
   };
 
   void CreateDecryptor(const nsCString& aNodeId,
                        const nsCString& aUpdate)
   {
     nsTArray<nsCString> updates;
     updates.AppendElement(aUpdate);
     nsCOMPtr<nsIRunnable> continuation(new Updates(this, Move(updates)));
@@ -674,17 +670,23 @@ class GMPStorageTest : public GMPDecrypt
 
     mNodeId = aNodeId;
     EXPECT_TRUE(!mNodeId.IsEmpty());
 
     nsTArray<nsCString> tags;
     tags.AppendElement(NS_LITERAL_CSTRING("fake"));
 
     UniquePtr<GetGMPDecryptorCallback> callback(
-      new CreateDecryptorDone(this, aContinuation));
+      new CreateDecryptorDone(this));
+
+    // Continue after the OnSetDecryptorId message, so that we don't
+    // get warnings in the async shutdown tests due to receiving the
+    // SetDecryptorId message after we've started shutdown.
+    mSetDecryptorIdContinuation = aContinuation;
+
     nsresult rv =
       service->GetGMPDecryptor(nullptr, &tags, mNodeId, Move(callback));
     EXPECT_TRUE(NS_SUCCEEDED(rv));
   }
 
   void TestBasicStorage() {
     AssertIsOnGMPThread();
     EXPECT_TRUE(IsGMPStorageIsEmpty());
@@ -1368,16 +1370,26 @@ class GMPStorageTest : public GMPDecrypt
       nsCOMPtr<nsIRunnable> continuation = mExpected[0].mContinuation;
       mExpected.RemoveElementAt(0);
       if (continuation) {
         NS_DispatchToCurrentThread(continuation);
       }
     }
   }
 
+  void SetDecryptorId(uint32_t aId) override
+  {
+    if (!mSetDecryptorIdContinuation) {
+      return;
+    }
+    nsCOMPtr<nsIThread> thread(GetGMPThread());
+    thread->Dispatch(mSetDecryptorIdContinuation, NS_DISPATCH_NORMAL);
+    mSetDecryptorIdContinuation = nullptr;
+  }
+
   void SetSessionId(uint32_t aCreateSessionToken,
                     const nsCString& aSessionId) override { }
   void ResolveLoadSessionPromise(uint32_t aPromiseId,
                                  bool aSuccess) override {}
   void ResolvePromise(uint32_t aPromiseId) override {}
   void RejectPromise(uint32_t aPromiseId,
                      nsresult aException,
                      const nsCString& aSessionId) override { }
@@ -1411,16 +1423,18 @@ private:
       , mContinuation(aContinuation)
     {}
     nsCString mMessage;
     nsCOMPtr<nsIRunnable> mContinuation;
   };
 
   nsTArray<ExpectedMessage> mExpected;
 
+  RefPtr<nsIRunnable> mSetDecryptorIdContinuation;
+
   GMPDecryptorProxy* mDecryptor;
   Monitor mMonitor;
   Atomic<bool> mFinished;
   nsCString mNodeId;
 };
 
 void
 GMPTestRunner::DoTest(void (GMPTestRunner::*aTestMethod)(GMPTestMonitor&))