/* 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 "GMPService.h"
#include "GMPUtils.h"
#include "MediaResult.h"
#include "content_decryption_module.h"
#include "mozilla/StaticPrefs_media.h"
#include "mozilla/dom/MediaKeySession.h"
#include "mozilla/dom/MediaKeysBinding.h"
#include "nsPrintfCString.h"
#define NS_DispatchToMainThread(...) CompileError_UseAbstractMainThreadInstead
namespace mozilla {
ChromiumCDMProxy::ChromiumCDMProxy(dom::MediaKeys* aKeys,
const nsAString& aKeySystem,
GMPCrashHelper* aCrashHelper,
bool aDistinctiveIdentifierRequired,
bool aPersistentStateRequired)
: CDMProxy(aKeys, aKeySystem, aDistinctiveIdentifierRequired,
aPersistentStateRequired),
mCrashHelper(aCrashHelper),
mCDMMutex(
"ChromiumCDMProxy"),
mGMPThread(GetGMPThread()) {
MOZ_ASSERT(NS_IsMainThread());
}
ChromiumCDMProxy::~ChromiumCDMProxy() {
EME_LOG(
"ChromiumCDMProxy::~ChromiumCDMProxy(this={})", fmt::ptr(this));
}
void ChromiumCDMProxy::Init(PromiseId aPromiseId,
const nsAString& aOrigin,
const nsAString& aTopLevelOrigin,
const nsAString& aGMPName) {
MOZ_ASSERT(NS_IsMainThread());
RefPtr<GMPCrashHelper> helper(std::move(mCrashHelper));
NS_ENSURE_TRUE_VOID(!mKeys.IsNull());
EME_LOG(
"ChromiumCDMProxy::Init(this={}, pid={}, origin={}, topLevelOrigin={}, "
"gmp={})",
fmt::ptr(this), aPromiseId, NS_ConvertUTF16toUTF8(aOrigin).get(),
NS_ConvertUTF16toUTF8(aTopLevelOrigin).get(),
NS_ConvertUTF16toUTF8(aGMPName).get());
if (!mGMPThread) {
RejectPromiseWithStateError(
aPromiseId,
"Couldn't get GMP thread ChromiumCDMProxy::Init"_ns);
return;
}
if (aGMPName.IsEmpty()) {
RejectPromiseWithStateError(
aPromiseId, nsPrintfCString(
"Unknown GMP for keysystem '%s'",
NS_ConvertUTF16toUTF8(mKeySystem).get()));
return;
}
gmp::NodeIdParts nodeIdParts{nsString(aOrigin), nsString(aTopLevelOrigin),
nsString(aGMPName)};
nsCOMPtr<nsISerialEventTarget> thread = mGMPThread;
RefPtr<ChromiumCDMProxy> self(this);
nsCString keySystem = NS_ConvertUTF16toUTF8(mKeySystem);
RefPtr<Runnable> task(NS_NewRunnableFunction(
"ChromiumCDMProxy::Init",
[self, nodeIdParts = std::move(nodeIdParts), helper = std::move(helper),
aPromiseId, thread = std::move(thread),
keySystem = std::move(keySystem)]() ->
void {
MOZ_ASSERT(self->IsOnOwnerThread());
RefPtr<gmp::GeckoMediaPluginService> service =
gmp::GeckoMediaPluginService::GetGeckoMediaPluginService();
if (!service) {
self->RejectPromiseWithStateError(
aPromiseId,
nsLiteralCString(
"Couldn't get GeckoMediaPluginService in "
"ChromiumCDMProxy::Init"));
return;
}
RefPtr<gmp::GetCDMParentPromise> promise =
service->GetCDM(nodeIdParts, keySystem, helper);
promise->Then(
thread, __func__,
[self, aPromiseId, thread](RefPtr<gmp::ChromiumCDMParent> cdm) {
// service->GetCDM succeeded
self->mCallback =
MakeUnique<ChromiumCDMCallbackProxy>(self, self->mMainThread);
cdm->Init(self->mCallback.get(),
self->mDistinctiveIdentifierRequired,
self->mPersistentStateRequired, self->mMainThread)
->Then(
self->mMainThread, __func__,
[self, aPromiseId, cdm](
bool /* unused */) {
// CDM init succeeded
{
MutexAutoLock lock(self->mCDMMutex);
self->mCDM = cdm;
}
if (self->mIsShutdown) {
self->RejectPromiseWithStateError(
aPromiseId, nsLiteralCString(
"ChromiumCDMProxy shutdown "
"during ChromiumCDMProxy::Init"));
// If shutdown happened while waiting to init, we
// need to explicitly shutdown the CDM to avoid it
// referencing this proxy which is on its way out.
self->ShutdownCDMIfExists();
return;
}
self->OnCDMCreated(aPromiseId);
},
[self, aPromiseId](MediaResult aResult) {
// CDM init failed.
ErrorResult rv;
// XXXbz MediaResult should really store a
// CopyableErrorResult or something. See
// <https://bugzilla.mozilla.org/show_bug.cgi?id=1612216>.
rv.
Throw(aResult.Code());
self->RejectPromise(aPromiseId, std::move(rv),
aResult.Message());
});
},
[self, aPromiseId](MediaResult rv) {
// service->GetCDM failed
ErrorResult result;
// XXXbz MediaResult should really store a CopyableErrorResult or
// something. See
// <https://bugzilla.mozilla.org/show_bug.cgi?id=1612216>.
result.
Throw(rv.Code());
self->RejectPromise(aPromiseId, std::move(result),
rv.Description());
});
}));
mGMPThread->Dispatch(task.forget());
}
void ChromiumCDMProxy::OnCDMCreated(uint32_t aPromiseId) {
EME_LOG(
"ChromiumCDMProxy::OnCDMCreated(this={}, pid={}) isMainThread={}",
fmt::ptr(this), aPromiseId, NS_IsMainThread());
MOZ_ASSERT(NS_IsMainThread());
if (mKeys.IsNull()) {
return;
}
RefPtr<gmp::ChromiumCDMParent> cdm = GetCDMParent();
// This should only be called once the CDM has been created.
MOZ_ASSERT(cdm);
if (cdm) {
mKeys->OnCDMCreated(aPromiseId, cdm->PluginId());
}
else {
// No CDM? Shouldn't be possible, but reject the promise anyway...
constexpr
auto err =
"Null CDM in OnCDMCreated()"_ns;
ErrorResult rv;
rv.ThrowInvalidStateError(err);
mKeys->RejectPromise(aPromiseId, std::move(rv), err);
}
}
void ChromiumCDMProxy::ShutdownCDMIfExists() {
EME_LOG(
"ChromiumCDMProxy::ShutdownCDMIfExists(this={}) mCDM={}, mIsShutdown={}",
fmt::ptr(this), fmt::ptr(mCDM.get()), mIsShutdown ?
"true" :
"false");
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(mGMPThread);
MOZ_ASSERT(mIsShutdown,
"Should only shutdown the CDM if the proxy is shutting down");
RefPtr<gmp::ChromiumCDMParent> cdm;
{
MutexAutoLock lock(mCDMMutex);
cdm.swap(mCDM);
}
if (cdm) {
// We need to keep this proxy alive until the parent has finished its
// Shutdown (as it may still try to use the proxy until then).
RefPtr<ChromiumCDMProxy> self(this);
nsCOMPtr<nsIRunnable> task = NS_NewRunnableFunction(
"ChromiumCDMProxy::Shutdown", [self, cdm]() { cdm->Shutdown(); });
mGMPThread->Dispatch(task.forget());
}
}
#ifdef DEBUG
bool ChromiumCDMProxy::IsOnOwnerThread() {
return mGMPThread && mGMPThread->IsOnCurrentThread();
}
#endif
static cdm::SessionType ToCDMSessionType(
dom::MediaKeySessionType aSessionType) {
switch (aSessionType) {
case dom::MediaKeySessionType::Temporary:
return cdm::SessionType::kTemporary;
case dom::MediaKeySessionType::Persistent_license:
return cdm::SessionType::kPersistentLicense;
default:
return cdm::SessionType::kTemporary;
};
};
static cdm::InitDataType ToCDMInitDataType(
const nsAString& aInitDataType) {
if (aInitDataType.EqualsLiteral(
"cenc")) {
return cdm::InitDataType::kCenc;
}
if (aInitDataType.EqualsLiteral(
"webm")) {
return cdm::InitDataType::kWebM;
}
if (aInitDataType.EqualsLiteral(
"keyids")) {
return cdm::InitDataType::kKeyIds;
}
return cdm::InitDataType::kCenc;
}
void ChromiumCDMProxy::CreateSession(uint32_t aCreateSessionToken,
dom::MediaKeySessionType aSessionType,
PromiseId aPromiseId,
const nsAString& aInitDataType,
nsTArray<uint8_t>& aInitData) {
MOZ_ASSERT(NS_IsMainThread());
EME_LOG(
"ChromiumCDMProxy::CreateSession(this={}, token={}, type={}, pid={}) "
"initDataLen={}",
fmt::ptr(this), aCreateSessionToken, (
int)aSessionType, aPromiseId,
aInitData.Length());
cdm::SessionType sessionType = ToCDMSessionType(aSessionType);
cdm::InitDataType initDataType = ToCDMInitDataType(aInitDataType);
RefPtr<gmp::ChromiumCDMParent> cdm = GetCDMParent();
if (!cdm) {
RejectPromiseWithStateError(aPromiseId,
"Null CDM in CreateSession"_ns);
return;
}
mGMPThread->Dispatch(
NewRunnableMethod<uint32_t, cdm::SessionType, cdm::InitDataType, uint32_t,
nsTArray<uint8_t>>(
"gmp::ChromiumCDMParent::CreateSession", cdm,
&gmp::ChromiumCDMParent::CreateSession, aCreateSessionToken,
sessionType, initDataType, aPromiseId, std::move(aInitData)));
}
void ChromiumCDMProxy::LoadSession(PromiseId aPromiseId,
dom::MediaKeySessionType aSessionType,
const nsAString& aSessionId) {
MOZ_ASSERT(NS_IsMainThread());
RefPtr<gmp::ChromiumCDMParent> cdm = GetCDMParent();
if (!cdm) {
RejectPromiseWithStateError(aPromiseId,
"Null CDM in LoadSession"_ns);
return;
}
mGMPThread->Dispatch(NewRunnableMethod<uint32_t, cdm::SessionType, nsString>(
"gmp::ChromiumCDMParent::LoadSession", cdm,
&gmp::ChromiumCDMParent::LoadSession, aPromiseId,
ToCDMSessionType(aSessionType), aSessionId));
}
void ChromiumCDMProxy::SetServerCertificate(PromiseId aPromiseId,
nsTArray<uint8_t>& aCert) {
MOZ_ASSERT(NS_IsMainThread());
EME_LOG(
"ChromiumCDMProxy::SetServerCertificate(this={}, pid={}) certLen={}",
fmt::ptr(this), aPromiseId, aCert.Length());
RefPtr<gmp::ChromiumCDMParent> cdm = GetCDMParent();
if (!cdm) {
RejectPromiseWithStateError(aPromiseId,
"Null CDM in SetServerCertificate"_ns);
return;
}
mGMPThread->Dispatch(NewRunnableMethod<uint32_t, nsTArray<uint8_t>>(
"gmp::ChromiumCDMParent::SetServerCertificate", cdm,
&gmp::ChromiumCDMParent::SetServerCertificate, aPromiseId,
std::move(aCert)));
}
void ChromiumCDMProxy::UpdateSession(
const nsAString& aSessionId,
PromiseId aPromiseId,
nsTArray<uint8_t>& aResponse) {
MOZ_ASSERT(NS_IsMainThread());
EME_LOG(
"ChromiumCDMProxy::UpdateSession(this={}, sid='{}', pid={}) "
"responseLen={}",
fmt::ptr(this), NS_ConvertUTF16toUTF8(aSessionId).get(), aPromiseId,
aResponse.Length());
RefPtr<gmp::ChromiumCDMParent> cdm = GetCDMParent();
if (!cdm) {
RejectPromiseWithStateError(aPromiseId,
"Null CDM in UpdateSession"_ns);
return;
}
mGMPThread->Dispatch(
NewRunnableMethod<nsCString, uint32_t, nsTArray<uint8_t>>(
"gmp::ChromiumCDMParent::UpdateSession", cdm,
&gmp::ChromiumCDMParent::UpdateSession,
NS_ConvertUTF16toUTF8(aSessionId), aPromiseId, std::move(aResponse)));
}
void ChromiumCDMProxy::CloseSession(
const nsAString& aSessionId,
PromiseId aPromiseId) {
MOZ_ASSERT(NS_IsMainThread());
EME_LOG(
"ChromiumCDMProxy::CloseSession(this={}, sid='{}', pid={})",
fmt::ptr(this), NS_ConvertUTF16toUTF8(aSessionId).get(), aPromiseId);
RefPtr<gmp::ChromiumCDMParent> cdm = GetCDMParent();
if (!cdm) {
RejectPromiseWithStateError(aPromiseId,
"Null CDM in CloseSession"_ns);
return;
}
mGMPThread->Dispatch(NewRunnableMethod<nsCString, uint32_t>(
"gmp::ChromiumCDMParent::CloseSession", cdm,
&gmp::ChromiumCDMParent::CloseSession, NS_ConvertUTF16toUTF8(aSessionId),
aPromiseId));
}
void ChromiumCDMProxy::RemoveSession(
const nsAString& aSessionId,
PromiseId aPromiseId) {
MOZ_ASSERT(NS_IsMainThread());
EME_LOG(
"ChromiumCDMProxy::RemoveSession(this={}, sid='{}', pid={})",
fmt::ptr(this), NS_ConvertUTF16toUTF8(aSessionId).get(), aPromiseId);
RefPtr<gmp::ChromiumCDMParent> cdm = GetCDMParent();
if (!cdm) {
RejectPromiseWithStateError(aPromiseId,
"Null CDM in RemoveSession"_ns);
return;
}
mGMPThread->Dispatch(NewRunnableMethod<nsCString, uint32_t>(
"gmp::ChromiumCDMParent::RemoveSession", cdm,
&gmp::ChromiumCDMParent::RemoveSession, NS_ConvertUTF16toUTF8(aSessionId),
aPromiseId));
}
void ChromiumCDMProxy::QueryOutputProtectionStatus() {
MOZ_ASSERT(NS_IsMainThread());
EME_LOG(
"ChromiumCDMProxy::QueryOutputProtectionStatus(this={})",
fmt::ptr(this));
if (mKeys.IsNull()) {
EME_LOG(
"ChromiumCDMProxy::QueryOutputProtectionStatus(this={}), mKeys "
"missing!",
fmt::ptr(this));
// If we can't get mKeys, we're probably in shutdown. But do our best to
// respond to the request and indicate the check failed.
NotifyOutputProtectionStatus(OutputProtectionCheckStatus::CheckFailed,
OutputProtectionCaptureStatus::Unused);
return;
}
// The keys will call back via `NotifyOutputProtectionStatus` to notify the
// result of the check.
mKeys->CheckIsElementCapturePossible();
}
void ChromiumCDMProxy::NotifyOutputProtectionStatus(
OutputProtectionCheckStatus aCheckStatus,
OutputProtectionCaptureStatus aCaptureStatus) {
MOZ_ASSERT(NS_IsMainThread());
// If the check failed aCaptureStatus should be unused, otherwise not.
MOZ_ASSERT_IF(aCheckStatus == OutputProtectionCheckStatus::CheckFailed,
aCaptureStatus == OutputProtectionCaptureStatus::Unused);
MOZ_ASSERT_IF(aCheckStatus == OutputProtectionCheckStatus::CheckSuccessful,
aCaptureStatus != OutputProtectionCaptureStatus::Unused);
EME_LOG(
"ChromiumCDMProxy::NotifyOutputProtectionStatus(this={}) "
"aCheckStatus={} aCaptureStatus={}",
fmt::ptr(this), static_cast<uint8_t>(aCheckStatus),
static_cast<uint8_t>(aCaptureStatus));
RefPtr<gmp::ChromiumCDMParent> cdm = GetCDMParent();
if (!cdm) {
// If we're in shutdown the CDM may have been cleared while a notification
// is in flight. If this happens outside of shutdown we have a bug.
MOZ_ASSERT(mIsShutdown);
return;
}
uint32_t linkMask{};
uint32_t protectionMask{};
if (aCheckStatus == OutputProtectionCheckStatus::CheckSuccessful &&
aCaptureStatus == OutputProtectionCaptureStatus::CapturePossilbe) {
// The result indicates the capture is possible, so set the mask
// to indicate this.
linkMask |= cdm::OutputLinkTypes::kLinkTypeNetwork;
}
// `kProtectionNone` can cause playback to stop if HDCP_V1 is required. Report
// HDCP protection if there's no potential capturing.
if (linkMask == cdm::OutputLinkTypes::kLinkTypeNone &&
StaticPrefs::media_widevine_hdcp_protection_mask()) {
protectionMask = cdm::OutputProtectionMethods::kProtectionHDCP;
}
mGMPThread->Dispatch(NewRunnableMethod<
bool, uint32_t, uint32_t>(
"gmp::ChromiumCDMParent::NotifyOutputProtectionStatus", cdm,
&gmp::ChromiumCDMParent::NotifyOutputProtectionStatus,
aCheckStatus == OutputProtectionCheckStatus::CheckSuccessful, linkMask,
protectionMask));
}
void ChromiumCDMProxy::Shutdown() {
MOZ_ASSERT(NS_IsMainThread());
EME_LOG(
"ChromiumCDMProxy::Shutdown(this={}) mCDM={}, mIsShutdown={}",
fmt::ptr(this), fmt::ptr(mCDM.get()), mIsShutdown ?
"true" :
"false");
if (mIsShutdown) {
return;
}
mIsShutdown = true;
mKeys.Clear();
ShutdownCDMIfExists();
}
void ChromiumCDMProxy::RejectPromise(PromiseId aId, ErrorResult&& aException,
const nsCString& aReason) {
if (!NS_IsMainThread()) {
// Use CopyableErrorResult to store our exception in the runnable,
// because ErrorResult is not OK to move across threads.
mMainThread->Dispatch(
NewRunnableMethod<PromiseId, StoreCopyPassByRRef<CopyableErrorResult>,
nsCString>(
"ChromiumCDMProxy::RejectPromise", this,
&ChromiumCDMProxy::RejectPromiseOnMainThread, aId,
std::move(aException), aReason),
NS_DISPATCH_NORMAL);
return;
}
EME_LOG(
"ChromiumCDMProxy::RejectPromise(this={}, pid={}, code=0x{:x}, "
"reason='{}')",
fmt::ptr(this), aId, aException.ErrorCodeAsInt(), aReason.get());
if (!mKeys.IsNull()) {
mKeys->RejectPromise(aId, std::move(aException), aReason);
}
else {
// We don't have a MediaKeys object to pass the exception to, so silence
// the exception to avoid it asserting due to being unused.
aException.SuppressException();
}
}
void ChromiumCDMProxy::RejectPromiseWithStateError(PromiseId aId,
const nsCString& aReason) {
ErrorResult rv;
rv.ThrowInvalidStateError(aReason);
RejectPromise(aId, std::move(rv), aReason);
}
void ChromiumCDMProxy::RejectPromiseOnMainThread(
PromiseId aId, CopyableErrorResult&& aException,
const nsCString& aReason) {
// Moving into or out of a non-copyable ErrorResult will assert that both
// ErorResults are from our current thread. Avoid the assertion by moving
// into a current-thread CopyableErrorResult first. Note that this is safe,
// because CopyableErrorResult never holds state that can't move across
// threads.
CopyableErrorResult rv(std::move(aException));
RejectPromise(aId, std::move(rv), aReason);
}
void ChromiumCDMProxy::ResolvePromise(PromiseId aId) {
if (!NS_IsMainThread()) {
mMainThread->Dispatch(
NewRunnableMethod<PromiseId>(
"ChromiumCDMProxy::ResolvePromise", this,
&ChromiumCDMProxy::ResolvePromise, aId),
NS_DISPATCH_NORMAL);
return;
}
EME_LOG(
"ChromiumCDMProxy::ResolvePromise(this={}, pid={})", fmt::ptr(this),
aId);
if (!mKeys.IsNull()) {
mKeys->ResolvePromise(aId);
}
else {
NS_WARNING(
"ChromiumCDMProxy unable to resolve promise!");
}
}
void ChromiumCDMProxy::OnSetSessionId(uint32_t aCreateSessionToken,
const nsAString& aSessionId) {
MOZ_ASSERT(NS_IsMainThread());
EME_LOG(
"ChromiumCDMProxy::OnSetSessionId(this={}, token={}, sid='{}')",
fmt::ptr(this), aCreateSessionToken,
NS_ConvertUTF16toUTF8(aSessionId).get());
if (mKeys.IsNull()) {
return;
}
RefPtr<dom::MediaKeySession> session(
mKeys->GetPendingSession(aCreateSessionToken));
if (session) {
session->SetSessionId(aSessionId);
}
}
void ChromiumCDMProxy::OnResolveLoadSessionPromise(uint32_t aPromiseId,
bool aSuccess) {
MOZ_ASSERT(NS_IsMainThread());
if (mKeys.IsNull()) {
return;
}
mKeys->OnSessionLoaded(aPromiseId, aSuccess);
}
void ChromiumCDMProxy::OnResolvePromiseWithKeyStatus(
uint32_t aPromiseId, dom::MediaKeyStatus aKeyStatus) {
MOZ_ASSERT(NS_IsMainThread());
if (mKeys.IsNull()) {
return;
}
mKeys->ResolvePromiseWithKeyStatus(aPromiseId, aKeyStatus);
}
void ChromiumCDMProxy::OnSessionMessage(
const nsAString& aSessionId,
dom::MediaKeyMessageType aMessageType,
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);
}
}
void ChromiumCDMProxy::OnKeyStatusesChange(
const nsAString& aSessionId) {
MOZ_ASSERT(NS_IsMainThread());
if (mKeys.IsNull()) {
return;
}
RefPtr<dom::MediaKeySession> session(mKeys->GetSession(aSessionId));
if (session) {
session->DispatchKeyStatusesChange();
}
}
void ChromiumCDMProxy::OnExpirationChange(
const nsAString& aSessionId,
UnixTime aExpiryTime) {
MOZ_ASSERT(NS_IsMainThread());
if (mKeys.IsNull()) {
return;
}
RefPtr<dom::MediaKeySession> session(mKeys->GetSession(aSessionId));
if (session) {
// Expiry of 0 is interpreted as "never expire". See bug 1345341.
double t = (aExpiryTime ==
0) ? std::numeric_limits<
double>::quiet_NaN()
: static_cast<
double>(aExpiryTime);
session->SetExpiration(t);
}
}
void ChromiumCDMProxy::OnSessionClosed(
const nsAString& aSessionId, dom::MediaKeySessionClosedReason aReason) {
MOZ_ASSERT(NS_IsMainThread());
bool keyStatusesChange =
false;
{
auto caps = Capabilites().Lock();
keyStatusesChange = caps->RemoveKeysForSession(nsString(aSessionId));
}
if (keyStatusesChange) {
OnKeyStatusesChange(aSessionId);
}
if (mKeys.IsNull()) {
return;
}
RefPtr<dom::MediaKeySession> session(mKeys->GetSession(aSessionId));
if (session) {
session->OnClosed(aReason);
}
}
void ChromiumCDMProxy::OnDecrypted(uint32_t aId, DecryptStatus aResult,
const nsTArray<uint8_t>& aDecryptedData) {}
void ChromiumCDMProxy::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 ChromiumCDMProxy::OnRejectPromise(uint32_t aPromiseId,
ErrorResult&& aException,
const nsCString& aMsg) {
MOZ_ASSERT(NS_IsMainThread());
RejectPromise(aPromiseId, std::move(aException), aMsg);
}
RefPtr<DecryptPromise> ChromiumCDMProxy::Decrypt(MediaRawData* aSample) {
RefPtr<gmp::ChromiumCDMParent> cdm = GetCDMParent();
if (!cdm) {
return DecryptPromise::CreateAndReject(
DecryptResult(eme::AbortedErr, aSample), __func__);
}
RefPtr<MediaRawData> sample = aSample;
return InvokeAsync(mGMPThread, __func__,
[cdm, sample]() {
return cdm->Decrypt(sample); });
}
void ChromiumCDMProxy::GetStatusForPolicy(
PromiseId aPromiseId,
const dom::HDCPVersion& aMinHdcpVersion) {
MOZ_ASSERT(NS_IsMainThread());
EME_LOG(
"ChromiumCDMProxy::GetStatusForPolicy(this={}, pid={}) minHdcpVersion={}",
fmt::ptr(this), aPromiseId, dom::GetEnumString(aMinHdcpVersion).get());
RefPtr<gmp::ChromiumCDMParent> cdm = GetCDMParent();
if (!cdm) {
RejectPromiseWithStateError(aPromiseId,
"Null CDM in GetStatusForPolicy"_ns);
return;
}
mGMPThread->Dispatch(NewRunnableMethod<uint32_t, dom::HDCPVersion>(
"gmp::ChromiumCDMParent::GetStatusForPolicy", cdm,
&gmp::ChromiumCDMParent::GetStatusForPolicy, aPromiseId,
aMinHdcpVersion));
}
void ChromiumCDMProxy::Terminated() {
if (!mKeys.IsNull()) {
mKeys->Terminated();
}
}
already_AddRefed<gmp::ChromiumCDMParent> ChromiumCDMProxy::GetCDMParent() {
MutexAutoLock lock(mCDMMutex);
RefPtr<gmp::ChromiumCDMParent> cdm = mCDM;
return cdm.forget();
}
}
// namespace mozilla
#undef NS_DispatchToMainThread