Bug 1397123 - [Part2] Decouple ChromiumCDMProxy from ChromiumCDMParent. r=cpearce
authorJames Cheng <jacheng@mozilla.com>
Mon, 11 Sep 2017 12:17:36 +0800
changeset 429694 a359e9bd31c700bf36f985e00d64903ecebc4756
parent 429693 7d56316ea722d001298a6dd7171ac2265201c658
child 429695 9232bc78ae68dd7978bb0474af1ef8be581dd35a
push id7761
push userjlund@mozilla.com
push dateFri, 15 Sep 2017 00:19:52 +0000
treeherdermozilla-beta@c38455951db4 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerscpearce
bugs1397123
milestone57.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 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',