summaryrefslogtreecommitdiffstats
path: root/dom/media/ipc/MFCDMChild.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'dom/media/ipc/MFCDMChild.cpp')
-rw-r--r--dom/media/ipc/MFCDMChild.cpp468
1 files changed, 468 insertions, 0 deletions
diff --git a/dom/media/ipc/MFCDMChild.cpp b/dom/media/ipc/MFCDMChild.cpp
new file mode 100644
index 0000000000..292d0cd6bd
--- /dev/null
+++ b/dom/media/ipc/MFCDMChild.cpp
@@ -0,0 +1,468 @@
+/* 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 "MFCDMChild.h"
+
+#include "mozilla/EMEUtils.h"
+#include "mozilla/KeySystemConfig.h"
+#include "mozilla/RefPtr.h"
+#include "mozilla/WindowsVersion.h"
+#include "mozilla/WMFCDMProxyCallback.h"
+#include "nsString.h"
+#include "RemoteDecoderManagerChild.h"
+
+namespace mozilla {
+
+#define LOG(msg, ...) \
+ EME_LOG("MFCDMChild[%p]@%s: " msg, this, __func__, ##__VA_ARGS__)
+#define SLOG(msg, ...) EME_LOG("MFCDMChild@%s: " msg, __func__, ##__VA_ARGS__)
+
+MFCDMChild::MFCDMChild(const nsAString& aKeySystem)
+ : mKeySystem(aKeySystem),
+ mManagerThread(RemoteDecoderManagerChild::GetManagerThread()),
+ mState(NS_ERROR_NOT_INITIALIZED),
+ mShutdown(false) {
+ mRemotePromise = EnsureRemote();
+}
+
+MFCDMChild::~MFCDMChild() {}
+
+RefPtr<MFCDMChild::RemotePromise> MFCDMChild::EnsureRemote() {
+ if (!mManagerThread) {
+ LOG("no manager thread");
+ mState = NS_ERROR_NOT_AVAILABLE;
+ return RemotePromise::CreateAndReject(mState, __func__);
+ }
+
+ if (!IsWin10OrLater()) {
+ LOG("only support MF CDM on Windows 10+");
+ mState = NS_ERROR_NOT_AVAILABLE;
+ return RemotePromise::CreateAndReject(mState, __func__);
+ }
+
+ RefPtr<MFCDMChild> self = this;
+ RemoteDecoderManagerChild::LaunchUtilityProcessIfNeeded(
+ RemoteDecodeIn::UtilityProcess_MFMediaEngineCDM)
+ ->Then(
+ mManagerThread, __func__,
+ [self, this](bool) {
+ mRemoteRequest.Complete();
+ RefPtr<RemoteDecoderManagerChild> manager =
+ RemoteDecoderManagerChild::GetSingleton(
+ RemoteDecodeIn::UtilityProcess_MFMediaEngineCDM);
+ if (!manager || !manager->CanSend()) {
+ LOG("manager not exists or can't send");
+ mState = NS_ERROR_NOT_AVAILABLE;
+ mRemotePromiseHolder.RejectIfExists(mState, __func__);
+ return;
+ }
+
+ mIPDLSelfRef = this;
+ Unused << manager->SendPMFCDMConstructor(this, mKeySystem);
+ mState = NS_OK;
+ mRemotePromiseHolder.ResolveIfExists(true, __func__);
+ },
+ [self, this](nsresult rv) {
+ mRemoteRequest.Complete();
+ LOG("fail to launch MFCDM process");
+ mState = rv;
+ mRemotePromiseHolder.RejectIfExists(rv, __func__);
+ })
+ ->Track(mRemoteRequest);
+ return mRemotePromiseHolder.Ensure(__func__);
+}
+
+void MFCDMChild::Shutdown() {
+ MOZ_ASSERT(!mShutdown);
+
+ mShutdown = true;
+ mProxyCallback = nullptr;
+
+ mRemoteRequest.DisconnectIfExists();
+ mInitRequest.DisconnectIfExists();
+
+ if (mState == NS_OK) {
+ mManagerThread->Dispatch(
+ NS_NewRunnableFunction(__func__, [self = RefPtr{this}, this]() {
+ for (auto& promise : mPendingSessionPromises) {
+ promise.second.RejectIfExists(NS_ERROR_ABORT, __func__);
+ }
+ mPendingSessionPromises.clear();
+
+ for (auto& promise : mPendingGenericPromises) {
+ promise.second.RejectIfExists(NS_ERROR_ABORT, __func__);
+ }
+ mPendingGenericPromises.clear();
+
+ mRemotePromiseHolder.RejectIfExists(NS_ERROR_ABORT, __func__);
+ mCapabilitiesPromiseHolder.RejectIfExists(NS_ERROR_ABORT, __func__);
+
+ Send__delete__(this);
+ }));
+ }
+}
+
+RefPtr<MFCDMChild::CapabilitiesPromise> MFCDMChild::GetCapabilities() {
+ MOZ_ASSERT(mManagerThread);
+
+ if (mShutdown) {
+ return CapabilitiesPromise::CreateAndReject(NS_ERROR_ABORT, __func__);
+ }
+
+ if (mState != NS_OK && mState != NS_ERROR_NOT_INITIALIZED) {
+ LOG("error=%x", nsresult(mState));
+ return CapabilitiesPromise::CreateAndReject(mState, __func__);
+ }
+
+ auto doSend = [self = RefPtr{this}, this]() {
+ SendGetCapabilities()->Then(
+ mManagerThread, __func__,
+ [self, this](MFCDMCapabilitiesResult&& aResult) {
+ if (aResult.type() == MFCDMCapabilitiesResult::Tnsresult) {
+ mCapabilitiesPromiseHolder.RejectIfExists(aResult.get_nsresult(),
+ __func__);
+ return;
+ }
+ mCapabilitiesPromiseHolder.ResolveIfExists(
+ std::move(aResult.get_MFCDMCapabilitiesIPDL()), __func__);
+ },
+ [self, this](const mozilla::ipc::ResponseRejectReason& aReason) {
+ mCapabilitiesPromiseHolder.RejectIfExists(NS_ERROR_FAILURE, __func__);
+ });
+ };
+
+ return InvokeAsync(doSend, __func__, mCapabilitiesPromiseHolder);
+}
+
+// Neither error nor shutdown.
+void MFCDMChild::AssertSendable() {
+ MOZ_ASSERT((mState == NS_ERROR_NOT_INITIALIZED || mState == NS_OK) &&
+ mShutdown == false);
+}
+
+template <typename PromiseType>
+already_AddRefed<PromiseType> MFCDMChild::InvokeAsync(
+ std::function<void()>&& aCall, const char* aCallerName,
+ MozPromiseHolder<PromiseType>& aPromise) {
+ AssertSendable();
+
+ if (mState == NS_OK) {
+ // mRemotePromise is resolved, send on manager thread.
+ mManagerThread->Dispatch(
+ NS_NewRunnableFunction(aCallerName, std::move(aCall)));
+ } else if (mState == NS_ERROR_NOT_INITIALIZED) {
+ // mRemotePromise is pending, chain to it.
+ mRemotePromise->Then(
+ mManagerThread, __func__, std::move(aCall),
+ [self = RefPtr{this}, this, &aPromise, aCallerName](nsresult rv) {
+ LOG("error=%x", rv);
+ mState = rv;
+ aPromise.RejectIfExists(rv, aCallerName);
+ });
+ }
+
+ return aPromise.Ensure(aCallerName);
+}
+
+RefPtr<MFCDMChild::InitPromise> MFCDMChild::Init(
+ const nsAString& aOrigin, const CopyableTArray<nsString>& aInitDataTypes,
+ const KeySystemConfig::Requirement aPersistentState,
+ const KeySystemConfig::Requirement aDistinctiveID, const bool aHWSecure,
+ WMFCDMProxyCallback* aProxyCallback) {
+ MOZ_ASSERT(mManagerThread);
+
+ if (mShutdown) {
+ return InitPromise::CreateAndReject(NS_ERROR_ABORT, __func__);
+ }
+
+ if (mState != NS_OK && mState != NS_ERROR_NOT_INITIALIZED) {
+ LOG("error=%x", nsresult(mState));
+ return InitPromise::CreateAndReject(mState, __func__);
+ }
+
+ mProxyCallback = aProxyCallback;
+ MFCDMInitParamsIPDL params{nsString(aOrigin), aInitDataTypes, aDistinctiveID,
+ aPersistentState, aHWSecure};
+ auto doSend = [self = RefPtr{this}, this, params]() {
+ SendInit(params)
+ ->Then(
+ mManagerThread, __func__,
+ [self, this](MFCDMInitResult&& aResult) {
+ mInitRequest.Complete();
+ if (aResult.type() == MFCDMInitResult::Tnsresult) {
+ nsresult rv = aResult.get_nsresult();
+ mInitPromiseHolder.RejectIfExists(rv, __func__);
+ return;
+ }
+ mId = aResult.get_MFCDMInitIPDL().id();
+ mInitPromiseHolder.ResolveIfExists(aResult.get_MFCDMInitIPDL(),
+ __func__);
+ },
+ [self, this](const mozilla::ipc::ResponseRejectReason& aReason) {
+ mInitRequest.Complete();
+ mInitPromiseHolder.RejectIfExists(NS_ERROR_FAILURE, __func__);
+ })
+ ->Track(mInitRequest);
+ };
+
+ return InvokeAsync(std::move(doSend), __func__, mInitPromiseHolder);
+}
+
+RefPtr<MFCDMChild::SessionPromise> MFCDMChild::CreateSessionAndGenerateRequest(
+ uint32_t aPromiseId, KeySystemConfig::SessionType aSessionType,
+ const nsAString& aInitDataType, const nsTArray<uint8_t>& aInitData) {
+ MOZ_ASSERT(mManagerThread);
+ MOZ_ASSERT(mId > 0, "Should call Init() first and wait for it");
+
+ if (mShutdown) {
+ return MFCDMChild::SessionPromise::CreateAndReject(NS_ERROR_ABORT,
+ __func__);
+ }
+
+ MOZ_ASSERT(mPendingSessionPromises.find(aPromiseId) ==
+ mPendingSessionPromises.end());
+ mPendingSessionPromises.emplace(aPromiseId,
+ MozPromiseHolder<SessionPromise>{});
+ mManagerThread->Dispatch(NS_NewRunnableFunction(
+ __func__, [self = RefPtr{this}, this,
+ params =
+ MFCDMCreateSessionParamsIPDL{
+ aSessionType, nsString{aInitDataType}, aInitData},
+ aPromiseId] {
+ SendCreateSessionAndGenerateRequest(params)->Then(
+ mManagerThread, __func__,
+ [self, aPromiseId, this](const MFCDMSessionResult& result) {
+ auto iter = mPendingSessionPromises.find(aPromiseId);
+ if (iter == mPendingSessionPromises.end()) {
+ return;
+ }
+ auto& promiseHolder = iter->second;
+ if (result.type() == MFCDMSessionResult::Tnsresult) {
+ promiseHolder.RejectIfExists(result.get_nsresult(), __func__);
+ } else {
+ LOG("session ID=[%zu]%s", result.get_nsString().Length(),
+ NS_ConvertUTF16toUTF8(result.get_nsString()).get());
+ promiseHolder.ResolveIfExists(result.get_nsString(), __func__);
+ }
+ mPendingSessionPromises.erase(iter);
+ },
+ [self, aPromiseId,
+ this](const mozilla::ipc::ResponseRejectReason& aReason) {
+ auto iter = mPendingSessionPromises.find(aPromiseId);
+ if (iter == mPendingSessionPromises.end()) {
+ return;
+ }
+ auto& promiseHolder = iter->second;
+ promiseHolder.RejectIfExists(NS_ERROR_FAILURE, __func__);
+ mPendingSessionPromises.erase(iter);
+ });
+ }));
+ return mPendingSessionPromises[aPromiseId].Ensure(__func__);
+}
+
+RefPtr<GenericPromise> MFCDMChild::LoadSession(
+ uint32_t aPromiseId, const KeySystemConfig::SessionType aSessionType,
+ const nsAString& aSessionId) {
+ MOZ_ASSERT(mManagerThread);
+ MOZ_ASSERT(mId > 0, "Should call Init() first and wait for it");
+
+ if (mShutdown) {
+ return GenericPromise::CreateAndReject(NS_ERROR_ABORT, __func__);
+ }
+
+ MOZ_ASSERT(mPendingGenericPromises.find(aPromiseId) ==
+ mPendingGenericPromises.end());
+ mPendingGenericPromises.emplace(aPromiseId,
+ MozPromiseHolder<GenericPromise>{});
+ mManagerThread->Dispatch(NS_NewRunnableFunction(
+ __func__, [self = RefPtr{this}, this, aSessionType,
+ sessionId = nsString{aSessionId}, aPromiseId] {
+ SendLoadSession(aSessionType, sessionId)
+ ->Then(mManagerThread, __func__,
+ [self, this, aPromiseId](
+ PMFCDMChild::LoadSessionPromise::ResolveOrRejectValue&&
+ aResult) {
+ auto iter = mPendingGenericPromises.find(aPromiseId);
+ if (iter == mPendingGenericPromises.end()) {
+ return;
+ }
+ auto& promiseHolder = iter->second;
+ if (aResult.IsResolve()) {
+ if (NS_SUCCEEDED(aResult.ResolveValue())) {
+ promiseHolder.ResolveIfExists(true, __func__);
+ } else {
+ promiseHolder.RejectIfExists(aResult.ResolveValue(),
+ __func__);
+ }
+ } else {
+ // IPC died
+ promiseHolder.RejectIfExists(NS_ERROR_FAILURE, __func__);
+ }
+ mPendingGenericPromises.erase(iter);
+ });
+ }));
+ return mPendingGenericPromises[aPromiseId].Ensure(__func__);
+}
+
+RefPtr<GenericPromise> MFCDMChild::UpdateSession(uint32_t aPromiseId,
+ const nsAString& aSessionId,
+ nsTArray<uint8_t>& aResponse) {
+ MOZ_ASSERT(mManagerThread);
+ MOZ_ASSERT(mId > 0, "Should call Init() first and wait for it");
+
+ if (mShutdown) {
+ return GenericPromise::CreateAndReject(NS_ERROR_ABORT, __func__);
+ }
+
+ MOZ_ASSERT(mPendingGenericPromises.find(aPromiseId) ==
+ mPendingGenericPromises.end());
+ mPendingGenericPromises.emplace(aPromiseId,
+ MozPromiseHolder<GenericPromise>{});
+ mManagerThread->Dispatch(NS_NewRunnableFunction(
+ __func__, [self = RefPtr{this}, this, sessionId = nsString{aSessionId},
+ response = std::move(aResponse), aPromiseId] {
+ SendUpdateSession(sessionId, response)
+ ->Then(mManagerThread, __func__,
+ [self, this, aPromiseId](
+ PMFCDMChild::UpdateSessionPromise::ResolveOrRejectValue&&
+ aResult) {
+ auto iter = mPendingGenericPromises.find(aPromiseId);
+ if (iter == mPendingGenericPromises.end()) {
+ return;
+ }
+ auto& promiseHolder = iter->second;
+ if (aResult.IsResolve()) {
+ if (NS_SUCCEEDED(aResult.ResolveValue())) {
+ promiseHolder.ResolveIfExists(true, __func__);
+ } else {
+ promiseHolder.RejectIfExists(aResult.ResolveValue(),
+ __func__);
+ }
+ } else {
+ // IPC died
+ promiseHolder.RejectIfExists(NS_ERROR_FAILURE, __func__);
+ }
+ mPendingGenericPromises.erase(iter);
+ });
+ }));
+ return mPendingGenericPromises[aPromiseId].Ensure(__func__);
+}
+
+RefPtr<GenericPromise> MFCDMChild::CloseSession(uint32_t aPromiseId,
+ const nsAString& aSessionId) {
+ MOZ_ASSERT(mManagerThread);
+ MOZ_ASSERT(mId > 0, "Should call Init() first and wait for it");
+
+ if (mShutdown) {
+ return GenericPromise::CreateAndReject(NS_ERROR_ABORT, __func__);
+ }
+
+ MOZ_ASSERT(mPendingGenericPromises.find(aPromiseId) ==
+ mPendingGenericPromises.end());
+ mPendingGenericPromises.emplace(aPromiseId,
+ MozPromiseHolder<GenericPromise>{});
+ mManagerThread->Dispatch(NS_NewRunnableFunction(
+ __func__, [self = RefPtr{this}, this, sessionId = nsString{aSessionId},
+ aPromiseId] {
+ SendCloseSession(sessionId)->Then(
+ mManagerThread, __func__,
+ [self, this, aPromiseId](
+ PMFCDMChild::CloseSessionPromise::ResolveOrRejectValue&&
+ aResult) {
+ auto iter = mPendingGenericPromises.find(aPromiseId);
+ if (iter == mPendingGenericPromises.end()) {
+ return;
+ }
+ auto& promiseHolder = iter->second;
+ if (aResult.IsResolve()) {
+ if (NS_SUCCEEDED(aResult.ResolveValue())) {
+ promiseHolder.ResolveIfExists(true, __func__);
+ } else {
+ promiseHolder.RejectIfExists(aResult.ResolveValue(),
+ __func__);
+ }
+ } else {
+ // IPC died
+ promiseHolder.RejectIfExists(NS_ERROR_FAILURE, __func__);
+ }
+ mPendingGenericPromises.erase(iter);
+ });
+ }));
+ return mPendingGenericPromises[aPromiseId].Ensure(__func__);
+}
+
+RefPtr<GenericPromise> MFCDMChild::RemoveSession(uint32_t aPromiseId,
+ const nsAString& aSessionId) {
+ MOZ_ASSERT(mManagerThread);
+ MOZ_ASSERT(mId > 0, "Should call Init() first and wait for it");
+
+ if (mShutdown) {
+ return GenericPromise::CreateAndReject(NS_ERROR_ABORT, __func__);
+ }
+
+ MOZ_ASSERT(mPendingGenericPromises.find(aPromiseId) ==
+ mPendingGenericPromises.end());
+ mPendingGenericPromises.emplace(aPromiseId,
+ MozPromiseHolder<GenericPromise>{});
+ mManagerThread->Dispatch(NS_NewRunnableFunction(
+ __func__, [self = RefPtr{this}, this, sessionId = nsString{aSessionId},
+ aPromiseId] {
+ SendRemoveSession(sessionId)->Then(
+ mManagerThread, __func__,
+ [self, this, aPromiseId](
+ PMFCDMChild::RemoveSessionPromise::ResolveOrRejectValue&&
+ aResult) {
+ auto iter = mPendingGenericPromises.find(aPromiseId);
+ if (iter == mPendingGenericPromises.end()) {
+ return;
+ }
+ auto& promiseHolder = iter->second;
+ if (aResult.IsResolve()) {
+ if (NS_SUCCEEDED(aResult.ResolveValue())) {
+ promiseHolder.ResolveIfExists(true, __func__);
+ } else {
+ promiseHolder.RejectIfExists(aResult.ResolveValue(),
+ __func__);
+ }
+ } else {
+ // IPC died
+ promiseHolder.RejectIfExists(NS_ERROR_FAILURE, __func__);
+ }
+ mPendingGenericPromises.erase(iter);
+ });
+ }));
+ return mPendingGenericPromises[aPromiseId].Ensure(__func__);
+}
+
+mozilla::ipc::IPCResult MFCDMChild::RecvOnSessionKeyMessage(
+ const MFCDMKeyMessage& aMessage) {
+ LOG("RecvOnSessionKeyMessage, sessionId=%s",
+ NS_ConvertUTF16toUTF8(aMessage.sessionId()).get());
+ MOZ_ASSERT(mProxyCallback);
+ mProxyCallback->OnSessionMessage(aMessage);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult MFCDMChild::RecvOnSessionKeyStatusesChanged(
+ const MFCDMKeyStatusChange& aKeyStatuses) {
+ LOG("RecvOnSessionKeyStatusesChanged, sessionId=%s",
+ NS_ConvertUTF16toUTF8(aKeyStatuses.sessionId()).get());
+ MOZ_ASSERT(mProxyCallback);
+ mProxyCallback->OnSessionKeyStatusesChange(aKeyStatuses);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult MFCDMChild::RecvOnSessionKeyExpiration(
+ const MFCDMKeyExpiration& aExpiration) {
+ LOG("RecvOnSessionKeyExpiration, sessionId=%s",
+ NS_ConvertUTF16toUTF8(aExpiration.sessionId()).get());
+ MOZ_ASSERT(mProxyCallback);
+ mProxyCallback->OnSessionKeyExpiration(aExpiration);
+ return IPC_OK();
+}
+
+#undef SLOG
+#undef LOG
+
+} // namespace mozilla