mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 1043147 - Update CDMProxy and EME JS APIs to proxy calls to a GMP/CDM. r=ehsan
This commit is contained in:
parent
00b5341e5c
commit
8e5a39d66d
284
content/media/eme/CDMCallbackProxy.cpp
Normal file
284
content/media/eme/CDMCallbackProxy.cpp
Normal file
@ -0,0 +1,284 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* 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 "mozilla/CDMCallbackProxy.h"
|
||||
#include "mozilla/CDMProxy.h"
|
||||
#include "nsString.h"
|
||||
#include "mozilla/dom/MediaKeys.h"
|
||||
#include "mozilla/dom/MediaKeySession.h"
|
||||
#include "mozIGeckoMediaPluginService.h"
|
||||
#include "nsContentCID.h"
|
||||
#include "nsServiceManagerUtils.h"
|
||||
#include "MainThreadUtils.h"
|
||||
#include "EMELog.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
CDMCallbackProxy::CDMCallbackProxy(CDMProxy* aProxy)
|
||||
: mProxy(aProxy)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
class NewSessionTask : public nsRunnable {
|
||||
public:
|
||||
NewSessionTask(CDMProxy* aProxy,
|
||||
uint32_t aPromiseId,
|
||||
const nsCString& aSessionId)
|
||||
: mProxy(aProxy)
|
||||
, mPid(aPromiseId)
|
||||
, mSid(NS_ConvertUTF8toUTF16(aSessionId))
|
||||
{
|
||||
}
|
||||
|
||||
NS_IMETHOD Run() {
|
||||
mProxy->OnResolveNewSessionPromise(mPid, mSid);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsRefPtr<CDMProxy> mProxy;
|
||||
dom::PromiseId mPid;
|
||||
nsString mSid;
|
||||
};
|
||||
|
||||
void
|
||||
CDMCallbackProxy::ResolveNewSessionPromise(uint32_t aPromiseId,
|
||||
const nsCString& aSessionId)
|
||||
{
|
||||
MOZ_ASSERT(mProxy->IsOnGMPThread());
|
||||
|
||||
nsRefPtr<nsIRunnable> task(new NewSessionTask(mProxy,
|
||||
aPromiseId,
|
||||
aSessionId));
|
||||
NS_DispatchToMainThread(task);
|
||||
}
|
||||
|
||||
void
|
||||
CDMCallbackProxy::ResolvePromise(uint32_t aPromiseId)
|
||||
{
|
||||
MOZ_ASSERT(mProxy->IsOnGMPThread());
|
||||
|
||||
// Note: CDMProxy proxies this from non-main threads to main thread.
|
||||
mProxy->ResolvePromise(aPromiseId);
|
||||
}
|
||||
|
||||
class RejectPromiseTask : public nsRunnable {
|
||||
public:
|
||||
RejectPromiseTask(CDMProxy* aProxy,
|
||||
uint32_t aPromiseId,
|
||||
nsresult aException,
|
||||
const nsCString& aMessage)
|
||||
: mProxy(aProxy)
|
||||
, mPid(aPromiseId)
|
||||
, mException(aException)
|
||||
, mMsg(NS_ConvertUTF8toUTF16(aMessage))
|
||||
{
|
||||
}
|
||||
|
||||
NS_IMETHOD Run() {
|
||||
mProxy->OnRejectPromise(mPid, mException, mMsg);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsRefPtr<CDMProxy> mProxy;
|
||||
dom::PromiseId mPid;
|
||||
nsresult mException;
|
||||
nsString mMsg;
|
||||
};
|
||||
|
||||
|
||||
void
|
||||
CDMCallbackProxy::RejectPromise(uint32_t aPromiseId,
|
||||
nsresult aException,
|
||||
const nsCString& aMessage)
|
||||
{
|
||||
MOZ_ASSERT(mProxy->IsOnGMPThread());
|
||||
|
||||
nsRefPtr<nsIRunnable> task;
|
||||
task = new RejectPromiseTask(mProxy,
|
||||
aPromiseId,
|
||||
aException,
|
||||
aMessage);
|
||||
NS_DispatchToMainThread(task);
|
||||
}
|
||||
|
||||
class SessionMessageTask : public nsRunnable {
|
||||
public:
|
||||
SessionMessageTask(CDMProxy* aProxy,
|
||||
const nsCString& aSessionId,
|
||||
const nsTArray<uint8_t>& aMessage,
|
||||
const nsCString& aDestinationURL)
|
||||
: mProxy(aProxy)
|
||||
, mSid(NS_ConvertUTF8toUTF16(aSessionId))
|
||||
, mURL(NS_ConvertUTF8toUTF16(aDestinationURL))
|
||||
{
|
||||
mMsg.AppendElements(aMessage);
|
||||
}
|
||||
|
||||
NS_IMETHOD Run() {
|
||||
mProxy->OnSessionMessage(mSid, mMsg, mURL);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsRefPtr<CDMProxy> mProxy;
|
||||
dom::PromiseId mPid;
|
||||
nsString mSid;
|
||||
nsTArray<uint8_t> mMsg;
|
||||
nsString mURL;
|
||||
};
|
||||
|
||||
void
|
||||
CDMCallbackProxy::SessionMessage(const nsCString& aSessionId,
|
||||
const nsTArray<uint8_t>& aMessage,
|
||||
const nsCString& aDestinationURL)
|
||||
{
|
||||
MOZ_ASSERT(mProxy->IsOnGMPThread());
|
||||
|
||||
nsRefPtr<nsIRunnable> task;
|
||||
task = new SessionMessageTask(mProxy,
|
||||
aSessionId,
|
||||
aMessage,
|
||||
aDestinationURL);
|
||||
NS_DispatchToMainThread(task);
|
||||
}
|
||||
|
||||
class ExpirationChangeTask : public nsRunnable {
|
||||
public:
|
||||
ExpirationChangeTask(CDMProxy* aProxy,
|
||||
const nsCString& aSessionId,
|
||||
GMPTimestamp aExpiryTime)
|
||||
: mProxy(aProxy)
|
||||
, mSid(NS_ConvertUTF8toUTF16(aSessionId))
|
||||
, mTimestamp(aExpiryTime)
|
||||
{}
|
||||
|
||||
NS_IMETHOD Run() {
|
||||
mProxy->OnExpirationChange(mSid, mTimestamp);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsRefPtr<CDMProxy> mProxy;
|
||||
nsString mSid;
|
||||
GMPTimestamp mTimestamp;
|
||||
};
|
||||
|
||||
void
|
||||
CDMCallbackProxy::ExpirationChange(const nsCString& aSessionId,
|
||||
GMPTimestamp aExpiryTime)
|
||||
{
|
||||
MOZ_ASSERT(mProxy->IsOnGMPThread());
|
||||
|
||||
nsRefPtr<nsIRunnable> task;
|
||||
task = new ExpirationChangeTask(mProxy,
|
||||
aSessionId,
|
||||
aExpiryTime);
|
||||
NS_DispatchToMainThread(task);
|
||||
}
|
||||
|
||||
void
|
||||
CDMCallbackProxy::SessionClosed(const nsCString& aSessionId)
|
||||
{
|
||||
MOZ_ASSERT(mProxy->IsOnGMPThread());
|
||||
|
||||
nsRefPtr<nsIRunnable> task;
|
||||
task = NS_NewRunnableMethodWithArg<nsString>(mProxy,
|
||||
&CDMProxy::OnSessionClosed,
|
||||
NS_ConvertUTF8toUTF16(aSessionId));
|
||||
NS_DispatchToMainThread(task);
|
||||
}
|
||||
|
||||
class SessionErrorTask : public nsRunnable {
|
||||
public:
|
||||
SessionErrorTask(CDMProxy* aProxy,
|
||||
const nsCString& aSessionId,
|
||||
nsresult aException,
|
||||
uint32_t aSystemCode,
|
||||
const nsCString& aMessage)
|
||||
: mProxy(aProxy)
|
||||
, mSid(NS_ConvertUTF8toUTF16(aSessionId))
|
||||
, mException(aException)
|
||||
, mSystemCode(aSystemCode)
|
||||
, mMsg(NS_ConvertUTF8toUTF16(aMessage))
|
||||
{}
|
||||
|
||||
NS_IMETHOD Run() {
|
||||
mProxy->OnSessionError(mSid, mException, mSystemCode, mMsg);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsRefPtr<CDMProxy> mProxy;
|
||||
dom::PromiseId mPid;
|
||||
nsString mSid;
|
||||
nsresult mException;
|
||||
uint32_t mSystemCode;
|
||||
nsString mMsg;
|
||||
};
|
||||
|
||||
void
|
||||
CDMCallbackProxy::SessionError(const nsCString& aSessionId,
|
||||
nsresult aException,
|
||||
uint32_t aSystemCode,
|
||||
const nsCString& aMessage)
|
||||
{
|
||||
MOZ_ASSERT(mProxy->IsOnGMPThread());
|
||||
|
||||
nsRefPtr<nsIRunnable> task;
|
||||
task = new SessionErrorTask(mProxy,
|
||||
aSessionId,
|
||||
aException,
|
||||
aSystemCode,
|
||||
aMessage);
|
||||
NS_DispatchToMainThread(task);
|
||||
}
|
||||
|
||||
void
|
||||
CDMCallbackProxy::KeyIdUsable(const nsCString& aSessionId,
|
||||
const nsTArray<uint8_t>& aKeyId)
|
||||
{
|
||||
MOZ_ASSERT(mProxy->IsOnGMPThread());
|
||||
|
||||
CDMCaps::AutoLock caps(mProxy->Capabilites());
|
||||
caps.SetKeyUsable(aKeyId, NS_ConvertUTF8toUTF16(aSessionId));
|
||||
}
|
||||
|
||||
void
|
||||
CDMCallbackProxy::KeyIdNotUsable(const nsCString& aSessionId,
|
||||
const nsTArray<uint8_t>& aKeyId)
|
||||
{
|
||||
MOZ_ASSERT(mProxy->IsOnGMPThread());
|
||||
|
||||
CDMCaps::AutoLock caps(mProxy->Capabilites());
|
||||
caps.SetKeyUnusable(aKeyId, NS_ConvertUTF8toUTF16(aSessionId));
|
||||
}
|
||||
|
||||
void
|
||||
CDMCallbackProxy::SetCaps(uint64_t aCaps)
|
||||
{
|
||||
MOZ_ASSERT(mProxy->IsOnGMPThread());
|
||||
|
||||
CDMCaps::AutoLock caps(mProxy->Capabilites());
|
||||
caps.SetCaps(aCaps);
|
||||
}
|
||||
|
||||
void
|
||||
CDMCallbackProxy::Decrypted(uint32_t aId,
|
||||
GMPErr aResult,
|
||||
const nsTArray<uint8_t>& aDecryptedData)
|
||||
{
|
||||
MOZ_ASSERT(mProxy->IsOnGMPThread());
|
||||
|
||||
mProxy->gmp_Decrypted(aId, aResult, aDecryptedData);
|
||||
}
|
||||
|
||||
void
|
||||
CDMCallbackProxy::Terminated()
|
||||
{
|
||||
MOZ_ASSERT(mProxy->IsOnGMPThread());
|
||||
|
||||
mProxy->gmp_Terminated();
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
69
content/media/eme/CDMCallbackProxy.h
Normal file
69
content/media/eme/CDMCallbackProxy.h
Normal file
@ -0,0 +1,69 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* 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/. */
|
||||
|
||||
#ifndef CDMCallbackProxy_h_
|
||||
#define CDMCallbackProxy_h_
|
||||
|
||||
#include "mozilla/CDMProxy.h"
|
||||
#include "gmp-decryption.h"
|
||||
#include "GMPDecryptorProxy.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
// Proxies call backs from the CDM on the GMP thread back to the MediaKeys
|
||||
// object on the main thread.
|
||||
class CDMCallbackProxy : public GMPDecryptorProxyCallback {
|
||||
public:
|
||||
virtual void ResolveNewSessionPromise(uint32_t aPromiseId,
|
||||
const nsCString& aSessionId) MOZ_OVERRIDE;
|
||||
|
||||
virtual void ResolvePromise(uint32_t aPromiseId) MOZ_OVERRIDE;
|
||||
|
||||
virtual void RejectPromise(uint32_t aPromiseId,
|
||||
nsresult aException,
|
||||
const nsCString& aSessionId) MOZ_OVERRIDE;
|
||||
|
||||
virtual void SessionMessage(const nsCString& aSessionId,
|
||||
const nsTArray<uint8_t>& aMessage,
|
||||
const nsCString& aDestinationURL) MOZ_OVERRIDE;
|
||||
|
||||
virtual void ExpirationChange(const nsCString& aSessionId,
|
||||
GMPTimestamp aExpiryTime) MOZ_OVERRIDE;
|
||||
|
||||
virtual void SessionClosed(const nsCString& aSessionId) MOZ_OVERRIDE;
|
||||
|
||||
virtual void SessionError(const nsCString& aSessionId,
|
||||
nsresult aException,
|
||||
uint32_t aSystemCode,
|
||||
const nsCString& aMessage) MOZ_OVERRIDE;
|
||||
|
||||
virtual void KeyIdUsable(const nsCString& aSessionId,
|
||||
const nsTArray<uint8_t>& aKeyId) MOZ_OVERRIDE;
|
||||
|
||||
virtual void KeyIdNotUsable(const nsCString& aSessionId,
|
||||
const nsTArray<uint8_t>& aKeyId) MOZ_OVERRIDE;
|
||||
|
||||
virtual void SetCaps(uint64_t aCaps) MOZ_OVERRIDE;
|
||||
|
||||
virtual void Decrypted(uint32_t aId,
|
||||
GMPErr aResult,
|
||||
const nsTArray<uint8_t>& aDecryptedData) MOZ_OVERRIDE;
|
||||
|
||||
virtual void Terminated() MOZ_OVERRIDE;
|
||||
|
||||
~CDMCallbackProxy() {}
|
||||
|
||||
private:
|
||||
friend class CDMProxy;
|
||||
explicit CDMCallbackProxy(CDMProxy* aProxy);
|
||||
|
||||
// Warning: Weak ref.
|
||||
CDMProxy* mProxy;
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // CDMCallbackProxy_h_
|
187
content/media/eme/CDMCaps.cpp
Normal file
187
content/media/eme/CDMCaps.cpp
Normal file
@ -0,0 +1,187 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* 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 "CDMCaps.h"
|
||||
#include "gmp-decryption.h"
|
||||
#include "EMELog.h"
|
||||
#include "nsThreadUtils.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
CDMCaps::CDMCaps()
|
||||
: mMonitor("CDMCaps")
|
||||
, mCaps(0)
|
||||
{
|
||||
}
|
||||
|
||||
CDMCaps::~CDMCaps()
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
CDMCaps::Lock()
|
||||
{
|
||||
mMonitor.Lock();
|
||||
}
|
||||
|
||||
void
|
||||
CDMCaps::Unlock()
|
||||
{
|
||||
mMonitor.Unlock();
|
||||
}
|
||||
|
||||
bool
|
||||
CDMCaps::HasCap(uint64_t aCap)
|
||||
{
|
||||
mMonitor.AssertCurrentThreadOwns();
|
||||
return (mCaps & aCap) == aCap;
|
||||
}
|
||||
|
||||
CDMCaps::AutoLock::AutoLock(CDMCaps& aInstance)
|
||||
: mData(aInstance)
|
||||
{
|
||||
mData.Lock();
|
||||
}
|
||||
|
||||
CDMCaps::AutoLock::~AutoLock()
|
||||
{
|
||||
mData.Unlock();
|
||||
}
|
||||
|
||||
void
|
||||
CDMCaps::AutoLock::SetCaps(uint64_t aCaps)
|
||||
{
|
||||
EME_LOG("SetCaps()");
|
||||
mData.mMonitor.AssertCurrentThreadOwns();
|
||||
mData.mCaps = aCaps;
|
||||
for (size_t i = 0; i < mData.mWaitForCaps.Length(); i++) {
|
||||
NS_DispatchToMainThread(mData.mWaitForCaps[i], NS_DISPATCH_NORMAL);
|
||||
}
|
||||
mData.mWaitForCaps.Clear();
|
||||
}
|
||||
|
||||
void
|
||||
CDMCaps::AutoLock::CallOnMainThreadWhenCapsAvailable(nsIRunnable* aContinuation)
|
||||
{
|
||||
mData.mMonitor.AssertCurrentThreadOwns();
|
||||
if (mData.mCaps) {
|
||||
NS_DispatchToMainThread(aContinuation, NS_DISPATCH_NORMAL);
|
||||
MOZ_ASSERT(mData.mWaitForCaps.IsEmpty());
|
||||
} else {
|
||||
mData.mWaitForCaps.AppendElement(aContinuation);
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
CDMCaps::AutoLock::IsKeyUsable(const CencKeyId& aKeyId)
|
||||
{
|
||||
mData.mMonitor.AssertCurrentThreadOwns();
|
||||
const auto& keys = mData.mUsableKeyIds;
|
||||
for (size_t i = 0; i < keys.Length(); i++) {
|
||||
if (keys[i].mId == aKeyId) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void
|
||||
CDMCaps::AutoLock::SetKeyUsable(const CencKeyId& aKeyId,
|
||||
const nsString& aSessionId)
|
||||
{
|
||||
mData.mMonitor.AssertCurrentThreadOwns();
|
||||
mData.mUsableKeyIds.AppendElement(UsableKey(aKeyId, aSessionId));
|
||||
auto& waiters = mData.mWaitForKeys;
|
||||
size_t i = 0;
|
||||
while (i < waiters.Length()) {
|
||||
auto& w = waiters[i];
|
||||
if (w.mKeyId == aKeyId) {
|
||||
if (waiters[i].mTarget) {
|
||||
EME_LOG("SetKeyUsable() notified waiter.");
|
||||
w.mTarget->Dispatch(w.mContinuation, NS_DISPATCH_NORMAL);
|
||||
} else {
|
||||
w.mContinuation->Run();
|
||||
}
|
||||
waiters.RemoveElementAt(i);
|
||||
} else {
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
CDMCaps::AutoLock::SetKeyUnusable(const CencKeyId& aKeyId,
|
||||
const nsString& aSessionId)
|
||||
{
|
||||
mData.mMonitor.AssertCurrentThreadOwns();
|
||||
auto& keys = mData.mUsableKeyIds;
|
||||
for (size_t i = 0; i < keys.Length(); i++) {
|
||||
if (keys[i].mId == aKeyId &&
|
||||
keys[i].mSessionId == aSessionId) {
|
||||
keys.RemoveElementAt(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
CDMCaps::AutoLock::CallWhenKeyUsable(const CencKeyId& aKey,
|
||||
nsIRunnable* aContinuation,
|
||||
nsIThread* aTarget)
|
||||
{
|
||||
mData.mMonitor.AssertCurrentThreadOwns();
|
||||
MOZ_ASSERT(!IsKeyUsable(aKey));
|
||||
MOZ_ASSERT(aContinuation);
|
||||
mData.mWaitForKeys.AppendElement(WaitForKeys(aKey, aContinuation, aTarget));
|
||||
}
|
||||
|
||||
void
|
||||
CDMCaps::AutoLock::DropKeysForSession(const nsAString& aSessionId)
|
||||
{
|
||||
mData.mMonitor.AssertCurrentThreadOwns();
|
||||
auto& keys = mData.mUsableKeyIds;
|
||||
size_t i = 0;
|
||||
while (i < keys.Length()) {
|
||||
if (keys[i].mSessionId == aSessionId) {
|
||||
keys.RemoveElementAt(i);
|
||||
} else {
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
CDMCaps::AutoLock::AreCapsKnown()
|
||||
{
|
||||
mData.mMonitor.AssertCurrentThreadOwns();
|
||||
return mData.mCaps != 0;
|
||||
}
|
||||
|
||||
bool
|
||||
CDMCaps::AutoLock::CanDecryptAndDecodeAudio()
|
||||
{
|
||||
return mData.HasCap(GMP_EME_CAP_DECRYPT_AND_DECODE_AUDIO);
|
||||
}
|
||||
|
||||
bool
|
||||
CDMCaps::AutoLock::CanDecryptAndDecodeVideo()
|
||||
{
|
||||
return mData.HasCap(GMP_EME_CAP_DECRYPT_AND_DECODE_VIDEO);
|
||||
}
|
||||
|
||||
bool
|
||||
CDMCaps::AutoLock::CanDecryptAudio()
|
||||
{
|
||||
return mData.HasCap(GMP_EME_CAP_DECRYPT_AUDIO);
|
||||
}
|
||||
|
||||
bool
|
||||
CDMCaps::AutoLock::CanDecryptVideo()
|
||||
{
|
||||
return mData.HasCap(GMP_EME_CAP_DECRYPT_VIDEO);
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
117
content/media/eme/CDMCaps.h
Normal file
117
content/media/eme/CDMCaps.h
Normal file
@ -0,0 +1,117 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* 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/. */
|
||||
|
||||
#ifndef CDMCaps_h_
|
||||
#define CDMCaps_h_
|
||||
|
||||
#include "nsString.h"
|
||||
#include "nsAutoPtr.h"
|
||||
#include "mozilla/Monitor.h"
|
||||
#include "nsIThread.h"
|
||||
#include "nsTArray.h"
|
||||
#include "mozilla/Attributes.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
typedef nsTArray<uint8_t> CencKeyId;
|
||||
|
||||
// CDM capabilities; what keys a CDMProxy can use, and whether it can decrypt, or
|
||||
// decrypt-and-decode on a per stream basis. Must be locked to access state.
|
||||
class CDMCaps {
|
||||
public:
|
||||
CDMCaps();
|
||||
~CDMCaps();
|
||||
|
||||
// Locks the CDMCaps. It must be locked to access its shared state.
|
||||
// Threadsafe when locked.
|
||||
class MOZ_STACK_CLASS AutoLock {
|
||||
public:
|
||||
explicit AutoLock(CDMCaps& aKeyCaps);
|
||||
~AutoLock();
|
||||
|
||||
// Returns true if the capabilities of the CDM are known, i.e. they have
|
||||
// been reported by the CDM to Gecko.
|
||||
bool AreCapsKnown();
|
||||
|
||||
bool IsKeyUsable(const CencKeyId& aKeyId);
|
||||
|
||||
void SetKeyUsable(const CencKeyId& aKeyId, const nsString& aSessionId);
|
||||
void SetKeyUnusable(const CencKeyId& aKeyId, const nsString& aSessionId);
|
||||
|
||||
void DropKeysForSession(const nsAString& aSessionId);
|
||||
|
||||
// Sets the capabilities of the CDM. aCaps is the logical OR of the
|
||||
// GMP_EME_CAP_* flags from gmp-decryption.h.
|
||||
void SetCaps(uint64_t aCaps);
|
||||
|
||||
bool CanDecryptAndDecodeAudio();
|
||||
bool CanDecryptAndDecodeVideo();
|
||||
|
||||
bool CanDecryptAudio();
|
||||
bool CanDecryptVideo();
|
||||
|
||||
void CallOnMainThreadWhenCapsAvailable(nsIRunnable* aContinuation);
|
||||
|
||||
// Calls aContinuation on aTarget thread when key become usable.
|
||||
// Pass aTarget=nullptr and runnable will be called on the GMP thread
|
||||
// when key becomes usable.
|
||||
void CallWhenKeyUsable(const CencKeyId& aKey,
|
||||
nsIRunnable* aContinuation,
|
||||
nsIThread* aTarget = nullptr);
|
||||
|
||||
private:
|
||||
// Not taking a strong ref, since this should be allocated on the stack.
|
||||
CDMCaps& mData;
|
||||
};
|
||||
|
||||
private:
|
||||
void Lock();
|
||||
void Unlock();
|
||||
bool HasCap(uint64_t);
|
||||
|
||||
struct WaitForKeys {
|
||||
WaitForKeys(const CencKeyId& aKeyId,
|
||||
nsIRunnable* aContinuation,
|
||||
nsIThread* aTarget)
|
||||
: mKeyId(aKeyId)
|
||||
, mContinuation(aContinuation)
|
||||
, mTarget(aTarget)
|
||||
{}
|
||||
CencKeyId mKeyId;
|
||||
nsRefPtr<nsIRunnable> mContinuation;
|
||||
nsCOMPtr<nsIThread> mTarget;
|
||||
};
|
||||
|
||||
Monitor mMonitor;
|
||||
|
||||
struct UsableKey {
|
||||
UsableKey(const CencKeyId& aId,
|
||||
const nsString& aSessionId)
|
||||
: mId(aId)
|
||||
, mSessionId(aSessionId)
|
||||
{}
|
||||
UsableKey(const UsableKey& aOther)
|
||||
: mId(aOther.mId)
|
||||
, mSessionId(aOther.mSessionId)
|
||||
{}
|
||||
CencKeyId mId;
|
||||
nsString mSessionId;
|
||||
};
|
||||
nsTArray<UsableKey> mUsableKeyIds;
|
||||
|
||||
nsTArray<WaitForKeys> mWaitForKeys;
|
||||
|
||||
nsTArray<nsRefPtr<nsIRunnable>> mWaitForCaps;
|
||||
uint64_t mCaps;
|
||||
|
||||
// It is not safe to copy this object.
|
||||
CDMCaps(const CDMCaps&) MOZ_DELETE;
|
||||
CDMCaps& operator=(const CDMCaps&) MOZ_DELETE;
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif
|
@ -12,16 +12,19 @@
|
||||
#include "nsContentCID.h"
|
||||
#include "nsServiceManagerUtils.h"
|
||||
#include "MainThreadUtils.h"
|
||||
|
||||
// TODO: Change the functions in this file to do IPC via the Gecko Media
|
||||
// Plugins API. In the meantime, the code here merely implements the
|
||||
// interface we expect will be required when the IPC is working.
|
||||
#include "mozilla/EMELog.h"
|
||||
#include "nsIConsoleService.h"
|
||||
#include "prenv.h"
|
||||
#include "mozilla/PodOperations.h"
|
||||
#include "mozilla/CDMCallbackProxy.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
CDMProxy::CDMProxy(dom::MediaKeys* aKeys, const nsAString& aKeySystem)
|
||||
: mKeys(aKeys)
|
||||
, mKeySystem(aKeySystem)
|
||||
, mCDM(nullptr)
|
||||
, mDecryptionJobCount(0)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_COUNT_CTOR(CDMProxy);
|
||||
@ -36,6 +39,15 @@ void
|
||||
CDMProxy::Init(PromiseId aPromiseId)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
nsresult rv = mKeys->GetOrigin(mOrigin);
|
||||
if (NS_FAILED(rv)) {
|
||||
RejectPromise(aPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR);
|
||||
return;
|
||||
}
|
||||
EME_LOG("Creating CDMProxy for origin='%s'",
|
||||
NS_ConvertUTF16toUTF8(GetOrigin()).get());
|
||||
|
||||
if (!mGMPThread) {
|
||||
nsCOMPtr<mozIGeckoMediaPluginService> mps =
|
||||
do_GetService("@mozilla.org/gecko-media-plugin-service;1");
|
||||
@ -50,12 +62,53 @@ CDMProxy::Init(PromiseId aPromiseId)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Dispatch task to GMPThread to initialize CDM via IPC.
|
||||
|
||||
mKeys->OnCDMCreated(aPromiseId);
|
||||
nsRefPtr<nsIRunnable> task(NS_NewRunnableMethodWithArg<uint32_t>(this, &CDMProxy::gmp_Init, aPromiseId));
|
||||
mGMPThread->Dispatch(task, NS_DISPATCH_NORMAL);
|
||||
}
|
||||
|
||||
static int sFakeSessionIdNum = 0;
|
||||
#ifdef DEBUG
|
||||
bool
|
||||
CDMProxy::IsOnGMPThread()
|
||||
{
|
||||
return NS_GetCurrentThread() == mGMPThread;
|
||||
}
|
||||
#endif
|
||||
|
||||
void
|
||||
CDMProxy::gmp_Init(uint32_t aPromiseId)
|
||||
{
|
||||
MOZ_ASSERT(IsOnGMPThread());
|
||||
|
||||
nsCOMPtr<mozIGeckoMediaPluginService> mps =
|
||||
do_GetService("@mozilla.org/gecko-media-plugin-service;1");
|
||||
if (!mps) {
|
||||
RejectPromise(aPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR);
|
||||
return;
|
||||
}
|
||||
|
||||
nsTArray<nsCString> tags;
|
||||
tags.AppendElement(NS_ConvertUTF16toUTF8(mKeySystem));
|
||||
nsresult rv = mps->GetGMPDecryptor(&tags, GetOrigin(), &mCDM);
|
||||
if (NS_FAILED(rv) || !mCDM) {
|
||||
RejectPromise(aPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR);
|
||||
} else {
|
||||
mCallback = new CDMCallbackProxy(this);
|
||||
mCDM->Init(mCallback);
|
||||
nsRefPtr<nsIRunnable> task(NS_NewRunnableMethodWithArg<uint32_t>(this, &CDMProxy::OnCDMCreated, aPromiseId));
|
||||
NS_DispatchToMainThread(task);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
CDMProxy::OnCDMCreated(uint32_t aPromiseId)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
if (!mKeys.IsNull()) {
|
||||
mKeys->OnCDMCreated(aPromiseId);
|
||||
} else {
|
||||
NS_WARNING("CDMProxy unable to reject promise!");
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
CDMProxy::CreateSession(dom::SessionType aSessionType,
|
||||
@ -66,14 +119,38 @@ CDMProxy::CreateSession(dom::SessionType aSessionType,
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(mGMPThread);
|
||||
|
||||
// TODO: Dispatch task to GMPThread to call CDM CreateSession via IPC.
|
||||
nsAutoPtr<CreateSessionData> data(new CreateSessionData());
|
||||
data->mSessionType = aSessionType;
|
||||
data->mPromiseId = aPromiseId;
|
||||
data->mInitDataType = NS_ConvertUTF16toUTF8(aInitDataType);
|
||||
data->mInitData.AppendElements(aInitData.Data(), aInitData.Length());
|
||||
|
||||
// Make a fake session id. We'll get this from the CDM normally.
|
||||
nsAutoString id;
|
||||
id.AppendASCII("FakeSessionId_");
|
||||
id.AppendInt(sFakeSessionIdNum++);
|
||||
nsRefPtr<nsIRunnable> task(
|
||||
NS_NewRunnableMethodWithArg<nsAutoPtr<CreateSessionData>>(this, &CDMProxy::gmp_CreateSession, data));
|
||||
mGMPThread->Dispatch(task, NS_DISPATCH_NORMAL);
|
||||
}
|
||||
|
||||
mKeys->OnSessionActivated(aPromiseId, id);
|
||||
GMPSessionType
|
||||
ToGMPSessionType(dom::SessionType aSessionType) {
|
||||
switch (aSessionType) {
|
||||
case dom::SessionType::Temporary: return kGMPTemporySession;
|
||||
case dom::SessionType::Persistent: return kGMPPersistentSession;
|
||||
default: return kGMPTemporySession;
|
||||
};
|
||||
};
|
||||
|
||||
void
|
||||
CDMProxy::gmp_CreateSession(nsAutoPtr<CreateSessionData> aData)
|
||||
{
|
||||
MOZ_ASSERT(IsOnGMPThread());
|
||||
if (!mCDM) {
|
||||
RejectPromise(aData->mPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR);
|
||||
return;
|
||||
}
|
||||
mCDM->CreateSession(aData->mPromiseId,
|
||||
aData->mInitDataType,
|
||||
aData->mInitData,
|
||||
ToGMPSessionType(aData->mSessionType));
|
||||
}
|
||||
|
||||
void
|
||||
@ -83,25 +160,51 @@ CDMProxy::LoadSession(PromiseId aPromiseId,
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(mGMPThread);
|
||||
|
||||
// TODO: Dispatch task to GMPThread to call CDM LoadSession via IPC.
|
||||
// make MediaKeys::mPendingSessions CC'd
|
||||
nsAutoPtr<SessionOpData> data(new SessionOpData());
|
||||
data->mPromiseId = aPromiseId;
|
||||
data->mSessionId = NS_ConvertUTF16toUTF8(aSessionId);
|
||||
nsRefPtr<nsIRunnable> task(
|
||||
NS_NewRunnableMethodWithArg<nsAutoPtr<SessionOpData>>(this, &CDMProxy::gmp_LoadSession, data));
|
||||
mGMPThread->Dispatch(task, NS_DISPATCH_NORMAL);
|
||||
}
|
||||
|
||||
mKeys->OnSessionActivated(aPromiseId, aSessionId);
|
||||
void
|
||||
CDMProxy::gmp_LoadSession(nsAutoPtr<SessionOpData> aData)
|
||||
{
|
||||
MOZ_ASSERT(IsOnGMPThread());
|
||||
|
||||
if (!mCDM) {
|
||||
RejectPromise(aData->mPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR);
|
||||
return;
|
||||
}
|
||||
mCDM->LoadSession(aData->mPromiseId, aData->mSessionId);
|
||||
}
|
||||
|
||||
void
|
||||
CDMProxy::SetServerCertificate(PromiseId aPromiseId,
|
||||
const Uint8Array& aCertData)
|
||||
const Uint8Array& aCert)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(mGMPThread);
|
||||
|
||||
// TODO: Dispatch task to GMPThread to call CDM SetServerCertificate via IPC.
|
||||
|
||||
ResolvePromise(aPromiseId);
|
||||
nsAutoPtr<SetServerCertificateData> data;
|
||||
data->mPromiseId = aPromiseId;
|
||||
data->mCert.AppendElements(aCert.Data(), aCert.Length());
|
||||
nsRefPtr<nsIRunnable> task(
|
||||
NS_NewRunnableMethodWithArg<nsAutoPtr<SetServerCertificateData>>(this, &CDMProxy::gmp_SetServerCertificate, data));
|
||||
mGMPThread->Dispatch(task, NS_DISPATCH_NORMAL);
|
||||
}
|
||||
|
||||
static int sUpdateCount = 0;
|
||||
void
|
||||
CDMProxy::gmp_SetServerCertificate(nsAutoPtr<SetServerCertificateData> aData)
|
||||
{
|
||||
MOZ_ASSERT(IsOnGMPThread());
|
||||
if (!mCDM) {
|
||||
RejectPromise(aData->mPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR);
|
||||
return;
|
||||
}
|
||||
mCDM->SetServerCertificate(aData->mPromiseId, aData->mCert);
|
||||
}
|
||||
|
||||
void
|
||||
CDMProxy::UpdateSession(const nsAString& aSessionId,
|
||||
@ -112,15 +215,26 @@ CDMProxy::UpdateSession(const nsAString& aSessionId,
|
||||
MOZ_ASSERT(mGMPThread);
|
||||
NS_ENSURE_TRUE_VOID(!mKeys.IsNull());
|
||||
|
||||
// TODO: Dispatch task to GMPThread to call CDM UpdateSession via IPC.
|
||||
nsAutoPtr<UpdateSessionData> data(new UpdateSessionData());
|
||||
data->mPromiseId = aPromiseId;
|
||||
data->mSessionId = NS_ConvertUTF16toUTF8(aSessionId);
|
||||
data->mResponse.AppendElements(aResponse.Data(), aResponse.Length());
|
||||
nsRefPtr<nsIRunnable> task(
|
||||
NS_NewRunnableMethodWithArg<nsAutoPtr<UpdateSessionData>>(this, &CDMProxy::gmp_UpdateSession, data));
|
||||
mGMPThread->Dispatch(task, NS_DISPATCH_NORMAL);
|
||||
}
|
||||
|
||||
nsRefPtr<dom::MediaKeySession> session(mKeys->GetSession(aSessionId));
|
||||
nsAutoCString str(NS_LITERAL_CSTRING("Update_"));
|
||||
str.AppendInt(sUpdateCount++);
|
||||
nsTArray<uint8_t> msg;
|
||||
msg.AppendElements(str.get(), str.Length());
|
||||
session->DispatchKeyMessage(msg, NS_LITERAL_STRING("http://bogus.url"));
|
||||
ResolvePromise(aPromiseId);
|
||||
void
|
||||
CDMProxy::gmp_UpdateSession(nsAutoPtr<UpdateSessionData> aData)
|
||||
{
|
||||
MOZ_ASSERT(IsOnGMPThread());
|
||||
if (!mCDM) {
|
||||
RejectPromise(aData->mPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR);
|
||||
return;
|
||||
}
|
||||
mCDM->UpdateSession(aData->mPromiseId,
|
||||
aData->mSessionId,
|
||||
aData->mResponse);
|
||||
}
|
||||
|
||||
void
|
||||
@ -130,17 +244,27 @@ CDMProxy::CloseSession(const nsAString& aSessionId,
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
NS_ENSURE_TRUE_VOID(!mKeys.IsNull());
|
||||
|
||||
// TODO: Dispatch task to GMPThread to call CDM CloseSession via IPC.
|
||||
{
|
||||
CDMCaps::AutoLock caps(Capabilites());
|
||||
caps.DropKeysForSession(aSessionId);
|
||||
}
|
||||
nsAutoPtr<SessionOpData> data(new SessionOpData());
|
||||
data->mPromiseId = aPromiseId;
|
||||
data->mSessionId = NS_ConvertUTF16toUTF8(aSessionId);
|
||||
nsRefPtr<nsIRunnable> task(
|
||||
NS_NewRunnableMethodWithArg<nsAutoPtr<SessionOpData>>(this, &CDMProxy::gmp_CloseSession, data));
|
||||
mGMPThread->Dispatch(task, NS_DISPATCH_NORMAL);
|
||||
}
|
||||
|
||||
nsRefPtr<dom::MediaKeySession> session(mKeys->GetSession(aSessionId));
|
||||
|
||||
// Pretend that the CDM actually does close the session...
|
||||
// "...the [MediaKeySession's] closed attribute promise is resolved
|
||||
// when the session is closed."
|
||||
session->OnClosed();
|
||||
|
||||
// "The promise is resolved when the request has been processed."
|
||||
ResolvePromise(aPromiseId);
|
||||
void
|
||||
CDMProxy::gmp_CloseSession(nsAutoPtr<SessionOpData> aData)
|
||||
{
|
||||
MOZ_ASSERT(IsOnGMPThread());
|
||||
if (!mCDM) {
|
||||
RejectPromise(aData->mPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR);
|
||||
return;
|
||||
}
|
||||
mCDM->CloseSession(aData->mPromiseId, aData->mSessionId);
|
||||
}
|
||||
|
||||
void
|
||||
@ -148,12 +272,29 @@ CDMProxy::RemoveSession(const nsAString& aSessionId,
|
||||
PromiseId aPromiseId)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
NS_ENSURE_TRUE_VOID(!mKeys.IsNull());
|
||||
|
||||
// TODO: Dispatch task to GMPThread to call CDM RemoveSession via IPC.
|
||||
{
|
||||
CDMCaps::AutoLock caps(Capabilites());
|
||||
caps.DropKeysForSession(aSessionId);
|
||||
}
|
||||
nsAutoPtr<SessionOpData> data(new SessionOpData());
|
||||
data->mPromiseId = aPromiseId;
|
||||
data->mSessionId = NS_ConvertUTF16toUTF8(aSessionId);
|
||||
nsRefPtr<nsIRunnable> task(
|
||||
NS_NewRunnableMethodWithArg<nsAutoPtr<SessionOpData>>(this, &CDMProxy::gmp_RemoveSession, data));
|
||||
mGMPThread->Dispatch(task, NS_DISPATCH_NORMAL);
|
||||
}
|
||||
|
||||
// Assume CDM immediately removes session's data, then close the session
|
||||
// as per the spec.
|
||||
CloseSession(aSessionId, aPromiseId);
|
||||
void
|
||||
CDMProxy::gmp_RemoveSession(nsAutoPtr<SessionOpData> aData)
|
||||
{
|
||||
MOZ_ASSERT(IsOnGMPThread());
|
||||
if (!mCDM) {
|
||||
RejectPromise(aData->mPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR);
|
||||
return;
|
||||
}
|
||||
mCDM->RemoveSession(aData->mPromiseId, aData->mSessionId);
|
||||
}
|
||||
|
||||
void
|
||||
@ -196,4 +337,158 @@ CDMProxy::ResolvePromise(PromiseId aId)
|
||||
}
|
||||
}
|
||||
|
||||
const nsAString&
|
||||
CDMProxy::GetOrigin() const
|
||||
{
|
||||
return mOrigin;
|
||||
}
|
||||
|
||||
void
|
||||
CDMProxy::OnResolveNewSessionPromise(uint32_t aPromiseId,
|
||||
const nsAString& aSessionId)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
mKeys->OnSessionCreated(aPromiseId, aSessionId);
|
||||
}
|
||||
|
||||
void
|
||||
CDMProxy::OnSessionMessage(const nsAString& aSessionId,
|
||||
nsTArray<uint8_t>& aMessage,
|
||||
const nsAString& aDestinationURL)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
nsRefPtr<dom::MediaKeySession> session(mKeys->GetSession(aSessionId));
|
||||
if (session) {
|
||||
session->DispatchKeyMessage(aMessage, aDestinationURL);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
CDMProxy::OnExpirationChange(const nsAString& aSessionId,
|
||||
GMPTimestamp aExpiryTime)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
NS_WARNING("CDMProxy::OnExpirationChange() not implemented");
|
||||
}
|
||||
|
||||
void
|
||||
CDMProxy::OnSessionClosed(const nsAString& aSessionId)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
NS_WARNING("CDMProxy::OnSessionClosed() not implemented");
|
||||
}
|
||||
|
||||
static void
|
||||
LogToConsole(const nsAString& aMsg)
|
||||
{
|
||||
nsCOMPtr<nsIConsoleService> console(
|
||||
do_GetService("@mozilla.org/consoleservice;1"));
|
||||
if (!console) {
|
||||
NS_WARNING("Failed to log message to console.");
|
||||
return;
|
||||
}
|
||||
nsAutoString msg(aMsg);
|
||||
console->LogStringMessage(msg.get());
|
||||
}
|
||||
|
||||
void
|
||||
CDMProxy::OnSessionError(const nsAString& aSessionId,
|
||||
nsresult aException,
|
||||
uint32_t aSystemCode,
|
||||
const nsAString& aMsg)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
nsRefPtr<dom::MediaKeySession> session(mKeys->GetSession(aSessionId));
|
||||
if (session) {
|
||||
session->DispatchKeyError(aSystemCode);
|
||||
}
|
||||
LogToConsole(aMsg);
|
||||
}
|
||||
|
||||
void
|
||||
CDMProxy::OnRejectPromise(uint32_t aPromiseId,
|
||||
nsresult aDOMException,
|
||||
const nsAString& aMsg)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
RejectPromise(aPromiseId, aDOMException);
|
||||
LogToConsole(aMsg);
|
||||
}
|
||||
|
||||
const nsString&
|
||||
CDMProxy::KeySystem() const
|
||||
{
|
||||
return mKeySystem;
|
||||
}
|
||||
|
||||
CDMCaps&
|
||||
CDMProxy::Capabilites() {
|
||||
return mCapabilites;
|
||||
}
|
||||
|
||||
void
|
||||
CDMProxy::Decrypt(mp4_demuxer::MP4Sample* aSample,
|
||||
DecryptionClient* aClient)
|
||||
{
|
||||
nsAutoPtr<DecryptJob> job(new DecryptJob(aSample, aClient));
|
||||
nsRefPtr<nsIRunnable> task(
|
||||
NS_NewRunnableMethodWithArg<nsAutoPtr<DecryptJob>>(this, &CDMProxy::gmp_Decrypt, job));
|
||||
mGMPThread->Dispatch(task, NS_DISPATCH_NORMAL);
|
||||
}
|
||||
|
||||
void
|
||||
CDMProxy::gmp_Decrypt(nsAutoPtr<DecryptJob> aJob)
|
||||
{
|
||||
MOZ_ASSERT(IsOnGMPThread());
|
||||
MOZ_ASSERT(aJob->mClient);
|
||||
MOZ_ASSERT(aJob->mSample);
|
||||
|
||||
if (!mCDM) {
|
||||
aJob->mClient->Decrypted(NS_ERROR_FAILURE, nullptr);
|
||||
return;
|
||||
}
|
||||
|
||||
aJob->mId = ++mDecryptionJobCount;
|
||||
nsTArray<uint8_t> data;
|
||||
data.AppendElements(aJob->mSample->data, aJob->mSample->size);
|
||||
mCDM->Decrypt(aJob->mId, aJob->mSample->crypto, data);
|
||||
mDecryptionJobs.AppendElement(aJob.forget());
|
||||
}
|
||||
|
||||
void
|
||||
CDMProxy::gmp_Decrypted(uint32_t aId,
|
||||
GMPErr aResult,
|
||||
const nsTArray<uint8_t>& aDecryptedData)
|
||||
{
|
||||
MOZ_ASSERT(IsOnGMPThread());
|
||||
for (size_t i = 0; i < mDecryptionJobs.Length(); i++) {
|
||||
DecryptJob* job = mDecryptionJobs[i];
|
||||
if (job->mId == aId) {
|
||||
if (aDecryptedData.Length() != job->mSample->size) {
|
||||
NS_WARNING("CDM returned incorrect number of decrypted bytes");
|
||||
}
|
||||
PodCopy(job->mSample->data,
|
||||
aDecryptedData.Elements(),
|
||||
std::min<size_t>(aDecryptedData.Length(), job->mSample->size));
|
||||
nsresult rv = GMP_SUCCEEDED(aResult) ? NS_OK : NS_ERROR_FAILURE;
|
||||
job->mClient->Decrypted(rv, job->mSample.forget());
|
||||
mDecryptionJobs.RemoveElementAt(i);
|
||||
return;
|
||||
} else {
|
||||
NS_WARNING("GMPDecryptorChild returned incorrect job ID");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
CDMProxy::gmp_Terminated()
|
||||
{
|
||||
MOZ_ASSERT(IsOnGMPThread());
|
||||
EME_LOG("CDM terminated");
|
||||
if (mCDM) {
|
||||
mCDM->Close();
|
||||
mCDM = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
||||
|
@ -9,22 +9,30 @@
|
||||
|
||||
#include "nsString.h"
|
||||
#include "nsAutoPtr.h"
|
||||
#include "nsProxyRelease.h"
|
||||
#include "mozilla/dom/MediaKeys.h"
|
||||
#include "mozilla/dom/TypedArray.h"
|
||||
|
||||
class nsIThread;
|
||||
#include "mozilla/Monitor.h"
|
||||
#include "nsIThread.h"
|
||||
#include "GMPDecryptorProxy.h"
|
||||
#include "mozilla/CDMCaps.h"
|
||||
#include "mp4_demuxer/DecoderData.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
class CDMCallbackProxy;
|
||||
|
||||
namespace dom {
|
||||
class MediaKeySession;
|
||||
}
|
||||
|
||||
// A placeholder proxy to the CDM.
|
||||
// TODO: The functions here need to do IPC to talk to the CDM via the
|
||||
// Gecko Media Plugin API, which we'll need to extend for H.264 and EME
|
||||
// content.
|
||||
class DecryptionClient {
|
||||
public:
|
||||
virtual ~DecryptionClient() {}
|
||||
virtual void Decrypted(nsresult aResult,
|
||||
mp4_demuxer::MP4Sample* aSample) = 0;
|
||||
};
|
||||
|
||||
// Proxies calls GMP/CDM, and proxies calls back.
|
||||
// Note: Promises are passed in via a PromiseId, so that the ID can be
|
||||
// passed via IPC to the CDM, which can then signal when to reject or
|
||||
// resolve the promise using its PromiseId.
|
||||
@ -95,8 +103,124 @@ public:
|
||||
// Main thread only.
|
||||
void Shutdown();
|
||||
|
||||
// Threadsafe.
|
||||
const nsAString& GetOrigin() const;
|
||||
|
||||
// Main thread only.
|
||||
void OnResolveNewSessionPromise(uint32_t aPromiseId,
|
||||
const nsAString& aSessionId);
|
||||
|
||||
// Main thread only.
|
||||
void OnSessionMessage(const nsAString& aSessionId,
|
||||
nsTArray<uint8_t>& aMessage,
|
||||
const nsAString& aDestinationURL);
|
||||
|
||||
// Main thread only.
|
||||
void OnExpirationChange(const nsAString& aSessionId,
|
||||
GMPTimestamp aExpiryTime);
|
||||
|
||||
// Main thread only.
|
||||
void OnSessionClosed(const nsAString& aSessionId);
|
||||
|
||||
// Main thread only.
|
||||
void OnSessionError(const nsAString& aSessionId,
|
||||
nsresult aException,
|
||||
uint32_t aSystemCode,
|
||||
const nsAString& aMsg);
|
||||
|
||||
// Main thread only.
|
||||
void OnRejectPromise(uint32_t aPromiseId,
|
||||
nsresult aDOMException,
|
||||
const nsAString& aMsg);
|
||||
|
||||
// Threadsafe.
|
||||
void Decrypt(mp4_demuxer::MP4Sample* aSample,
|
||||
DecryptionClient* aSink);
|
||||
|
||||
// Reject promise with DOMException corresponding to aExceptionCode.
|
||||
// Can be called from any thread.
|
||||
void RejectPromise(PromiseId aId, nsresult aExceptionCode);
|
||||
|
||||
// Resolves promise with "undefined".
|
||||
// Can be called from any thread.
|
||||
void ResolvePromise(PromiseId aId);
|
||||
|
||||
// Threadsafe.
|
||||
const nsString& KeySystem() const;
|
||||
|
||||
// GMP thread only.
|
||||
void gmp_Decrypted(uint32_t aId,
|
||||
GMPErr aResult,
|
||||
const nsTArray<uint8_t>& aDecryptedData);
|
||||
|
||||
// GMP thread only.
|
||||
void gmp_Terminated();
|
||||
|
||||
CDMCaps& Capabilites();
|
||||
|
||||
#ifdef DEBUG
|
||||
bool IsOnGMPThread();
|
||||
#endif
|
||||
|
||||
private:
|
||||
|
||||
// GMP thread only.
|
||||
void gmp_Init(uint32_t aPromiseId);
|
||||
|
||||
// Main thread only.
|
||||
void OnCDMCreated(uint32_t aPromiseId);
|
||||
|
||||
struct CreateSessionData {
|
||||
dom::SessionType mSessionType;
|
||||
PromiseId mPromiseId;
|
||||
nsAutoCString mInitDataType;
|
||||
nsTArray<uint8_t> mInitData;
|
||||
};
|
||||
// GMP thread only.
|
||||
void gmp_CreateSession(nsAutoPtr<CreateSessionData> aData);
|
||||
|
||||
struct SessionOpData {
|
||||
PromiseId mPromiseId;
|
||||
nsAutoCString mSessionId;
|
||||
};
|
||||
// GMP thread only.
|
||||
void gmp_LoadSession(nsAutoPtr<SessionOpData> aData);
|
||||
|
||||
struct SetServerCertificateData {
|
||||
PromiseId mPromiseId;
|
||||
nsTArray<uint8_t> mCert;
|
||||
};
|
||||
// GMP thread only.
|
||||
void gmp_SetServerCertificate(nsAutoPtr<SetServerCertificateData> aData);
|
||||
|
||||
struct UpdateSessionData {
|
||||
PromiseId mPromiseId;
|
||||
nsAutoCString mSessionId;
|
||||
nsTArray<uint8_t> mResponse;
|
||||
};
|
||||
// GMP thread only.
|
||||
void gmp_UpdateSession(nsAutoPtr<UpdateSessionData> aData);
|
||||
|
||||
// GMP thread only.
|
||||
void gmp_CloseSession(nsAutoPtr<SessionOpData> aData);
|
||||
|
||||
// GMP thread only.
|
||||
void gmp_RemoveSession(nsAutoPtr<SessionOpData> aData);
|
||||
|
||||
struct DecryptJob {
|
||||
DecryptJob(mp4_demuxer::MP4Sample* aSample,
|
||||
DecryptionClient* aClient)
|
||||
: mId(0)
|
||||
, mSample(aSample)
|
||||
, mClient(aClient)
|
||||
{}
|
||||
uint32_t mId;
|
||||
nsAutoPtr<mp4_demuxer::MP4Sample> mSample;
|
||||
nsAutoPtr<DecryptionClient> mClient;
|
||||
};
|
||||
// GMP thread only.
|
||||
void gmp_Decrypt(nsAutoPtr<DecryptJob> aJob);
|
||||
|
||||
class RejectPromiseTask : public nsRunnable {
|
||||
public:
|
||||
RejectPromiseTask(CDMProxy* aProxy,
|
||||
@ -117,13 +241,6 @@ private:
|
||||
nsresult mCode;
|
||||
};
|
||||
|
||||
// Reject promise with DOMException corresponding to aExceptionCode.
|
||||
// Can be called from any thread.
|
||||
void RejectPromise(PromiseId aId, nsresult aExceptionCode);
|
||||
// Resolves promise with "undefined".
|
||||
// Can be called from any thread.
|
||||
void ResolvePromise(PromiseId aId);
|
||||
|
||||
~CDMProxy();
|
||||
|
||||
// Helper to enforce that a raw pointer is only accessed on the main thread.
|
||||
@ -164,8 +281,26 @@ private:
|
||||
// Gecko Media Plugin thread. All interactions with the out-of-process
|
||||
// EME plugin must come from this thread.
|
||||
nsRefPtr<nsIThread> mGMPThread;
|
||||
|
||||
nsAutoString mOrigin;
|
||||
|
||||
GMPDecryptorProxy* mCDM;
|
||||
CDMCaps mCapabilites;
|
||||
nsAutoPtr<CDMCallbackProxy> mCallback;
|
||||
|
||||
// Decryption jobs sent to CDM, awaiting result.
|
||||
// GMP thread only.
|
||||
nsTArray<nsAutoPtr<DecryptJob>> mDecryptionJobs;
|
||||
|
||||
// Number of buffers we've decrypted. Used to uniquely identify
|
||||
// decryption jobs sent to CDM. Note we can't just use the length of
|
||||
// mDecryptionJobs as that shrinks as jobs are completed and removed
|
||||
// from it.
|
||||
// GMP thread only.
|
||||
uint32_t mDecryptionJobCount;
|
||||
};
|
||||
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // CDMProxy_h_
|
||||
|
@ -4,32 +4,45 @@
|
||||
* 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/. */
|
||||
|
||||
#ifndef EME_LOG_H_
|
||||
#define EME_LOG_H_
|
||||
|
||||
#include "prlog.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
#ifdef PR_LOGGING
|
||||
|
||||
#ifndef EME_LOG
|
||||
PRLogModuleInfo* GetEMELog();
|
||||
#define EME_LOG(...) PR_LOG(GetEMELog(), PR_LOG_DEBUG, (__VA_ARGS__))
|
||||
#endif
|
||||
#ifndef EME_LOG
|
||||
PRLogModuleInfo* GetEMELog();
|
||||
#define EME_LOG(...) PR_LOG(GetEMELog(), PR_LOG_DEBUG, (__VA_ARGS__))
|
||||
#endif
|
||||
|
||||
#ifndef EME_VERBOSE_LOG
|
||||
PRLogModuleInfo* GetEMEVerboseLog();
|
||||
#define EME_VERBOSE_LOG(...) PR_LOG(GetEMEVerboseLog(), PR_LOG_DEBUG, (__VA_ARGS__))
|
||||
#ifndef EME_VERBOSE_LOG
|
||||
PRLogModuleInfo* GetEMEVerboseLog();
|
||||
#define EME_VERBOSE_LOG(...) PR_LOG(GetEMEVerboseLog(), PR_LOG_DEBUG, (__VA_ARGS__))
|
||||
#else
|
||||
#ifndef EME_LOG
|
||||
#define EME_LOG(...)
|
||||
#endif
|
||||
|
||||
#ifndef EME_VERBOSE_LOG
|
||||
#define EME_VERBOSE_LOG(...)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#else
|
||||
|
||||
#ifndef EME_LOG
|
||||
#define EME_LOG(...)
|
||||
#endif
|
||||
#ifndef EME_LOG
|
||||
#define EME_LOG(...)
|
||||
#endif
|
||||
|
||||
#ifndef EME_VERBOSE_LOG
|
||||
#define EME_VERBOSE_LOG(...)
|
||||
#endif
|
||||
|
||||
#endif
|
||||
#ifndef EME_VERBOSE_LOG
|
||||
#define EME_VERBOSE_LOG(...)
|
||||
#endif
|
||||
|
||||
#endif // PR_LOGGING
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // EME_LOG_H_
|
||||
|
@ -16,6 +16,7 @@ MediaKeyError::MediaKeyError(EventTarget* aOwner, uint32_t aSystemCode)
|
||||
, mSystemCode(aSystemCode)
|
||||
{
|
||||
SetIsDOMBinding();
|
||||
InitEvent(NS_LITERAL_STRING("error"), false, false);
|
||||
}
|
||||
|
||||
MediaKeyError::~MediaKeyError()
|
||||
|
@ -67,7 +67,13 @@ MediaKeySession::GetKeySystem(nsString& aKeySystem) const
|
||||
void
|
||||
MediaKeySession::GetSessionId(nsString& aSessionId) const
|
||||
{
|
||||
aSessionId = mSessionId;
|
||||
aSessionId = GetSessionId();
|
||||
}
|
||||
|
||||
const nsString&
|
||||
MediaKeySession::GetSessionId() const
|
||||
{
|
||||
return mSessionId;
|
||||
}
|
||||
|
||||
JSObject*
|
||||
@ -166,7 +172,7 @@ MediaKeySession::Remove(ErrorResult& aRv)
|
||||
|
||||
void
|
||||
MediaKeySession::DispatchKeyMessage(const nsTArray<uint8_t>& aMessage,
|
||||
const nsString& aURL)
|
||||
const nsAString& aURL)
|
||||
{
|
||||
nsRefPtr<MediaKeyMessageEvent> event(
|
||||
MediaKeyMessageEvent::Constructor(this, aURL, aMessage));
|
||||
|
@ -53,6 +53,8 @@ public:
|
||||
|
||||
void GetSessionId(nsString& aRetval) const;
|
||||
|
||||
const nsString& GetSessionId() const;
|
||||
|
||||
// Number of ms since epoch at which expiration occurs, or NaN if unknown.
|
||||
// TODO: The type of this attribute is still under contention.
|
||||
// https://www.w3.org/Bugs/Public/show_bug.cgi?id=25902
|
||||
@ -68,7 +70,7 @@ public:
|
||||
already_AddRefed<Promise> Remove(ErrorResult& aRv);
|
||||
|
||||
void DispatchKeyMessage(const nsTArray<uint8_t>& aMessage,
|
||||
const nsString& aURL);
|
||||
const nsAString& aURL);
|
||||
|
||||
void DispatchKeyError(uint32_t system_code);
|
||||
|
||||
|
@ -12,8 +12,13 @@
|
||||
#include "mozilla/dom/MediaKeySession.h"
|
||||
#include "mozilla/dom/DOMException.h"
|
||||
#include "mozilla/CDMProxy.h"
|
||||
#include "mozilla/EMELog.h"
|
||||
#include "nsContentUtils.h"
|
||||
#include "EMELog.h"
|
||||
#include "nsIScriptObjectPrincipal.h"
|
||||
#include "mozilla/Preferences.h"
|
||||
#ifdef XP_WIN
|
||||
#include "mozilla/WindowsVersion.h"
|
||||
#endif
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
@ -32,8 +37,9 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(MediaKeys)
|
||||
NS_INTERFACE_MAP_END
|
||||
|
||||
MediaKeys::MediaKeys(nsPIDOMWindow* aParent, const nsAString& aKeySystem)
|
||||
: mParent(aParent),
|
||||
mKeySystem(aKeySystem)
|
||||
: mParent(aParent)
|
||||
, mKeySystem(aKeySystem)
|
||||
, mCreatePromiseId(0)
|
||||
{
|
||||
SetIsDOMBinding();
|
||||
}
|
||||
@ -76,6 +82,18 @@ MediaKeys::SetServerCertificate(const Uint8Array& aCert, ErrorResult& aRv)
|
||||
return promise.forget();
|
||||
}
|
||||
|
||||
static bool
|
||||
IsSupportedKeySystem(const nsAString& aKeySystem)
|
||||
{
|
||||
return aKeySystem.EqualsASCII("org.w3.clearkey") ||
|
||||
#ifdef XP_WIN
|
||||
(aKeySystem.EqualsASCII("com.adobe.access") &&
|
||||
IsVistaOrLater() &&
|
||||
Preferences::GetBool("media.eme.adobe-access.enabled", false)) ||
|
||||
#endif
|
||||
false;
|
||||
}
|
||||
|
||||
/* static */
|
||||
IsTypeSupportedResult
|
||||
MediaKeys::IsTypeSupported(const GlobalObject& aGlobal,
|
||||
@ -84,10 +102,10 @@ MediaKeys::IsTypeSupported(const GlobalObject& aGlobal,
|
||||
const Optional<nsAString>& aContentType,
|
||||
const Optional<nsAString>& aCapability)
|
||||
{
|
||||
// TODO: Query list of known CDMs and their supported content types.
|
||||
// TODO: Should really get spec changed to this is async, so we can wait
|
||||
// for user to consent to running plugin.
|
||||
return IsTypeSupportedResult::Maybe;
|
||||
return IsSupportedKeySystem(aKeySystem) ? IsTypeSupportedResult::Maybe
|
||||
: IsTypeSupportedResult::_empty;
|
||||
}
|
||||
|
||||
already_AddRefed<Promise>
|
||||
@ -115,6 +133,7 @@ MediaKeys::StorePromise(Promise* aPromise)
|
||||
already_AddRefed<Promise>
|
||||
MediaKeys::RetrievePromise(PromiseId aId)
|
||||
{
|
||||
MOZ_ASSERT(mPromises.Contains(aId));
|
||||
nsRefPtr<Promise> promise;
|
||||
mPromises.Remove(aId, getter_AddRefs(promise));
|
||||
return promise.forget();
|
||||
@ -138,6 +157,11 @@ MediaKeys::RejectPromise(PromiseId aId, nsresult aExceptionCode)
|
||||
|
||||
MOZ_ASSERT(NS_FAILED(aExceptionCode));
|
||||
promise->MaybeReject(aExceptionCode);
|
||||
|
||||
if (mCreatePromiseId == aId) {
|
||||
// Note: This will probably destroy the MediaKeys object!
|
||||
Release();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
@ -148,10 +172,24 @@ MediaKeys::ResolvePromise(PromiseId aId)
|
||||
NS_WARNING("MediaKeys tried to resolve a non-existent promise");
|
||||
return;
|
||||
}
|
||||
// We should not resolve CreateSession or LoadSession calls via this path,
|
||||
// OnSessionActivated() should be called instead.
|
||||
MOZ_ASSERT(!mPendingSessions.Contains(aId));
|
||||
promise->MaybeResolve(JS::UndefinedHandleValue);
|
||||
if (mPendingSessions.Contains(aId)) {
|
||||
// We should only resolve LoadSession calls via this path,
|
||||
// not CreateSession() promises.
|
||||
nsRefPtr<MediaKeySession> session;
|
||||
if (!mPendingSessions.Get(aId, getter_AddRefs(session)) ||
|
||||
!session ||
|
||||
session->GetSessionId().IsEmpty()) {
|
||||
NS_WARNING("Received activation for non-existent session!");
|
||||
promise->MaybeReject(NS_ERROR_DOM_INVALID_ACCESS_ERR);
|
||||
mPendingSessions.Remove(aId);
|
||||
return;
|
||||
}
|
||||
mPendingSessions.Remove(aId);
|
||||
mKeySessions.Put(session->GetSessionId(), session);
|
||||
promise->MaybeResolve(session);
|
||||
} else {
|
||||
promise->MaybeResolve(JS::UndefinedHandleValue);
|
||||
}
|
||||
}
|
||||
|
||||
/* static */
|
||||
@ -174,13 +212,25 @@ MediaKeys::Create(const GlobalObject& aGlobal,
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (!aKeySystem.EqualsASCII("org.w3.clearkey")) {
|
||||
if (!IsSupportedKeySystem(aKeySystem)) {
|
||||
aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
keys->mProxy = new CDMProxy(keys, aKeySystem);
|
||||
keys->mProxy->Init(keys->StorePromise(promise));
|
||||
|
||||
// The CDMProxy's initialization is asynchronous. The MediaKeys is
|
||||
// refcounted, and its instance is returned to JS by promise once
|
||||
// it's been initialized. No external refs exist to the MediaKeys while
|
||||
// we're waiting for the promise to be resolved, so we must hold a
|
||||
// reference to the new MediaKeys object until it's been created,
|
||||
// or its creation has failed. Store the id of the promise returned
|
||||
// here, and hold a self-reference until that promise is resolved or
|
||||
// rejected.
|
||||
MOZ_ASSERT(!keys->mCreatePromiseId, "Should only be created once!");
|
||||
keys->mCreatePromiseId = keys->StorePromise(promise);
|
||||
keys->AddRef();
|
||||
keys->mProxy->Init(keys->mCreatePromiseId);
|
||||
|
||||
return promise.forget();
|
||||
}
|
||||
@ -195,6 +245,9 @@ MediaKeys::OnCDMCreated(PromiseId aId)
|
||||
}
|
||||
nsRefPtr<MediaKeys> keys(this);
|
||||
promise->MaybeResolve(keys);
|
||||
if (mCreatePromiseId == aId) {
|
||||
Release();
|
||||
}
|
||||
}
|
||||
|
||||
already_AddRefed<Promise>
|
||||
@ -224,9 +277,10 @@ MediaKeys::LoadSession(const nsAString& aSessionId, ErrorResult& aRv)
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Proxy owns session object until resolving promise.
|
||||
mProxy->LoadSession(StorePromise(promise),
|
||||
aSessionId);
|
||||
session->Init(aSessionId);
|
||||
auto pid = StorePromise(promise);
|
||||
mPendingSessions.Put(pid, session);
|
||||
mProxy->LoadSession(pid, aSessionId);
|
||||
|
||||
return promise.forget();
|
||||
}
|
||||
@ -262,7 +316,7 @@ MediaKeys::CreateSession(const nsAString& initDataType,
|
||||
}
|
||||
|
||||
void
|
||||
MediaKeys::OnSessionActivated(PromiseId aId, const nsAString& aSessionId)
|
||||
MediaKeys::OnSessionCreated(PromiseId aId, const nsAString& aSessionId)
|
||||
{
|
||||
nsRefPtr<Promise> promise(RetrievePromise(aId));
|
||||
if (!promise) {
|
||||
@ -272,16 +326,17 @@ MediaKeys::OnSessionActivated(PromiseId aId, const nsAString& aSessionId)
|
||||
MOZ_ASSERT(mPendingSessions.Contains(aId));
|
||||
|
||||
nsRefPtr<MediaKeySession> session;
|
||||
if (!mPendingSessions.Get(aId, getter_AddRefs(session)) || !session) {
|
||||
bool gotSession = mPendingSessions.Get(aId, getter_AddRefs(session));
|
||||
// Session has completed creation/loading, remove it from mPendingSessions,
|
||||
// and resolve the promise with it. We store it in mKeySessions, so we can
|
||||
// find it again if we need to send messages to it etc.
|
||||
mPendingSessions.Remove(aId);
|
||||
if (!gotSession || !session) {
|
||||
NS_WARNING("Received activation for non-existent session!");
|
||||
promise->MaybeReject(NS_ERROR_DOM_INVALID_ACCESS_ERR);
|
||||
return;
|
||||
}
|
||||
|
||||
// Session has completed creation/loading, remove it from mPendingSessions,
|
||||
// and resolve the promise with it. We store it in mKeySessions, so we can
|
||||
// find it again if we need to send messages to it etc.
|
||||
mPendingSessions.Remove(aId);
|
||||
session->Init(aSessionId);
|
||||
mKeySessions.Put(aSessionId, session);
|
||||
promise->MaybeResolve(session);
|
||||
@ -303,5 +358,27 @@ MediaKeys::GetSession(const nsAString& aSessionId)
|
||||
return session.forget();
|
||||
}
|
||||
|
||||
nsresult
|
||||
MediaKeys::GetOrigin(nsString& aOutOrigin)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
// TODO: Bug 1035637, return a combination of origin and URL bar origin.
|
||||
|
||||
nsIPrincipal* principal = nullptr;
|
||||
nsCOMPtr<nsPIDOMWindow> pWindow = do_QueryInterface(GetParentObject());
|
||||
nsCOMPtr<nsIScriptObjectPrincipal> scriptPrincipal =
|
||||
do_QueryInterface(pWindow);
|
||||
if (scriptPrincipal) {
|
||||
principal = scriptPrincipal->GetPrincipal();
|
||||
}
|
||||
NS_ENSURE_TRUE(principal, NS_ERROR_FAILURE);
|
||||
|
||||
nsresult res = nsContentUtils::GetUTFOrigin(principal, aOutOrigin);
|
||||
|
||||
EME_LOG("EME Origin = '%s'", NS_ConvertUTF16toUTF8(aOutOrigin).get());
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
@ -83,7 +83,7 @@ public:
|
||||
// Called once a Create() operation succeeds.
|
||||
void OnCDMCreated(PromiseId aId);
|
||||
// Called once a CreateSession or LoadSession succeeds.
|
||||
void OnSessionActivated(PromiseId aId, const nsAString& aSessionId);
|
||||
void OnSessionCreated(PromiseId aId, const nsAString& aSessionId);
|
||||
// Called once a session has closed.
|
||||
void OnSessionClosed(MediaKeySession* aSession);
|
||||
|
||||
@ -101,6 +101,8 @@ public:
|
||||
// Resolves promise with "undefined".
|
||||
void ResolvePromise(PromiseId aId);
|
||||
|
||||
nsresult GetOrigin(nsString& aOutOrigin);
|
||||
|
||||
private:
|
||||
|
||||
// Removes promise from mPromises, and returns it.
|
||||
@ -115,6 +117,7 @@ private:
|
||||
KeySessionHashMap mKeySessions;
|
||||
PromiseHashMap mPromises;
|
||||
PendingKeySessionsHashMap mPendingSessions;
|
||||
PromiseId mCreatePromiseId;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
|
@ -13,10 +13,15 @@ EXPORTS.mozilla.dom += [
|
||||
]
|
||||
|
||||
EXPORTS.mozilla += [
|
||||
'CDMCallbackProxy.h',
|
||||
'CDMCaps.h',
|
||||
'CDMProxy.h',
|
||||
'EMELog.h'
|
||||
]
|
||||
|
||||
UNIFIED_SOURCES += [
|
||||
'CDMCallbackProxy.cpp',
|
||||
'CDMCaps.cpp',
|
||||
'CDMProxy.cpp',
|
||||
'EMELog.cpp',
|
||||
'MediaKeyError.cpp',
|
||||
|
@ -7,6 +7,7 @@
|
||||
#define GMPDecryptorProxy_h_
|
||||
|
||||
#include "GMPCallbackBase.h"
|
||||
#include "gmp-decryption.h"
|
||||
|
||||
namespace mp4_demuxer {
|
||||
class CryptoSample;
|
||||
|
Loading…
Reference in New Issue
Block a user