gecko-dev/dom/media/gmp/GMPDecryptorChild.cpp
Chris Pearce b430a8a481 Bug 1306314 - Add an ID to GMPDecryptor instances, reflect that on CDMProxy. r=gerald
This enables us to identify GMPDecryptor instances in the child process, so that
in a later patch when we create a GMPVideoDecoder instance, we can associate it
with a GMPDecryptor. Then the cdm::ContentDecryptionModule8 instance that these
two actors are adapted to can know whom it's supposed to respond to.

We use the IPDL PGMPDecryptorChild actor ID as the GMPDecryptor's ID. This is unique
per GMP process, which is sufficient.

MozReview-Commit-ID: 7NKND9VjPUW

--HG--
extra : rebase_source : da14d9a8a7313a609e30649af1a23e79b3e401fe
2016-11-11 12:10:43 +13:00

404 lines
12 KiB
C++

/* -*- 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