diff options
Diffstat (limited to 'dom/media/gmp/GMPCDMProxy.cpp')
-rw-r--r-- | dom/media/gmp/GMPCDMProxy.cpp | 799 |
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 |