summaryrefslogtreecommitdiffstats
path: root/dom/media/gmp/GMPCDMProxy.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'dom/media/gmp/GMPCDMProxy.cpp')
-rw-r--r--dom/media/gmp/GMPCDMProxy.cpp799
1 files changed, 799 insertions, 0 deletions
diff --git a/dom/media/gmp/GMPCDMProxy.cpp b/dom/media/gmp/GMPCDMProxy.cpp
new file mode 100644
index 000000000..58c5596ee
--- /dev/null
+++ b/dom/media/gmp/GMPCDMProxy.cpp
@@ -0,0 +1,799 @@
+/* -*- 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 "GMPCDMProxy.h"
+#include "mozilla/EMEUtils.h"
+#include "mozilla/PodOperations.h"
+
+#include "mozilla/dom/MediaKeys.h"
+#include "mozilla/dom/MediaKeySession.h"
+
+#include "mozIGeckoMediaPluginService.h"
+#include "nsContentCID.h"
+#include "nsIConsoleService.h"
+#include "nsPrintfCString.h"
+#include "nsServiceManagerUtils.h"
+#include "nsString.h"
+#include "prenv.h"
+#include "GMPCDMCallbackProxy.h"
+#include "GMPService.h"
+#include "MainThreadUtils.h"
+#include "MediaData.h"
+
+namespace mozilla {
+
+GMPCDMProxy::GMPCDMProxy(dom::MediaKeys* aKeys,
+ const nsAString& aKeySystem,
+ GMPCrashHelper* aCrashHelper,
+ bool aDistinctiveIdentifierRequired,
+ bool aPersistentStateRequired)
+ : CDMProxy(aKeys,
+ aKeySystem,
+ aDistinctiveIdentifierRequired,
+ aPersistentStateRequired)
+ , mCrashHelper(aCrashHelper)
+ , mCDM(nullptr)
+ , mDecryptionJobCount(0)
+ , mShutdownCalled(false)
+ , mDecryptorId(0)
+ , mCreatePromiseId(0)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_COUNT_CTOR(GMPCDMProxy);
+}
+
+GMPCDMProxy::~GMPCDMProxy()
+{
+ MOZ_COUNT_DTOR(GMPCDMProxy);
+}
+
+void
+GMPCDMProxy::Init(PromiseId aPromiseId,
+ const nsAString& aOrigin,
+ const nsAString& aTopLevelOrigin,
+ const nsAString& aGMPName,
+ bool aInPrivateBrowsing)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+ NS_ENSURE_TRUE_VOID(!mKeys.IsNull());
+
+ EME_LOG("GMPCDMProxy::Init (%s, %s) %s",
+ NS_ConvertUTF16toUTF8(aOrigin).get(),
+ NS_ConvertUTF16toUTF8(aTopLevelOrigin).get(),
+ (aInPrivateBrowsing ? "PrivateBrowsing" : "NonPrivateBrowsing"));
+
+ nsCString pluginVersion;
+ if (!mOwnerThread) {
+ nsCOMPtr<mozIGeckoMediaPluginService> mps =
+ do_GetService("@mozilla.org/gecko-media-plugin-service;1");
+ if (!mps) {
+ RejectPromise(aPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR,
+ NS_LITERAL_CSTRING("Couldn't get MediaPluginService in GMPCDMProxy::Init"));
+ return;
+ }
+ mps->GetThread(getter_AddRefs(mOwnerThread));
+ if (!mOwnerThread) {
+ RejectPromise(aPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR,
+ NS_LITERAL_CSTRING("Couldn't get GMP thread GMPCDMProxy::Init"));
+ return;
+ }
+ }
+
+ if (aGMPName.IsEmpty()) {
+ RejectPromise(aPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR,
+ nsPrintfCString("Unknown GMP for keysystem '%s'", NS_ConvertUTF16toUTF8(mKeySystem).get()));
+ return;
+ }
+
+ nsAutoPtr<InitData> data(new InitData());
+ data->mPromiseId = aPromiseId;
+ data->mOrigin = aOrigin;
+ data->mTopLevelOrigin = aTopLevelOrigin;
+ data->mGMPName = aGMPName;
+ data->mInPrivateBrowsing = aInPrivateBrowsing;
+ data->mCrashHelper = mCrashHelper;
+ nsCOMPtr<nsIRunnable> task(
+ NewRunnableMethod<nsAutoPtr<InitData>>(this,
+ &GMPCDMProxy::gmp_Init,
+ Move(data)));
+ mOwnerThread->Dispatch(task, NS_DISPATCH_NORMAL);
+}
+
+#ifdef DEBUG
+bool
+GMPCDMProxy::IsOnOwnerThread()
+{
+ return NS_GetCurrentThread() == mOwnerThread;
+}
+#endif
+
+void
+GMPCDMProxy::gmp_InitDone(GMPDecryptorProxy* aCDM, nsAutoPtr<InitData>&& aData)
+{
+ EME_LOG("GMPCDMProxy::gmp_InitDone");
+ if (mShutdownCalled) {
+ if (aCDM) {
+ aCDM->Close();
+ }
+ RejectPromise(aData->mPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR,
+ NS_LITERAL_CSTRING("GMPCDMProxy was shut down before init could complete"));
+ return;
+ }
+ if (!aCDM) {
+ RejectPromise(aData->mPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR,
+ NS_LITERAL_CSTRING("GetGMPDecryptor failed to return a CDM"));
+ return;
+ }
+
+ mCDM = aCDM;
+ mCallback = new GMPCDMCallbackProxy(this);
+ mCDM->Init(mCallback,
+ mDistinctiveIdentifierRequired,
+ mPersistentStateRequired);
+
+ // Await the OnSetDecryptorId callback.
+ mCreatePromiseId = aData->mPromiseId;
+}
+
+void GMPCDMProxy::OnSetDecryptorId(uint32_t aId)
+{
+ MOZ_ASSERT(mCreatePromiseId);
+ mDecryptorId = aId;
+ nsCOMPtr<nsIRunnable> task(
+ NewRunnableMethod<uint32_t>(this,
+ &GMPCDMProxy::OnCDMCreated,
+ mCreatePromiseId));
+ NS_DispatchToMainThread(task);
+}
+
+class gmp_InitDoneCallback : public GetGMPDecryptorCallback
+{
+public:
+ gmp_InitDoneCallback(GMPCDMProxy* aGMPCDMProxy,
+ nsAutoPtr<GMPCDMProxy::InitData>&& aData)
+ : mGMPCDMProxy(aGMPCDMProxy),
+ mData(Move(aData))
+ {
+ }
+
+ void Done(GMPDecryptorProxy* aCDM)
+ {
+ mGMPCDMProxy->gmp_InitDone(aCDM, Move(mData));
+ }
+
+private:
+ RefPtr<GMPCDMProxy> mGMPCDMProxy;
+ nsAutoPtr<GMPCDMProxy::InitData> mData;
+};
+
+class gmp_InitGetGMPDecryptorCallback : public GetNodeIdCallback
+{
+public:
+ gmp_InitGetGMPDecryptorCallback(GMPCDMProxy* aGMPCDMProxy,
+ nsAutoPtr<GMPCDMProxy::InitData>&& aData)
+ : mGMPCDMProxy(aGMPCDMProxy),
+ mData(aData)
+ {
+ }
+
+ void Done(nsresult aResult, const nsACString& aNodeId)
+ {
+ mGMPCDMProxy->gmp_InitGetGMPDecryptor(aResult, aNodeId, Move(mData));
+ }
+
+private:
+ RefPtr<GMPCDMProxy> mGMPCDMProxy;
+ nsAutoPtr<GMPCDMProxy::InitData> mData;
+};
+
+void
+GMPCDMProxy::gmp_Init(nsAutoPtr<InitData>&& aData)
+{
+ MOZ_ASSERT(IsOnOwnerThread());
+
+ nsCOMPtr<mozIGeckoMediaPluginService> mps =
+ do_GetService("@mozilla.org/gecko-media-plugin-service;1");
+ if (!mps) {
+ RejectPromise(aData->mPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR,
+ NS_LITERAL_CSTRING("Couldn't get MediaPluginService in GMPCDMProxy::gmp_Init"));
+ return;
+ }
+
+ // Make a copy before we transfer ownership of aData to the
+ // gmp_InitGetGMPDecryptorCallback.
+ InitData data(*aData);
+ UniquePtr<GetNodeIdCallback> callback(
+ new gmp_InitGetGMPDecryptorCallback(this, Move(aData)));
+ nsresult rv = mps->GetNodeId(data.mOrigin,
+ data.mTopLevelOrigin,
+ data.mGMPName,
+ data.mInPrivateBrowsing,
+ Move(callback));
+ if (NS_FAILED(rv)) {
+ RejectPromise(data.mPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR,
+ NS_LITERAL_CSTRING("Call to GetNodeId() failed early"));
+ }
+}
+
+void
+GMPCDMProxy::gmp_InitGetGMPDecryptor(nsresult aResult,
+ const nsACString& aNodeId,
+ nsAutoPtr<InitData>&& aData)
+{
+ uint32_t promiseID = aData->mPromiseId;
+ if (NS_FAILED(aResult)) {
+ RejectPromise(promiseID, NS_ERROR_DOM_INVALID_STATE_ERR,
+ NS_LITERAL_CSTRING("GetNodeId() called back, but with a failure result"));
+ return;
+ }
+
+ mNodeId = aNodeId;
+ MOZ_ASSERT(!GetNodeId().IsEmpty());
+
+ nsCOMPtr<mozIGeckoMediaPluginService> mps =
+ do_GetService("@mozilla.org/gecko-media-plugin-service;1");
+ if (!mps) {
+ RejectPromise(promiseID, NS_ERROR_DOM_INVALID_STATE_ERR,
+ NS_LITERAL_CSTRING("Couldn't get MediaPluginService in GMPCDMProxy::gmp_InitGetGMPDecryptor"));
+ return;
+ }
+
+ EME_LOG("GMPCDMProxy::gmp_Init (%s, %s) %s NodeId=%s",
+ NS_ConvertUTF16toUTF8(aData->mOrigin).get(),
+ NS_ConvertUTF16toUTF8(aData->mTopLevelOrigin).get(),
+ (aData->mInPrivateBrowsing ? "PrivateBrowsing" : "NonPrivateBrowsing"),
+ GetNodeId().get());
+
+ nsTArray<nsCString> tags;
+ tags.AppendElement(NS_ConvertUTF16toUTF8(mKeySystem));
+
+ // Note: must capture helper refptr here, before the Move()
+ // when we create the GetGMPDecryptorCallback below.
+ RefPtr<GMPCrashHelper> crashHelper = Move(aData->mCrashHelper);
+ UniquePtr<GetGMPDecryptorCallback> callback(new gmp_InitDoneCallback(this,
+ Move(aData)));
+ nsresult rv = mps->GetGMPDecryptor(crashHelper,
+ &tags,
+ GetNodeId(),
+ Move(callback));
+ if (NS_FAILED(rv)) {
+ RejectPromise(promiseID, NS_ERROR_DOM_INVALID_STATE_ERR,
+ NS_LITERAL_CSTRING("Call to GetGMPDecryptor() failed early"));
+ }
+}
+
+void
+GMPCDMProxy::OnCDMCreated(uint32_t aPromiseId)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+ if (mKeys.IsNull()) {
+ return;
+ }
+ MOZ_ASSERT(!GetNodeId().IsEmpty());
+ if (mCDM) {
+ mKeys->OnCDMCreated(aPromiseId, GetNodeId(), mCDM->GetPluginId());
+ } else {
+ // No CDM? Just reject the promise.
+ mKeys->RejectPromise(aPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR,
+ NS_LITERAL_CSTRING("Null CDM in OnCDMCreated()"));
+ }
+}
+
+void
+GMPCDMProxy::CreateSession(uint32_t aCreateSessionToken,
+ dom::MediaKeySessionType aSessionType,
+ PromiseId aPromiseId,
+ const nsAString& aInitDataType,
+ nsTArray<uint8_t>& aInitData)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(mOwnerThread);
+
+ nsAutoPtr<CreateSessionData> data(new CreateSessionData());
+ data->mSessionType = aSessionType;
+ data->mCreateSessionToken = aCreateSessionToken;
+ data->mPromiseId = aPromiseId;
+ data->mInitDataType = NS_ConvertUTF16toUTF8(aInitDataType);
+ data->mInitData = Move(aInitData);
+
+ nsCOMPtr<nsIRunnable> task(
+ NewRunnableMethod<nsAutoPtr<CreateSessionData>>(this, &GMPCDMProxy::gmp_CreateSession, data));
+ mOwnerThread->Dispatch(task, NS_DISPATCH_NORMAL);
+}
+
+GMPSessionType
+ToGMPSessionType(dom::MediaKeySessionType aSessionType) {
+ switch (aSessionType) {
+ case dom::MediaKeySessionType::Temporary: return kGMPTemporySession;
+ case dom::MediaKeySessionType::Persistent_license: return kGMPPersistentSession;
+ default: return kGMPTemporySession;
+ };
+};
+
+void
+GMPCDMProxy::gmp_CreateSession(nsAutoPtr<CreateSessionData> aData)
+{
+ MOZ_ASSERT(IsOnOwnerThread());
+ if (!mCDM) {
+ RejectPromise(aData->mPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR,
+ NS_LITERAL_CSTRING("Null CDM in gmp_CreateSession"));
+ return;
+ }
+ mCDM->CreateSession(aData->mCreateSessionToken,
+ aData->mPromiseId,
+ aData->mInitDataType,
+ aData->mInitData,
+ ToGMPSessionType(aData->mSessionType));
+}
+
+void
+GMPCDMProxy::LoadSession(PromiseId aPromiseId,
+ const nsAString& aSessionId)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(mOwnerThread);
+
+ nsAutoPtr<SessionOpData> data(new SessionOpData());
+ data->mPromiseId = aPromiseId;
+ data->mSessionId = NS_ConvertUTF16toUTF8(aSessionId);
+ nsCOMPtr<nsIRunnable> task(
+ NewRunnableMethod<nsAutoPtr<SessionOpData>>(this, &GMPCDMProxy::gmp_LoadSession, data));
+ mOwnerThread->Dispatch(task, NS_DISPATCH_NORMAL);
+}
+
+void
+GMPCDMProxy::gmp_LoadSession(nsAutoPtr<SessionOpData> aData)
+{
+ MOZ_ASSERT(IsOnOwnerThread());
+
+ if (!mCDM) {
+ RejectPromise(aData->mPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR,
+ NS_LITERAL_CSTRING("Null CDM in gmp_LoadSession"));
+ return;
+ }
+ mCDM->LoadSession(aData->mPromiseId, aData->mSessionId);
+}
+
+void
+GMPCDMProxy::SetServerCertificate(PromiseId aPromiseId,
+ nsTArray<uint8_t>& aCert)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(mOwnerThread);
+
+ nsAutoPtr<SetServerCertificateData> data(new SetServerCertificateData());
+ data->mPromiseId = aPromiseId;
+ data->mCert = Move(aCert);
+ nsCOMPtr<nsIRunnable> task(
+ NewRunnableMethod<nsAutoPtr<SetServerCertificateData>>(this, &GMPCDMProxy::gmp_SetServerCertificate, data));
+ mOwnerThread->Dispatch(task, NS_DISPATCH_NORMAL);
+}
+
+void
+GMPCDMProxy::gmp_SetServerCertificate(nsAutoPtr<SetServerCertificateData> aData)
+{
+ MOZ_ASSERT(IsOnOwnerThread());
+ if (!mCDM) {
+ RejectPromise(aData->mPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR,
+ NS_LITERAL_CSTRING("Null CDM in gmp_SetServerCertificate"));
+ return;
+ }
+ mCDM->SetServerCertificate(aData->mPromiseId, aData->mCert);
+}
+
+void
+GMPCDMProxy::UpdateSession(const nsAString& aSessionId,
+ PromiseId aPromiseId,
+ nsTArray<uint8_t>& aResponse)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(mOwnerThread);
+ NS_ENSURE_TRUE_VOID(!mKeys.IsNull());
+
+ nsAutoPtr<UpdateSessionData> data(new UpdateSessionData());
+ data->mPromiseId = aPromiseId;
+ data->mSessionId = NS_ConvertUTF16toUTF8(aSessionId);
+ data->mResponse = Move(aResponse);
+ nsCOMPtr<nsIRunnable> task(
+ NewRunnableMethod<nsAutoPtr<UpdateSessionData>>(this, &GMPCDMProxy::gmp_UpdateSession, data));
+ mOwnerThread->Dispatch(task, NS_DISPATCH_NORMAL);
+}
+
+void
+GMPCDMProxy::gmp_UpdateSession(nsAutoPtr<UpdateSessionData> aData)
+{
+ MOZ_ASSERT(IsOnOwnerThread());
+ if (!mCDM) {
+ RejectPromise(aData->mPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR,
+ NS_LITERAL_CSTRING("Null CDM in gmp_UpdateSession"));
+ return;
+ }
+ mCDM->UpdateSession(aData->mPromiseId,
+ aData->mSessionId,
+ aData->mResponse);
+}
+
+void
+GMPCDMProxy::CloseSession(const nsAString& aSessionId,
+ PromiseId aPromiseId)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+ NS_ENSURE_TRUE_VOID(!mKeys.IsNull());
+
+ nsAutoPtr<SessionOpData> data(new SessionOpData());
+ data->mPromiseId = aPromiseId;
+ data->mSessionId = NS_ConvertUTF16toUTF8(aSessionId);
+ nsCOMPtr<nsIRunnable> task(
+ NewRunnableMethod<nsAutoPtr<SessionOpData>>(this, &GMPCDMProxy::gmp_CloseSession, data));
+ mOwnerThread->Dispatch(task, NS_DISPATCH_NORMAL);
+}
+
+void
+GMPCDMProxy::gmp_CloseSession(nsAutoPtr<SessionOpData> aData)
+{
+ MOZ_ASSERT(IsOnOwnerThread());
+ if (!mCDM) {
+ RejectPromise(aData->mPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR,
+ NS_LITERAL_CSTRING("Null CDM in gmp_CloseSession"));
+ return;
+ }
+ mCDM->CloseSession(aData->mPromiseId, aData->mSessionId);
+}
+
+void
+GMPCDMProxy::RemoveSession(const nsAString& aSessionId,
+ PromiseId aPromiseId)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+ NS_ENSURE_TRUE_VOID(!mKeys.IsNull());
+
+ nsAutoPtr<SessionOpData> data(new SessionOpData());
+ data->mPromiseId = aPromiseId;
+ data->mSessionId = NS_ConvertUTF16toUTF8(aSessionId);
+ nsCOMPtr<nsIRunnable> task(
+ NewRunnableMethod<nsAutoPtr<SessionOpData>>(this, &GMPCDMProxy::gmp_RemoveSession, data));
+ mOwnerThread->Dispatch(task, NS_DISPATCH_NORMAL);
+}
+
+void
+GMPCDMProxy::gmp_RemoveSession(nsAutoPtr<SessionOpData> aData)
+{
+ MOZ_ASSERT(IsOnOwnerThread());
+ if (!mCDM) {
+ RejectPromise(aData->mPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR,
+ NS_LITERAL_CSTRING("Null CDM in gmp_RemoveSession"));
+ return;
+ }
+ mCDM->RemoveSession(aData->mPromiseId, aData->mSessionId);
+}
+
+void
+GMPCDMProxy::Shutdown()
+{
+ MOZ_ASSERT(NS_IsMainThread());
+ mKeys.Clear();
+ // Note: This may end up being the last owning reference to the GMPCDMProxy.
+ nsCOMPtr<nsIRunnable> task(NewRunnableMethod(this, &GMPCDMProxy::gmp_Shutdown));
+ if (mOwnerThread) {
+ mOwnerThread->Dispatch(task, NS_DISPATCH_NORMAL);
+ }
+}
+
+void
+GMPCDMProxy::gmp_Shutdown()
+{
+ MOZ_ASSERT(IsOnOwnerThread());
+
+ mShutdownCalled = true;
+
+ // Abort any pending decrypt jobs, to awaken any clients waiting on a job.
+ for (size_t i = 0; i < mDecryptionJobs.Length(); i++) {
+ DecryptJob* job = mDecryptionJobs[i];
+ job->PostResult(AbortedErr);
+ }
+ mDecryptionJobs.Clear();
+
+ if (mCDM) {
+ mCDM->Close();
+ mCDM = nullptr;
+ }
+}
+
+void
+GMPCDMProxy::RejectPromise(PromiseId aId, nsresult aCode,
+ const nsCString& aReason)
+{
+ if (NS_IsMainThread()) {
+ if (!mKeys.IsNull()) {
+ mKeys->RejectPromise(aId, aCode, aReason);
+ }
+ } else {
+ nsCOMPtr<nsIRunnable> task(new RejectPromiseTask(this, aId, aCode,
+ aReason));
+ NS_DispatchToMainThread(task);
+ }
+}
+
+void
+GMPCDMProxy::ResolvePromise(PromiseId aId)
+{
+ if (NS_IsMainThread()) {
+ if (!mKeys.IsNull()) {
+ mKeys->ResolvePromise(aId);
+ } else {
+ NS_WARNING("GMPCDMProxy unable to resolve promise!");
+ }
+ } else {
+ nsCOMPtr<nsIRunnable> task;
+ task = NewRunnableMethod<PromiseId>(this,
+ &GMPCDMProxy::ResolvePromise,
+ aId);
+ NS_DispatchToMainThread(task);
+ }
+}
+
+const nsCString&
+GMPCDMProxy::GetNodeId() const
+{
+ return mNodeId;
+}
+
+void
+GMPCDMProxy::OnSetSessionId(uint32_t aCreateSessionToken,
+ const nsAString& aSessionId)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+ if (mKeys.IsNull()) {
+ return;
+ }
+
+ RefPtr<dom::MediaKeySession> session(mKeys->GetPendingSession(aCreateSessionToken));
+ if (session) {
+ session->SetSessionId(aSessionId);
+ }
+}
+
+void
+GMPCDMProxy::OnResolveLoadSessionPromise(uint32_t aPromiseId, bool aSuccess)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+ if (mKeys.IsNull()) {
+ return;
+ }
+ mKeys->OnSessionLoaded(aPromiseId, aSuccess);
+}
+
+void
+GMPCDMProxy::OnSessionMessage(const nsAString& aSessionId,
+ dom::MediaKeyMessageType aMessageType,
+ 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);
+ }
+}
+
+void
+GMPCDMProxy::OnKeyStatusesChange(const nsAString& aSessionId)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+ if (mKeys.IsNull()) {
+ return;
+ }
+ RefPtr<dom::MediaKeySession> session(mKeys->GetSession(aSessionId));
+ if (session) {
+ session->DispatchKeyStatusesChange();
+ }
+}
+
+void
+GMPCDMProxy::OnExpirationChange(const nsAString& aSessionId,
+ GMPTimestamp aExpiryTime)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+ if (mKeys.IsNull()) {
+ return;
+ }
+ RefPtr<dom::MediaKeySession> session(mKeys->GetSession(aSessionId));
+ if (session) {
+ session->SetExpiration(static_cast<double>(aExpiryTime));
+ }
+}
+
+void
+GMPCDMProxy::OnSessionClosed(const nsAString& aSessionId)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+ if (mKeys.IsNull()) {
+ return;
+ }
+ RefPtr<dom::MediaKeySession> session(mKeys->GetSession(aSessionId));
+ if (session) {
+ session->OnClosed();
+ }
+}
+
+void
+GMPCDMProxy::OnDecrypted(uint32_t aId,
+ DecryptStatus aResult,
+ const nsTArray<uint8_t>& aDecryptedData)
+{
+ MOZ_ASSERT(IsOnOwnerThread());
+ gmp_Decrypted(aId, aResult, aDecryptedData);
+}
+
+static void
+LogToConsole(const nsAString& aMsg)
+{
+ nsCOMPtr<nsIConsoleService> console(
+ do_GetService("@mozilla.org/consoleservice;1"));
+ if (!console) {
+ NS_WARNING("Failed to log message to console.");
+ return;
+ }
+ nsAutoString msg(aMsg);
+ console->LogStringMessage(msg.get());
+}
+
+void
+GMPCDMProxy::OnSessionError(const nsAString& aSessionId,
+ nsresult aException,
+ uint32_t aSystemCode,
+ const nsAString& aMsg)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+ if (mKeys.IsNull()) {
+ return;
+ }
+ RefPtr<dom::MediaKeySession> session(mKeys->GetSession(aSessionId));
+ if (session) {
+ session->DispatchKeyError(aSystemCode);
+ }
+ LogToConsole(aMsg);
+}
+
+void
+GMPCDMProxy::OnRejectPromise(uint32_t aPromiseId,
+ nsresult aDOMException,
+ const nsCString& aMsg)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+ RejectPromise(aPromiseId, aDOMException, aMsg);
+}
+
+const nsString&
+GMPCDMProxy::KeySystem() const
+{
+ return mKeySystem;
+}
+
+CDMCaps&
+GMPCDMProxy::Capabilites() {
+ return mCapabilites;
+}
+
+RefPtr<GMPCDMProxy::DecryptPromise>
+GMPCDMProxy::Decrypt(MediaRawData* aSample)
+{
+ RefPtr<DecryptJob> job(new DecryptJob(aSample));
+ RefPtr<DecryptPromise> promise(job->Ensure());
+
+ nsCOMPtr<nsIRunnable> task(
+ NewRunnableMethod<RefPtr<DecryptJob>>(this, &GMPCDMProxy::gmp_Decrypt, job));
+ mOwnerThread->Dispatch(task, NS_DISPATCH_NORMAL);
+ return promise;
+}
+
+void
+GMPCDMProxy::gmp_Decrypt(RefPtr<DecryptJob> aJob)
+{
+ MOZ_ASSERT(IsOnOwnerThread());
+
+ if (!mCDM) {
+ aJob->PostResult(AbortedErr);
+ return;
+ }
+
+ aJob->mId = ++mDecryptionJobCount;
+ nsTArray<uint8_t> data;
+ data.AppendElements(aJob->mSample->Data(), aJob->mSample->Size());
+ mCDM->Decrypt(aJob->mId, aJob->mSample->mCrypto, data);
+ mDecryptionJobs.AppendElement(aJob.forget());
+}
+
+void
+GMPCDMProxy::gmp_Decrypted(uint32_t aId,
+ DecryptStatus aResult,
+ const nsTArray<uint8_t>& aDecryptedData)
+{
+ MOZ_ASSERT(IsOnOwnerThread());
+#ifdef DEBUG
+ bool jobIdFound = false;
+#endif
+ for (size_t i = 0; i < mDecryptionJobs.Length(); i++) {
+ DecryptJob* job = mDecryptionJobs[i];
+ if (job->mId == aId) {
+#ifdef DEBUG
+ jobIdFound = true;
+#endif
+ job->PostResult(aResult, aDecryptedData);
+ mDecryptionJobs.RemoveElementAt(i);
+ }
+ }
+#ifdef DEBUG
+ if (!jobIdFound) {
+ NS_WARNING("GMPDecryptorChild returned incorrect job ID");
+ }
+#endif
+}
+
+void
+GMPCDMProxy::DecryptJob::PostResult(DecryptStatus aResult)
+{
+ nsTArray<uint8_t> empty;
+ PostResult(aResult, empty);
+}
+
+void
+GMPCDMProxy::DecryptJob::PostResult(DecryptStatus aResult,
+ const nsTArray<uint8_t>& aDecryptedData)
+{
+ if (aDecryptedData.Length() != mSample->Size()) {
+ NS_WARNING("CDM returned incorrect number of decrypted bytes");
+ }
+ if (aResult == Ok) {
+ nsAutoPtr<MediaRawDataWriter> writer(mSample->CreateWriter());
+ PodCopy(writer->Data(),
+ aDecryptedData.Elements(),
+ std::min<size_t>(aDecryptedData.Length(), mSample->Size()));
+ } else if (aResult == NoKeyErr) {
+ NS_WARNING("CDM returned NoKeyErr");
+ // We still have the encrypted sample, so we can re-enqueue it to be
+ // decrypted again once the key is usable again.
+ } else {
+ nsAutoCString str("CDM returned decode failure DecryptStatus=");
+ str.AppendInt(aResult);
+ NS_WARNING(str.get());
+ }
+ mPromise.Resolve(DecryptResult(aResult, mSample), __func__);
+}
+
+void
+GMPCDMProxy::GetSessionIdsForKeyId(const nsTArray<uint8_t>& aKeyId,
+ nsTArray<nsCString>& aSessionIds)
+{
+ CDMCaps::AutoLock caps(Capabilites());
+ caps.GetSessionIdsForKeyId(aKeyId, aSessionIds);
+}
+
+void
+GMPCDMProxy::Terminated()
+{
+ MOZ_ASSERT(NS_IsMainThread());
+ NS_WARNING("CDM terminated");
+ if (mCreatePromiseId) {
+ RejectPromise(mCreatePromiseId,
+ NS_ERROR_DOM_MEDIA_FATAL_ERR,
+ NS_LITERAL_CSTRING("Crashed waiting for CDM to initialize"));
+ mCreatePromiseId = 0;
+ }
+ if (!mKeys.IsNull()) {
+ mKeys->Terminated();
+ }
+}
+
+uint32_t
+GMPCDMProxy::GetDecryptorId()
+{
+ return mDecryptorId;
+}
+
+} // namespace mozilla