Bug 1315850 - Handle sending and receiving key session messages. r=gerald
MozReview-Commit-ID: 6hLPFLFRD5I
--- a/dom/media/gmp/ChromiumCDMParent.cpp
+++ b/dom/media/gmp/ChromiumCDMParent.cpp
@@ -3,16 +3,18 @@
* 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 "ChromiumCDMParent.h"
#include "mozilla/gmp/GMPTypes.h"
#include "GMPContentChild.h"
#include "mozilla/Unused.h"
#include "ChromiumCDMProxy.h"
+#include "mozilla/dom/MediaKeyMessageEventBinding.h"
+#include "content_decryption_module.h"
namespace mozilla {
namespace gmp {
ChromiumCDMParent::ChromiumCDMParent(GMPContentParent* aContentParent,
uint32_t aPluginId)
: mPluginId(aPluginId)
, mContentParent(aContentParent)
@@ -23,79 +25,272 @@ bool
ChromiumCDMParent::Init(ChromiumCDMProxy* aProxy,
bool aAllowDistinctiveIdentifier,
bool aAllowPersistentState)
{
mProxy = aProxy;
return SendInit(aAllowDistinctiveIdentifier, aAllowPersistentState);
}
+void
+ChromiumCDMParent::SetServerCertificate(uint32_t aPromiseId,
+ const nsTArray<uint8_t>& aCert)
+{
+ if (!SendSetServerCertificate(aPromiseId, aCert)) {
+ RejectPromise(
+ aPromiseId,
+ NS_ERROR_DOM_INVALID_STATE_ERR,
+ NS_LITERAL_CSTRING("Failed to send setServerCertificate to CDM process"));
+ }
+}
+
+void
+ChromiumCDMParent::UpdateSession(const nsCString& aSessionId,
+ uint32_t aPromiseId,
+ const nsTArray<uint8_t>& aResponse)
+{
+ if (!SendUpdateSession(aPromiseId, aSessionId, aResponse)) {
+ RejectPromise(
+ aPromiseId,
+ NS_ERROR_DOM_INVALID_STATE_ERR,
+ NS_LITERAL_CSTRING("Failed to send updateSession to CDM process"));
+ }
+}
+
+void
+ChromiumCDMParent::CloseSession(const nsCString& aSessionId,
+ uint32_t aPromiseId)
+{
+ if (!SendCloseSession(aPromiseId, aSessionId)) {
+ RejectPromise(
+ aPromiseId,
+ NS_ERROR_DOM_INVALID_STATE_ERR,
+ NS_LITERAL_CSTRING("Failed to send closeSession to CDM process"));
+ }
+}
+
+void
+ChromiumCDMParent::RemoveSession(const nsCString& aSessionId,
+ uint32_t aPromiseId)
+{
+ if (!SendRemoveSession(aPromiseId, aSessionId)) {
+ RejectPromise(
+ aPromiseId,
+ NS_ERROR_DOM_INVALID_STATE_ERR,
+ NS_LITERAL_CSTRING("Failed to send removeSession to CDM process"));
+ }
+}
+
ipc::IPCResult
ChromiumCDMParent::Recv__delete__()
{
return IPC_OK();
}
ipc::IPCResult
ChromiumCDMParent::RecvOnResolveNewSessionPromise(const uint32_t& aPromiseId,
const nsCString& aSessionId)
{
return IPC_OK();
}
ipc::IPCResult
ChromiumCDMParent::RecvOnResolvePromise(const uint32_t& aPromiseId)
{
+ if (!mProxy) {
+ return IPC_OK();
+ }
+ NS_DispatchToMainThread(NewRunnableMethod<uint32_t>(
+ mProxy, &ChromiumCDMProxy::ResolvePromise, aPromiseId));
return IPC_OK();
}
+static nsresult
+ToNsresult(uint32_t aError)
+{
+ switch (static_cast<cdm::Error>(aError)) {
+ case cdm::kNotSupportedError:
+ return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
+ case cdm::kInvalidStateError:
+ return NS_ERROR_DOM_INVALID_STATE_ERR;
+ case cdm::kInvalidAccessError:
+ // Note: Chrome converts kInvalidAccessError to TypeError, since the
+ // Chromium CDM API doesn't have a type error enum value. The EME spec
+ // requires TypeError in some places, so we do the same conversion.
+ // See bug 1313202.
+ return NS_ERROR_DOM_TYPE_ERR;
+ case cdm::kQuotaExceededError:
+ return NS_ERROR_DOM_QUOTA_EXCEEDED_ERR;
+ case cdm::kUnknownError:
+ return NS_ERROR_DOM_UNKNOWN_ERR; // Note: Unique placeholder.
+ case cdm::kClientError:
+ return NS_ERROR_DOM_ABORT_ERR; // Note: Unique placeholder.
+ case cdm::kOutputError:
+ return NS_ERROR_DOM_SECURITY_ERR; // Note: Unique placeholder.
+ };
+ MOZ_ASSERT_UNREACHABLE("Invalid cdm::Error enum value.");
+ return NS_ERROR_DOM_TIMEOUT_ERR; // Note: Unique placeholder.
+}
+
+void
+ChromiumCDMParent::RejectPromise(uint32_t aPromiseId,
+ nsresult aError,
+ const nsCString& aErrorMessage)
+{
+ if (!mProxy) {
+ return;
+ }
+ NS_DispatchToMainThread(NewRunnableMethod<uint32_t, nsresult, nsCString>(
+ mProxy,
+ &ChromiumCDMProxy::RejectPromise,
+ aPromiseId,
+ aError,
+ aErrorMessage));
+}
+
ipc::IPCResult
ChromiumCDMParent::RecvOnRejectPromise(const uint32_t& aPromiseId,
const uint32_t& aError,
const uint32_t& aSystemCode,
const nsCString& aErrorMessage)
{
+ RejectPromise(aPromiseId, ToNsresult(aError), aErrorMessage);
return IPC_OK();
}
+static dom::MediaKeyMessageType
+ToDOMMessageType(uint32_t aMessageType)
+{
+ switch (static_cast<cdm::MessageType>(aMessageType)) {
+ case cdm::kLicenseRequest:
+ return dom::MediaKeyMessageType::License_request;
+ case cdm::kLicenseRenewal:
+ return dom::MediaKeyMessageType::License_renewal;
+ case cdm::kLicenseRelease:
+ return dom::MediaKeyMessageType::License_release;
+ }
+ MOZ_ASSERT_UNREACHABLE("Invalid cdm::MessageType enum value.");
+ return dom::MediaKeyMessageType::License_request;
+}
+
ipc::IPCResult
ChromiumCDMParent::RecvOnSessionMessage(const nsCString& aSessionId,
const uint32_t& aMessageType,
nsTArray<uint8_t>&& aMessage)
{
+ if (!mProxy) {
+ return IPC_OK();
+ }
+ RefPtr<CDMProxy> proxy = mProxy;
+ nsString sid = NS_ConvertUTF8toUTF16(aSessionId);
+ dom::MediaKeyMessageType messageType = ToDOMMessageType(aMessageType);
+ nsTArray<uint8_t> msg(Move(aMessage));
+ NS_DispatchToMainThread(
+ NS_NewRunnableFunction([proxy, sid, messageType, msg]() mutable {
+ proxy->OnSessionMessage(sid, messageType, msg);
+ }));
return IPC_OK();
}
+static dom::MediaKeyStatus
+ToDOMMediaKeyStatus(uint32_t aStatus)
+{
+ switch (static_cast<cdm::KeyStatus>(aStatus)) {
+ case cdm::kUsable:
+ return dom::MediaKeyStatus::Usable;
+ case cdm::kInternalError:
+ return dom::MediaKeyStatus::Internal_error;
+ case cdm::kExpired:
+ return dom::MediaKeyStatus::Expired;
+ case cdm::kOutputRestricted:
+ return dom::MediaKeyStatus::Output_restricted;
+ case cdm::kOutputDownscaled:
+ return dom::MediaKeyStatus::Output_downscaled;
+ case cdm::kStatusPending:
+ return dom::MediaKeyStatus::Status_pending;
+ case cdm::kReleased:
+ return dom::MediaKeyStatus::Released;
+ }
+ MOZ_ASSERT_UNREACHABLE("Invalid cdm::KeyStatus enum value.");
+ return dom::MediaKeyStatus::Internal_error;
+}
+
ipc::IPCResult
ChromiumCDMParent::RecvOnSessionKeysChange(
const nsCString& aSessionId,
nsTArray<CDMKeyInformation>&& aKeysInfo)
{
+ if (!mProxy) {
+ return IPC_OK();
+ }
+ bool keyStatusesChange = false;
+ {
+ CDMCaps::AutoLock caps(mProxy->Capabilites());
+ for (size_t i = 0; i < aKeysInfo.Length(); i++) {
+ keyStatusesChange |=
+ caps.SetKeyStatus(aKeysInfo[i].mKeyId(),
+ NS_ConvertUTF8toUTF16(aSessionId),
+ dom::Optional<dom::MediaKeyStatus>(
+ ToDOMMediaKeyStatus(aKeysInfo[i].mStatus())));
+ }
+ }
+ if (keyStatusesChange) {
+ NS_DispatchToMainThread(
+ NewRunnableMethod<nsString>(mProxy,
+ &ChromiumCDMProxy::OnKeyStatusesChange,
+ NS_ConvertUTF8toUTF16(aSessionId)));
+ }
return IPC_OK();
}
ipc::IPCResult
ChromiumCDMParent::RecvOnExpirationChange(const nsCString& aSessionId,
const double& aSecondsSinceEpoch)
{
+ if (!mProxy) {
+ return IPC_OK();
+ }
+ NS_DispatchToMainThread(NewRunnableMethod<nsString, UnixTime>(
+ mProxy,
+ &ChromiumCDMProxy::OnExpirationChange,
+ NS_ConvertUTF8toUTF16(aSessionId),
+ GMPTimestamp(aSecondsSinceEpoch * 1000)));
return IPC_OK();
}
ipc::IPCResult
ChromiumCDMParent::RecvOnSessionClosed(const nsCString& aSessionId)
{
+ if (!mProxy) {
+ return IPC_OK();
+ }
+ NS_DispatchToMainThread(
+ NewRunnableMethod<nsString>(mProxy,
+ &ChromiumCDMProxy::OnSessionClosed,
+ NS_ConvertUTF8toUTF16(aSessionId)));
return IPC_OK();
}
ipc::IPCResult
ChromiumCDMParent::RecvOnLegacySessionError(const nsCString& aSessionId,
const uint32_t& aError,
const uint32_t& aSystemCode,
const nsCString& aMessage)
{
+ if (!mProxy) {
+ return IPC_OK();
+ }
+ NS_DispatchToMainThread(
+ NewRunnableMethod<nsString, nsresult, uint32_t, nsString>(
+ mProxy,
+ &ChromiumCDMProxy::OnSessionError,
+ NS_ConvertUTF8toUTF16(aSessionId),
+ ToNsresult(aError),
+ aSystemCode,
+ NS_ConvertUTF8toUTF16(aMessage)));
return IPC_OK();
}
ipc::IPCResult
ChromiumCDMParent::RecvDecrypted(const uint32_t& aStatus,
nsTArray<uint8_t>&& aData)
{
return IPC_OK();
--- a/dom/media/gmp/ChromiumCDMParent.h
+++ b/dom/media/gmp/ChromiumCDMParent.h
@@ -30,17 +30,27 @@ public:
ChromiumCDMParent(GMPContentParent* aContentParent, uint32_t aPluginId);
uint32_t PluginId() const { return mPluginId; }
bool Init(ChromiumCDMProxy* aProxy,
bool aAllowDistinctiveIdentifier,
bool aAllowPersistentState);
- // TODO: Add functions for clients to send data to CDM, and
+ void SetServerCertificate(uint32_t aPromiseId,
+ const nsTArray<uint8_t>& aCert);
+
+ void UpdateSession(const nsCString& aSessionId,
+ uint32_t aPromiseId,
+ const nsTArray<uint8_t>& aResponse);
+
+ void CloseSession(const nsCString& aSessionId, uint32_t aPromiseId);
+
+ void RemoveSession(const nsCString& aSessionId, uint32_t aPromiseId);
+
// a Close() function.
protected:
~ChromiumCDMParent() {}
ipc::IPCResult Recv__delete__() override;
ipc::IPCResult RecvOnResolveNewSessionPromise(
const uint32_t& aPromiseId,
@@ -66,16 +76,20 @@ protected:
const nsCString& aMessage) override;
ipc::IPCResult RecvDecrypted(const uint32_t& aStatus,
nsTArray<uint8_t>&& aData) override;
ipc::IPCResult RecvDecoded(const CDMVideoFrame& aFrame) override;
ipc::IPCResult RecvDecodeFailed(const uint32_t& aStatus) override;
ipc::IPCResult RecvShutdown() override;
void ActorDestroy(ActorDestroyReason aWhy) override;
+ void RejectPromise(uint32_t aPromiseId,
+ nsresult aError,
+ const nsCString& aErrorMessage);
+
const uint32_t mPluginId;
GMPContentParent* mContentParent;
// Note: this pointer is a weak reference because otherwise it would cause
// a cycle, as ChromiumCDMProxy has a strong reference to the
// ChromiumCDMParent.
ChromiumCDMProxy* mProxy = nullptr;
};
--- a/dom/media/gmp/ChromiumCDMProxy.cpp
+++ b/dom/media/gmp/ChromiumCDMProxy.cpp
@@ -3,16 +3,17 @@
/* 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 "GMPUtils.h"
#include "nsPrintfCString.h"
#include "GMPService.h"
+#include "mozilla/dom/MediaKeySession.h"
namespace mozilla {
ChromiumCDMProxy::ChromiumCDMProxy(dom::MediaKeys* aKeys,
const nsAString& aKeySystem,
GMPCrashHelper* aCrashHelper,
bool aDistinctiveIdentifierRequired,
bool aPersistentStateRequired,
@@ -147,41 +148,71 @@ ChromiumCDMProxy::CreateSession(uint32_t
const nsAString& aInitDataType,
nsTArray<uint8_t>& aInitData)
{
}
void
ChromiumCDMProxy::LoadSession(PromiseId aPromiseId, const nsAString& aSessionId)
{
+ MOZ_ASSERT(NS_IsMainThread());
+
+ RejectPromise(aPromiseId,
+ NS_ERROR_DOM_NOT_SUPPORTED_ERR,
+ NS_LITERAL_CSTRING("loadSession is not supported"));
}
void
ChromiumCDMProxy::SetServerCertificate(PromiseId aPromiseId,
nsTArray<uint8_t>& aCert)
{
+ MOZ_ASSERT(NS_IsMainThread());
+
+ mGMPThread->Dispatch(NewRunnableMethod<uint32_t, nsTArray<uint8_t>>(
+ mCDM,
+ &gmp::ChromiumCDMParent::SetServerCertificate,
+ aPromiseId,
+ Move(aCert)));
}
void
ChromiumCDMProxy::UpdateSession(const nsAString& aSessionId,
PromiseId aPromiseId,
nsTArray<uint8_t>& aResponse)
{
+ MOZ_ASSERT(NS_IsMainThread());
+
+ mGMPThread->Dispatch(NewRunnableMethod<nsCString, uint32_t, nsTArray<uint8_t>>(
+ mCDM,
+ &gmp::ChromiumCDMParent::UpdateSession,
+ NS_ConvertUTF16toUTF8(aSessionId),
+ aPromiseId,
+ Move(aResponse)));
}
void
ChromiumCDMProxy::CloseSession(const nsAString& aSessionId,
PromiseId aPromiseId)
{
+ mGMPThread->Dispatch(NewRunnableMethod<nsCString, uint32_t>(
+ mCDM,
+ &gmp::ChromiumCDMParent::CloseSession,
+ NS_ConvertUTF16toUTF8(aSessionId),
+ aPromiseId));
}
void
ChromiumCDMProxy::RemoveSession(const nsAString& aSessionId,
PromiseId aPromiseId)
{
+ mGMPThread->Dispatch(NewRunnableMethod<nsCString, uint32_t>(
+ mCDM,
+ &gmp::ChromiumCDMParent::RemoveSession,
+ NS_ConvertUTF16toUTF8(aSessionId),
+ aPromiseId));
}
void
ChromiumCDMProxy::Shutdown()
{
}
void
@@ -237,47 +268,100 @@ ChromiumCDMProxy::OnResolveLoadSessionPr
{
}
void
ChromiumCDMProxy::OnSessionMessage(const nsAString& aSessionId,
dom::MediaKeyMessageType aMessageType,
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,
GMPTimestamp 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)
{
+ MOZ_ASSERT(NS_IsMainThread());
+
+ bool keyStatusesChange = false;
+ {
+ CDMCaps::AutoLock caps(Capabilites());
+ keyStatusesChange = caps.RemoveKeysForSession(nsString(aSessionId));
+ }
+ if (keyStatusesChange) {
+ OnKeyStatusesChange(aSessionId);
+ }
+ if (mKeys.IsNull()) {
+ return;
+ }
+ RefPtr<dom::MediaKeySession> session(mKeys->GetSession(aSessionId));
+ if (session) {
+ session->OnClosed();
+ }
}
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,
nsresult aDOMException,
const nsCString& aMsg)
{
MOZ_ASSERT(NS_IsMainThread());
@@ -302,16 +386,21 @@ ChromiumCDMProxy::Decrypt(MediaRawData*
return DecryptPromise::CreateAndReject(DecryptResult(GenericErr, nullptr),
__func__);
}
void
ChromiumCDMProxy::GetSessionIdsForKeyId(const nsTArray<uint8_t>& aKeyId,
nsTArray<nsCString>& aSessionIds)
{
+ CDMCaps::AutoLock caps(Capabilites());
+ caps.GetSessionIdsForKeyId(aKeyId, aSessionIds);
}
void
ChromiumCDMProxy::Terminated()
{
+ if (!mKeys.IsNull()) {
+ mKeys->Terminated();
+ }
}
} // namespace mozilla