Bug 1491889 - Update chromium CDM interface to accommodate async init. r=cpearce
authorBryce Van Dyk <bvandyk@mozilla.com>
Tue, 25 Sep 2018 13:33:09 +0000
changeset 438114 53dfb7e2bc86017a921eb3f19cdadad3731d99ec
parent 438113 b61f58586c264fa6192a70a33a3b47e187c94e95
child 438115 8a1695f0c08c5e8036f12d4adbdd825e4e764fde
push id34710
push useraciure@mozilla.com
push dateTue, 25 Sep 2018 21:48:21 +0000
treeherdermozilla-central@17254f49a52e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerscpearce
bugs1491889
milestone64.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 1491889 - Update chromium CDM interface to accommodate async init. r=cpearce Starting at the Widevine CDM10 interface, the CDM is expected to make a callback to an `OnInititalized` function to signal initialization has taken place. Prior to this, it was sufficient to call the init function on the CDM, with no waiting for a callback. This changeset puts in place the IPDL to support async init, as well as the handling for the ChromiumCDMParent and ChromiumCDMProxy. The code is not fully updated to handle CDM10, so CDM9 is the only compatible CDM. Because CDM9 does not perform the init callback, we immediately call our IPDL to signal init has taken place. This also accommodates the clearkey case, which uses the CDM9 interface. Further changesets will put in place more elaborate handling to accommodate the possible failure of init, as well as implementing the handling `OnInitialized` function explicitly. Differential Revision: https://phabricator.services.mozilla.com/D6061
dom/media/gmp/ChromiumCDMChild.cpp
dom/media/gmp/ChromiumCDMChild.h
dom/media/gmp/ChromiumCDMParent.cpp
dom/media/gmp/ChromiumCDMParent.h
dom/media/gmp/ChromiumCDMProxy.cpp
dom/media/gmp/PChromiumCDM.ipdl
dom/media/gtest/TestCDMStorage.cpp
--- a/dom/media/gmp/ChromiumCDMChild.cpp
+++ b/dom/media/gmp/ChromiumCDMChild.cpp
@@ -449,29 +449,31 @@ ipc::IPCResult
 ChromiumCDMChild::RecvPurgeShmems()
 {
   PurgeShmems();
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
 ChromiumCDMChild::RecvInit(const bool& aAllowDistinctiveIdentifier,
-                           const bool& aAllowPersistentState)
+                           const bool& aAllowPersistentState,
+                           InitResolver&& aResolver)
 {
   MOZ_ASSERT(IsOnMessageLoopThread());
   GMP_LOG("ChromiumCDMChild::RecvInit(distinctiveId=%s, persistentState=%s)",
           aAllowDistinctiveIdentifier ? "true" : "false",
           aAllowPersistentState ? "true" : "false");
   mPersistentStateAllowed = aAllowPersistentState;
   if (mCDM) {
     mCDM->Initialize(aAllowDistinctiveIdentifier,
                      aAllowPersistentState,
                      // We do not yet support hardware secure codecs
                      false);
   }
+  aResolver(true /* unused */);
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
 ChromiumCDMChild::RecvSetServerCertificate(const uint32_t& aPromiseId,
                                            nsTArray<uint8_t>&& aServerCert)
 
 {
--- a/dom/media/gmp/ChromiumCDMChild.h
+++ b/dom/media/gmp/ChromiumCDMChild.h
@@ -84,17 +84,18 @@ protected:
                                           const nsCString& aSessionId);
 
   bool IsOnMessageLoopThread();
 
   ipc::IPCResult RecvGiveBuffer(ipc::Shmem&& aShmem) override;
   ipc::IPCResult RecvPurgeShmems() override;
   void PurgeShmems();
   ipc::IPCResult RecvInit(const bool& aAllowDistinctiveIdentifier,
-                          const bool& aAllowPersistentState) override;
+                          const bool& aAllowPersistentState,
+                          InitResolver&& aResolver) override;
   ipc::IPCResult RecvSetServerCertificate(
     const uint32_t& aPromiseId,
     nsTArray<uint8_t>&& aServerCert) override;
   ipc::IPCResult RecvCreateSessionAndGenerateRequest(
     const uint32_t& aPromiseId,
     const uint32_t& aSessionType,
     const uint32_t& aInitDataType,
     nsTArray<uint8_t>&& aInitData) override;
--- a/dom/media/gmp/ChromiumCDMParent.cpp
+++ b/dom/media/gmp/ChromiumCDMParent.cpp
@@ -36,57 +36,85 @@ ChromiumCDMParent::ChromiumCDMParent(GMP
 {
   GMP_LOG(
     "ChromiumCDMParent::ChromiumCDMParent(this=%p, contentParent=%p, id=%u)",
     this,
     aContentParent,
     aPluginId);
 }
 
-bool
+RefPtr<ChromiumCDMParent::InitPromise>
 ChromiumCDMParent::Init(ChromiumCDMCallback* aCDMCallback,
                         bool aAllowDistinctiveIdentifier,
                         bool aAllowPersistentState,
-                        nsIEventTarget* aMainThread,
-                        nsCString& aOutFailureReason)
+                        nsIEventTarget* aMainThread)
 {
-  GMP_LOG("ChromiumCDMParent::Init(this=%p) shutdown=%d abormalShutdown=%d "
-          "actorDestroyed=%d",
+  GMP_LOG("ChromiumCDMParent::Init(this=%p) shutdown=%s abormalShutdown=%s "
+          "actorDestroyed=%s",
           this,
-          mIsShutdown,
-          mAbnormalShutdown,
-          mActorDestroyed);
+          mIsShutdown ? "true" : "false",
+          mAbnormalShutdown ? "true" : "false",
+          mActorDestroyed ? "true" : "false");
   if (!aCDMCallback || !aMainThread) {
-    aOutFailureReason = nsPrintfCString("ChromiumCDMParent::Init() failed "
-                                        "nullCallback=%d nullMainThread=%d",
-                                        !aCDMCallback,
-                                        !aMainThread);
-    GMP_LOG("ChromiumCDMParent::Init(this=%p) failure since aCDMCallback(%p) or"
-            " aMainThread(%p) is nullptr", this, aCDMCallback, aMainThread);
-    return false;
+    GMP_LOG("ChromiumCDMParent::Init(this=%p) failed "
+            "nullCallback=%s nullMainThread=%s",
+            this,
+            !aCDMCallback ? "true" : "false",
+            !aMainThread ? "true" : "false");
+
+    return ChromiumCDMParent::InitPromise::CreateAndReject(
+      MediaResult(NS_ERROR_FAILURE,
+                  nsPrintfCString("ChromiumCDMParent::Init() failed "
+                                  "nullCallback=%s nullMainThread=%s",
+                                  !aCDMCallback ? "true" : "false",
+                                  !aMainThread ? "true" : "false")),
+      __func__);
   }
   mCDMCallback = aCDMCallback;
   mMainThread = aMainThread;
 
-  if (SendInit(aAllowDistinctiveIdentifier,
-               aAllowPersistentState)) {
-    return true;
-  }
-
-  RefPtr<gmp::GeckoMediaPluginService> service =
-    gmp::GeckoMediaPluginService::GetGeckoMediaPluginService();
-  bool xpcomWillShutdown = service && service->XPCOMWillShutdownReceived();
-  aOutFailureReason = nsPrintfCString(
-    "ChromiumCDMParent::Init() failed "
-    "shutdown=%d cdmCrash=%d actorDestroyed=%d browserShutdown=%d",
-    mIsShutdown,
-    mAbnormalShutdown,
-    mActorDestroyed,
-    xpcomWillShutdown);
-  return false;
+  RefPtr<ChromiumCDMParent::InitPromise> promise =
+    mInitPromise.Ensure(__func__);
+  RefPtr<ChromiumCDMParent> self = this;
+  SendInit(aAllowDistinctiveIdentifier, aAllowPersistentState)
+    ->Then(AbstractThread::GetCurrent(),
+           __func__,
+           [self](bool /* unused */) {
+             GMP_LOG(
+               "ChromiumCDMParent::Init() succeeded with callback from child");
+             self->mInitPromise.ResolveIfExists(true /* unused */, __func__);
+           },
+           [self](ResponseRejectReason aReason) {
+             RefPtr<gmp::GeckoMediaPluginService> service =
+               gmp::GeckoMediaPluginService::GetGeckoMediaPluginService();
+             bool xpcomWillShutdown =
+               service && service->XPCOMWillShutdownReceived();
+             GMP_LOG("ChromiumCDMParent::Init(this=%p) failed "
+                     "shutdown=%s cdmCrash=%s actorDestroyed=%s "
+                     "browserShutdown=%s promiseRejectReason=%d",
+                     self.get(),
+                     self->mIsShutdown ? "true" : "false",
+                     self->mAbnormalShutdown ? "true" : "false",
+                     self->mActorDestroyed ? "true" : "false",
+                     xpcomWillShutdown ? "true" : "false",
+                     static_cast<int>(aReason));
+             self->mInitPromise.RejectIfExists(
+               MediaResult(
+                 NS_ERROR_FAILURE,
+                 nsPrintfCString("ChromiumCDMParent::Init() failed "
+                                 "shutdown=%s cdmCrash=%s actorDestroyed=%s "
+                                 "browserShutdown=%s promiseRejectReason=%d",
+                                 self->mIsShutdown ? "true" : "false",
+                                 self->mAbnormalShutdown ? "true" : "false",
+                                 self->mActorDestroyed ? "true" : "false",
+                                 xpcomWillShutdown ? "true" : "false",
+                                 static_cast<int>(aReason))),
+               __func__);
+           });
+  return promise;
 }
 
 void
 ChromiumCDMParent::CreateSession(uint32_t aCreateSessionToken,
                                  uint32_t aSessionType,
                                  uint32_t aInitDataType,
                                  uint32_t aPromiseId,
                                  const nsTArray<uint8_t>& aInitData)
@@ -1134,16 +1162,21 @@ ChromiumCDMParent::Shutdown()
   if (mVideoDecoderInitialized && !mActorDestroyed) {
     Unused << SendDeinitializeVideoDecoder();
     mVideoDecoderInitialized = false;
   }
 
   // Note: MediaKeys rejects all outstanding promises when it initiates shutdown.
   mPromiseToCreateSessionToken.Clear();
 
+  mInitPromise.RejectIfExists(
+    MediaResult(NS_ERROR_DOM_ABORT_ERR,
+                RESULT_DETAIL("ChromiumCDMParent is shutdown")),
+    __func__);
+
   mInitVideoDecoderPromise.RejectIfExists(
     MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR,
                 RESULT_DETAIL("ChromiumCDMParent is shutdown")),
     __func__);
   mDecodePromise.RejectIfExists(
     MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR,
                 RESULT_DETAIL("ChromiumCDMParent is shutdown")),
     __func__);
--- a/dom/media/gmp/ChromiumCDMParent.h
+++ b/dom/media/gmp/ChromiumCDMParent.h
@@ -29,27 +29,28 @@ namespace gmp {
 
 class GMPContentParent;
 
 class ChromiumCDMParent final
   : public PChromiumCDMParent
   , public GMPCrashHelperHolder
 {
 public:
+  typedef MozPromise<bool, MediaResult, /* IsExclusive = */ true> InitPromise;
+
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(ChromiumCDMParent)
 
   ChromiumCDMParent(GMPContentParent* aContentParent, uint32_t aPluginId);
 
   uint32_t PluginId() const { return mPluginId; }
 
-  bool Init(ChromiumCDMCallback* aCDMCallback,
-            bool aAllowDistinctiveIdentifier,
-            bool aAllowPersistentState,
-            nsIEventTarget* aMainThread,
-            nsCString& aOutFailureReason);
+  RefPtr<InitPromise> Init(ChromiumCDMCallback* aCDMCallback,
+                           bool aAllowDistinctiveIdentifier,
+                           bool aAllowPersistentState,
+                           nsIEventTarget* aMainThread);
 
   void CreateSession(uint32_t aCreateSessionToken,
                      uint32_t aSessionType,
                      uint32_t aInitDataType,
                      uint32_t aPromiseId,
                      const nsTArray<uint8_t>& aInitData);
 
   void LoadSession(uint32_t aPromiseId,
@@ -154,16 +155,18 @@ protected:
   const uint32_t mPluginId;
   GMPContentParent* mContentParent;
   // Note: this pointer is a weak reference as ChromiumCDMProxy has a strong reference to the
   // ChromiumCDMCallback.
   ChromiumCDMCallback* mCDMCallback = nullptr;
   nsDataHashtable<nsUint32HashKey, uint32_t> mPromiseToCreateSessionToken;
   nsTArray<RefPtr<DecryptJob>> mDecrypts;
 
+  MozPromiseHolder<InitPromise> mInitPromise;
+
   MozPromiseHolder<MediaDataDecoder::InitPromise> mInitVideoDecoderPromise;
   MozPromiseHolder<MediaDataDecoder::DecodePromise> mDecodePromise;
 
   RefPtr<layers::ImageContainer> mImageContainer;
   VideoInfo mVideoInfo;
   uint64_t mLastStreamOffset = 0;
 
   MozPromiseHolder<MediaDataDecoder::FlushPromise> mFlushDecoderPromise;
--- a/dom/media/gmp/ChromiumCDMProxy.cpp
+++ b/dom/media/gmp/ChromiumCDMProxy.cpp
@@ -93,35 +93,43 @@ ChromiumCDMProxy::Init(PromiseId aPromis
             "Couldn't get GeckoMediaPluginService in ChromiumCDMProxy::Init"));
         return;
       }
       RefPtr<gmp::GetCDMParentPromise> promise =
         service->GetCDM(nodeId, { keySystem }, helper);
       promise->Then(
         thread,
         __func__,
-        [self, aPromiseId](RefPtr<gmp::ChromiumCDMParent> cdm) {
+        [self, aPromiseId, thread](RefPtr<gmp::ChromiumCDMParent> cdm) {
+          // service->GetCDM succeeded
           self->mCallback =
             MakeUnique<ChromiumCDMCallbackProxy>(self, self->mMainThread);
-          nsCString failureReason;
-          if (!cdm->Init(self->mCallback.get(),
-                         self->mDistinctiveIdentifierRequired,
-                         self->mPersistentStateRequired,
-                         self->mMainThread,
-                         failureReason)) {
-            self->RejectPromise(aPromiseId, NS_ERROR_FAILURE, failureReason);
-            return;
-          }
-          {
-            MutexAutoLock lock(self->mCDMMutex);
-            self->mCDM = cdm;
-          }
-          self->OnCDMCreated(aPromiseId);
+          cdm
+            ->Init(self->mCallback.get(),
+                   self->mDistinctiveIdentifierRequired,
+                   self->mPersistentStateRequired,
+                   self->mMainThread)
+            ->Then(thread,
+                   __func__,
+                   [self, aPromiseId, cdm](bool /* unused */) {
+                     // CDM init succeeded
+                     {
+                       MutexAutoLock lock(self->mCDMMutex);
+                       self->mCDM = cdm;
+                     }
+                     self->OnCDMCreated(aPromiseId);
+                   },
+                   [self, aPromiseId](MediaResult aResult) {
+                     // CDM init failed
+                     self->RejectPromise(
+                       aPromiseId, aResult.Code(), aResult.Message());
+                   });
         },
         [self, aPromiseId](MediaResult rv) {
+          // service->GetCDM failed
           self->RejectPromise(
             aPromiseId, rv.Code(), rv.Description());
         });
     }));
 
   mGMPThread->Dispatch(task.forget());
 }
 
--- a/dom/media/gmp/PChromiumCDM.ipdl
+++ b/dom/media/gmp/PChromiumCDM.ipdl
@@ -11,17 +11,17 @@ namespace gmp {
 
 async protocol PChromiumCDM
 {
   manager PGMPContent;
 child:
 
   // cdm::ContentDecryptionModule9+10
   async Init(bool aAllowDistinctiveIdentifier,
-             bool aAllowPersistentState);
+             bool aAllowPersistentState) returns (bool unused);
 
   async GetStatusForPolicy(uint32_t aPromiseId,
                            nsCString aMinHdcpVersion);
 
   async SetServerCertificate(uint32_t aPromiseId,
                              uint8_t[] aServerCert);
 
   async CreateSessionAndGenerateRequest(uint32_t aPromiseId,
--- a/dom/media/gtest/TestCDMStorage.cpp
+++ b/dom/media/gtest/TestCDMStorage.cpp
@@ -443,34 +443,37 @@ class CDMStorageTest
 
     nsTArray<nsCString> tags;
     tags.AppendElement(NS_LITERAL_CSTRING("fake"));
 
     RefPtr<CDMStorageTest> self = this;
     RefPtr<gmp::GetCDMParentPromise> promise =
           service->GetCDM(aNodeId, std::move(tags), nullptr);
     auto thread = GetAbstractGMPThread();
-    promise->Then(thread,
-                  __func__,
-                  [self, aUpdates](RefPtr<gmp::ChromiumCDMParent> cdm) {
-                    self->mCDM = cdm;
-                    EXPECT_TRUE(!!self->mCDM);
-                    self->mCallback.reset(new CallbackProxy(self));
-                    nsCString failureReason;
-                    self->mCDM->Init(self->mCallback.get(),
-                                     false,
-                                     true,
-                                     GetMainThreadEventTarget(),
-                                     failureReason);
+    promise->Then(
+      thread,
+      __func__,
+      [self, aUpdates, thread](RefPtr<gmp::ChromiumCDMParent> cdm) {
+        self->mCDM = cdm;
+        EXPECT_TRUE(!!self->mCDM);
+        self->mCallback.reset(new CallbackProxy(self));
+        nsCString failureReason;
+        self->mCDM
+          ->Init(self->mCallback.get(), false, true, GetMainThreadEventTarget())
+          ->Then(thread,
+                 __func__,
+                 [self, aUpdates] {
+                   for (auto& update : aUpdates) {
+                     self->Update(update);
+                   }
+                 },
+                 [](MediaResult rv) { EXPECT_TRUE(false); });
 
-                    for (auto& update : aUpdates) {
-                      self->Update(update);
-                    }
-                  },
-                  [](MediaResult rv) { EXPECT_TRUE(false); });
+      },
+      [](MediaResult rv) { EXPECT_TRUE(false); });
   }
 
   void TestBasicStorage() {
     AssertIsOnGMPThread();
     EXPECT_TRUE(IsCDMStorageIsEmpty());
 
     RefPtr<GeckoMediaPluginService> service =
       GeckoMediaPluginService::GetGeckoMediaPluginService();