Bug 1306314 - Add an ID to GMPDecryptor instances, reflect that on CDMProxy. r=gerald
This enables us to identify GMPDecryptor instances in the child process, so that
in a later patch when we create a GMPVideoDecoder instance, we can associate it
with a GMPDecryptor. Then the cdm::ContentDecryptionModule8 instance that these
two actors are adapted to can know whom it's supposed to respond to.
We use the IPDL PGMPDecryptorChild actor ID as the GMPDecryptor's ID. This is unique
per GMP process, which is sufficient.
MozReview-Commit-ID: 7NKND9VjPUW
--- a/dom/media/eme/CDMProxy.h
+++ b/dom/media/eme/CDMProxy.h
@@ -93,16 +93,18 @@ public:
// Loads the CDM corresponding to mKeySystem.
// Calls MediaKeys::OnCDMCreated() when the CDM is created.
virtual void Init(PromiseId aPromiseId,
const nsAString& aOrigin,
const nsAString& aTopLevelOrigin,
const nsAString& aName,
bool aInPrivateBrowsing) = 0;
+ virtual void OnSetDecryptorId(uint32_t aId) {}
+
// Main thread only.
// Uses the CDM to create a key session.
// Calls MediaKeys::OnSessionActivated() when session is created.
// Assumes ownership of (Move()s) aInitData's contents.
virtual void CreateSession(uint32_t aCreateSessionToken,
MediaKeySessionType aSessionType,
PromiseId aPromiseId,
const nsAString& aInitDataType,
@@ -214,16 +216,18 @@ public:
virtual void GetSessionIdsForKeyId(const nsTArray<uint8_t>& aKeyId,
nsTArray<nsCString>& aSessionIds) = 0;
#ifdef DEBUG
virtual bool IsOnOwnerThread() = 0;
#endif
+ virtual uint32_t GetDecryptorId() { return 0; }
+
protected:
virtual ~CDMProxy() {}
// Helper to enforce that a raw pointer is only accessed on the main thread.
template<class Type>
class MainThreadOnlyRawPtr {
public:
explicit MainThreadOnlyRawPtr(Type* aPtr)
--- a/dom/media/eme/DecryptorProxyCallback.h
+++ b/dom/media/eme/DecryptorProxyCallback.h
@@ -10,16 +10,18 @@
#include "mozilla/dom/MediaKeyMessageEventBinding.h" // For MediaKeyMessageType
#include "mozilla/CDMProxy.h"
class DecryptorProxyCallback {
public:
virtual ~DecryptorProxyCallback() {}
+ virtual void SetDecryptorId(uint32_t aId) = 0;
+
virtual void SetSessionId(uint32_t aCreateSessionId,
const nsCString& aSessionId) = 0;
virtual void ResolveLoadSessionPromise(uint32_t aPromiseId,
bool aSuccess) = 0;
virtual void ResolvePromise(uint32_t aPromiseId) = 0;
--- a/dom/media/eme/mediadrm/MediaDrmCDMCallbackProxy.h
+++ b/dom/media/eme/mediadrm/MediaDrmCDMCallbackProxy.h
@@ -12,16 +12,19 @@
namespace mozilla {
class CDMProxy;
// Proxies call backs from the MediaDrmProxy -> MediaDrmProxySupport back to the MediaKeys
// object on the main thread.
// We used annotation calledFrom = "gecko" to ensure running on main thread.
class MediaDrmCDMCallbackProxy : public DecryptorProxyCallback {
public:
+
+ void SetDecryptorId(uint32_t aId) override {}
+
void SetSessionId(uint32_t aCreateSessionToken,
const nsCString& aSessionId) override;
void ResolveLoadSessionPromise(uint32_t aPromiseId,
bool aSuccess) override;
void ResolvePromise(uint32_t aPromiseId) override;
--- a/dom/media/gmp/GMPCDMCallbackProxy.cpp
+++ b/dom/media/gmp/GMPCDMCallbackProxy.cpp
@@ -17,16 +17,29 @@
namespace mozilla {
GMPCDMCallbackProxy::GMPCDMCallbackProxy(CDMProxy* aProxy)
: mProxy(aProxy)
{}
void
+GMPCDMCallbackProxy::SetDecryptorId(uint32_t aId)
+{
+ MOZ_ASSERT(mProxy->IsOnOwnerThread());
+
+ RefPtr<CDMProxy> proxy = mProxy;
+ NS_DispatchToMainThread(
+ NS_NewRunnableFunction([proxy, aId] ()
+ {
+ proxy->OnSetDecryptorId(aId);
+ })
+ );}
+
+void
GMPCDMCallbackProxy::SetSessionId(uint32_t aToken,
const nsCString& aSessionId)
{
MOZ_ASSERT(mProxy->IsOnOwnerThread());
RefPtr<CDMProxy> proxy = mProxy;
auto sid = NS_ConvertUTF8toUTF16(aSessionId);
NS_DispatchToMainThread(
--- a/dom/media/gmp/GMPCDMCallbackProxy.h
+++ b/dom/media/gmp/GMPCDMCallbackProxy.h
@@ -12,16 +12,19 @@
#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 GMPCDMCallbackProxy : public GMPDecryptorProxyCallback {
public:
+
+ void SetDecryptorId(uint32_t aId) override;
+
void SetSessionId(uint32_t aCreateSessionToken,
const nsCString& aSessionId) override;
void ResolveLoadSessionPromise(uint32_t aPromiseId,
bool aSuccess) override;
void ResolvePromise(uint32_t aPromiseId) override;
--- a/dom/media/gmp/GMPCDMProxy.cpp
+++ b/dom/media/gmp/GMPCDMProxy.cpp
@@ -33,16 +33,18 @@ GMPCDMProxy::GMPCDMProxy(dom::MediaKeys*
: CDMProxy(aKeys,
aKeySystem,
aDistinctiveIdentifierRequired,
aPersistentStateRequired)
, mCrashHelper(aCrashHelper)
, mCDM(nullptr)
, mDecryptionJobCount(0)
, mShutdownCalled(false)
+ , mDecryptorId(0)
+ , mCreatePromiseId(0)
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_COUNT_CTOR(GMPCDMProxy);
}
GMPCDMProxy::~GMPCDMProxy()
{
MOZ_COUNT_DTOR(GMPCDMProxy);
@@ -126,20 +128,29 @@ GMPCDMProxy::gmp_InitDone(GMPDecryptorPr
return;
}
mCDM = aCDM;
mCallback = new GMPCDMCallbackProxy(this);
mCDM->Init(mCallback,
mDistinctiveIdentifierRequired,
mPersistentStateRequired);
+
+ // Await the OnSetDecryptorId callback.
+ mCreatePromiseId = aData->mPromiseId;
+}
+
+void GMPCDMProxy::OnSetDecryptorId(uint32_t aId)
+{
+ MOZ_ASSERT(mCreatePromiseId);
+ mDecryptorId = aId;
nsCOMPtr<nsIRunnable> task(
NewRunnableMethod<uint32_t>(this,
&GMPCDMProxy::OnCDMCreated,
- aData->mPromiseId));
+ mCreatePromiseId));
NS_DispatchToMainThread(task);
}
class gmp_InitDoneCallback : public GetGMPDecryptorCallback
{
public:
gmp_InitDoneCallback(GMPCDMProxy* aGMPCDMProxy,
nsAutoPtr<GMPCDMProxy::InitData>&& aData)
@@ -763,14 +774,26 @@ GMPCDMProxy::GetSessionIdsForKeyId(const
caps.GetSessionIdsForKeyId(aKeyId, aSessionIds);
}
void
GMPCDMProxy::Terminated()
{
MOZ_ASSERT(NS_IsMainThread());
NS_WARNING("CDM terminated");
+ if (mCreatePromiseId) {
+ RejectPromise(mCreatePromiseId,
+ NS_ERROR_DOM_MEDIA_FATAL_ERR,
+ NS_LITERAL_CSTRING("Crashed waiting for CDM to initialize"));
+ mCreatePromiseId = 0;
+ }
if (!mKeys.IsNull()) {
mKeys->Terminated();
}
}
+uint32_t
+GMPCDMProxy::GetDecryptorId()
+{
+ return mDecryptorId;
+}
+
} // namespace mozilla
--- a/dom/media/gmp/GMPCDMProxy.h
+++ b/dom/media/gmp/GMPCDMProxy.h
@@ -29,16 +29,18 @@ public:
bool aPersistentStateRequired);
void Init(PromiseId aPromiseId,
const nsAString& aOrigin,
const nsAString& aTopLevelOrigin,
const nsAString& aGMPName,
bool aInPrivateBrowsing) override;
+ void OnSetDecryptorId(uint32_t aId) override;
+
void CreateSession(uint32_t aCreateSessionToken,
dom::MediaKeySessionType aSessionType,
PromiseId aPromiseId,
const nsAString& aInitDataType,
nsTArray<uint8_t>& aInitData) override;
void LoadSession(PromiseId aPromiseId,
const nsAString& aSessionId) override;
@@ -104,16 +106,18 @@ public:
void GetSessionIdsForKeyId(const nsTArray<uint8_t>& aKeyId,
nsTArray<nsCString>& aSessionIds) override;
#ifdef DEBUG
bool IsOnOwnerThread() override;
#endif
+ uint32_t GetDecryptorId() override;
+
private:
friend class gmp_InitDoneCallback;
friend class gmp_InitGetGMPDecryptorCallback;
struct InitData {
uint32_t mPromiseId;
nsString mOrigin;
nsString mTopLevelOrigin;
@@ -245,14 +249,18 @@ private:
// mDecryptionJobs as that shrinks as jobs are completed and removed
// from it.
// GMP thread only.
uint32_t mDecryptionJobCount;
// True if GMPCDMProxy::gmp_Shutdown was called.
// GMP thread only.
bool mShutdownCalled;
+
+ uint32_t mDecryptorId;
+
+ PromiseId mCreatePromiseId;
};
} // namespace mozilla
#endif // GMPCDMProxy_h_
--- a/dom/media/gmp/GMPCrashHelperHolder.h
+++ b/dom/media/gmp/GMPCrashHelperHolder.h
@@ -48,22 +48,22 @@ namespace mozilla {
// In the crashing case, the GMPCrashHelpers are deallocated when the crash
// report is processed in GeckoMediaPluginService::RunPluginCrashCallbacks().
//
// It's a bit yuck that we have to have two paths for disconnecting the crash
// helpers, but there aren't really any better options.
class GMPCrashHelperHolder
{
public:
-
+
void SetCrashHelper(GMPCrashHelper* aHelper)
{
mCrashHelper = aHelper;
}
-
+
GMPCrashHelper* GetCrashHelper()
{
return mCrashHelper;
}
void MaybeDisconnect(bool aAbnormalShutdown)
{
if (!aAbnormalShutdown) {
--- a/dom/media/gmp/GMPDecryptorChild.cpp
+++ b/dom/media/gmp/GMPDecryptorChild.cpp
@@ -68,16 +68,21 @@ GMPDecryptorChild::CallOnGMPThread(Metho
}
}
void
GMPDecryptorChild::Init(GMPDecryptor* aSession)
{
MOZ_ASSERT(aSession);
mSession = aSession;
+ // The ID of this decryptor is the IPDL actor ID. Note it's unique inside
+ // the child process, but not necessarily across all gecko processes. However,
+ // since GMPDecryptors are segregated by node ID/origin, we shouldn't end up
+ // with clashes in the content process.
+ SendSetDecryptorId(Id());
}
void
GMPDecryptorChild::SetSessionId(uint32_t aCreateSessionToken,
const char* aSessionId,
uint32_t aSessionIdLength)
{
CALL_ON_GMP_THREAD(SendSetSessionId,
--- a/dom/media/gmp/GMPDecryptorParent.cpp
+++ b/dom/media/gmp/GMPDecryptorParent.cpp
@@ -35,16 +35,27 @@ GMPDecryptorParent::GMPDecryptorParent(G
{
MOZ_ASSERT(mPlugin && mGMPThread);
}
GMPDecryptorParent::~GMPDecryptorParent()
{
}
+bool
+GMPDecryptorParent::RecvSetDecryptorId(const uint32_t& aId)
+{
+ if (!mIsOpen) {
+ NS_WARNING("Trying to use a dead GMP decrypter!");
+ return false;
+ }
+ mCallback->SetDecryptorId(aId);
+ return true;
+}
+
nsresult
GMPDecryptorParent::Init(GMPDecryptorProxyCallback* aCallback,
bool aDistinctiveIdentifierRequired,
bool aPersistentStateRequired)
{
LOGD(("GMPDecryptorParent[%p]::Init()", this));
if (mIsOpen) {
--- a/dom/media/gmp/GMPDecryptorParent.h
+++ b/dom/media/gmp/GMPDecryptorParent.h
@@ -67,16 +67,18 @@ public:
void Shutdown();
private:
~GMPDecryptorParent();
// PGMPDecryptorParent
+ bool RecvSetDecryptorId(const uint32_t& aId) override;
+
bool RecvSetSessionId(const uint32_t& aCreateSessionToken,
const nsCString& aSessionId) override;
bool RecvResolveLoadSessionPromise(const uint32_t& aPromiseId,
const bool& aSuccess) override;
bool RecvResolvePromise(const uint32_t& aPromiseId) override;
--- a/dom/media/gmp/PGMPDecryptor.ipdl
+++ b/dom/media/gmp/PGMPDecryptor.ipdl
@@ -48,16 +48,18 @@ child:
uint8_t[] aBuffer,
GMPDecryptionData aMetadata);
async DecryptingComplete();
parent:
async __delete__();
+ async SetDecryptorId(uint32_t aId);
+
async SetSessionId(uint32_t aCreateSessionToken,
nsCString aSessionId);
async ResolveLoadSessionPromise(uint32_t aPromiseId,
bool aSuccess);
async ResolvePromise(uint32_t aPromiseId);
--- a/dom/media/gtest/TestGMPCrossOrigin.cpp
+++ b/dom/media/gtest/TestGMPCrossOrigin.cpp
@@ -591,37 +591,33 @@ class GMPStorageTest : public GMPDecrypt
EXPECT_TRUE(!aNodeId1.Equals(nodeId3));
SetFinished();
}
class CreateDecryptorDone : public GetGMPDecryptorCallback
{
public:
- CreateDecryptorDone(GMPStorageTest* aRunner, nsIRunnable* aContinuation)
- : mRunner(aRunner),
- mContinuation(aContinuation)
+ explicit CreateDecryptorDone(GMPStorageTest* aRunner)
+ : mRunner(aRunner)
{
}
void Done(GMPDecryptorProxy* aDecryptor) override
{
mRunner->mDecryptor = aDecryptor;
EXPECT_TRUE(!!mRunner->mDecryptor);
if (mRunner->mDecryptor) {
mRunner->mDecryptor->Init(mRunner, false, true);
}
- nsCOMPtr<nsIThread> thread(GetGMPThread());
- thread->Dispatch(mContinuation, NS_DISPATCH_NORMAL);
}
private:
RefPtr<GMPStorageTest> mRunner;
- nsCOMPtr<nsIRunnable> mContinuation;
};
void CreateDecryptor(const nsCString& aNodeId,
const nsCString& aUpdate)
{
nsTArray<nsCString> updates;
updates.AppendElement(aUpdate);
nsCOMPtr<nsIRunnable> continuation(new Updates(this, Move(updates)));
@@ -674,17 +670,23 @@ class GMPStorageTest : public GMPDecrypt
mNodeId = aNodeId;
EXPECT_TRUE(!mNodeId.IsEmpty());
nsTArray<nsCString> tags;
tags.AppendElement(NS_LITERAL_CSTRING("fake"));
UniquePtr<GetGMPDecryptorCallback> callback(
- new CreateDecryptorDone(this, aContinuation));
+ new CreateDecryptorDone(this));
+
+ // Continue after the OnSetDecryptorId message, so that we don't
+ // get warnings in the async shutdown tests due to receiving the
+ // SetDecryptorId message after we've started shutdown.
+ mSetDecryptorIdContinuation = aContinuation;
+
nsresult rv =
service->GetGMPDecryptor(nullptr, &tags, mNodeId, Move(callback));
EXPECT_TRUE(NS_SUCCEEDED(rv));
}
void TestBasicStorage() {
AssertIsOnGMPThread();
EXPECT_TRUE(IsGMPStorageIsEmpty());
@@ -1368,16 +1370,26 @@ class GMPStorageTest : public GMPDecrypt
nsCOMPtr<nsIRunnable> continuation = mExpected[0].mContinuation;
mExpected.RemoveElementAt(0);
if (continuation) {
NS_DispatchToCurrentThread(continuation);
}
}
}
+ void SetDecryptorId(uint32_t aId) override
+ {
+ if (!mSetDecryptorIdContinuation) {
+ return;
+ }
+ nsCOMPtr<nsIThread> thread(GetGMPThread());
+ thread->Dispatch(mSetDecryptorIdContinuation, NS_DISPATCH_NORMAL);
+ mSetDecryptorIdContinuation = nullptr;
+ }
+
void SetSessionId(uint32_t aCreateSessionToken,
const nsCString& aSessionId) override { }
void ResolveLoadSessionPromise(uint32_t aPromiseId,
bool aSuccess) override {}
void ResolvePromise(uint32_t aPromiseId) override {}
void RejectPromise(uint32_t aPromiseId,
nsresult aException,
const nsCString& aSessionId) override { }
@@ -1411,16 +1423,18 @@ private:
, mContinuation(aContinuation)
{}
nsCString mMessage;
nsCOMPtr<nsIRunnable> mContinuation;
};
nsTArray<ExpectedMessage> mExpected;
+ RefPtr<nsIRunnable> mSetDecryptorIdContinuation;
+
GMPDecryptorProxy* mDecryptor;
Monitor mMonitor;
Atomic<bool> mFinished;
nsCString mNodeId;
};
void
GMPTestRunner::DoTest(void (GMPTestRunner::*aTestMethod)(GMPTestMonitor&))