Bug 1404230 - Part4 - Add GetStatusForPolicy method in ipdl and implement it by calling CDM. draft
authorJames Cheng <jacheng@mozilla.com>
Tue, 24 Oct 2017 10:55:03 +0800
changeset 688581 55431c83354fd85587f8e6066c472da08af63ad5
parent 688580 a45be3c24369f2bb74707dfd9a3005e930bffeeb
child 688582 40f7d874184736a021a2250dc4183dab190f5465
push id86792
push userbmo:jacheng@mozilla.com
push dateMon, 30 Oct 2017 09:42:43 +0000
bugs1404230
milestone58.0a1
Bug 1404230 - Part4 - Add GetStatusForPolicy method in ipdl and implement it by calling CDM. MozReview-Commit-ID: 8L0qKgnKMES
dom/media/eme/MediaKeys.cpp
dom/media/eme/MediaKeys.h
dom/media/gmp/ChromiumCDMCallback.h
dom/media/gmp/ChromiumCDMCallbackProxy.cpp
dom/media/gmp/ChromiumCDMCallbackProxy.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/PChromiumCDM.ipdl
dom/media/gtest/TestCDMStorage.cpp
--- a/dom/media/eme/MediaKeys.cpp
+++ b/dom/media/eme/MediaKeys.cpp
@@ -622,15 +622,26 @@ MediaKeys::GetStatusForPolicy(MediaKeysP
    promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR,
                         NS_LITERAL_CSTRING("Null CDM in MediaKeys.GetStatusForPolicy()"));
    return promise.forget();
   }
 
   nsString minHdcpVersion;
   aPolicy.GetMinHdcpVersion(minHdcpVersion);
   EME_LOG("GetStatusForPolicy minHdcpVersion = %s.", NS_ConvertUTF16toUTF8(minHdcpVersion).get());
-  // TODO: Ask CDM to get the real policy.
-  promise->MaybeResolve(MediaKeyStatus::Usable);
+  mProxy->GetStatusForPolicy(StorePromise(promise), minHdcpVersion);
   return promise.forget();
 }
 
+void
+MediaKeys::ResolvePromiseWithKeyStatus(PromiseId aId, MediaKeyStatus aMediaKeyStatus)
+{
+  RefPtr<DetailedPromise> promise(RetrievePromise(aId));
+  if (!promise) {
+    return;
+  }
+  RefPtr<MediaKeys> keys(this);
+  EME_LOG("MediaKeys[%p]::ResolvePromiseWithKeyStatus() resolve promise id=%d", this, aId);
+  promise->MaybeResolve(aMediaKeyStatus);
+}
+
 } // namespace dom
 } // namespace mozilla
--- a/dom/media/eme/MediaKeys.h
+++ b/dom/media/eme/MediaKeys.h
@@ -11,16 +11,17 @@
 #include "nsISupports.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/RefPtr.h"
 #include "nsCOMPtr.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsRefPtrHashtable.h"
 #include "mozilla/dom/Promise.h"
 #include "mozilla/dom/MediaKeysBinding.h"
+#include "mozilla/dom/MediaKeyStatusMapBinding.h" // For MediaKeyStatus
 #include "mozilla/dom/MediaKeySystemAccessBinding.h"
 #include "mozIGeckoMediaPluginService.h"
 #include "mozilla/DetailedPromise.h"
 #include "mozilla/WeakPtr.h"
 
 namespace mozilla {
 
 class CDMProxy;
@@ -129,16 +130,18 @@ public:
   // Returns true if this MediaKeys has been bound to a media element.
   bool IsBoundToMediaElement() const;
 
   void GetSessionsInfo(nsString& sessionsInfo);
 
   // JavaScript: MediaKeys.GetStatusForPolicy()
   already_AddRefed<Promise> GetStatusForPolicy(MediaKeysPolicy& aPolicy,
                                                ErrorResult& aR);
+  // Called by CDMProxy when CDM successfully GetStatusForPolicy.
+  void ResolvePromiseWithKeyStatus(PromiseId aId, dom::MediaKeyStatus aMediaKeyStatus);
 
 private:
 
   // Instantiate CDMProxy instance.
   // It could be MediaDrmCDMProxy (Widevine on Fennec) or ChromiumCDMProxy (the rest).
   already_AddRefed<CDMProxy> CreateCDMProxy(nsIEventTarget* aMainThread);
 
   // Removes promise from mPromises, and returns it.
--- a/dom/media/gmp/ChromiumCDMCallback.h
+++ b/dom/media/gmp/ChromiumCDMCallback.h
@@ -17,16 +17,19 @@ public:
   virtual ~ChromiumCDMCallback() {}
 
   virtual void SetSessionId(uint32_t aPromiseId,
                             const nsCString& aSessionId) = 0;
 
   virtual void ResolveLoadSessionPromise(uint32_t aPromiseId,
                                          bool aSuccessful) = 0;
 
+  virtual void ResolvePromiseWithKeyStatus(uint32_t aPromiseId,
+                                           uint32_t aKeyStatus) = 0;
+
   virtual void ResolvePromise(uint32_t aPromiseId) = 0;
 
   virtual void RejectPromise(uint32_t aPromiseId,
                              nsresult aError,
                              const nsCString& aErrorMessage) = 0;
 
   virtual void SessionMessage(const nsACString& aSessionId,
                               uint32_t aMessageType,
--- a/dom/media/gmp/ChromiumCDMCallbackProxy.cpp
+++ b/dom/media/gmp/ChromiumCDMCallbackProxy.cpp
@@ -112,16 +112,26 @@ ToDOMMediaKeyStatus(uint32_t aStatus)
     case cdm::kReleased:
       return dom::MediaKeyStatus::Released;
   }
   MOZ_ASSERT_UNREACHABLE("Invalid cdm::KeyStatus enum value.");
   return dom::MediaKeyStatus::Internal_error;
 }
 
 void
+ChromiumCDMCallbackProxy::ResolvePromiseWithKeyStatus(uint32_t aPromiseId,
+                                                      uint32_t aKeyStatus)
+{
+  DispatchToMainThread("ChromiumCDMProxy::OnResolvePromiseWithKeyStatus",
+                       &ChromiumCDMProxy::OnResolvePromiseWithKeyStatus,
+                       aPromiseId,
+                       ToDOMMediaKeyStatus(aKeyStatus));
+}
+
+void
 ChromiumCDMCallbackProxy::SessionKeysChange(const nsCString& aSessionId,
                                             nsTArray<mozilla::gmp::CDMKeyInformation> && aKeysInfo)
 {
   bool keyStatusesChange = false;
   {
     CDMCaps::AutoLock caps(mProxy->Capabilites());
     for (const auto& keyInfo : aKeysInfo) {
       keyStatusesChange |=
--- a/dom/media/gmp/ChromiumCDMCallbackProxy.h
+++ b/dom/media/gmp/ChromiumCDMCallbackProxy.h
@@ -22,16 +22,19 @@ public:
   }
 
   void SetSessionId(uint32_t aPromiseId,
                     const nsCString& aSessionId) override;
 
   void ResolveLoadSessionPromise(uint32_t aPromiseId,
                                  bool aSuccessful) override;
 
+  void ResolvePromiseWithKeyStatus(uint32_t aPromiseId,
+                                   uint32_t aKeyStatus) override;
+
   void ResolvePromise(uint32_t aPromiseId) override;
 
   void RejectPromise(uint32_t aPromiseId,
                      nsresult aError,
                      const nsCString& aErrorMessage) override;
 
   void SessionMessage(const nsACString& aSessionId,
                       uint32_t aMessageType,
--- a/dom/media/gmp/ChromiumCDMChild.cpp
+++ b/dom/media/gmp/ChromiumCDMChild.cpp
@@ -221,17 +221,23 @@ ChromiumCDMChild::CallOnMessageLoopThrea
     mPlugin->GMPMessageLoop()->PostTask(t.forget());
   }
 }
 
 // cdm::Host_9 interface
 void
 ChromiumCDMChild::OnResolveKeyStatusPromise(uint32_t aPromiseId,
                                             cdm::KeyStatus aKeyStatus) {
-  //TODO: The callback of GetStatusForPolicy, will implement it in Bug 1404230.
+  GMP_LOG("ChromiumCDMChild::OnResolveKeyStatusPromise(pid=%" PRIu32 "keystatus=%d)",
+          aPromiseId,
+          aKeyStatus);
+  CallOnMessageLoopThread("gmp::ChromiumCDMChild::OnResolveKeyStatusPromise",
+                          &ChromiumCDMChild::SendOnResolvePromiseWithKeyStatus,
+                          aPromiseId,
+                          static_cast<uint32_t>(aKeyStatus));
 }
 
 bool
 ChromiumCDMChild::OnResolveNewSessionPromiseInternal(uint32_t aPromiseId,
                                                      const nsCString& aSessionId)
 {
   MOZ_ASSERT(IsOnMessageLoopThread());
   if (mLoadSessionPromiseIds.Contains(aPromiseId)) {
@@ -618,16 +624,73 @@ ChromiumCDMChild::RecvRemoveSession(cons
           aPromiseId,
           aSessionId.get());
   if (mCDM) {
     mCDM->RemoveSession(aPromiseId, aSessionId.get(), aSessionId.Length());
   }
   return IPC_OK();
 }
 
+static cdm::HdcpVersion
+ToCDMHdcpVersion(const nsCString& aMinHdcpVersion)
+{
+  // String compare with ignoring case.
+  if (aMinHdcpVersion.IsEmpty()) {
+    return cdm::HdcpVersion::kHdcpVersionNone;
+  }
+  if (aMinHdcpVersion.EqualsIgnoreCase("hdcp-1.0")) {
+    return cdm::HdcpVersion::kHdcpVersion1_0;
+  }
+  if (aMinHdcpVersion.EqualsIgnoreCase("hdcp-1.1")) {
+    return cdm::HdcpVersion::kHdcpVersion1_1;
+  }
+  if (aMinHdcpVersion.EqualsIgnoreCase("hdcp-1.2")) {
+    return cdm::HdcpVersion::kHdcpVersion1_2;
+  }
+  if (aMinHdcpVersion.EqualsIgnoreCase("hdcp-1.3")) {
+    return cdm::HdcpVersion::kHdcpVersion1_3;
+  }
+  if (aMinHdcpVersion.EqualsIgnoreCase("hdcp-1.4")) {
+    return cdm::HdcpVersion::kHdcpVersion1_4;
+  }
+  if (aMinHdcpVersion.EqualsIgnoreCase("hdcp-2.0")) {
+    return cdm::HdcpVersion::kHdcpVersion2_0;
+  }
+  if (aMinHdcpVersion.EqualsIgnoreCase("hdcp-2.1")) {
+    return cdm::HdcpVersion::kHdcpVersion2_1;
+  }
+  if (aMinHdcpVersion.EqualsIgnoreCase("hdcp-2.2")) {
+    return cdm::HdcpVersion::kHdcpVersion2_2;
+  }
+
+  // Invalid hdcp version string.
+  return cdm::HdcpVersion::kHdcpVersionNone;
+}
+
+mozilla::ipc::IPCResult
+ChromiumCDMChild::RecvGetStatusForPolicy(const uint32_t& aPromiseId,
+                                         const nsCString& aMinHdcpVersion)
+{
+  MOZ_ASSERT(IsOnMessageLoopThread());
+  GMP_LOG("ChromiumCDMChild::RecvGetStatusForPolicy(pid=%" PRIu32 ", MinHdcpVersion=%s)",
+          aPromiseId,
+          aMinHdcpVersion.get());
+  if (mCDM) {
+    cdm::Policy policy;
+    // We didn't check the return value of ToCDMHdcpVersion.
+    // Let CDM to handle the cdm::HdcpVersion::kHdcpVersionNone case.
+    // ChromiumCDM8BackwardsCompat::GetStatusForPolicy will reject the promise
+    // since this API is only supported by CDM version 9.
+    // CDM will callback by OnResolveKeyStatusPromise when it successfully executes.
+    policy.min_hdcp_version = ToCDMHdcpVersion(aMinHdcpVersion);
+    mCDM->GetStatusForPolicy(aPromiseId, policy);
+  }
+  return IPC_OK();
+}
+
 static void
 InitInputBuffer(const CDMInputBuffer& aBuffer,
                 nsTArray<cdm::SubsampleEntry>& aSubSamples,
                 cdm::InputBuffer& aInputBuffer)
 {
   aInputBuffer.data = aBuffer.mData().get<uint8_t>();
   aInputBuffer.data_size = aBuffer.mData().Size<uint8_t>();
 
--- a/dom/media/gmp/ChromiumCDMChild.h
+++ b/dom/media/gmp/ChromiumCDMChild.h
@@ -123,16 +123,18 @@ protected:
                                  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 RecvGetStatusForPolicy(const uint32_t& aPromiseId,
+                                        const nsCString& aMinHdcpVersion) override;
   ipc::IPCResult RecvDecrypt(const uint32_t& aId,
                              const CDMInputBuffer& aBuffer) override;
   ipc::IPCResult RecvInitializeVideoDecoder(
     const CDMVideoDecoderConfig& aConfig) override;
   ipc::IPCResult RecvDeinitializeVideoDecoder() override;
   ipc::IPCResult RecvResetVideoDecoder() override;
   ipc::IPCResult RecvDecryptAndDecodeFrame(
     const CDMInputBuffer& aBuffer) override;
--- a/dom/media/gmp/ChromiumCDMParent.cpp
+++ b/dom/media/gmp/ChromiumCDMParent.cpp
@@ -181,16 +181,35 @@ ChromiumCDMParent::RemoveSession(const n
   if (!SendRemoveSession(aPromiseId, aSessionId)) {
     RejectPromise(
       aPromiseId,
       NS_ERROR_DOM_INVALID_STATE_ERR,
       NS_LITERAL_CSTRING("Failed to send removeSession to CDM process"));
   }
 }
 
+void
+ChromiumCDMParent::GetStatusForPolicy(uint32_t aPromiseId,
+                                      const nsCString& aMinHdcpVersion)
+{
+  GMP_LOG("ChromiumCDMParent::GetStatusForPolicy(this=%p)", this);
+  if (mIsShutdown) {
+    RejectPromise(aPromiseId,
+                  NS_ERROR_DOM_INVALID_STATE_ERR,
+                  NS_LITERAL_CSTRING("CDM is shutdown."));
+    return;
+  }
+  if (!SendGetStatusForPolicy(aPromiseId, aMinHdcpVersion)) {
+    RejectPromise(
+      aPromiseId,
+      NS_ERROR_DOM_INVALID_STATE_ERR,
+      NS_LITERAL_CSTRING("Failed to send getStatusForPolicy to CDM process"));
+  }
+}
+
 bool
 ChromiumCDMParent::InitCDMInputBuffer(gmp::CDMInputBuffer& aBuffer,
                                       MediaRawData* aSample)
 {
   const CryptoSample& crypto = aSample->mCrypto;
   if (crypto.mEncryptedSizes.Length() != crypto.mPlainSizes.Length()) {
     GMP_LOG("InitCDMInputBuffer clear/cipher subsamples don't match");
     return false;
@@ -270,16 +289,34 @@ ChromiumCDMParent::Recv__delete__()
   if (mContentParent) {
     mContentParent->ChromiumCDMDestroyed(this);
     mContentParent = nullptr;
   }
   return IPC_OK();
 }
 
 ipc::IPCResult
+ChromiumCDMParent::RecvOnResolvePromiseWithKeyStatus(const uint32_t& aPromiseId,
+                                                     const uint32_t& aKeyStatus)
+{
+  GMP_LOG("ChromiumCDMParent::RecvOnResolvePromiseWithKeyStatus(this=%p, pid=%u, "
+          "keystatus=%u)",
+          this,
+          aPromiseId,
+          aKeyStatus);
+  if (!mCDMCallback || mIsShutdown) {
+    return IPC_OK();
+  }
+
+  mCDMCallback->ResolvePromiseWithKeyStatus(aPromiseId, aKeyStatus);
+
+  return IPC_OK();
+}
+
+ipc::IPCResult
 ChromiumCDMParent::RecvOnResolveNewSessionPromise(const uint32_t& aPromiseId,
                                                   const nsCString& aSessionId)
 {
   GMP_LOG("ChromiumCDMParent::RecvOnResolveNewSessionPromise(this=%p, pid=%u, "
           "sid=%s)",
           this,
           aPromiseId,
           aSessionId.get());
--- a/dom/media/gmp/ChromiumCDMParent.h
+++ b/dom/media/gmp/ChromiumCDMParent.h
@@ -61,16 +61,19 @@ public:
   void UpdateSession(const nsCString& aSessionId,
                      uint32_t aPromiseId,
                      const nsTArray<uint8_t>& aResponse);
 
   void CloseSession(const nsCString& aSessionId, uint32_t aPromiseId);
 
   void RemoveSession(const nsCString& aSessionId, uint32_t aPromiseId);
 
+  void GetStatusForPolicy(uint32_t aPromiseId,
+                          const nsCString& aMinHdcpVersion);
+
   RefPtr<DecryptPromise> Decrypt(MediaRawData* aSample);
 
   // TODO: Add functions for clients to send data to CDM, and
   // a Close() function.
   RefPtr<MediaDataDecoder::InitPromise> InitializeVideoDecoder(
     const gmp::CDMVideoDecoderConfig& aConfig,
     const VideoInfo& aInfo,
     RefPtr<layers::ImageContainer> aImageContainer);
@@ -85,16 +88,19 @@ public:
   RefPtr<ShutdownPromise> ShutdownVideoDecoder();
 
   void Shutdown();
 
 protected:
   ~ChromiumCDMParent() {}
 
   ipc::IPCResult Recv__delete__() override;
+  ipc::IPCResult RecvOnResolvePromiseWithKeyStatus(
+    const uint32_t& aPromiseId,
+    const uint32_t& aKeyStatus) 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,
--- a/dom/media/gmp/ChromiumCDMProxy.cpp
+++ b/dom/media/gmp/ChromiumCDMProxy.cpp
@@ -461,16 +461,27 @@ ChromiumCDMProxy::OnResolveLoadSessionPr
   MOZ_ASSERT(NS_IsMainThread());
   if (mKeys.IsNull()) {
     return;
   }
   mKeys->OnSessionLoaded(aPromiseId, aSuccess);
 }
 
 void
+ChromiumCDMProxy::OnResolvePromiseWithKeyStatus(uint32_t aPromiseId,
+                                                dom::MediaKeyStatus aKeyStatus)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  if (mKeys.IsNull()) {
+    return;
+  }
+  mKeys->ResolvePromiseWithKeyStatus(aPromiseId, aKeyStatus);
+}
+
+void
 ChromiumCDMProxy::OnSessionMessage(const nsAString& aSessionId,
                                    dom::MediaKeyMessageType aMessageType,
                                    const nsTArray<uint8_t>& aMessage)
 {
   MOZ_ASSERT(NS_IsMainThread());
   if (mKeys.IsNull()) {
     return;
   }
@@ -609,16 +620,23 @@ ChromiumCDMProxy::GetStatusForPolicy(Pro
 
   RefPtr<gmp::ChromiumCDMParent> cdm = GetCDMParent();
   if (!cdm) {
     RejectPromise(aPromiseId,
                   NS_ERROR_DOM_INVALID_STATE_ERR,
                   NS_LITERAL_CSTRING("Null CDM in GetStatusForPolicy"));
     return;
   }
+
+  mGMPThread->Dispatch(NewRunnableMethod<uint32_t, nsCString>(
+    "gmp::ChromiumCDMParent::GetStatusForPolicy",
+    cdm,
+    &gmp::ChromiumCDMParent::GetStatusForPolicy,
+    aPromiseId,
+    NS_ConvertUTF16toUTF8(aMinHdcpVersion)));
 }
 
 void
 ChromiumCDMProxy::Terminated()
 {
   if (!mKeys.IsNull()) {
     mKeys->Terminated();
   }
--- a/dom/media/gmp/ChromiumCDMProxy.h
+++ b/dom/media/gmp/ChromiumCDMProxy.h
@@ -113,16 +113,19 @@ public:
 #endif
 
   ChromiumCDMProxy* AsChromiumCDMProxy() override { return this; }
 
   // Threadsafe. Note this may return a reference to a shutdown
   // CDM, which will fail on all operations.
   already_AddRefed<gmp::ChromiumCDMParent> GetCDMParent();
 
+  void OnResolvePromiseWithKeyStatus(uint32_t aPromiseId,
+                                     dom::MediaKeyStatus aKeyStatus);
+
 private:
   void OnCDMCreated(uint32_t aPromiseId);
 
   ~ChromiumCDMProxy();
 
   GMPCrashHelper* mCrashHelper;
 
   Mutex mCDMMutex;
--- a/dom/media/gmp/PChromiumCDM.ipdl
+++ b/dom/media/gmp/PChromiumCDM.ipdl
@@ -53,19 +53,26 @@ child:
   async Drain();
 
   async Destroy();
 
   async GiveBuffer(Shmem aShmem);
 
   async PurgeShmems();
 
+  // cdm::ContentDecryptionModule9
+  async GetStatusForPolicy(uint32_t aPromiseId,
+                           nsCString aMinHdcpVersion);
+
 parent:
   async __delete__();
 
+  // cdm::Host9
+  async OnResolvePromiseWithKeyStatus(uint32_t aPromiseId, uint32_t aKeyStatus);
+
   // cdm::Host8
   async OnResolveNewSessionPromise(uint32_t aPromiseId, nsCString aSessionId);
 
   async OnResolvePromise(uint32_t aPromiseId);
 
   async OnRejectPromise(uint32_t aPromiseId,
                         uint32_t aError,
                         uint32_t aSystemCode,
--- a/dom/media/gtest/TestCDMStorage.cpp
+++ b/dom/media/gtest/TestCDMStorage.cpp
@@ -1067,16 +1067,19 @@ private:
     }
 
     void SetSessionId(uint32_t aPromiseId,
                       const nsCString& aSessionId) override { }
 
     void ResolveLoadSessionPromise(uint32_t aPromiseId,
                                    bool aSuccessful) override { }
 
+    void ResolvePromiseWithKeyStatus(uint32_t aPromiseId,
+                                     uint32_t aKeyStatus) override { }
+
     void ResolvePromise(uint32_t aPromiseId) override { }
 
     void RejectPromise(uint32_t aPromiseId,
                        nsresult aError,
                        const nsCString& aErrorMessage) override {  }
 
     void SessionMessage(const nsACString& aSessionId,
                         uint32_t aMessageType,