Bug 1315850 - Implement CDM persistent sessions. r=gerald draft
authorChris Pearce <cpearce@mozilla.com>
Thu, 09 Mar 2017 19:09:43 +1300
changeset 504179 80c2133e26742410fda983e3c18c35736fc013d0
parent 504178 186f35455264aaa144fd7b1887b8ca2476ac03b2
child 504180 ef6cab901d74b78f613660f263f5e453d6044536
push id50748
push userbmo:cpearce@mozilla.com
push dateFri, 24 Mar 2017 01:10:17 +0000
reviewersgerald
bugs1315850
milestone55.0a1
Bug 1315850 - Implement CDM persistent sessions. r=gerald This is required for the browser clearing persistence tests to pass. MozReview-Commit-ID: Ai9qc6Ds1IG
dom/media/eme/CDMProxy.h
dom/media/eme/MediaKeySession.cpp
dom/media/eme/mediadrm/MediaDrmCDMProxy.cpp
dom/media/eme/mediadrm/MediaDrmCDMProxy.h
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/ChromiumCDMProxy.h
dom/media/gmp/GMPCDMProxy.cpp
dom/media/gmp/GMPCDMProxy.h
dom/media/gmp/PChromiumCDM.ipdl
--- a/dom/media/eme/CDMProxy.h
+++ b/dom/media/eme/CDMProxy.h
@@ -110,16 +110,17 @@ public:
                              PromiseId aPromiseId,
                              const nsAString& aInitDataType,
                              nsTArray<uint8_t>& aInitData) = 0;
 
   // Main thread only.
   // Uses the CDM to load a presistent session stored on disk.
   // Calls MediaKeys::OnSessionActivated() when session is loaded.
   virtual void LoadSession(PromiseId aPromiseId,
+                           dom::MediaKeySessionType aSessionType,
                            const nsAString& aSessionId) = 0;
 
   // Main thread only.
   // Sends a new certificate to the CDM.
   // Calls MediaKeys->ResolvePromise(aPromiseId) after the CDM has
   // processed the request.
   // Assumes ownership of (Move()s) aCert's contents.
   virtual void SetServerCertificate(PromiseId aPromiseId,
--- a/dom/media/eme/MediaKeySession.cpp
+++ b/dom/media/eme/MediaKeySession.cpp
@@ -391,17 +391,17 @@ MediaKeySession::Load(const nsAString& a
   // session from its owning MediaKey's set of sessions awaiting a sessionId.
   RefPtr<MediaKeySession> session(mKeys->GetPendingSession(Token()));
   MOZ_ASSERT(session == this, "Session should be awaiting id on its own token");
 
   // Associate with the known sessionId.
   SetSessionId(aSessionId);
 
   PromiseId pid = mKeys->StorePromise(promise);
-  mKeys->GetCDMProxy()->LoadSession(pid, aSessionId);
+  mKeys->GetCDMProxy()->LoadSession(pid, mSessionType, aSessionId);
 
   EME_LOG("MediaKeySession[%p,'%s'] Load() sent to CDM, promiseId=%d",
     this, NS_ConvertUTF16toUTF8(mSessionId).get(), pid);
 
   return promise.forget();
 }
 
 already_AddRefed<Promise>
--- a/dom/media/eme/mediadrm/MediaDrmCDMProxy.cpp
+++ b/dom/media/eme/mediadrm/MediaDrmCDMProxy.cpp
@@ -96,16 +96,17 @@ MediaDrmCDMProxy::CreateSession(uint32_t
     NewRunnableMethod<UniquePtr<CreateSessionData>&&>(this,
                                                       &MediaDrmCDMProxy::md_CreateSession,
                                                       Move(data)));
   mOwnerThread->Dispatch(task, NS_DISPATCH_NORMAL);
 }
 
 void
 MediaDrmCDMProxy::LoadSession(PromiseId aPromiseId,
+                              dom::MediaKeySessionType aSessionType,
                               const nsAString& aSessionId)
 {
   // TODO: Implement LoadSession.
   RejectPromise(aPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR,
                 NS_LITERAL_CSTRING("Currently Fennec did not support LoadSession"));
 }
 
 void
--- a/dom/media/eme/mediadrm/MediaDrmCDMProxy.h
+++ b/dom/media/eme/mediadrm/MediaDrmCDMProxy.h
@@ -8,16 +8,17 @@
 #define MediaDrmCDMProxy_h_
 
 #include <jni.h>
 #include "mozilla/jni/Types.h"
 #include "GeneratedJNINatives.h"
 #include "mozilla/CDMProxy.h"
 #include "mozilla/CDMCaps.h"
 #include "mozilla/dom/MediaKeys.h"
+#include "mozilla/dom/MediaKeySession.h"
 #include "mozilla/MediaDrmProxySupport.h"
 #include "mozilla/UniquePtr.h"
 
 #include "MediaCodec.h"
 #include "nsString.h"
 
 using namespace mozilla::java;
 
@@ -42,16 +43,17 @@ public:
 
   void CreateSession(uint32_t aCreateSessionToken,
                      MediaKeySessionType aSessionType,
                      PromiseId aPromiseId,
                      const nsAString& aInitDataType,
                      nsTArray<uint8_t>& aInitData) override;
 
   void LoadSession(PromiseId aPromiseId,
+                   dom::MediaKeySessionType aSessionType,
                    const nsAString& aSessionId) override;
 
   void SetServerCertificate(PromiseId aPromiseId,
                             nsTArray<uint8_t>& aCert) override;
 
   void UpdateSession(const nsAString& aSessionId,
                      PromiseId aPromiseId,
                      nsTArray<uint8_t>& aResponse) override;
--- a/dom/media/gmp/ChromiumCDMChild.cpp
+++ b/dom/media/gmp/ChromiumCDMChild.cpp
@@ -74,16 +74,33 @@ ChromiumCDMChild::OnResolveNewSessionPro
                                              const char* aSessionId,
                                              uint32_t aSessionIdSize)
 {
   MOZ_ASSERT(IsOnMessageLoopThread());
   GMP_LOG("ChromiumCDMChild::OnResolveNewSessionPromise(pid=%" PRIu32
           ", sid=%s)",
           aPromiseId,
           aSessionId);
+
+  if (mLoadSessionPromiseIds.Contains(aPromiseId)) {
+    // As laid out in the Chromium CDM API, if the CDM fails to load
+    // a session it calls OnResolveNewSessionPromise with nullptr as the sessionId.
+    // We can safely assume this means that we have failed to load a session
+    // as the other methods specify calling 'OnRejectPromise' when they fail.
+    bool loadSuccessful = aSessionId != nullptr;
+    GMP_LOG("ChromiumCDMChild::OnResolveNewSessionPromise(pid=%u, sid=%s) "
+            "resolving %s load session ",
+            aPromiseId,
+            aSessionId,
+            (loadSuccessful ? "successful" : "failed"));
+    Unused << SendResolveLoadSessionPromise(aPromiseId, loadSuccessful);
+    mLoadSessionPromiseIds.RemoveElement(aPromiseId);
+    return;
+  }
+
   Unused << SendOnResolveNewSessionPromise(aPromiseId,
                                            nsCString(aSessionId, aSessionIdSize));
 }
 
 void ChromiumCDMChild::OnResolvePromise(uint32_t aPromiseId)
 {
   MOZ_ASSERT(IsOnMessageLoopThread());
   GMP_LOG("ChromiumCDMChild::OnResolvePromise(pid=%" PRIu32 ")", aPromiseId);
@@ -290,16 +307,35 @@ ChromiumCDMChild::RecvCreateSessionAndGe
                                           static_cast<cdm::InitDataType>(aInitDataType),
                                           aInitData.Elements(),
                                           aInitData.Length());
   }
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
+ChromiumCDMChild::RecvLoadSession(const uint32_t& aPromiseId,
+                                  const uint32_t& aSessionType,
+                                  const nsCString& aSessionId)
+{
+  GMP_LOG("ChromiumCDMChild::RecvLoadSession(pid=%u, type=%u, sessionId=%s)",
+          aPromiseId,
+          aSessionType,
+          aSessionId.get());
+  if (mCDM) {
+    mLoadSessionPromiseIds.AppendElement(aPromiseId);
+    mCDM->LoadSession(aPromiseId,
+                      static_cast<cdm::SessionType>(aSessionType),
+                      aSessionId.get(),
+                      aSessionId.Length());
+  }
+  return IPC_OK();
+}
+
+mozilla::ipc::IPCResult
 ChromiumCDMChild::RecvUpdateSession(const uint32_t& aPromiseId,
                                     const nsCString& aSessionId,
                                     nsTArray<uint8_t>&& aResponse)
 {
   MOZ_ASSERT(IsOnMessageLoopThread());
   GMP_LOG("ChromiumCDMChild::RecvUpdateSession(pid=%" PRIu32
           ", sid=%s) responseLen=%zu",
           aPromiseId,
--- a/dom/media/gmp/ChromiumCDMChild.h
+++ b/dom/media/gmp/ChromiumCDMChild.h
@@ -83,16 +83,19 @@ protected:
   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;
+  ipc::IPCResult RecvLoadSession(const uint32_t& aPromiseId,
+                                 const uint32_t& aSessionType,
+                                 const nsCString& aSessionId) override;
   ipc::IPCResult RecvUpdateSession(const uint32_t& aPromiseId,
                                    const nsCString& aSessionId,
                                    nsTArray<uint8_t>&& aResponse) override;
   ipc::IPCResult RecvCloseSession(const uint32_t& aPromiseId,
                                   const nsCString& aSessionId) override;
   ipc::IPCResult RecvRemoveSession(const uint32_t& aPromiseId,
                                    const nsCString& aSessionId) override;
   ipc::IPCResult RecvDecrypt(const uint32_t& aId,
@@ -109,16 +112,17 @@ protected:
   void DecryptFailed(uint32_t aId, cdm::Status aStatus);
   void ReturnOutput(WidevineVideoFrame& aFrame);
 
   GMPContentChild* mPlugin = nullptr;
   cdm::ContentDecryptionModule_8* mCDM = nullptr;
 
   typedef SimpleMap<uint64_t> DurationMap;
   DurationMap mFrameDurations;
+  nsTArray<uint32_t> mLoadSessionPromiseIds;
 
   bool mDecoderInitialized = false;
   bool mPersistentStateAllowed = false;
 };
 
 } // namespace gmp
 } // namespace mozilla
 
--- a/dom/media/gmp/ChromiumCDMParent.cpp
+++ b/dom/media/gmp/ChromiumCDMParent.cpp
@@ -62,16 +62,42 @@ ChromiumCDMParent::CreateSession(uint32_
       NS_ERROR_DOM_INVALID_STATE_ERR,
       NS_LITERAL_CSTRING("Failed to send generateRequest to CDM process."));
     return;
   }
   mPromiseToCreateSessionToken.Put(aPromiseId, aCreateSessionToken);
 }
 
 void
+ChromiumCDMParent::LoadSession(uint32_t aPromiseId,
+                               uint32_t aSessionType,
+                               nsString aSessionId)
+{
+  GMP_LOG("ChromiumCDMParent::LoadSession(this=%p, pid=%u, type=%u, sid=%s)",
+          this,
+          aPromiseId,
+          aSessionType,
+          NS_ConvertUTF16toUTF8(aSessionId).get());
+  if (mIsShutdown) {
+    RejectPromise(aPromiseId,
+                  NS_ERROR_DOM_INVALID_STATE_ERR,
+                  NS_LITERAL_CSTRING("CDM is shutdown."));
+    return;
+  }
+  if (!SendLoadSession(
+        aPromiseId, aSessionType, NS_ConvertUTF16toUTF8(aSessionId))) {
+    RejectPromise(
+      aPromiseId,
+      NS_ERROR_DOM_INVALID_STATE_ERR,
+      NS_LITERAL_CSTRING("Failed to send loadSession to CDM process."));
+    return;
+  }
+}
+
+void
 ChromiumCDMParent::SetServerCertificate(uint32_t aPromiseId,
                                         const nsTArray<uint8_t>& aCert)
 {
   GMP_LOG("ChromiumCDMParent::SetServerCertificate(this=%p)", this);
   if (mIsShutdown) {
     RejectPromise(aPromiseId,
                   NS_ERROR_DOM_INVALID_STATE_ERR,
                   NS_LITERAL_CSTRING("CDM is shutdown."));
@@ -231,16 +257,36 @@ ChromiumCDMParent::RecvOnResolveNewSessi
                                           NS_ConvertUTF8toUTF16(aSessionId));
   NS_DispatchToMainThread(task);
 
   ResolvePromise(aPromiseId);
 
   return IPC_OK();
 }
 
+ipc::IPCResult
+ChromiumCDMParent::RecvResolveLoadSessionPromise(const uint32_t& aPromiseId,
+                                                 const bool& aSuccessful)
+{
+  GMP_LOG("ChromiumCDMParent::RecvResolveLoadSessionPromise(this=%p, pid=%u, "
+          "successful=%d)",
+          this,
+          aPromiseId,
+          aSuccessful);
+  if (!mProxy || mIsShutdown) {
+    return IPC_OK();
+  }
+
+  NS_DispatchToMainThread(NewRunnableMethod<uint32_t, bool>(
+    mProxy,
+    &ChromiumCDMProxy::OnResolveLoadSessionPromise,
+    aPromiseId,
+    aSuccessful));
+  return IPC_OK();
+}
 void
 ChromiumCDMParent::ResolvePromise(uint32_t aPromiseId)
 {
   GMP_LOG(
     "ChromiumCDMParent::ResolvePromise(this=%p, pid=%u)", this, aPromiseId);
 
   // Note: The MediaKeys rejects all pending DOM promises when it
   // initiates shutdown.
--- a/dom/media/gmp/ChromiumCDMParent.h
+++ b/dom/media/gmp/ChromiumCDMParent.h
@@ -41,16 +41,20 @@ public:
             bool aAllowPersistentState);
 
   void CreateSession(uint32_t aCreateSessionToken,
                      uint32_t aSessionType,
                      uint32_t aInitDataType,
                      uint32_t aPromiseId,
                      const nsTArray<uint8_t>& aInitData);
 
+  void LoadSession(uint32_t aPromiseId,
+                   uint32_t aSessionType,
+                   nsString aSessionId);
+
   void SetServerCertificate(uint32_t aPromiseId,
                             const nsTArray<uint8_t>& aCert);
 
   void UpdateSession(const nsCString& aSessionId,
                      uint32_t aPromiseId,
                      const nsTArray<uint8_t>& aResponse);
 
   void CloseSession(const nsCString& aSessionId, uint32_t aPromiseId);
@@ -79,16 +83,19 @@ public:
 
 protected:
   ~ChromiumCDMParent() {}
 
   ipc::IPCResult Recv__delete__() override;
   ipc::IPCResult RecvOnResolveNewSessionPromise(
     const uint32_t& aPromiseId,
     const nsCString& aSessionId) override;
+  ipc::IPCResult RecvResolveLoadSessionPromise(
+    const uint32_t& aPromiseId,
+    const bool& aSuccessful) override;
   ipc::IPCResult RecvOnResolvePromise(const uint32_t& aPromiseId) override;
   ipc::IPCResult RecvOnRejectPromise(const uint32_t& aPromiseId,
                                      const uint32_t& aError,
                                      const uint32_t& aSystemCode,
                                      const nsCString& aErrorMessage) override;
   ipc::IPCResult RecvOnSessionMessage(const nsCString& aSessionId,
                                       const uint32_t& aMessageType,
                                       nsTArray<uint8_t>&& aMessage) override;
--- a/dom/media/gmp/ChromiumCDMProxy.cpp
+++ b/dom/media/gmp/ChromiumCDMProxy.cpp
@@ -219,23 +219,36 @@ ChromiumCDMProxy::CreateSession(uint32_t
                                          aCreateSessionToken,
                                          sessionType,
                                          initDataType,
                                          aPromiseId,
                                          Move(aInitData)));
 }
 
 void
-ChromiumCDMProxy::LoadSession(PromiseId aPromiseId, const nsAString& aSessionId)
+ChromiumCDMProxy::LoadSession(PromiseId aPromiseId,
+                              dom::MediaKeySessionType aSessionType,
+                              const nsAString& aSessionId)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
-  RejectPromise(aPromiseId,
-                NS_ERROR_DOM_NOT_SUPPORTED_ERR,
-                NS_LITERAL_CSTRING("loadSession is not supported"));
+  RefPtr<gmp::ChromiumCDMParent> cdm = GetCDMParent();
+  if (!cdm) {
+    RejectPromise(aPromiseId,
+                  NS_ERROR_DOM_INVALID_STATE_ERR,
+                  NS_LITERAL_CSTRING("Null CDM in LoadSession"));
+    return;
+  }
+
+  mGMPThread->Dispatch(NewRunnableMethod<uint32_t, uint32_t, nsString>(
+    cdm,
+    &gmp::ChromiumCDMParent::LoadSession,
+    aPromiseId,
+    ToCDMSessionType(aSessionType),
+    aSessionId));
 }
 
 void
 ChromiumCDMProxy::SetServerCertificate(PromiseId aPromiseId,
                                        nsTArray<uint8_t>& aCert)
 {
   MOZ_ASSERT(NS_IsMainThread());
   EME_LOG("ChromiumCDMProxy::SetServerCertificate(pid=%u) certLen=%zu",
@@ -412,16 +425,21 @@ ChromiumCDMProxy::OnSetSessionId(uint32_
     session->SetSessionId(aSessionId);
   }
 }
 
 void
 ChromiumCDMProxy::OnResolveLoadSessionPromise(uint32_t aPromiseId,
                                               bool aSuccess)
 {
+  MOZ_ASSERT(NS_IsMainThread());
+  if (mKeys.IsNull()) {
+    return;
+  }
+  mKeys->OnSessionLoaded(aPromiseId, aSuccess);
 }
 
 void
 ChromiumCDMProxy::OnSessionMessage(const nsAString& aSessionId,
                                    dom::MediaKeyMessageType aMessageType,
                                    nsTArray<uint8_t>& aMessage)
 {
   MOZ_ASSERT(NS_IsMainThread());
--- a/dom/media/gmp/ChromiumCDMProxy.h
+++ b/dom/media/gmp/ChromiumCDMProxy.h
@@ -34,17 +34,19 @@ public:
             const nsAString& aGMPName) 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;
+  void LoadSession(PromiseId aPromiseId,
+                   dom::MediaKeySessionType aSessionType,
+                   const nsAString& aSessionId) override;
 
   void SetServerCertificate(PromiseId aPromiseId,
                             nsTArray<uint8_t>& aCert) override;
 
   void UpdateSession(const nsAString& aSessionId,
                      PromiseId aPromiseId,
                      nsTArray<uint8_t>& aResponse) override;
 
--- a/dom/media/gmp/GMPCDMProxy.cpp
+++ b/dom/media/gmp/GMPCDMProxy.cpp
@@ -323,16 +323,17 @@ GMPCDMProxy::gmp_CreateSession(UniquePtr
                       aData->mPromiseId,
                       aData->mInitDataType,
                       aData->mInitData,
                       ToGMPSessionType(aData->mSessionType));
 }
 
 void
 GMPCDMProxy::LoadSession(PromiseId aPromiseId,
+                         dom::MediaKeySessionType aSessionType,
                          const nsAString& aSessionId)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(mOwnerThread);
 
   UniquePtr<SessionOpData> data(new SessionOpData());
   data->mPromiseId = aPromiseId;
   data->mSessionId = NS_ConvertUTF16toUTF8(aSessionId);
--- a/dom/media/gmp/GMPCDMProxy.h
+++ b/dom/media/gmp/GMPCDMProxy.h
@@ -38,16 +38,17 @@ public:
 
   void CreateSession(uint32_t aCreateSessionToken,
                      dom::MediaKeySessionType aSessionType,
                      PromiseId aPromiseId,
                      const nsAString& aInitDataType,
                      nsTArray<uint8_t>& aInitData) override;
 
   void LoadSession(PromiseId aPromiseId,
+                   dom::MediaKeySessionType aSessionType,
                    const nsAString& aSessionId) override;
 
   void SetServerCertificate(PromiseId aPromiseId,
                             nsTArray<uint8_t>& aCert) override;
 
   void UpdateSession(const nsAString& aSessionId,
                      PromiseId aPromiseId,
                      nsTArray<uint8_t>& aResponse) override;
--- a/dom/media/gmp/PChromiumCDM.ipdl
+++ b/dom/media/gmp/PChromiumCDM.ipdl
@@ -21,16 +21,20 @@ child:
   async SetServerCertificate(uint32_t aPromiseId,
                              uint8_t[] aServerCert);
 
   async CreateSessionAndGenerateRequest(uint32_t aPromiseId,
                                         uint32_t aSessionType,
                                         uint32_t aInitDataType,
                                         uint8_t[] aInitData);
 
+  async LoadSession(uint32_t aPromiseId,
+                    uint32_t aSessionType,
+                    nsCString aSessionId);
+
   async UpdateSession(uint32_t aPromiseId,
                       nsCString aSessionId,
                       uint8_t[] aResponse);
 
   async CloseSession(uint32_t aPromiseId,
                      nsCString aSessionId);
 
   async RemoveSession(uint32_t aPromiseId,
@@ -75,16 +79,18 @@ parent:
 
   async OnSessionClosed(nsCString aSessionId);
 
   async OnLegacySessionError(nsCString aSessionId,
                              uint32_t aError,
                              uint32_t aSystemCode,
                              nsCString aMessage);
 
+  async ResolveLoadSessionPromise(uint32_t aPromiseId, bool aSuccessful);
+
   // Return values of cdm::ContentDecryptionModule8::Decrypt
   async Decrypted(uint32_t aId, uint32_t aStatus, uint8_t[] aData);
 
   async OnDecoderInitDone(uint32_t aStatus);
 
   // Return values of cdm::ContentDecryptionModule8::DecryptAndDecodeFrame
   async Decoded(CDMVideoFrame aFrame);
   async DecodeFailed(uint32_t aStatus);