Bug 1397123 - [Part2] Decouple ChromiumCDMProxy from ChromiumCDMParent. r?cpearce draft
authorJames Cheng <jacheng@mozilla.com>
Mon, 11 Sep 2017 12:17:36 +0800
changeset 662843 12667b736684f4863efa7c7b31a8fc619fe7bbda
parent 662842 de04ca24c23abb5e0f9af55f6a2e9be2a297bbc1
child 662879 991dc36437f3c285cb7583cc5fb7604d816b371c
child 662900 c9648f92e276647f6a8389a6c44c9c5a753611dd
child 662937 9506f47dd30785807d3b58d5be37b09efed90e13
push id79207
push userbmo:jacheng@mozilla.com
push dateTue, 12 Sep 2017 05:49:55 +0000
reviewerscpearce
bugs1397123
milestone57.0a1
Bug 1397123 - [Part2] Decouple ChromiumCDMProxy from ChromiumCDMParent. r?cpearce 1. Pass ChromiumCDMCallback interface to ChromiumCDMParent instead of ChromiumCDMProxy directly. 2. Wrap dispatching to main thread function to clean up the redundant code. MozReview-Commit-ID: 5HxS9Fc1yr
dom/media/gmp/ChromiumCDMCallback.h
dom/media/gmp/ChromiumCDMCallbackProxy.cpp
dom/media/gmp/ChromiumCDMCallbackProxy.h
dom/media/gmp/ChromiumCDMParent.cpp
dom/media/gmp/ChromiumCDMParent.h
dom/media/gmp/ChromiumCDMProxy.cpp
dom/media/gmp/ChromiumCDMProxy.h
dom/media/gmp/moz.build
new file mode 100644
--- /dev/null
+++ b/dom/media/gmp/ChromiumCDMCallback.h
@@ -0,0 +1,52 @@
+/* -*- 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/. */
+
+#ifndef ChromiumCDMCallback_h_
+#define ChromiumCDMCallback_h_
+
+#include "mozilla/CDMProxy.h"
+#include "mozilla/dom/MediaKeyStatusMapBinding.h" // For MediaKeyStatus
+#include "mozilla/dom/MediaKeyMessageEventBinding.h" // For MediaKeyMessageType
+#include "mozilla/gmp/GMPTypes.h" // For CDMKeyInformation
+
+class ChromiumCDMCallback {
+public:
+
+  virtual ~ChromiumCDMCallback() {}
+
+  virtual void SetSessionId(uint32_t aPromiseId,
+                            const nsCString& aSessionId) = 0;
+
+  virtual void ResolveLoadSessionPromise(uint32_t aPromiseId,
+                                         bool aSuccessful) = 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,
+                              nsTArray<uint8_t>&& aMessage) = 0;
+
+  virtual void SessionKeysChange(const nsCString& aSessionId,
+                                 nsTArray<mozilla::gmp::CDMKeyInformation>&& aKeysInfo) = 0;
+
+  virtual void ExpirationChange(const nsCString& aSessionId,
+                                double aSecondsSinceEpoch) = 0;
+
+  virtual void SessionClosed(const nsCString& aSessionId) = 0;
+
+  virtual void LegacySessionError(const nsCString& aSessionId,
+                                  nsresult aError,
+                                  uint32_t aSystemCode,
+                                  const nsCString& aMessage) = 0;
+  virtual void Terminated() = 0;
+
+  virtual void Shutdown() = 0;
+};
+
+#endif
new file mode 100644
--- /dev/null
+++ b/dom/media/gmp/ChromiumCDMCallbackProxy.cpp
@@ -0,0 +1,188 @@
+/* -*- 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 "ChromiumCDMCallbackProxy.h"
+
+#include "ChromiumCDMProxy.h"
+#include "content_decryption_module.h"
+
+namespace mozilla {
+
+template<class Func, class... Args>
+void ChromiumCDMCallbackProxy::DispatchToMainThread(const char* const aLabel,
+                                                    Func aFunc,
+                                                    Args&&... aArgs)
+{
+  mMainThread->Dispatch(
+    // Use Decay to ensure all the types are passed by value not by reference.
+    NewRunnableMethod<typename Decay<Args>::Type...>(
+      aLabel,
+      mProxy,
+      aFunc,
+      Forward<Args>(aArgs)...),
+    NS_DISPATCH_NORMAL);
+}
+
+void
+ChromiumCDMCallbackProxy::SetSessionId(uint32_t aPromiseId,
+                                       const nsCString& aSessionId)
+{
+  DispatchToMainThread("ChromiumCDMProxy::OnSetSessionId",
+                       &ChromiumCDMProxy::OnSetSessionId,
+                       aPromiseId,
+                       NS_ConvertUTF8toUTF16(aSessionId));
+}
+
+void
+ChromiumCDMCallbackProxy::ResolveLoadSessionPromise(uint32_t aPromiseId,
+                                                    bool aSuccessful)
+{
+  DispatchToMainThread("ChromiumCDMProxy::OnResolveLoadSessionPromise",
+                       &ChromiumCDMProxy::OnResolveLoadSessionPromise,
+                       aPromiseId,
+                       aSuccessful);
+}
+
+void
+ChromiumCDMCallbackProxy::ResolvePromise(uint32_t aPromiseId)
+{
+  DispatchToMainThread("ChromiumCDMProxy::ResolvePromise",
+                       &ChromiumCDMProxy::ResolvePromise,
+                       aPromiseId);
+}
+
+void
+ChromiumCDMCallbackProxy::RejectPromise(uint32_t aPromiseId,
+                                        nsresult aError,
+                                        const nsCString& aErrorMessage)
+{
+  DispatchToMainThread("ChromiumCDMProxy::RejectPromise",
+                       &ChromiumCDMProxy::RejectPromise,
+                       aPromiseId,
+                       aError,
+                       aErrorMessage);
+}
+
+
+static dom::MediaKeyMessageType
+ToDOMMessageType(uint32_t aMessageType)
+{
+  switch (static_cast<cdm::MessageType>(aMessageType)) {
+    case cdm::kLicenseRequest:
+      return dom::MediaKeyMessageType::License_request;
+    case cdm::kLicenseRenewal:
+      return dom::MediaKeyMessageType::License_renewal;
+    case cdm::kLicenseRelease:
+      return dom::MediaKeyMessageType::License_release;
+  }
+  MOZ_ASSERT_UNREACHABLE("Invalid cdm::MessageType enum value.");
+  return dom::MediaKeyMessageType::License_request;
+}
+
+void
+ChromiumCDMCallbackProxy::SessionMessage(const nsACString& aSessionId,
+                                         uint32_t aMessageType,
+                                         nsTArray<uint8_t>&& aMessage)
+{
+  DispatchToMainThread("ChromiumCDMProxy::OnSessionMessage",
+                       &ChromiumCDMProxy::OnSessionMessage,
+                       NS_ConvertUTF8toUTF16(aSessionId),
+                       ToDOMMessageType(aMessageType),
+                       Move(aMessage));
+}
+
+static dom::MediaKeyStatus
+ToDOMMediaKeyStatus(uint32_t aStatus)
+{
+  switch (static_cast<cdm::KeyStatus>(aStatus)) {
+    case cdm::kUsable:
+      return dom::MediaKeyStatus::Usable;
+    case cdm::kInternalError:
+      return dom::MediaKeyStatus::Internal_error;
+    case cdm::kExpired:
+      return dom::MediaKeyStatus::Expired;
+    case cdm::kOutputRestricted:
+      return dom::MediaKeyStatus::Output_restricted;
+    case cdm::kOutputDownscaled:
+      return dom::MediaKeyStatus::Output_downscaled;
+    case cdm::kStatusPending:
+      return dom::MediaKeyStatus::Status_pending;
+    case cdm::kReleased:
+      return dom::MediaKeyStatus::Released;
+  }
+  MOZ_ASSERT_UNREACHABLE("Invalid cdm::KeyStatus enum value.");
+  return dom::MediaKeyStatus::Internal_error;
+}
+
+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 |=
+        caps.SetKeyStatus(keyInfo.mKeyId(),
+                          NS_ConvertUTF8toUTF16(aSessionId),
+                          dom::Optional<dom::MediaKeyStatus>(
+                            ToDOMMediaKeyStatus(keyInfo.mStatus())));
+    }
+  }
+  if (keyStatusesChange) {
+    DispatchToMainThread("ChromiumCDMProxy::OnKeyStatusesChange",
+                         &ChromiumCDMProxy::OnKeyStatusesChange,
+                         NS_ConvertUTF8toUTF16(aSessionId));
+  }
+}
+
+void
+ChromiumCDMCallbackProxy::ExpirationChange(const nsCString& aSessionId,
+                                           double aSecondsSinceEpoch)
+{
+  DispatchToMainThread("ChromiumCDMProxy::OnExpirationChange",
+                       &ChromiumCDMProxy::OnExpirationChange,
+                       NS_ConvertUTF8toUTF16(aSessionId),
+                       UnixTime(aSecondsSinceEpoch * 1000));
+
+}
+
+void
+ChromiumCDMCallbackProxy::SessionClosed(const nsCString& aSessionId)
+{
+  DispatchToMainThread("ChromiumCDMProxy::OnSessionClosed",
+                       &ChromiumCDMProxy::OnSessionClosed ,
+                       NS_ConvertUTF8toUTF16(aSessionId));
+}
+
+void
+ChromiumCDMCallbackProxy::LegacySessionError(const nsCString& aSessionId,
+                                             nsresult aError,
+                                             uint32_t aSystemCode,
+                                             const nsCString& aMessage)
+{
+  DispatchToMainThread("ChromiumCDMProxy::OnSessionError",
+                       &ChromiumCDMProxy::OnSessionError ,
+                       NS_ConvertUTF8toUTF16(aSessionId),
+                       aError,
+                       aSystemCode,
+                       NS_ConvertUTF8toUTF16(aMessage));
+}
+
+void
+ChromiumCDMCallbackProxy::Terminated()
+{
+  DispatchToMainThread("ChromiumCDMProxy::Terminated",
+                       &ChromiumCDMProxy::Terminated);
+}
+
+void
+ChromiumCDMCallbackProxy::Shutdown()
+{
+  DispatchToMainThread("ChromiumCDMProxy::Shutdown",
+                       &ChromiumCDMProxy::Shutdown);
+}
+
+} //namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/media/gmp/ChromiumCDMCallbackProxy.h
@@ -0,0 +1,68 @@
+/* -*- 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/. */
+
+#ifndef ChromiumCDMCallbackProxy_h_
+#define ChromiumCDMCallbackProxy_h_
+
+#include "ChromiumCDMCallback.h"
+#include "ChromiumCDMProxy.h"
+#include "nsThreadUtils.h"
+
+namespace mozilla {
+
+class ChromiumCDMCallbackProxy : public ChromiumCDMCallback {
+public:
+
+  ChromiumCDMCallbackProxy(ChromiumCDMProxy* aProxy,
+                           nsIEventTarget* aMainThread)
+    : mProxy(aProxy), mMainThread(aMainThread)
+  {
+  }
+
+  void SetSessionId(uint32_t aPromiseId,
+                    const nsCString& aSessionId) override;
+
+  void ResolveLoadSessionPromise(uint32_t aPromiseId,
+                                 bool aSuccessful) 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,
+                      nsTArray<uint8_t>&& aMessage) override;
+
+  void SessionKeysChange(const nsCString& aSessionId,
+                         nsTArray<mozilla::gmp::CDMKeyInformation>&& aKeysInfo) override;
+
+  void ExpirationChange(const nsCString& aSessionId,
+                        double aSecondsSinceEpoch) override;
+
+  void SessionClosed(const nsCString& aSessionId) override;
+
+  void LegacySessionError(const nsCString& aSessionId,
+                          nsresult aError,
+                          uint32_t aSystemCode,
+                          const nsCString& aMessage) override;
+  void Terminated() override;
+
+  void Shutdown() override;
+
+private:
+  template<class Func, class... Args>
+  void DispatchToMainThread(const char* const aLabel,
+                            Func aFunc,
+                            Args&&... aArgs);
+  // Warning: Weak ref.
+  ChromiumCDMProxy* mProxy;
+  const nsCOMPtr<nsIEventTarget> mMainThread;
+
+};
+
+} //namespace mozilla
+#endif
--- a/dom/media/gmp/ChromiumCDMParent.cpp
+++ b/dom/media/gmp/ChromiumCDMParent.cpp
@@ -1,15 +1,17 @@
 /* -*- 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 "ChromiumCDMParent.h"
 
+#include "ChromiumCDMCallback.h"
+#include "ChromiumCDMCallbackProxy.h"
 #include "ChromiumCDMProxy.h"
 #include "content_decryption_module.h"
 #include "GMPContentChild.h"
 #include "GMPContentParent.h"
 #include "GMPLog.h"
 #include "GMPUtils.h"
 #include "MediaPrefs.h"
 #include "mozilla/dom/MediaKeyMessageEventBinding.h"
@@ -35,26 +37,26 @@ ChromiumCDMParent::ChromiumCDMParent(GMP
   GMP_LOG(
     "ChromiumCDMParent::ChromiumCDMParent(this=%p, contentParent=%p, id=%u)",
     this,
     aContentParent,
     aPluginId);
 }
 
 bool
-ChromiumCDMParent::Init(ChromiumCDMProxy* aProxy,
+ChromiumCDMParent::Init(ChromiumCDMCallback* aCDMCallback,
                         bool aAllowDistinctiveIdentifier,
                         bool aAllowPersistentState,
                         nsIEventTarget* aMainThread)
 {
   GMP_LOG("ChromiumCDMParent::Init(this=%p)", this);
-  if (!aProxy || !aMainThread) {
+  if (!aCDMCallback || !aMainThread) {
     return false;
   }
-  mProxy = aProxy;
+  mCDMCallback = aCDMCallback;
   mMainThread = aMainThread;
   return SendInit(aAllowDistinctiveIdentifier, aAllowPersistentState);
 }
 
 void
 ChromiumCDMParent::CreateSession(uint32_t aCreateSessionToken,
                                  uint32_t aSessionType,
                                  uint32_t aInitDataType,
@@ -274,92 +276,91 @@ 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());
-  if (!mProxy || mIsShutdown) {
+  if (!mCDMCallback || mIsShutdown) {
     return IPC_OK();
   }
 
   Maybe<uint32_t> token = mPromiseToCreateSessionToken.GetAndRemove(aPromiseId);
   if (token.isNothing()) {
     RejectPromise(aPromiseId,
                   NS_ERROR_DOM_INVALID_STATE_ERR,
                   NS_LITERAL_CSTRING("Lost session token for new session."));
     return IPC_OK();
   }
 
-  mMainThread->Dispatch(
-    NewRunnableMethod<uint32_t, nsString>("ChromiumCDMProxy::OnSetSessionId",
-                                          mProxy,
-                                          &ChromiumCDMProxy::OnSetSessionId,
-                                          token.value(),
-                                          NS_ConvertUTF8toUTF16(aSessionId)),
-    NS_DISPATCH_NORMAL);
+  mCDMCallback->SetSessionId(token.value(), aSessionId);
 
   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) {
+  if (!mCDMCallback || mIsShutdown) {
     return IPC_OK();
   }
 
-  mMainThread->Dispatch(
-    NewRunnableMethod<uint32_t, bool>(
-      "ChromiumCDMProxy::OnResolveLoadSessionPromise",
-      mProxy,
-      &ChromiumCDMProxy::OnResolveLoadSessionPromise,
-      aPromiseId,
-      aSuccessful),
-    NS_DISPATCH_NORMAL);
+  mCDMCallback->ResolveLoadSessionPromise(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.
-  if (!mProxy || mIsShutdown) {
+  if (!mCDMCallback || mIsShutdown) {
     return;
   }
 
-  mMainThread->Dispatch(
-    NewRunnableMethod<uint32_t>("ChromiumCDMProxy::ResolvePromise",
-                                mProxy,
-                                &ChromiumCDMProxy::ResolvePromise,
-                                aPromiseId),
-    NS_DISPATCH_NORMAL);
+  mCDMCallback->ResolvePromise(aPromiseId);
 }
 
 ipc::IPCResult
 ChromiumCDMParent::RecvOnResolvePromise(const uint32_t& aPromiseId)
 {
   ResolvePromise(aPromiseId);
   return IPC_OK();
 }
 
+void
+ChromiumCDMParent::RejectPromise(uint32_t aPromiseId,
+                                 nsresult aError,
+                                 const nsCString& aErrorMessage)
+{
+  GMP_LOG(
+    "ChromiumCDMParent::RejectPromise(this=%p, pid=%u)", this, aPromiseId);
+  // Note: The MediaKeys rejects all pending DOM promises when it
+  // initiates shutdown.
+  if (!mCDMCallback || mIsShutdown) {
+    return;
+  }
+
+  mCDMCallback->RejectPromise(aPromiseId, aError, aErrorMessage);
+}
+
 static nsresult
 ToNsresult(uint32_t aError)
 {
   switch (static_cast<cdm::Error>(aError)) {
     case cdm::kNotSupportedError:
       return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
     case cdm::kInvalidStateError:
       return NS_ERROR_DOM_INVALID_STATE_ERR;
@@ -377,204 +378,96 @@ ToNsresult(uint32_t aError)
       return NS_ERROR_DOM_ABORT_ERR; // Note: Unique placeholder.
     case cdm::kOutputError:
       return NS_ERROR_DOM_SECURITY_ERR; // Note: Unique placeholder.
   };
   MOZ_ASSERT_UNREACHABLE("Invalid cdm::Error enum value.");
   return NS_ERROR_DOM_TIMEOUT_ERR; // Note: Unique placeholder.
 }
 
-void
-ChromiumCDMParent::RejectPromise(uint32_t aPromiseId,
-                                 nsresult aError,
-                                 const nsCString& aErrorMessage)
-{
-  GMP_LOG(
-    "ChromiumCDMParent::RejectPromise(this=%p, pid=%u)", this, aPromiseId);
-  // Note: The MediaKeys rejects all pending DOM promises when it
-  // initiates shutdown.
-  if (!mProxy || mIsShutdown) {
-    return;
-  }
-
-  mMainThread->Dispatch(
-    NewRunnableMethod<uint32_t, nsresult, nsCString>(
-      "ChromiumCDMProxy::RejectPromise",
-      mProxy,
-      &ChromiumCDMProxy::RejectPromise,
-      aPromiseId,
-      aError,
-      aErrorMessage),
-    NS_DISPATCH_NORMAL);
-}
-
 ipc::IPCResult
 ChromiumCDMParent::RecvOnRejectPromise(const uint32_t& aPromiseId,
                                        const uint32_t& aError,
                                        const uint32_t& aSystemCode,
                                        const nsCString& aErrorMessage)
 {
   RejectPromise(aPromiseId, ToNsresult(aError), aErrorMessage);
   return IPC_OK();
 }
 
-static dom::MediaKeyMessageType
-ToDOMMessageType(uint32_t aMessageType)
-{
-  switch (static_cast<cdm::MessageType>(aMessageType)) {
-    case cdm::kLicenseRequest:
-      return dom::MediaKeyMessageType::License_request;
-    case cdm::kLicenseRenewal:
-      return dom::MediaKeyMessageType::License_renewal;
-    case cdm::kLicenseRelease:
-      return dom::MediaKeyMessageType::License_release;
-  }
-  MOZ_ASSERT_UNREACHABLE("Invalid cdm::MessageType enum value.");
-  return dom::MediaKeyMessageType::License_request;
-}
-
 ipc::IPCResult
 ChromiumCDMParent::RecvOnSessionMessage(const nsCString& aSessionId,
                                         const uint32_t& aMessageType,
                                         nsTArray<uint8_t>&& aMessage)
 {
   GMP_LOG("ChromiumCDMParent::RecvOnSessionMessage(this=%p, sid=%s)",
           this,
           aSessionId.get());
-  if (!mProxy || mIsShutdown) {
+  if (!mCDMCallback || mIsShutdown) {
     return IPC_OK();
   }
-  RefPtr<CDMProxy> proxy = mProxy;
-  nsString sid = NS_ConvertUTF8toUTF16(aSessionId);
-  dom::MediaKeyMessageType messageType = ToDOMMessageType(aMessageType);
-  nsTArray<uint8_t> msg(Move(aMessage));
 
-  mMainThread->Dispatch(
-    NS_NewRunnableFunction("gmp::ChromiumCDMParent::RecvOnSessionMessage",
-                           [proxy, sid, messageType, msg]() mutable {
-                             proxy->OnSessionMessage(sid, messageType, msg);
-                           }),
-    NS_DISPATCH_NORMAL);
+  mCDMCallback->SessionMessage(aSessionId, aMessageType, Move(aMessage));
   return IPC_OK();
 }
 
-static dom::MediaKeyStatus
-ToDOMMediaKeyStatus(uint32_t aStatus)
-{
-  switch (static_cast<cdm::KeyStatus>(aStatus)) {
-    case cdm::kUsable:
-      return dom::MediaKeyStatus::Usable;
-    case cdm::kInternalError:
-      return dom::MediaKeyStatus::Internal_error;
-    case cdm::kExpired:
-      return dom::MediaKeyStatus::Expired;
-    case cdm::kOutputRestricted:
-      return dom::MediaKeyStatus::Output_restricted;
-    case cdm::kOutputDownscaled:
-      return dom::MediaKeyStatus::Output_downscaled;
-    case cdm::kStatusPending:
-      return dom::MediaKeyStatus::Status_pending;
-    case cdm::kReleased:
-      return dom::MediaKeyStatus::Released;
-  }
-  MOZ_ASSERT_UNREACHABLE("Invalid cdm::KeyStatus enum value.");
-  return dom::MediaKeyStatus::Internal_error;
-}
-
 ipc::IPCResult
 ChromiumCDMParent::RecvOnSessionKeysChange(
   const nsCString& aSessionId,
   nsTArray<CDMKeyInformation>&& aKeysInfo)
 {
   GMP_LOG("ChromiumCDMParent::RecvOnSessionKeysChange(this=%p)", this);
-  if (!mProxy || mIsShutdown) {
+  if (!mCDMCallback || mIsShutdown) {
     return IPC_OK();
   }
-  bool keyStatusesChange = false;
-  {
-    CDMCaps::AutoLock caps(mProxy->Capabilites());
-    for (size_t i = 0; i < aKeysInfo.Length(); i++) {
-      keyStatusesChange |=
-        caps.SetKeyStatus(aKeysInfo[i].mKeyId(),
-                          NS_ConvertUTF8toUTF16(aSessionId),
-                          dom::Optional<dom::MediaKeyStatus>(
-                            ToDOMMediaKeyStatus(aKeysInfo[i].mStatus())));
-    }
-  }
-  if (keyStatusesChange) {
-    mMainThread->Dispatch(
-      NewRunnableMethod<nsString>("ChromiumCDMProxy::OnKeyStatusesChange",
-                                  mProxy,
-                                  &ChromiumCDMProxy::OnKeyStatusesChange,
-                                  NS_ConvertUTF8toUTF16(aSessionId)),
-      NS_DISPATCH_NORMAL);
-  }
+
+  mCDMCallback->SessionKeysChange(aSessionId, Move(aKeysInfo));
   return IPC_OK();
 }
 
 ipc::IPCResult
 ChromiumCDMParent::RecvOnExpirationChange(const nsCString& aSessionId,
                                           const double& aSecondsSinceEpoch)
 {
   GMP_LOG("ChromiumCDMParent::RecvOnExpirationChange(this=%p) time=%lf",
           this,
           aSecondsSinceEpoch);
-  if (!mProxy || mIsShutdown) {
+  if (!mCDMCallback || mIsShutdown) {
     return IPC_OK();
   }
 
-  mMainThread->Dispatch(
-    NewRunnableMethod<nsString, UnixTime>(
-      "ChromiumCDMProxy::OnExpirationChange",
-      mProxy,
-      &ChromiumCDMProxy::OnExpirationChange,
-      NS_ConvertUTF8toUTF16(aSessionId),
-      GMPTimestamp(aSecondsSinceEpoch * 1000)),
-    NS_DISPATCH_NORMAL);
+  mCDMCallback->ExpirationChange(aSessionId, aSecondsSinceEpoch);
   return IPC_OK();
 }
 
 ipc::IPCResult
 ChromiumCDMParent::RecvOnSessionClosed(const nsCString& aSessionId)
 {
   GMP_LOG("ChromiumCDMParent::RecvOnSessionClosed(this=%p)", this);
-  if (!mProxy || mIsShutdown) {
+  if (!mCDMCallback || mIsShutdown) {
     return IPC_OK();
   }
 
-  mMainThread->Dispatch(
-    NewRunnableMethod<nsString>("ChromiumCDMProxy::OnSessionClosed",
-                                mProxy,
-                                &ChromiumCDMProxy::OnSessionClosed,
-                                NS_ConvertUTF8toUTF16(aSessionId)),
-    NS_DISPATCH_NORMAL);
+  mCDMCallback->SessionClosed(aSessionId);
   return IPC_OK();
 }
 
 ipc::IPCResult
 ChromiumCDMParent::RecvOnLegacySessionError(const nsCString& aSessionId,
                                             const uint32_t& aError,
                                             const uint32_t& aSystemCode,
                                             const nsCString& aMessage)
 {
   GMP_LOG("ChromiumCDMParent::RecvOnLegacySessionError(this=%p)", this);
-  if (!mProxy || mIsShutdown) {
+  if (!mCDMCallback || mIsShutdown) {
     return IPC_OK();
   }
 
-  mMainThread->Dispatch(
-    NewRunnableMethod<nsString, nsresult, uint32_t, nsString>(
-      "ChromiumCDMProxy::OnSessionError",
-      mProxy,
-      &ChromiumCDMProxy::OnSessionError,
-      NS_ConvertUTF8toUTF16(aSessionId),
-      ToNsresult(aError),
-      aSystemCode,
-      NS_ConvertUTF8toUTF16(aMessage)),
-    NS_DISPATCH_NORMAL);
+  mCDMCallback->LegacySessionError(
+    aSessionId, ToNsresult(aError), aSystemCode, aMessage);
   return IPC_OK();
 }
 
 DecryptStatus
 ToDecryptStatus(uint32_t aError)
 {
   switch (static_cast<cdm::Status>(aError)) {
     case cdm::kSuccess:
@@ -929,37 +822,32 @@ ChromiumCDMParent::RecvShutdown()
 }
 
 void
 ChromiumCDMParent::ActorDestroy(ActorDestroyReason aWhy)
 {
   GMP_LOG("ChromiumCDMParent::ActorDestroy(this=%p, reason=%d)", this, aWhy);
   MOZ_ASSERT(!mActorDestroyed);
   mActorDestroyed = true;
-  // Shutdown() will clear mProxy, so let's keep a reference for later use.
-  RefPtr<ChromiumCDMProxy> proxy = mProxy;
+  // Shutdown() will clear mCDMCallback, so let's keep a reference for later use.
+  auto callback = mCDMCallback;
   if (!mIsShutdown) {
     // Plugin crash.
     MOZ_ASSERT(aWhy == AbnormalShutdown);
     Shutdown();
   }
   MOZ_ASSERT(mIsShutdown);
   RefPtr<ChromiumCDMParent> kungFuDeathGrip(this);
   if (mContentParent) {
     mContentParent->ChromiumCDMDestroyed(this);
     mContentParent = nullptr;
   }
   bool abnormalShutdown = (aWhy == AbnormalShutdown);
-  if (abnormalShutdown && proxy) {
-    mMainThread->Dispatch(
-      NewRunnableMethod(
-        "ChromiumCDMProxy::Terminated",
-        proxy,
-        &ChromiumCDMProxy::Terminated),
-      NS_DISPATCH_NORMAL);
+  if (abnormalShutdown && callback) {
+    callback->Terminated();
   }
   MaybeDisconnect(abnormalShutdown);
 }
 
 RefPtr<MediaDataDecoder::InitPromise>
 ChromiumCDMParent::InitializeVideoDecoder(
   const gmp::CDMVideoDecoderConfig& aConfig,
   const VideoInfo& aInfo,
@@ -1185,29 +1073,24 @@ ChromiumCDMParent::Shutdown()
   }
   mIsShutdown = true;
 
   // If we're shutting down due to the plugin shutting down due to application
   // shutdown, we should tell the CDM proxy to also shutdown. Otherwise the
   // proxy will shutdown when the owning MediaKeys is destroyed during cycle
   // collection, and that will not shut down cleanly as the GMP thread will be
   // shutdown by then.
-  if (mProxy) {
-    mMainThread->Dispatch(
-      NewRunnableMethod(
-        "ChromiumCDMProxy::Shutdown",
-        mProxy,
-        &ChromiumCDMProxy::Shutdown),
-      NS_DISPATCH_NORMAL);
+  if (mCDMCallback) {
+    mCDMCallback->Shutdown();
   }
 
-  // We may be called from a task holding the last reference to the proxy, so
+  // We may be called from a task holding the last reference to the CDM callback, so
   // let's clear our local weak pointer to ensure it will not be used afterward
   // (including from an already-queued task, e.g.: ActorDestroy).
-  mProxy = nullptr;
+  mCDMCallback = nullptr;
 
   mReorderQueue.Clear();
 
   for (RefPtr<DecryptJob>& decrypt : mDecrypts) {
     decrypt->PostResult(eme::AbortedErr);
   }
   mDecrypts.Clear();
 
--- a/dom/media/gmp/ChromiumCDMParent.h
+++ b/dom/media/gmp/ChromiumCDMParent.h
@@ -13,16 +13,18 @@
 #include "mozilla/gmp/PChromiumCDMParent.h"
 #include "mozilla/RefPtr.h"
 #include "nsDataHashtable.h"
 #include "PlatformDecoderModule.h"
 #include "ImageContainer.h"
 #include "mozilla/Span.h"
 #include "ReorderQueue.h"
 
+class ChromiumCDMCallback;
+
 namespace mozilla {
 
 class MediaRawData;
 class ChromiumCDMProxy;
 
 namespace gmp {
 
 class GMPContentParent;
@@ -33,17 +35,17 @@ class ChromiumCDMParent final
 {
 public:
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(ChromiumCDMParent)
 
   ChromiumCDMParent(GMPContentParent* aContentParent, uint32_t aPluginId);
 
   uint32_t PluginId() const { return mPluginId; }
 
-  bool Init(ChromiumCDMProxy* aProxy,
+  bool Init(ChromiumCDMCallback* aCDMCallback,
             bool aAllowDistinctiveIdentifier,
             bool aAllowPersistentState,
             nsIEventTarget* aMainThread);
 
   void CreateSession(uint32_t aCreateSessionToken,
                      uint32_t aSessionType,
                      uint32_t aInitDataType,
                      uint32_t aPromiseId,
@@ -143,20 +145,19 @@ protected:
 
   bool PurgeShmems();
   bool EnsureSufficientShmems(size_t aVideoFrameSize);
   already_AddRefed<VideoData> CreateVideoFrame(const CDMVideoFrame& aFrame,
                                                Span<uint8_t> aData);
 
   const uint32_t mPluginId;
   GMPContentParent* mContentParent;
-  // Note: this pointer is a weak reference because otherwise it would cause
-  // a cycle, as ChromiumCDMProxy has a strong reference to the
-  // ChromiumCDMParent.
-  ChromiumCDMProxy* mProxy = nullptr;
+  // 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<MediaDataDecoder::InitPromise> mInitVideoDecoderPromise;
   MozPromiseHolder<MediaDataDecoder::DecodePromise> mDecodePromise;
 
   RefPtr<layers::ImageContainer> mImageContainer;
   VideoInfo mVideoInfo;
@@ -183,15 +184,15 @@ protected:
   // before presenting. mMaxRefFrames is non-zero if we have an initialized
   // decoder and we are decoding H.264. If so, it stores the maximum length of
   // the reorder queue that we need. Note we may have multiple decoders for the
   // life time of this object, but never more than one active at once.
   uint32_t mMaxRefFrames = 0;
   ReorderQueue mReorderQueue;
 
   // The main thread associated with the root document. Must be set in Init().
-    nsCOMPtr<nsIEventTarget> mMainThread;
+  nsCOMPtr<nsIEventTarget> mMainThread;
 };
 
 } // namespace gmp
 } // namespace mozilla
 
 #endif // ChromiumCDMParent_h_
--- a/dom/media/gmp/ChromiumCDMProxy.cpp
+++ b/dom/media/gmp/ChromiumCDMProxy.cpp
@@ -1,15 +1,16 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* 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 "ChromiumCDMProxy.h"
+#include "ChromiumCDMCallbackProxy.h"
 #include "mozilla/dom/MediaKeySession.h"
 #include "GMPUtils.h"
 #include "nsPrintfCString.h"
 #include "GMPService.h"
 #include "content_decryption_module.h"
 
 #define NS_DispatchToMainThread(...) CompileError_UseAbstractMainThreadInstead
 
@@ -92,17 +93,19 @@ ChromiumCDMProxy::Init(PromiseId aPromis
         return;
       }
       RefPtr<gmp::GetCDMParentPromise> promise =
         service->GetCDM(nodeId, { keySystem }, helper);
       promise->Then(
         thread,
         __func__,
         [self, aPromiseId](RefPtr<gmp::ChromiumCDMParent> cdm) {
-          if (!cdm->Init(self,
+          self->mCallback =
+            MakeUnique<ChromiumCDMCallbackProxy>(self, self->mMainThread);
+          if (!cdm->Init(self->mCallback.get(),
                          self->mDistinctiveIdentifierRequired,
                          self->mPersistentStateRequired,
                          self->mMainThread)) {
             self->RejectPromise(aPromiseId,
                                 NS_ERROR_FAILURE,
                                 NS_LITERAL_CSTRING("GetCDM failed."));
             return;
           }
@@ -459,17 +462,17 @@ ChromiumCDMProxy::OnResolveLoadSessionPr
     return;
   }
   mKeys->OnSessionLoaded(aPromiseId, aSuccess);
 }
 
 void
 ChromiumCDMProxy::OnSessionMessage(const nsAString& aSessionId,
                                    dom::MediaKeyMessageType aMessageType,
-                                   nsTArray<uint8_t>& aMessage)
+                                   const nsTArray<uint8_t>& aMessage)
 {
   MOZ_ASSERT(NS_IsMainThread());
   if (mKeys.IsNull()) {
     return;
   }
   RefPtr<dom::MediaKeySession> session(mKeys->GetSession(aSessionId));
   if (session) {
     session->DispatchKeyMessage(aMessageType, aMessage);
--- a/dom/media/gmp/ChromiumCDMProxy.h
+++ b/dom/media/gmp/ChromiumCDMProxy.h
@@ -10,17 +10,17 @@
 #include "mozilla/CDMProxy.h"
 #include "mozilla/AbstractThread.h"
 #include "ChromiumCDMParent.h"
 
 namespace mozilla {
 
 class MediaRawData;
 class DecryptJob;
-
+class ChromiumCDMCallbackProxy;
 class ChromiumCDMProxy : public CDMProxy
 {
 public:
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(ChromiumCDMProxy, override)
 
   ChromiumCDMProxy(dom::MediaKeys* aKeys,
                    const nsAString& aKeySystem,
                    GMPCrashHelper* aCrashHelper,
@@ -120,13 +120,14 @@ private:
 
   ~ChromiumCDMProxy();
 
   GMPCrashHelper* mCrashHelper;
 
   Mutex mCDMMutex;
   RefPtr<gmp::ChromiumCDMParent> mCDM;
   RefPtr<AbstractThread> mGMPThread;
+  UniquePtr<ChromiumCDMCallbackProxy> mCallback;
 };
 
 } // namespace mozilla
 
 #endif // GMPCDMProxy_h_
--- a/dom/media/gmp/moz.build
+++ b/dom/media/gmp/moz.build
@@ -7,16 +7,17 @@
 XPIDL_MODULE = 'content_geckomediaplugins'
 
 XPIDL_SOURCES += [
     'mozIGeckoMediaPluginChromeService.idl',
     'mozIGeckoMediaPluginService.idl',
 ]
 
 EXPORTS += [
+    'ChromiumCDMCallback.h',
     'ChromiumCDMParent.h',
     'ChromiumCDMProxy.h',
     'DecryptJob.h',
     'gmp-api/gmp-decryption.h',
     'gmp-api/gmp-entrypoints.h',
     'gmp-api/gmp-errors.h',
     'gmp-api/gmp-platform.h',
     'gmp-api/gmp-storage.h',
@@ -67,16 +68,17 @@ EXPORTS += [
     'GMPVideoPlaneImpl.h',
     'widevine-adapter/content_decryption_module.h',
     'widevine-adapter/content_decryption_module_export.h',
     'widevine-adapter/content_decryption_module_ext.h',
 ]
 
 UNIFIED_SOURCES += [
     'ChromiumCDMAdapter.cpp',
+    'ChromiumCDMCallbackProxy.cpp',
     'ChromiumCDMChild.cpp',
     'ChromiumCDMParent.cpp',
     'ChromiumCDMProxy.cpp',
     'DecryptJob.cpp',
     'GMPCDMCallbackProxy.cpp',
     'GMPChild.cpp',
     'GMPContentChild.cpp',
     'GMPContentParent.cpp',