diff options
Diffstat (limited to 'dom/media/gmp/GMPDecryptorChild.cpp')
-rw-r--r-- | dom/media/gmp/GMPDecryptorChild.cpp | 403 |
1 files changed, 403 insertions, 0 deletions
diff --git a/dom/media/gmp/GMPDecryptorChild.cpp b/dom/media/gmp/GMPDecryptorChild.cpp new file mode 100644 index 000000000..6da3c6c43 --- /dev/null +++ b/dom/media/gmp/GMPDecryptorChild.cpp @@ -0,0 +1,403 @@ +/* -*- 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 "GMPDecryptorChild.h" +#include "GMPContentChild.h" +#include "GMPChild.h" +#include "base/task.h" +#include "mozilla/TimeStamp.h" +#include "mozilla/Unused.h" +#include "runnable_utils.h" +#include <ctime> + +#define ON_GMP_THREAD() (mPlugin->GMPMessageLoop() == MessageLoop::current()) + +#define CALL_ON_GMP_THREAD(_func, ...) \ + CallOnGMPThread(&GMPDecryptorChild::_func, __VA_ARGS__) + +namespace mozilla { +namespace gmp { + +GMPDecryptorChild::GMPDecryptorChild(GMPContentChild* aPlugin, + const nsTArray<uint8_t>& aPluginVoucher, + const nsTArray<uint8_t>& aSandboxVoucher) + : mSession(nullptr) + , mPlugin(aPlugin) + , mPluginVoucher(aPluginVoucher) + , mSandboxVoucher(aSandboxVoucher) +{ + MOZ_ASSERT(mPlugin); +} + +GMPDecryptorChild::~GMPDecryptorChild() +{ +} + +template <typename MethodType, typename... ParamType> +void +GMPDecryptorChild::CallMethod(MethodType aMethod, ParamType&&... aParams) +{ + MOZ_ASSERT(ON_GMP_THREAD()); + // Don't send IPC messages after tear-down. + if (mSession) { + (this->*aMethod)(Forward<ParamType>(aParams)...); + } +} + +template<typename T> +struct AddConstReference { + typedef const typename RemoveReference<T>::Type& Type; +}; + +template<typename MethodType, typename... ParamType> +void +GMPDecryptorChild::CallOnGMPThread(MethodType aMethod, ParamType&&... aParams) +{ + if (ON_GMP_THREAD()) { + // Use forwarding reference when we can. + CallMethod(aMethod, Forward<ParamType>(aParams)...); + } else { + // Use const reference when we have to. + auto m = &GMPDecryptorChild::CallMethod< + decltype(aMethod), typename AddConstReference<ParamType>::Type...>; + RefPtr<mozilla::Runnable> t = + dont_add_new_uses_of_this::NewRunnableMethod(this, m, aMethod, Forward<ParamType>(aParams)...); + mPlugin->GMPMessageLoop()->PostTask(t.forget()); + } +} + +void +GMPDecryptorChild::Init(GMPDecryptor* aSession) +{ + MOZ_ASSERT(aSession); + mSession = aSession; + // The ID of this decryptor is the IPDL actor ID. Note it's unique inside + // the child process, but not necessarily across all gecko processes. However, + // since GMPDecryptors are segregated by node ID/origin, we shouldn't end up + // with clashes in the content process. + SendSetDecryptorId(Id()); +} + +void +GMPDecryptorChild::SetSessionId(uint32_t aCreateSessionToken, + const char* aSessionId, + uint32_t aSessionIdLength) +{ + CALL_ON_GMP_THREAD(SendSetSessionId, + aCreateSessionToken, nsCString(aSessionId, aSessionIdLength)); +} + +void +GMPDecryptorChild::ResolveLoadSessionPromise(uint32_t aPromiseId, + bool aSuccess) +{ + CALL_ON_GMP_THREAD(SendResolveLoadSessionPromise, aPromiseId, aSuccess); +} + +void +GMPDecryptorChild::ResolvePromise(uint32_t aPromiseId) +{ + CALL_ON_GMP_THREAD(SendResolvePromise, aPromiseId); +} + +void +GMPDecryptorChild::RejectPromise(uint32_t aPromiseId, + GMPDOMException aException, + const char* aMessage, + uint32_t aMessageLength) +{ + CALL_ON_GMP_THREAD(SendRejectPromise, + aPromiseId, aException, nsCString(aMessage, aMessageLength)); +} + +void +GMPDecryptorChild::SessionMessage(const char* aSessionId, + uint32_t aSessionIdLength, + GMPSessionMessageType aMessageType, + const uint8_t* aMessage, + uint32_t aMessageLength) +{ + nsTArray<uint8_t> msg; + msg.AppendElements(aMessage, aMessageLength); + CALL_ON_GMP_THREAD(SendSessionMessage, + nsCString(aSessionId, aSessionIdLength), + aMessageType, Move(msg)); +} + +void +GMPDecryptorChild::ExpirationChange(const char* aSessionId, + uint32_t aSessionIdLength, + GMPTimestamp aExpiryTime) +{ + CALL_ON_GMP_THREAD(SendExpirationChange, + nsCString(aSessionId, aSessionIdLength), aExpiryTime); +} + +void +GMPDecryptorChild::SessionClosed(const char* aSessionId, + uint32_t aSessionIdLength) +{ + CALL_ON_GMP_THREAD(SendSessionClosed, + nsCString(aSessionId, aSessionIdLength)); +} + +void +GMPDecryptorChild::SessionError(const char* aSessionId, + uint32_t aSessionIdLength, + GMPDOMException aException, + uint32_t aSystemCode, + const char* aMessage, + uint32_t aMessageLength) +{ + CALL_ON_GMP_THREAD(SendSessionError, + nsCString(aSessionId, aSessionIdLength), + aException, aSystemCode, + nsCString(aMessage, aMessageLength)); +} + +void +GMPDecryptorChild::KeyStatusChanged(const char* aSessionId, + uint32_t aSessionIdLength, + const uint8_t* aKeyId, + uint32_t aKeyIdLength, + GMPMediaKeyStatus aStatus) +{ + AutoTArray<uint8_t, 16> kid; + kid.AppendElements(aKeyId, aKeyIdLength); + + nsTArray<GMPKeyInformation> keyInfos; + keyInfos.AppendElement(GMPKeyInformation(kid, aStatus)); + CALL_ON_GMP_THREAD(SendBatchedKeyStatusChanged, + nsCString(aSessionId, aSessionIdLength), + keyInfos); +} + +void +GMPDecryptorChild::BatchedKeyStatusChanged(const char* aSessionId, + uint32_t aSessionIdLength, + const GMPMediaKeyInfo* aKeyInfos, + uint32_t aKeyInfosLength) +{ + nsTArray<GMPKeyInformation> keyInfos; + for (uint32_t i = 0; i < aKeyInfosLength; i++) { + nsTArray<uint8_t> keyId; + keyId.AppendElements(aKeyInfos[i].keyid, aKeyInfos[i].keyid_size); + keyInfos.AppendElement(GMPKeyInformation(keyId, aKeyInfos[i].status)); + } + CALL_ON_GMP_THREAD(SendBatchedKeyStatusChanged, + nsCString(aSessionId, aSessionIdLength), + keyInfos); +} + +void +GMPDecryptorChild::Decrypted(GMPBuffer* aBuffer, GMPErr aResult) +{ + if (!ON_GMP_THREAD()) { + // We should run this whole method on the GMP thread since the buffer needs + // to be deleted after the SendDecrypted call. + mPlugin->GMPMessageLoop()->PostTask(NewRunnableMethod + <GMPBuffer*, GMPErr>(this, + &GMPDecryptorChild::Decrypted, + aBuffer, aResult)); + return; + } + + if (!aBuffer) { + NS_WARNING("GMPDecryptorCallback passed bull GMPBuffer"); + return; + } + + auto buffer = static_cast<GMPBufferImpl*>(aBuffer); + if (mSession) { + SendDecrypted(buffer->mId, aResult, buffer->mData); + } + delete buffer; +} + +void +GMPDecryptorChild::SetCapabilities(uint64_t aCaps) +{ + // Deprecated. +} + +void +GMPDecryptorChild::GetSandboxVoucher(const uint8_t** aVoucher, + uint32_t* aVoucherLength) +{ + if (!aVoucher || !aVoucherLength) { + return; + } + *aVoucher = mSandboxVoucher.Elements(); + *aVoucherLength = mSandboxVoucher.Length(); +} + +void +GMPDecryptorChild::GetPluginVoucher(const uint8_t** aVoucher, + uint32_t* aVoucherLength) +{ + if (!aVoucher || !aVoucherLength) { + return; + } + *aVoucher = mPluginVoucher.Elements(); + *aVoucherLength = mPluginVoucher.Length(); +} + +bool +GMPDecryptorChild::RecvInit(const bool& aDistinctiveIdentifierRequired, + const bool& aPersistentStateRequired) +{ + if (!mSession) { + return false; + } + mSession->Init(this, aDistinctiveIdentifierRequired, aPersistentStateRequired); + return true; +} + +bool +GMPDecryptorChild::RecvCreateSession(const uint32_t& aCreateSessionToken, + const uint32_t& aPromiseId, + const nsCString& aInitDataType, + InfallibleTArray<uint8_t>&& aInitData, + const GMPSessionType& aSessionType) +{ + if (!mSession) { + return false; + } + + mSession->CreateSession(aCreateSessionToken, + aPromiseId, + aInitDataType.get(), + aInitDataType.Length(), + aInitData.Elements(), + aInitData.Length(), + aSessionType); + + return true; +} + +bool +GMPDecryptorChild::RecvLoadSession(const uint32_t& aPromiseId, + const nsCString& aSessionId) +{ + if (!mSession) { + return false; + } + + mSession->LoadSession(aPromiseId, + aSessionId.get(), + aSessionId.Length()); + + return true; +} + +bool +GMPDecryptorChild::RecvUpdateSession(const uint32_t& aPromiseId, + const nsCString& aSessionId, + InfallibleTArray<uint8_t>&& aResponse) +{ + if (!mSession) { + return false; + } + + mSession->UpdateSession(aPromiseId, + aSessionId.get(), + aSessionId.Length(), + aResponse.Elements(), + aResponse.Length()); + + return true; +} + +bool +GMPDecryptorChild::RecvCloseSession(const uint32_t& aPromiseId, + const nsCString& aSessionId) +{ + if (!mSession) { + return false; + } + + mSession->CloseSession(aPromiseId, + aSessionId.get(), + aSessionId.Length()); + + return true; +} + +bool +GMPDecryptorChild::RecvRemoveSession(const uint32_t& aPromiseId, + const nsCString& aSessionId) +{ + if (!mSession) { + return false; + } + + mSession->RemoveSession(aPromiseId, + aSessionId.get(), + aSessionId.Length()); + + return true; +} + +bool +GMPDecryptorChild::RecvSetServerCertificate(const uint32_t& aPromiseId, + InfallibleTArray<uint8_t>&& aServerCert) +{ + if (!mSession) { + return false; + } + + mSession->SetServerCertificate(aPromiseId, + aServerCert.Elements(), + aServerCert.Length()); + + return true; +} + +bool +GMPDecryptorChild::RecvDecrypt(const uint32_t& aId, + InfallibleTArray<uint8_t>&& aBuffer, + const GMPDecryptionData& aMetadata) +{ + if (!mSession) { + return false; + } + + // Note: the GMPBufferImpl created here is deleted when the GMP passes + // it back in the Decrypted() callback above. + GMPBufferImpl* buffer = new GMPBufferImpl(aId, aBuffer); + + // |metadata| lifetime is managed by |buffer|. + GMPEncryptedBufferDataImpl* metadata = new GMPEncryptedBufferDataImpl(aMetadata); + buffer->SetMetadata(metadata); + + mSession->Decrypt(buffer, metadata); + return true; +} + +bool +GMPDecryptorChild::RecvDecryptingComplete() +{ + // Reset |mSession| before calling DecryptingComplete(). We should not send + // any IPC messages during tear-down. + auto session = mSession; + mSession = nullptr; + + if (!session) { + return false; + } + + session->DecryptingComplete(); + + Unused << Send__delete__(this); + + return true; +} + +} // namespace gmp +} // namespace mozilla + +// avoid redefined macro in unified build +#undef ON_GMP_THREAD +#undef CALL_ON_GMP_THREAD |