diff options
Diffstat (limited to 'dom/media/eme/mediafoundation/WMFCDMProxy.cpp')
-rw-r--r-- | dom/media/eme/mediafoundation/WMFCDMProxy.cpp | 306 |
1 files changed, 306 insertions, 0 deletions
diff --git a/dom/media/eme/mediafoundation/WMFCDMProxy.cpp b/dom/media/eme/mediafoundation/WMFCDMProxy.cpp new file mode 100644 index 0000000000..1d691168d8 --- /dev/null +++ b/dom/media/eme/mediafoundation/WMFCDMProxy.cpp @@ -0,0 +1,306 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim:set ts=2 sw=2 sts=2 et cindent: */ +/* 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 "WMFCDMProxy.h" + +#include "mozilla/dom/MediaKeySession.h" +#include "mozilla/WMFCDMProxyCallback.h" +#include "WMFCDMImpl.h" +#include "WMFCDMProxyCallback.h" + +namespace mozilla { + +#define LOG(msg, ...) \ + EME_LOG("WMFCDMProxy[%p]@%s: " msg, this, __func__, ##__VA_ARGS__) + +#define RETURN_IF_SHUTDOWN() \ + do { \ + MOZ_ASSERT(NS_IsMainThread()); \ + if (mIsShutdown) { \ + return; \ + } \ + } while (false) + +#define PERFORM_ON_CDM(operation, promiseId, ...) \ + do { \ + mCDM->operation(promiseId, __VA_ARGS__) \ + ->Then( \ + mMainThread, __func__, \ + [self = RefPtr{this}, this, promiseId]() { \ + RETURN_IF_SHUTDOWN(); \ + if (mKeys.IsNull()) { \ + EME_LOG("WMFCDMProxy(this=%p, pid=%" PRIu32 \ + ") : abort the " #operation " due to empty key", \ + this, promiseId); \ + return; \ + } \ + ResolvePromise(promiseId); \ + }, \ + [self = RefPtr{this}, this, promiseId]() { \ + RETURN_IF_SHUTDOWN(); \ + RejectPromiseWithStateError( \ + promiseId, nsLiteralCString("WMFCDMProxy::" #operation ": " \ + "failed to " #operation)); \ + }); \ + } while (false) + +WMFCDMProxy::WMFCDMProxy(dom::MediaKeys* aKeys, const nsAString& aKeySystem, + const dom::MediaKeySystemConfiguration& aConfig) + : CDMProxy( + aKeys, aKeySystem, + aConfig.mDistinctiveIdentifier == dom::MediaKeysRequirement::Required, + aConfig.mPersistentState == dom::MediaKeysRequirement::Required), + mConfig(aConfig) { + MOZ_ASSERT(NS_IsMainThread()); +} + +WMFCDMProxy::~WMFCDMProxy() {} + +void WMFCDMProxy::Init(PromiseId aPromiseId, const nsAString& aOrigin, + const nsAString& aTopLevelOrigin, + const nsAString& aName) { + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(!aOrigin.IsEmpty()); + + MOZ_ASSERT(!mOwnerThread); + if (NS_FAILED( + NS_NewNamedThread("WMFCDMThread", getter_AddRefs(mOwnerThread)))) { + RejectPromiseWithStateError( + aPromiseId, + nsLiteralCString("WMFCDMProxy::Init: couldn't create CDM thread")); + return; + } + + mCDM = MakeRefPtr<WMFCDMImpl>(mKeySystem); + mProxyCallback = new WMFCDMProxyCallback(this); + WMFCDMImpl::InitParams params{nsString(aOrigin), + mConfig.mInitDataTypes, + mPersistentStateRequired, + mDistinctiveIdentifierRequired, + false /* TODO : support HW secure */, + mProxyCallback}; + mCDM->Init(params)->Then( + mMainThread, __func__, + [self = RefPtr{this}, this, aPromiseId](const bool) { + MOZ_ASSERT(mCDM->Id() > 0); + mKeys->OnCDMCreated(aPromiseId, mCDM->Id()); + }, + [self = RefPtr{this}, this, aPromiseId](const nsresult rv) { + RejectPromiseWithStateError( + aPromiseId, + nsLiteralCString("WMFCDMProxy::Init: WMFCDM init error")); + }); +} + +void WMFCDMProxy::ResolvePromise(PromiseId aId) { + auto resolve = [self = RefPtr{this}, this, aId]() { + RETURN_IF_SHUTDOWN(); + EME_LOG("WMFCDMProxy::ResolvePromise(this=%p, pid=%" PRIu32 ")", this, aId); + if (!mKeys.IsNull()) { + mKeys->ResolvePromise(aId); + } else { + NS_WARNING("WMFCDMProxy unable to resolve promise!"); + } + }; + + if (NS_IsMainThread()) { + resolve(); + return; + } + mMainThread->Dispatch( + NS_NewRunnableFunction("WMFCDMProxy::ResolvePromise", resolve)); +} + +void WMFCDMProxy::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>("WMFCDMProxy::RejectPromise", this, + &WMFCDMProxy::RejectPromiseOnMainThread, + aId, std::move(aException), aReason), + NS_DISPATCH_NORMAL); + return; + } + EME_LOG("WMFCDMProxy::RejectPromise(this=%p, pid=%" PRIu32 + ", code=0x%x, " + "reason='%s')", + 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 WMFCDMProxy::RejectPromiseOnMainThread(PromiseId aId, + CopyableErrorResult&& aException, + const nsCString& aReason) { + RETURN_IF_SHUTDOWN(); + // 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 WMFCDMProxy::RejectPromiseWithStateError(PromiseId aId, + const nsCString& aReason) { + ErrorResult rv; + rv.ThrowInvalidStateError(aReason); + RejectPromise(aId, std::move(rv), aReason); +} + +void WMFCDMProxy::CreateSession(uint32_t aCreateSessionToken, + MediaKeySessionType aSessionType, + PromiseId aPromiseId, + const nsAString& aInitDataType, + nsTArray<uint8_t>& aInitData) { + MOZ_ASSERT(NS_IsMainThread()); + RETURN_IF_SHUTDOWN(); + const auto sessionType = ConvertToKeySystemConfigSessionType(aSessionType); + EME_LOG("WMFCDMProxy::CreateSession(this=%p, pid=%" PRIu32 + "), sessionType=%s", + this, aPromiseId, SessionTypeToStr(sessionType)); + mCDM->CreateSession(aPromiseId, sessionType, aInitDataType, aInitData) + ->Then( + mMainThread, __func__, + [self = RefPtr{this}, this, aCreateSessionToken, + aPromiseId](nsString sessionID) { + RETURN_IF_SHUTDOWN(); + if (mKeys.IsNull()) { + EME_LOG("WMFCDMProxy(this=%p, pid=%" PRIu32 + ") : abort the create session due to " + "empty key", + this, aPromiseId); + return; + } + if (RefPtr<dom::MediaKeySession> session = + mKeys->GetPendingSession(aCreateSessionToken)) { + session->SetSessionId(std::move(sessionID)); + } + ResolvePromise(aPromiseId); + }, + [self = RefPtr{this}, this, aPromiseId]() { + RETURN_IF_SHUTDOWN(); + RejectPromiseWithStateError( + aPromiseId, + nsLiteralCString( + "WMFCDMProxy::CreateSession: cannot create session")); + }); +} + +void WMFCDMProxy::LoadSession(PromiseId aPromiseId, + dom::MediaKeySessionType aSessionType, + const nsAString& aSessionId) { + MOZ_ASSERT(NS_IsMainThread()); + RETURN_IF_SHUTDOWN(); + const auto sessionType = ConvertToKeySystemConfigSessionType(aSessionType); + EME_LOG("WMFCDMProxy::LoadSession(this=%p, pid=%" PRIu32 + "), sessionType=%s, sessionId=%s", + this, aPromiseId, SessionTypeToStr(sessionType), + NS_ConvertUTF16toUTF8(aSessionId).get()); + PERFORM_ON_CDM(LoadSession, aPromiseId, sessionType, aSessionId); +} + +void WMFCDMProxy::UpdateSession(const nsAString& aSessionId, + PromiseId aPromiseId, + nsTArray<uint8_t>& aResponse) { + MOZ_ASSERT(NS_IsMainThread()); + RETURN_IF_SHUTDOWN(); + EME_LOG("WMFCDMProxy::UpdateSession(this=%p, pid=%" PRIu32 + "), sessionId=%s, responseLen=%zu", + this, aPromiseId, NS_ConvertUTF16toUTF8(aSessionId).get(), + aResponse.Length()); + PERFORM_ON_CDM(UpdateSession, aPromiseId, aSessionId, aResponse); +} + +void WMFCDMProxy::CloseSession(const nsAString& aSessionId, + PromiseId aPromiseId) { + MOZ_ASSERT(NS_IsMainThread()); + RETURN_IF_SHUTDOWN(); + EME_LOG("WMFCDMProxy::CloseSession(this=%p, pid=%" PRIu32 "), sessionId=%s", + this, aPromiseId, NS_ConvertUTF16toUTF8(aSessionId).get()); + PERFORM_ON_CDM(CloseSession, aPromiseId, aSessionId); +} + +void WMFCDMProxy::RemoveSession(const nsAString& aSessionId, + PromiseId aPromiseId) { + MOZ_ASSERT(NS_IsMainThread()); + RETURN_IF_SHUTDOWN(); + EME_LOG("WMFCDMProxy::RemoveSession(this=%p, pid=%" PRIu32 "), sessionId=%s", + this, aPromiseId, NS_ConvertUTF16toUTF8(aSessionId).get()); + PERFORM_ON_CDM(RemoveSession, aPromiseId, aSessionId); +} + +void WMFCDMProxy::Shutdown() { + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(!mIsShutdown); + if (mProxyCallback) { + mProxyCallback->Shutdown(); + mProxyCallback = nullptr; + } + mIsShutdown = true; +} + +void WMFCDMProxy::OnSessionMessage(const nsAString& aSessionId, + dom::MediaKeyMessageType aMessageType, + const nsTArray<uint8_t>& aMessage) { + MOZ_ASSERT(NS_IsMainThread()); + RETURN_IF_SHUTDOWN(); + if (mKeys.IsNull()) { + return; + } + if (RefPtr<dom::MediaKeySession> session = mKeys->GetSession(aSessionId)) { + LOG("Notify key message for session Id=%s", + NS_ConvertUTF16toUTF8(aSessionId).get()); + session->DispatchKeyMessage(aMessageType, aMessage); + } +} + +void WMFCDMProxy::OnKeyStatusesChange(const nsAString& aSessionId) { + MOZ_ASSERT(NS_IsMainThread()); + RETURN_IF_SHUTDOWN(); + if (mKeys.IsNull()) { + return; + } + if (RefPtr<dom::MediaKeySession> session = mKeys->GetSession(aSessionId)) { + LOG("Notify key statuses for session Id=%s", + NS_ConvertUTF16toUTF8(aSessionId).get()); + session->DispatchKeyStatusesChange(); + } +} + +void WMFCDMProxy::OnExpirationChange(const nsAString& aSessionId, + UnixTime aExpiryTime) { + MOZ_ASSERT(NS_IsMainThread()); + RETURN_IF_SHUTDOWN(); + if (mKeys.IsNull()) { + return; + } + if (RefPtr<dom::MediaKeySession> session = mKeys->GetSession(aSessionId)) { + LOG("Notify expiration for session Id=%s", + NS_ConvertUTF16toUTF8(aSessionId).get()); + session->SetExpiration(static_cast<double>(aExpiryTime)); + } +} + +uint64_t WMFCDMProxy::GetCDMProxyId() const { + MOZ_DIAGNOSTIC_ASSERT(mCDM); + return mCDM->Id(); +} + +#undef LOG +#undef RETURN_IF_SHUTDOWN +#undef PERFORM_ON_CDM + +} // namespace mozilla |