Bug 1057908 - GeckoMediaPluginService needs to be proxied from Content processes to parent process. Part 2 - support asynchronous GMP API getters. r=jwwang,rjesup.

This commit is contained in:
Peter Van der Beken 2015-02-10 11:48:29 +01:00
parent e98292c191
commit ace406a462
12 changed files with 1171 additions and 373 deletions

View File

@ -25,6 +25,7 @@ CDMProxy::CDMProxy(dom::MediaKeys* aKeys, const nsAString& aKeySystem)
, mKeySystem(aKeySystem) , mKeySystem(aKeySystem)
, mCDM(nullptr) , mCDM(nullptr)
, mDecryptionJobCount(0) , mDecryptionJobCount(0)
, mShutdownCalled(false)
{ {
MOZ_ASSERT(NS_IsMainThread()); MOZ_ASSERT(NS_IsMainThread());
MOZ_COUNT_CTOR(CDMProxy); MOZ_COUNT_CTOR(CDMProxy);
@ -70,7 +71,7 @@ CDMProxy::Init(PromiseId aPromiseId,
nsCOMPtr<nsIRunnable> task( nsCOMPtr<nsIRunnable> task(
NS_NewRunnableMethodWithArg<nsAutoPtr<InitData>>(this, NS_NewRunnableMethodWithArg<nsAutoPtr<InitData>>(this,
&CDMProxy::gmp_Init, &CDMProxy::gmp_Init,
data)); Move(data)));
mGMPThread->Dispatch(task, NS_DISPATCH_NORMAL); mGMPThread->Dispatch(task, NS_DISPATCH_NORMAL);
} }
@ -83,14 +84,57 @@ CDMProxy::IsOnGMPThread()
#endif #endif
void void
CDMProxy::gmp_Init(nsAutoPtr<InitData> aData) CDMProxy::gmp_InitDone(GMPDecryptorProxy* aCDM, nsAutoPtr<InitData>&& aData)
{
EME_LOG("CDMProxy::gmp_InitDone");
if (!aCDM || mShutdownCalled) {
if (aCDM) {
aCDM->Close();
}
RejectPromise(aData->mPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR);
return;
}
mCDM = aCDM;
mCallback = new CDMCallbackProxy(this);
mCDM->Init(mCallback);
nsCOMPtr<nsIRunnable> task(
NS_NewRunnableMethodWithArg<uint32_t>(this,
&CDMProxy::OnCDMCreated,
aData->mPromiseId));
NS_DispatchToMainThread(task);
}
class gmp_InitDoneCallback : public GetGMPDecryptorCallback
{
public:
gmp_InitDoneCallback(CDMProxy* aCDMProxy,
nsAutoPtr<CDMProxy::InitData>&& aData)
: mCDMProxy(aCDMProxy),
mData(Move(aData))
{
}
void Done(GMPDecryptorProxy* aCDM)
{
mCDMProxy->gmp_InitDone(aCDM, Move(mData));
}
private:
nsRefPtr<CDMProxy> mCDMProxy;
nsAutoPtr<CDMProxy::InitData> mData;
};
void
CDMProxy::gmp_Init(nsAutoPtr<InitData>&& aData)
{ {
MOZ_ASSERT(IsOnGMPThread()); MOZ_ASSERT(IsOnGMPThread());
uint32_t promiseID = aData->mPromiseId;
nsCOMPtr<mozIGeckoMediaPluginService> mps = nsCOMPtr<mozIGeckoMediaPluginService> mps =
do_GetService("@mozilla.org/gecko-media-plugin-service;1"); do_GetService("@mozilla.org/gecko-media-plugin-service;1");
if (!mps) { if (!mps) {
RejectPromise(aData->mPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR); RejectPromise(promiseID, NS_ERROR_DOM_INVALID_STATE_ERR);
return; return;
} }
@ -100,7 +144,7 @@ CDMProxy::gmp_Init(nsAutoPtr<InitData> aData)
mNodeId); mNodeId);
MOZ_ASSERT(!GetNodeId().IsEmpty()); MOZ_ASSERT(!GetNodeId().IsEmpty());
if (NS_FAILED(rv)) { if (NS_FAILED(rv)) {
RejectPromise(aData->mPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR); RejectPromise(promiseID, NS_ERROR_DOM_INVALID_STATE_ERR);
return; return;
} }
@ -112,17 +156,12 @@ CDMProxy::gmp_Init(nsAutoPtr<InitData> aData)
nsTArray<nsCString> tags; nsTArray<nsCString> tags;
tags.AppendElement(NS_ConvertUTF16toUTF8(mKeySystem)); tags.AppendElement(NS_ConvertUTF16toUTF8(mKeySystem));
rv = mps->GetGMPDecryptor(&tags, GetNodeId(), &mCDM);
if (NS_FAILED(rv) || !mCDM) { UniquePtr<GetGMPDecryptorCallback> callback(new gmp_InitDoneCallback(this,
RejectPromise(aData->mPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR); Move(aData)));
} else { rv = mps->GetGMPDecryptor(&tags, GetNodeId(), Move(callback));
mCallback = new CDMCallbackProxy(this); if (NS_FAILED(rv)) {
mCDM->Init(mCallback); RejectPromise(promiseID, NS_ERROR_DOM_INVALID_STATE_ERR);
nsCOMPtr<nsIRunnable> task(
NS_NewRunnableMethodWithArg<uint32_t>(this,
&CDMProxy::OnCDMCreated,
aData->mPromiseId));
NS_DispatchToMainThread(task);
} }
} }
@ -341,6 +380,8 @@ CDMProxy::gmp_Shutdown()
{ {
MOZ_ASSERT(IsOnGMPThread()); MOZ_ASSERT(IsOnGMPThread());
mShutdownCalled = true;
// Abort any pending decrypt jobs, to awaken any clients waiting on a job. // Abort any pending decrypt jobs, to awaken any clients waiting on a job.
for (size_t i = 0; i < mDecryptionJobs.Length(); i++) { for (size_t i = 0; i < mDecryptionJobs.Length(); i++) {
DecryptJob* job = mDecryptionJobs[i]; DecryptJob* job = mDecryptionJobs[i];

View File

@ -174,6 +174,7 @@ public:
#endif #endif
private: private:
friend class gmp_InitDoneCallback;
struct InitData { struct InitData {
uint32_t mPromiseId; uint32_t mPromiseId;
@ -183,7 +184,8 @@ private:
}; };
// GMP thread only. // GMP thread only.
void gmp_Init(nsAutoPtr<InitData> aData); void gmp_Init(nsAutoPtr<InitData>&& aData);
void gmp_InitDone(GMPDecryptorProxy* aCDM, nsAutoPtr<InitData>&& aData);
// GMP thread only. // GMP thread only.
void gmp_Shutdown(); void gmp_Shutdown();
@ -320,6 +322,10 @@ private:
// from it. // from it.
// GMP thread only. // GMP thread only.
uint32_t mDecryptionJobCount; uint32_t mDecryptionJobCount;
// True if CDMProxy::gmp_Shutdown was called.
// GMP thread only.
bool mShutdownCalled;
}; };

View File

@ -133,6 +133,39 @@ GMPAudioDecoder::GetNodeId()
return NS_LITERAL_CSTRING(""); return NS_LITERAL_CSTRING("");
} }
void
GMPAudioDecoder::GetGMPAPI(GMPInitDoneRunnable* aInitDone)
{
MOZ_ASSERT(IsOnGMPThread());
nsTArray<nsCString> tags;
InitTags(tags);
UniquePtr<GetGMPAudioDecoderCallback> callback(
new GMPInitDoneCallback(this, aInitDone));
if (NS_FAILED(mMPS->GetGMPAudioDecoder(&tags, GetNodeId(), Move(callback)))) {
aInitDone->Dispatch();
}
}
void
GMPAudioDecoder::GMPInitDone(GMPAudioDecoderProxy* aGMP)
{
MOZ_ASSERT(aGMP);
nsTArray<uint8_t> codecSpecific;
codecSpecific.AppendElements(mConfig.audio_specific_config->Elements(),
mConfig.audio_specific_config->Length());
nsresult rv = aGMP->InitDecode(kGMPAudioCodecAAC,
mConfig.channel_count,
mConfig.bits_per_sample,
mConfig.samples_per_second,
codecSpecific,
mAdapter);
if (NS_SUCCEEDED(rv)) {
mGMP = aGMP;
}
}
nsresult nsresult
GMPAudioDecoder::Init() GMPAudioDecoder::Init()
{ {
@ -141,25 +174,20 @@ GMPAudioDecoder::Init()
mMPS = do_GetService("@mozilla.org/gecko-media-plugin-service;1"); mMPS = do_GetService("@mozilla.org/gecko-media-plugin-service;1");
MOZ_ASSERT(mMPS); MOZ_ASSERT(mMPS);
nsTArray<nsCString> tags; nsCOMPtr<nsIThread> gmpThread = NS_GetCurrentThread();
InitTags(tags);
nsresult rv = mMPS->GetGMPAudioDecoder(&tags, GetNodeId(), &mGMP);
NS_ENSURE_SUCCESS(rv, rv);
MOZ_ASSERT(mGMP);
nsTArray<uint8_t> codecSpecific; nsRefPtr<GMPInitDoneRunnable> initDone(new GMPInitDoneRunnable());
codecSpecific.AppendElements(mConfig.audio_specific_config->Elements(), gmpThread->Dispatch(
mConfig.audio_specific_config->Length()); NS_NewRunnableMethodWithArg<GMPInitDoneRunnable*>(this,
&GMPAudioDecoder::GetGMPAPI,
initDone),
NS_DISPATCH_NORMAL);
rv = mGMP->InitDecode(kGMPAudioCodecAAC, while (!initDone->IsDone()) {
mConfig.channel_count, NS_ProcessNextEvent(gmpThread, true);
mConfig.bits_per_sample, }
mConfig.samples_per_second,
codecSpecific,
mAdapter);
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK; return mGMP ? NS_OK : NS_ERROR_FAILURE;
} }
nsresult nsresult

View File

@ -80,6 +80,61 @@ protected:
virtual nsCString GetNodeId(); virtual nsCString GetNodeId();
private: private:
class GMPInitDoneRunnable : public nsRunnable
{
public:
GMPInitDoneRunnable()
: mInitDone(false),
mThread(do_GetCurrentThread())
{
}
NS_IMETHOD Run()
{
mInitDone = true;
return NS_OK;
}
void Dispatch()
{
mThread->Dispatch(this, NS_DISPATCH_NORMAL);
}
bool IsDone()
{
MOZ_ASSERT(nsCOMPtr<nsIThread>(do_GetCurrentThread()) == mThread);
return mInitDone;
}
private:
bool mInitDone;
nsCOMPtr<nsIThread> mThread;
};
void GetGMPAPI(GMPInitDoneRunnable* aInitDone);
class GMPInitDoneCallback : public GetGMPAudioDecoderCallback
{
public:
GMPInitDoneCallback(GMPAudioDecoder* aDecoder,
GMPInitDoneRunnable* aGMPInitDone)
: mDecoder(aDecoder)
, mGMPInitDone(aGMPInitDone)
{
}
virtual void Done(GMPAudioDecoderProxy* aGMP)
{
if (aGMP) {
mDecoder->GMPInitDone(aGMP);
}
mGMPInitDone->Dispatch();
}
private:
nsRefPtr<GMPAudioDecoder> mDecoder;
nsRefPtr<GMPInitDoneRunnable> mGMPInitDone;
};
void GMPInitDone(GMPAudioDecoderProxy* aGMP);
const mp4_demuxer::AudioDecoderConfig& mConfig; const mp4_demuxer::AudioDecoderConfig& mConfig;
MediaDataDecoderCallbackProxy* mCallback; MediaDataDecoderCallbackProxy* mCallback;
nsCOMPtr<mozIGeckoMediaPluginService> mMPS; nsCOMPtr<mozIGeckoMediaPluginService> mMPS;

View File

@ -157,29 +157,24 @@ GMPVideoDecoder::CreateFrame(mp4_demuxer::MP4Sample* aSample)
return frame; return frame;
} }
nsresult void
GMPVideoDecoder::Init() GMPVideoDecoder::GetGMPAPI(GMPInitDoneRunnable* aInitDone)
{ {
MOZ_ASSERT(IsOnGMPThread()); MOZ_ASSERT(IsOnGMPThread());
mMPS = do_GetService("@mozilla.org/gecko-media-plugin-service;1");
MOZ_ASSERT(mMPS);
nsTArray<nsCString> tags; nsTArray<nsCString> tags;
InitTags(tags); InitTags(tags);
nsresult rv = mMPS->GetGMPVideoDecoder(&tags, GetNodeId(), &mHost, &mGMP); UniquePtr<GetGMPVideoDecoderCallback> callback(
NS_ENSURE_SUCCESS(rv, rv); new GMPInitDoneCallback(this, aInitDone));
MOZ_ASSERT(mHost && mGMP); if (NS_FAILED(mMPS->GetGMPVideoDecoder(&tags, GetNodeId(), Move(callback)))) {
aInitDone->Dispatch();
}
}
// GMP implementations have interpreted the meaning of GMP_BufferLength32 void
// differently. The OpenH264 GMP expects GMP_BufferLength32 to behave as GMPVideoDecoder::GMPInitDone(GMPVideoDecoderProxy* aGMP, GMPVideoHost* aHost)
// specified in the GMP API, where each buffer is prefixed by a 32-bit {
// host-endian buffer length that includes the size of the buffer length MOZ_ASSERT(aHost && aGMP);
// field. Other existing GMPs currently expect GMP_BufferLength32 (when
// combined with kGMPVideoCodecH264) to mean "like AVCC but restricted to
// 4-byte NAL lengths" (i.e. buffer lengths are specified in big-endian
// and do not include the length of the buffer length field.
mConvertNALUnitLengths = mGMP->GetDisplayName().EqualsLiteral("gmpopenh264");
GMPVideoCodec codec; GMPVideoCodec codec;
memset(&codec, 0, sizeof(codec)); memset(&codec, 0, sizeof(codec));
@ -195,13 +190,48 @@ GMPVideoDecoder::Init()
codecSpecific.AppendElements(mConfig.extra_data->Elements(), codecSpecific.AppendElements(mConfig.extra_data->Elements(),
mConfig.extra_data->Length()); mConfig.extra_data->Length());
rv = mGMP->InitDecode(codec, nsresult rv = aGMP->InitDecode(codec,
codecSpecific, codecSpecific,
mAdapter, mAdapter,
PR_GetNumberOfProcessors()); PR_GetNumberOfProcessors());
NS_ENSURE_SUCCESS(rv, rv); if (NS_SUCCEEDED(rv)) {
mGMP = aGMP;
mHost = aHost;
return NS_OK; // GMP implementations have interpreted the meaning of GMP_BufferLength32
// differently. The OpenH264 GMP expects GMP_BufferLength32 to behave as
// specified in the GMP API, where each buffer is prefixed by a 32-bit
// host-endian buffer length that includes the size of the buffer length
// field. Other existing GMPs currently expect GMP_BufferLength32 (when
// combined with kGMPVideoCodecH264) to mean "like AVCC but restricted to
// 4-byte NAL lengths" (i.e. buffer lengths are specified in big-endian
// and do not include the length of the buffer length field.
mConvertNALUnitLengths = mGMP->GetDisplayName().EqualsLiteral("gmpopenh264");
}
}
nsresult
GMPVideoDecoder::Init()
{
MOZ_ASSERT(IsOnGMPThread());
mMPS = do_GetService("@mozilla.org/gecko-media-plugin-service;1");
MOZ_ASSERT(mMPS);
nsCOMPtr<nsIThread> gmpThread = NS_GetCurrentThread();
nsRefPtr<GMPInitDoneRunnable> initDone(new GMPInitDoneRunnable());
gmpThread->Dispatch(
NS_NewRunnableMethodWithArg<GMPInitDoneRunnable*>(this,
&GMPVideoDecoder::GetGMPAPI,
initDone),
NS_DISPATCH_NORMAL);
while (!initDone->IsDone()) {
NS_ProcessNextEvent(gmpThread, true);
}
return mGMP ? NS_OK : NS_ERROR_FAILURE;
} }
nsresult nsresult

View File

@ -96,6 +96,61 @@ protected:
virtual GMPUnique<GMPVideoEncodedFrame>::Ptr CreateFrame(mp4_demuxer::MP4Sample* aSample); virtual GMPUnique<GMPVideoEncodedFrame>::Ptr CreateFrame(mp4_demuxer::MP4Sample* aSample);
private: private:
class GMPInitDoneRunnable : public nsRunnable
{
public:
GMPInitDoneRunnable()
: mInitDone(false),
mThread(do_GetCurrentThread())
{
}
NS_IMETHOD Run()
{
mInitDone = true;
return NS_OK;
}
void Dispatch()
{
mThread->Dispatch(this, NS_DISPATCH_NORMAL);
}
bool IsDone()
{
MOZ_ASSERT(nsCOMPtr<nsIThread>(do_GetCurrentThread()) == mThread);
return mInitDone;
}
private:
bool mInitDone;
nsCOMPtr<nsIThread> mThread;
};
void GetGMPAPI(GMPInitDoneRunnable* aInitDone);
class GMPInitDoneCallback : public GetGMPVideoDecoderCallback
{
public:
GMPInitDoneCallback(GMPVideoDecoder* aDecoder,
GMPInitDoneRunnable* aGMPInitDone)
: mDecoder(aDecoder)
, mGMPInitDone(aGMPInitDone)
{
}
virtual void Done(GMPVideoDecoderProxy* aGMP, GMPVideoHost* aHost)
{
if (aGMP) {
mDecoder->GMPInitDone(aGMP, aHost);
}
mGMPInitDone->Dispatch();
}
private:
nsRefPtr<GMPVideoDecoder> mDecoder;
nsRefPtr<GMPInitDoneRunnable> mGMPInitDone;
};
void GMPInitDone(GMPVideoDecoderProxy* aGMP, GMPVideoHost* aHost);
const mp4_demuxer::VideoDecoderConfig& mConfig; const mp4_demuxer::VideoDecoderConfig& mConfig;
MediaDataDecoderCallbackProxy* mCallback; MediaDataDecoderCallbackProxy* mCallback;
nsCOMPtr<mozIGeckoMediaPluginService> mMPS; nsCOMPtr<mozIGeckoMediaPluginService> mMPS;

View File

@ -281,14 +281,33 @@ GeckoMediaPluginService::GetThread(nsIThread** aThread)
return NS_OK; return NS_OK;
} }
class GetGMPParentForAudioDecoderDone
{
public:
explicit GetGMPParentForAudioDecoderDone(UniquePtr<GetGMPAudioDecoderCallback>&& aCallback)
: mCallback(Move(aCallback))
{
}
void Done(GMPParent* aGMPParent)
{
GMPAudioDecoderParent* gmpADP = nullptr;
aGMPParent->GetGMPAudioDecoder(&gmpADP);
mCallback->Done(gmpADP);
}
private:
UniquePtr<GetGMPAudioDecoderCallback> mCallback;
};
NS_IMETHODIMP NS_IMETHODIMP
GeckoMediaPluginService::GetGMPAudioDecoder(nsTArray<nsCString>* aTags, GeckoMediaPluginService::GetGMPAudioDecoder(nsTArray<nsCString>* aTags,
const nsACString& aNodeId, const nsACString& aNodeId,
GMPAudioDecoderProxy** aGMPAD) UniquePtr<GetGMPAudioDecoderCallback>&& aCallback)
{ {
MOZ_ASSERT(NS_GetCurrentThread() == mGMPThread); MOZ_ASSERT(NS_GetCurrentThread() == mGMPThread);
NS_ENSURE_ARG(aTags && aTags->Length() > 0); NS_ENSURE_ARG(aTags && aTags->Length() > 0);
NS_ENSURE_ARG(aGMPAD); NS_ENSURE_ARG(aCallback);
if (mShuttingDownOnGMPThread) { if (mShuttingDownOnGMPThread) {
return NS_ERROR_FAILURE; return NS_ERROR_FAILURE;
@ -301,27 +320,42 @@ GeckoMediaPluginService::GetGMPAudioDecoder(nsTArray<nsCString>* aTags,
return NS_ERROR_FAILURE; return NS_ERROR_FAILURE;
} }
GMPAudioDecoderParent* gmpADP; GetGMPParentForAudioDecoderDone(Move(aCallback)).Done(gmp);
nsresult rv = gmp->GetGMPAudioDecoder(&gmpADP);
if (NS_FAILED(rv)) {
return rv;
}
*aGMPAD = gmpADP;
return NS_OK; return NS_OK;
} }
class GetGMPParentForVideoDecoderDone
{
public:
explicit GetGMPParentForVideoDecoderDone(UniquePtr<GetGMPVideoDecoderCallback>&& aCallback)
: mCallback(Move(aCallback))
{
}
void Done(GMPParent* aGMPParent)
{
GMPVideoDecoderParent* gmpVDP = nullptr;
GMPVideoHostImpl* videoHost = nullptr;
nsresult rv = aGMPParent->GetGMPVideoDecoder(&gmpVDP);
if (NS_SUCCEEDED(rv)) {
videoHost = &gmpVDP->Host();
}
mCallback->Done(gmpVDP, videoHost);
}
private:
UniquePtr<GetGMPVideoDecoderCallback> mCallback;
};
NS_IMETHODIMP NS_IMETHODIMP
GeckoMediaPluginService::GetGMPVideoDecoder(nsTArray<nsCString>* aTags, GeckoMediaPluginService::GetGMPVideoDecoder(nsTArray<nsCString>* aTags,
const nsACString& aNodeId, const nsACString& aNodeId,
GMPVideoHost** aOutVideoHost, UniquePtr<GetGMPVideoDecoderCallback>&& aCallback)
GMPVideoDecoderProxy** aGMPVD)
{ {
MOZ_ASSERT(NS_GetCurrentThread() == mGMPThread); MOZ_ASSERT(NS_GetCurrentThread() == mGMPThread);
NS_ENSURE_ARG(aTags && aTags->Length() > 0); NS_ENSURE_ARG(aTags && aTags->Length() > 0);
NS_ENSURE_ARG(aOutVideoHost); NS_ENSURE_ARG(aCallback);
NS_ENSURE_ARG(aGMPVD);
if (mShuttingDownOnGMPThread) { if (mShuttingDownOnGMPThread) {
return NS_ERROR_FAILURE; return NS_ERROR_FAILURE;
@ -339,28 +373,42 @@ GeckoMediaPluginService::GetGMPVideoDecoder(nsTArray<nsCString>* aTags,
} }
GMPVideoDecoderParent* gmpVDP; GetGMPParentForVideoDecoderDone(Move(aCallback)).Done(gmp);
nsresult rv = gmp->GetGMPVideoDecoder(&gmpVDP);
if (NS_FAILED(rv)) {
return rv;
}
*aGMPVD = gmpVDP;
*aOutVideoHost = &gmpVDP->Host();
return NS_OK; return NS_OK;
} }
class GetGMPParentForVideoEncoderDone
{
public:
explicit GetGMPParentForVideoEncoderDone(UniquePtr<GetGMPVideoEncoderCallback>&& aCallback)
: mCallback(Move(aCallback))
{
}
void Done(GMPParent* aGMPParent)
{
GMPVideoEncoderParent* gmpVEP = nullptr;
GMPVideoHostImpl* videoHost = nullptr;
nsresult rv = aGMPParent->GetGMPVideoEncoder(&gmpVEP);
if (NS_SUCCEEDED(rv)) {
videoHost = &gmpVEP->Host();
}
mCallback->Done(gmpVEP, videoHost);
}
private:
UniquePtr<GetGMPVideoEncoderCallback> mCallback;
};
NS_IMETHODIMP NS_IMETHODIMP
GeckoMediaPluginService::GetGMPVideoEncoder(nsTArray<nsCString>* aTags, GeckoMediaPluginService::GetGMPVideoEncoder(nsTArray<nsCString>* aTags,
const nsACString& aNodeId, const nsACString& aNodeId,
GMPVideoHost** aOutVideoHost, UniquePtr<GetGMPVideoEncoderCallback>&& aCallback)
GMPVideoEncoderProxy** aGMPVE)
{ {
MOZ_ASSERT(NS_GetCurrentThread() == mGMPThread); MOZ_ASSERT(NS_GetCurrentThread() == mGMPThread);
NS_ENSURE_ARG(aTags && aTags->Length() > 0); NS_ENSURE_ARG(aTags && aTags->Length() > 0);
NS_ENSURE_ARG(aOutVideoHost); NS_ENSURE_ARG(aCallback);
NS_ENSURE_ARG(aGMPVE);
if (mShuttingDownOnGMPThread) { if (mShuttingDownOnGMPThread) {
return NS_ERROR_FAILURE; return NS_ERROR_FAILURE;
@ -377,22 +425,34 @@ GeckoMediaPluginService::GetGMPVideoEncoder(nsTArray<nsCString>* aTags,
return NS_ERROR_FAILURE; return NS_ERROR_FAILURE;
} }
GMPVideoEncoderParent* gmpVEP; GetGMPParentForVideoEncoderDone(Move(aCallback)).Done(gmp);
nsresult rv = gmp->GetGMPVideoEncoder(&gmpVEP);
if (NS_FAILED(rv)) {
return rv;
}
*aGMPVE = gmpVEP;
*aOutVideoHost = &gmpVEP->Host();
return NS_OK; return NS_OK;
} }
class GetGMPParentForDecryptorDone
{
public:
explicit GetGMPParentForDecryptorDone(UniquePtr<GetGMPDecryptorCallback>&& aCallback)
: mCallback(Move(aCallback))
{
}
void Done(GMPParent* aGMPParent)
{
GMPDecryptorParent* ksp = nullptr;
aGMPParent->GetGMPDecryptor(&ksp);
mCallback->Done(ksp);
}
private:
UniquePtr<GetGMPDecryptorCallback> mCallback;
};
NS_IMETHODIMP NS_IMETHODIMP
GeckoMediaPluginService::GetGMPDecryptor(nsTArray<nsCString>* aTags, GeckoMediaPluginService::GetGMPDecryptor(nsTArray<nsCString>* aTags,
const nsACString& aNodeId, const nsACString& aNodeId,
GMPDecryptorProxy** aDecryptor) UniquePtr<GetGMPDecryptorCallback>&& aCallback)
{ {
#if defined(XP_LINUX) && defined(MOZ_GMP_SANDBOX) #if defined(XP_LINUX) && defined(MOZ_GMP_SANDBOX)
if (!SandboxInfo::Get().CanSandboxMedia()) { if (!SandboxInfo::Get().CanSandboxMedia()) {
@ -404,7 +464,7 @@ GeckoMediaPluginService::GetGMPDecryptor(nsTArray<nsCString>* aTags,
MOZ_ASSERT(NS_GetCurrentThread() == mGMPThread); MOZ_ASSERT(NS_GetCurrentThread() == mGMPThread);
NS_ENSURE_ARG(aTags && aTags->Length() > 0); NS_ENSURE_ARG(aTags && aTags->Length() > 0);
NS_ENSURE_ARG(aDecryptor); NS_ENSURE_ARG(aCallback);
if (mShuttingDownOnGMPThread) { if (mShuttingDownOnGMPThread) {
return NS_ERROR_FAILURE; return NS_ERROR_FAILURE;
@ -417,13 +477,7 @@ GeckoMediaPluginService::GetGMPDecryptor(nsTArray<nsCString>* aTags,
return NS_ERROR_FAILURE; return NS_ERROR_FAILURE;
} }
GMPDecryptorParent* ksp; GetGMPParentForDecryptorDone(Move(aCallback)).Done(gmp);
nsresult rv = gmp->GetGMPDecryptor(&ksp);
if (NS_FAILED(rv)) {
return rv;
}
*aDecryptor = static_cast<GMPDecryptorProxy*>(ksp);
return NS_OK; return NS_OK;
} }

View File

@ -44,18 +44,20 @@ public:
bool *aRetVal) override; bool *aRetVal) override;
NS_IMETHOD GetGMPVideoDecoder(nsTArray<nsCString>* aTags, NS_IMETHOD GetGMPVideoDecoder(nsTArray<nsCString>* aTags,
const nsACString& aNodeId, const nsACString& aNodeId,
GMPVideoHost** aOutVideoHost, UniquePtr<GetGMPVideoDecoderCallback>&& aCallback)
GMPVideoDecoderProxy** aGMPVD) override; override;
NS_IMETHOD GetGMPVideoEncoder(nsTArray<nsCString>* aTags, NS_IMETHOD GetGMPVideoEncoder(nsTArray<nsCString>* aTags,
const nsACString& aNodeId, const nsACString& aNodeId,
GMPVideoHost **aOutVideoHost, UniquePtr<GetGMPVideoEncoderCallback>&& aCallback)
GMPVideoEncoderProxy** aGMPVE) override; override;
NS_IMETHOD GetGMPAudioDecoder(nsTArray<nsCString>* aTags, NS_IMETHOD GetGMPAudioDecoder(nsTArray<nsCString>* aTags,
const nsACString& aNodeId, const nsACString& aNodeId,
GMPAudioDecoderProxy **aGMPAD) override; UniquePtr<GetGMPAudioDecoderCallback>&& aCallback)
override;
NS_IMETHOD GetGMPDecryptor(nsTArray<nsCString>* aTags, NS_IMETHOD GetGMPDecryptor(nsTArray<nsCString>* aTags,
const nsACString& aNodeId, const nsACString& aNodeId,
GMPDecryptorProxy** aDecryptor) override; UniquePtr<GetGMPDecryptorCallback>&& aCallback)
override;
int32_t AsyncShutdownTimeoutMs(); int32_t AsyncShutdownTimeoutMs();

View File

@ -7,6 +7,7 @@
#include "nsIThread.idl" #include "nsIThread.idl"
%{C++ %{C++
#include "mozilla/UniquePtr.h"
#include "nsTArray.h" #include "nsTArray.h"
#include "nsStringGlue.h" #include "nsStringGlue.h"
class GMPAudioDecoderProxy; class GMPAudioDecoderProxy;
@ -14,16 +15,36 @@ class GMPDecryptorProxy;
class GMPVideoDecoderProxy; class GMPVideoDecoderProxy;
class GMPVideoEncoderProxy; class GMPVideoEncoderProxy;
class GMPVideoHost; class GMPVideoHost;
template<class T>
class GMPGetterCallback
{
public:
GMPGetterCallback() { MOZ_COUNT_CTOR(GMPGetterCallback<T>); }
virtual ~GMPGetterCallback() { MOZ_COUNT_DTOR(GMPGetterCallback<T>); }
virtual void Done(T*) = 0;
};
template<class T>
class GMPVideoGetterCallback
{
public:
GMPVideoGetterCallback() { MOZ_COUNT_CTOR(GMPVideoGetterCallback<T>); }
virtual ~GMPVideoGetterCallback() { MOZ_COUNT_DTOR(GMPVideoGetterCallback<T>); }
virtual void Done(T*, GMPVideoHost*) = 0;
};
typedef GMPGetterCallback<GMPDecryptorProxy> GetGMPDecryptorCallback;
typedef GMPGetterCallback<GMPAudioDecoderProxy> GetGMPAudioDecoderCallback;
typedef GMPVideoGetterCallback<GMPVideoDecoderProxy> GetGMPVideoDecoderCallback;
typedef GMPVideoGetterCallback<GMPVideoEncoderProxy> GetGMPVideoEncoderCallback;
%} %}
[ptr] native GMPVideoDecoderProxy(GMPVideoDecoderProxy);
[ptr] native GMPVideoEncoderProxy(GMPVideoEncoderProxy);
[ptr] native GMPVideoHost(GMPVideoHost);
[ptr] native TagArray(nsTArray<nsCString>); [ptr] native TagArray(nsTArray<nsCString>);
[ptr] native GMPDecryptorProxy(GMPDecryptorProxy); native GetGMPDecryptorCallback(mozilla::UniquePtr<GetGMPDecryptorCallback>&&);
[ptr] native GMPAudioDecoderProxy(GMPAudioDecoderProxy); native GetGMPAudioDecoderCallback(mozilla::UniquePtr<GetGMPAudioDecoderCallback>&&);
native GetGMPVideoDecoderCallback(mozilla::UniquePtr<GetGMPVideoDecoderCallback>&&);
native GetGMPVideoEncoderCallback(mozilla::UniquePtr<GetGMPVideoEncoderCallback>&&);
[scriptable, uuid(fed4d2d8-87d8-42fe-a141-98c15b5f7a1e)] [scriptable, uuid(11bb248b-7a4b-4f62-bebb-8211551d8c20)]
interface mozIGeckoMediaPluginService : nsISupports interface mozIGeckoMediaPluginService : nsISupports
{ {
@ -52,35 +73,63 @@ interface mozIGeckoMediaPluginService : nsISupports
* The array of tags should at least contain a codec tag, and optionally * The array of tags should at least contain a codec tag, and optionally
* other tags such as for EME keysystem. * other tags such as for EME keysystem.
* Callable only on GMP thread. * Callable only on GMP thread.
* This is an asynchronous operation, the Done method of the callback object
* will be called on the GMP thread with the result (which might be null in
* the case of failure). This method always takes ownership of the callback
* object, but if this method returns an error then the Done method of the
* callback object will not be called at all.
*/ */
[noscript] [noscript]
GMPVideoDecoderProxy getGMPVideoDecoder(in TagArray tags, void getGMPVideoDecoder(in TagArray tags,
[optional] in ACString nodeId, [optional] in ACString nodeId,
out GMPVideoHost outVideoHost); in GetGMPVideoDecoderCallback callback);
/** /**
* Get a video encoder that supports the specified tags. * Get a video encoder that supports the specified tags.
* The array of tags should at least contain a codec tag, and optionally * The array of tags should at least contain a codec tag, and optionally
* other tags. * other tags.
* Callable only on GMP thread. * Callable only on GMP thread.
* This is an asynchronous operation, the Done method of the callback object
* will be called on the GMP thread with the result (which might be null in
* the case of failure). This method always takes ownership of the callback
* object, but if this method returns an error then the Done method of the
* callback object will not be called at all.
*/ */
[noscript] [noscript]
GMPVideoEncoderProxy getGMPVideoEncoder(in TagArray tags, void getGMPVideoEncoder(in TagArray tags,
[optional] in ACString nodeId, [optional] in ACString nodeId,
out GMPVideoHost outVideoHost); in GetGMPVideoEncoderCallback callback);
// Returns an audio decoder that supports the specified tags. /**
// The array of tags should at least contain a codec tag, and optionally * Returns an audio decoder that supports the specified tags.
// other tags such as for EME keysystem. * The array of tags should at least contain a codec tag, and optionally
// Callable only on GMP thread. * other tags such as for EME keysystem.
GMPAudioDecoderProxy getGMPAudioDecoder(in TagArray tags, * Callable only on GMP thread.
[optional] in ACString nodeId); * This is an asynchronous operation, the Done method of the callback object
* will be called on the GMP thread with the result (which might be null in
* the case of failure). This method always takes ownership of the callback
* object, but if this method returns an error then the Done method of the
* callback object will not be called at all.
*/
[noscript]
void getGMPAudioDecoder(in TagArray tags,
[optional] in ACString nodeId,
in GetGMPAudioDecoderCallback callback);
// Returns a decryption session manager that supports the specified tags. /**
// The array of tags should at least contain a key system tag, and optionally * Returns a decryption session manager that supports the specified tags.
// other tags. * The array of tags should at least contain a key system tag, and optionally
// Callable only on GMP thread. * other tags.
GMPDecryptorProxy getGMPDecryptor(in TagArray tags, in ACString nodeId); * Callable only on GMP thread.
* This is an asynchronous operation, the Done method of the callback object
* will be called on the GMP thread with the result (which might be null in
* the case of failure). This method always takes ownership of the callback
* object, but if this method returns an error then the Done method of the
* callback object will not be called at all.
*/
[noscript]
void getGMPDecryptor(in TagArray tags, in ACString nodeId,
in GetGMPDecryptorCallback callback);
/** /**
* Gets the NodeId for a (origin, urlbarOrigin, isInprivateBrowsing) tuple. * Gets the NodeId for a (origin, urlbarOrigin, isInprivateBrowsing) tuple.

View File

@ -28,87 +28,234 @@ using namespace std;
using namespace mozilla; using namespace mozilla;
using namespace mozilla::gmp; using namespace mozilla::gmp;
class GMPTestMonitor
{
public:
GMPTestMonitor()
: mFinished(false)
{
}
void AwaitFinished()
{
MOZ_ASSERT(NS_IsMainThread());
while (!mFinished) {
NS_ProcessNextEvent(nullptr, true);
}
mFinished = false;
}
private:
void MarkFinished()
{
mFinished = true;
}
public:
void SetFinished()
{
NS_DispatchToMainThread(NS_NewNonOwningRunnableMethod(this,
&GMPTestMonitor::MarkFinished));
}
private:
bool mFinished;
};
struct GMPTestRunner struct GMPTestRunner
{ {
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(GMPTestRunner) NS_INLINE_DECL_THREADSAFE_REFCOUNTING(GMPTestRunner)
void DoTest(void (GMPTestRunner::*aTestMethod)()); void DoTest(void (GMPTestRunner::*aTestMethod)(GMPTestMonitor&));
void RunTestGMPTestCodec(); void RunTestGMPTestCodec1(GMPTestMonitor& aMonitor);
void RunTestGMPCrossOrigin(); void RunTestGMPTestCodec2(GMPTestMonitor& aMonitor);
void RunTestGMPTestCodec3(GMPTestMonitor& aMonitor);
void RunTestGMPCrossOrigin1(GMPTestMonitor& aMonitor);
void RunTestGMPCrossOrigin2(GMPTestMonitor& aMonitor);
void RunTestGMPCrossOrigin3(GMPTestMonitor& aMonitor);
void RunTestGMPCrossOrigin4(GMPTestMonitor& aMonitor);
private: private:
~GMPTestRunner() { } ~GMPTestRunner() { }
}; };
void template<class T, class Base,
GMPTestRunner::RunTestGMPTestCodec() nsresult (NS_STDCALL GeckoMediaPluginService::*Getter)(nsTArray<nsCString>*,
const nsACString&,
UniquePtr<Base>&&)>
class RunTestGMPVideoCodec : public Base
{ {
nsRefPtr<GeckoMediaPluginService> service = public:
GeckoMediaPluginService::GetGeckoMediaPluginService(); virtual void Done(T* aGMP, GMPVideoHost* aHost)
{
EXPECT_TRUE(aGMP);
EXPECT_TRUE(aHost);
if (aGMP) {
aGMP->Close();
}
mMonitor.SetFinished();
}
GMPVideoHost* host = nullptr; static void Run(GMPTestMonitor& aMonitor, const nsCString& aOrigin)
GMPVideoDecoderProxy* decoder = nullptr; {
GMPVideoDecoderProxy* decoder2 = nullptr; UniquePtr<GMPCallbackType> callback(new RunTestGMPVideoCodec(aMonitor));
GMPVideoEncoderProxy* encoder = nullptr; Get(aOrigin, Move(callback));
}
nsTArray<nsCString> tags; protected:
tags.AppendElement(NS_LITERAL_CSTRING("h264")); typedef T GMPCodecType;
typedef Base GMPCallbackType;
service->GetGMPVideoDecoder(&tags, NS_LITERAL_CSTRING("o"), &host, &decoder2); explicit RunTestGMPVideoCodec(GMPTestMonitor& aMonitor)
service->GetGMPVideoDecoder(&tags, NS_LITERAL_CSTRING(""), &host, &decoder); : mMonitor(aMonitor)
{
}
service->GetGMPVideoEncoder(&tags, NS_LITERAL_CSTRING(""), &host, &encoder); static nsresult Get(const nsACString& aNodeId, UniquePtr<Base>&& aCallback)
{
nsTArray<nsCString> tags;
tags.AppendElement(NS_LITERAL_CSTRING("h264"));
EXPECT_TRUE(host); nsRefPtr<GeckoMediaPluginService> service =
EXPECT_TRUE(decoder); GeckoMediaPluginService::GetGeckoMediaPluginService();
EXPECT_TRUE(decoder2); return ((*service).*Getter)(&tags, aNodeId, Move(aCallback));
EXPECT_TRUE(encoder); }
if (decoder) decoder->Close(); protected:
if (decoder2) decoder2->Close(); GMPTestMonitor& mMonitor;
if (encoder) encoder->Close(); };
typedef RunTestGMPVideoCodec<GMPVideoDecoderProxy,
GetGMPVideoDecoderCallback,
&GeckoMediaPluginService::GetGMPVideoDecoder>
RunTestGMPVideoDecoder;
typedef RunTestGMPVideoCodec<GMPVideoEncoderProxy,
GetGMPVideoEncoderCallback,
&GeckoMediaPluginService::GetGMPVideoEncoder>
RunTestGMPVideoEncoder;
void
GMPTestRunner::RunTestGMPTestCodec1(GMPTestMonitor& aMonitor)
{
RunTestGMPVideoDecoder::Run(aMonitor, NS_LITERAL_CSTRING("o"));
} }
void void
GMPTestRunner::RunTestGMPCrossOrigin() GMPTestRunner::RunTestGMPTestCodec2(GMPTestMonitor& aMonitor)
{ {
nsRefPtr<GeckoMediaPluginService> service = RunTestGMPVideoDecoder::Run(aMonitor, NS_LITERAL_CSTRING(""));
GeckoMediaPluginService::GetGeckoMediaPluginService(); }
GMPVideoHost* host = nullptr; void
nsTArray<nsCString> tags; GMPTestRunner::RunTestGMPTestCodec3(GMPTestMonitor& aMonitor)
tags.AppendElement(NS_LITERAL_CSTRING("h264")); {
RunTestGMPVideoEncoder::Run(aMonitor, NS_LITERAL_CSTRING(""));
}
GMPVideoDecoderProxy* decoder1 = nullptr; template<class Base>
GMPVideoDecoderProxy* decoder2 = nullptr; class RunTestGMPCrossOrigin : public Base
GMPVideoEncoderProxy* encoder1 = nullptr; {
GMPVideoEncoderProxy* encoder2 = nullptr; public:
virtual void Done(typename Base::GMPCodecType* aGMP, GMPVideoHost* aHost)
{
EXPECT_TRUE(aGMP);
service->GetGMPVideoDecoder(&tags, NS_LITERAL_CSTRING("origin1"), &host, &decoder1); UniquePtr<typename Base::GMPCallbackType> callback(
service->GetGMPVideoDecoder(&tags, NS_LITERAL_CSTRING("origin2"), &host, &decoder2); new Step2(Base::mMonitor, aGMP, mShouldBeEqual));
EXPECT_TRUE(!!decoder1 && !!decoder2 && nsresult rv = Base::Get(mOrigin2, Move(callback));
decoder1->ParentID() != decoder2->ParentID()); EXPECT_TRUE(NS_SUCCEEDED(rv));
if (NS_FAILED(rv)) {
Base::mMonitor.SetFinished();
}
}
service->GetGMPVideoEncoder(&tags, NS_LITERAL_CSTRING("origin1"), &host, &encoder1); static void Run(GMPTestMonitor& aMonitor, const nsCString& aOrigin1,
service->GetGMPVideoEncoder(&tags, NS_LITERAL_CSTRING("origin2"), &host, &encoder2); const nsCString& aOrigin2)
EXPECT_TRUE(!!encoder1 && !!encoder2 && {
encoder1->ParentID() != encoder2->ParentID()); UniquePtr<typename Base::GMPCallbackType> callback(
new RunTestGMPCrossOrigin<Base>(aMonitor, aOrigin1, aOrigin2));
nsresult rv = Base::Get(aOrigin1, Move(callback));
EXPECT_TRUE(NS_SUCCEEDED(rv));
if (NS_FAILED(rv)) {
aMonitor.SetFinished();
}
}
if (decoder2) decoder2->Close(); private:
if (encoder2) encoder2->Close(); RunTestGMPCrossOrigin(GMPTestMonitor& aMonitor, const nsCString& aOrigin1,
const nsCString& aOrigin2)
: Base(aMonitor),
mGMP(nullptr),
mOrigin2(aOrigin2),
mShouldBeEqual(aOrigin1.Equals(aOrigin2))
{
}
service->GetGMPVideoDecoder(&tags, NS_LITERAL_CSTRING("origin1"), &host, &decoder2); class Step2 : public Base
EXPECT_TRUE(!!decoder1 && !!decoder2 && {
decoder1->ParentID() == decoder2->ParentID()); public:
Step2(GMPTestMonitor& aMonitor,
typename Base::GMPCodecType* aGMP,
bool aShouldBeEqual)
: Base(aMonitor),
mGMP(aGMP),
mShouldBeEqual(aShouldBeEqual)
{
}
virtual void Done(typename Base::GMPCodecType* aGMP, GMPVideoHost* aHost)
{
EXPECT_TRUE(aGMP);
if (aGMP) {
EXPECT_TRUE(mGMP &&
(mGMP->ParentID() == aGMP->ParentID()) == mShouldBeEqual);
}
if (mGMP) {
mGMP->Close();
}
Base::Done(aGMP, aHost);
}
service->GetGMPVideoEncoder(&tags, NS_LITERAL_CSTRING("origin1"), &host, &encoder2); private:
EXPECT_TRUE(!!encoder1 && !!encoder2 && typename Base::GMPCodecType* mGMP;
encoder1->ParentID() == encoder2->ParentID()); bool mShouldBeEqual;
};
if (decoder1) decoder1->Close(); typename Base::GMPCodecType* mGMP;
if (decoder2) decoder2->Close(); nsCString mOrigin2;
if (encoder1) encoder1->Close(); bool mShouldBeEqual;
if (encoder2) encoder2->Close(); };
typedef RunTestGMPCrossOrigin<RunTestGMPVideoDecoder>
RunTestGMPVideoDecoderCrossOrigin;
typedef RunTestGMPCrossOrigin<RunTestGMPVideoEncoder>
RunTestGMPVideoEncoderCrossOrigin;
void
GMPTestRunner::RunTestGMPCrossOrigin1(GMPTestMonitor& aMonitor)
{
RunTestGMPVideoDecoderCrossOrigin::Run(
aMonitor, NS_LITERAL_CSTRING("origin1"), NS_LITERAL_CSTRING("origin2"));
}
void
GMPTestRunner::RunTestGMPCrossOrigin2(GMPTestMonitor& aMonitor)
{
RunTestGMPVideoEncoderCrossOrigin::Run(
aMonitor, NS_LITERAL_CSTRING("origin1"), NS_LITERAL_CSTRING("origin2"));
}
void
GMPTestRunner::RunTestGMPCrossOrigin3(GMPTestMonitor& aMonitor)
{
RunTestGMPVideoDecoderCrossOrigin::Run(
aMonitor, NS_LITERAL_CSTRING("origin1"), NS_LITERAL_CSTRING("origin1"));
}
void
GMPTestRunner::RunTestGMPCrossOrigin4(GMPTestMonitor& aMonitor)
{
RunTestGMPVideoEncoderCrossOrigin::Run(
aMonitor, NS_LITERAL_CSTRING("origin1"), NS_LITERAL_CSTRING("origin1"));
} }
static already_AddRefed<nsIThread> static already_AddRefed<nsIThread>
@ -438,9 +585,73 @@ class GMPStorageTest : public GMPDecryptorProxyCallback
SetFinished(); SetFinished();
} }
class CreateDecryptorDone : public GetGMPDecryptorCallback
{
public:
CreateDecryptorDone(GMPStorageTest* aRunner, nsIRunnable* aContinuation)
: mRunner(aRunner),
mContinuation(aContinuation)
{
}
virtual void Done(GMPDecryptorProxy* aDecryptor) override
{
mRunner->mDecryptor = aDecryptor;
EXPECT_TRUE(!!mRunner->mDecryptor);
if (mRunner->mDecryptor) {
mRunner->mDecryptor->Init(mRunner);
}
nsCOMPtr<nsIThread> thread(GetGMPThread());
thread->Dispatch(mContinuation, NS_DISPATCH_NORMAL);
}
private:
nsRefPtr<GMPStorageTest> mRunner;
nsCOMPtr<nsIRunnable> mContinuation;
};
void CreateDecryptor(const nsAString& aOrigin, void CreateDecryptor(const nsAString& aOrigin,
const nsAString& aTopLevelOrigin, const nsAString& aTopLevelOrigin,
bool aInPBMode) { bool aInPBMode,
const nsCString& aUpdate)
{
nsTArray<nsCString> updates;
updates.AppendElement(aUpdate);
CreateDecryptor(aOrigin, aTopLevelOrigin, aInPBMode, Move(updates));
}
class Updates : public nsRunnable
{
public:
Updates(GMPStorageTest* aRunner, nsTArray<nsCString>&& aUpdates)
: mRunner(aRunner),
mUpdates(aUpdates)
{
}
NS_IMETHOD Run()
{
for (auto& update : mUpdates) {
mRunner->Update(update);
}
return NS_OK;
}
private:
nsRefPtr<GMPStorageTest> mRunner;
nsTArray<nsCString> mUpdates;
};
void CreateDecryptor(const nsAString& aOrigin,
const nsAString& aTopLevelOrigin,
bool aInPBMode,
nsTArray<nsCString>&& aUpdates) {
nsCOMPtr<nsIRunnable> updates(new Updates(this, Move(aUpdates)));
CreateDecryptor(aOrigin, aTopLevelOrigin, aInPBMode, updates);
}
void CreateDecryptor(const nsAString& aOrigin,
const nsAString& aTopLevelOrigin,
bool aInPBMode,
nsIRunnable* aContinuation) {
nsRefPtr<GeckoMediaPluginService> service = nsRefPtr<GeckoMediaPluginService> service =
GeckoMediaPluginService::GetGeckoMediaPluginService(); GeckoMediaPluginService::GetGeckoMediaPluginService();
EXPECT_TRUE(service); EXPECT_TRUE(service);
@ -451,13 +662,11 @@ class GMPStorageTest : public GMPDecryptorProxyCallback
nsTArray<nsCString> tags; nsTArray<nsCString> tags;
tags.AppendElement(NS_LITERAL_CSTRING("fake")); tags.AppendElement(NS_LITERAL_CSTRING("fake"));
nsresult rv = service->GetGMPDecryptor(&tags, mNodeId, &mDecryptor); UniquePtr<GetGMPDecryptorCallback> callback(
new CreateDecryptorDone(this, aContinuation));
nsresult rv =
service->GetGMPDecryptor(&tags, mNodeId, Move(callback));
EXPECT_TRUE(NS_SUCCEEDED(rv)); EXPECT_TRUE(NS_SUCCEEDED(rv));
EXPECT_TRUE(!!mDecryptor);
if (mDecryptor) {
mDecryptor->Init(this);
}
} }
void TestBasicStorage() { void TestBasicStorage() {
@ -467,16 +676,16 @@ class GMPStorageTest : public GMPDecryptorProxyCallback
nsRefPtr<GeckoMediaPluginService> service = nsRefPtr<GeckoMediaPluginService> service =
GeckoMediaPluginService::GetGeckoMediaPluginService(); GeckoMediaPluginService::GetGeckoMediaPluginService();
CreateDecryptor(NS_LITERAL_STRING("example1.com"),
NS_LITERAL_STRING("example2.com"),
false);
// Send a message to the fake GMP for it to run its own tests internally. // Send a message to the fake GMP for it to run its own tests internally.
// It sends us a "test-storage complete" message when its passed, or // It sends us a "test-storage complete" message when its passed, or
// some other message if its tests fail. // some other message if its tests fail.
Expect(NS_LITERAL_CSTRING("test-storage complete"), Expect(NS_LITERAL_CSTRING("test-storage complete"),
NS_NewRunnableMethod(this, &GMPStorageTest::SetFinished)); NS_NewRunnableMethod(this, &GMPStorageTest::SetFinished));
Update(NS_LITERAL_CSTRING("test-storage"));
CreateDecryptor(NS_LITERAL_STRING("example1.com"),
NS_LITERAL_STRING("example2.com"),
false,
NS_LITERAL_CSTRING("test-storage"));
} }
/** /**
@ -490,28 +699,28 @@ class GMPStorageTest : public GMPDecryptorProxyCallback
EXPECT_TRUE(IsGMPStorageIsEmpty()); EXPECT_TRUE(IsGMPStorageIsEmpty());
// Generate storage data for some site. // Generate storage data for some site.
CreateDecryptor(NS_LITERAL_STRING("example1.com"),
NS_LITERAL_STRING("example2.com"),
false);
nsCOMPtr<nsIRunnable> r = NS_NewRunnableMethod( nsCOMPtr<nsIRunnable> r = NS_NewRunnableMethod(
this, &GMPStorageTest::TestForgetThisSite_AnotherSite); this, &GMPStorageTest::TestForgetThisSite_AnotherSite);
Expect(NS_LITERAL_CSTRING("test-storage complete"), r); Expect(NS_LITERAL_CSTRING("test-storage complete"), r);
Update(NS_LITERAL_CSTRING("test-storage"));
CreateDecryptor(NS_LITERAL_STRING("example1.com"),
NS_LITERAL_STRING("example2.com"),
false,
NS_LITERAL_CSTRING("test-storage"));
} }
void TestForgetThisSite_AnotherSite() { void TestForgetThisSite_AnotherSite() {
Shutdown(); Shutdown();
// Generate storage data for another site. // Generate storage data for another site.
CreateDecryptor(NS_LITERAL_STRING("example3.com"),
NS_LITERAL_STRING("example4.com"),
false);
nsCOMPtr<nsIRunnable> r = NS_NewRunnableMethod( nsCOMPtr<nsIRunnable> r = NS_NewRunnableMethod(
this, &GMPStorageTest::TestForgetThisSite_CollectSiteInfo); this, &GMPStorageTest::TestForgetThisSite_CollectSiteInfo);
Expect(NS_LITERAL_CSTRING("test-storage complete"), r); Expect(NS_LITERAL_CSTRING("test-storage complete"), r);
Update(NS_LITERAL_CSTRING("test-storage"));
CreateDecryptor(NS_LITERAL_STRING("example3.com"),
NS_LITERAL_STRING("example4.com"),
false,
NS_LITERAL_CSTRING("test-storage"));
} }
struct NodeInfo { struct NodeInfo {
@ -623,16 +832,15 @@ class GMPStorageTest : public GMPDecryptorProxyCallback
EXPECT_TRUE(IsGMPStorageIsEmpty()); EXPECT_TRUE(IsGMPStorageIsEmpty());
// Generate storage data for some site. // Generate storage data for some site.
CreateDecryptor(NS_LITERAL_STRING("example1.com"),
NS_LITERAL_STRING("example2.com"),
false);
nsCOMPtr<nsIRunnable> r = NS_NewRunnableMethod( nsCOMPtr<nsIRunnable> r = NS_NewRunnableMethod(
this, &GMPStorageTest::TestClearRecentHistory1_Clear); this, &GMPStorageTest::TestClearRecentHistory1_Clear);
Expect(NS_LITERAL_CSTRING("test-storage complete"), r); Expect(NS_LITERAL_CSTRING("test-storage complete"), r);
Update(NS_LITERAL_CSTRING("test-storage"));
} CreateDecryptor(NS_LITERAL_STRING("example1.com"),
NS_LITERAL_STRING("example2.com"),
false,
NS_LITERAL_CSTRING("test-storage"));
}
/** /**
* 1. Generate some storage data. * 1. Generate some storage data.
@ -646,15 +854,14 @@ class GMPStorageTest : public GMPDecryptorProxyCallback
EXPECT_TRUE(IsGMPStorageIsEmpty()); EXPECT_TRUE(IsGMPStorageIsEmpty());
// Generate storage data for some site. // Generate storage data for some site.
CreateDecryptor(NS_LITERAL_STRING("example1.com"),
NS_LITERAL_STRING("example2.com"),
false);
nsCOMPtr<nsIRunnable> r = NS_NewRunnableMethod( nsCOMPtr<nsIRunnable> r = NS_NewRunnableMethod(
this, &GMPStorageTest::TestClearRecentHistory2_Clear); this, &GMPStorageTest::TestClearRecentHistory2_Clear);
Expect(NS_LITERAL_CSTRING("test-storage complete"), r); Expect(NS_LITERAL_CSTRING("test-storage complete"), r);
Update(NS_LITERAL_CSTRING("test-storage"));
CreateDecryptor(NS_LITERAL_STRING("example1.com"),
NS_LITERAL_STRING("example2.com"),
false,
NS_LITERAL_CSTRING("test-storage"));
} }
/** /**
@ -669,15 +876,14 @@ class GMPStorageTest : public GMPDecryptorProxyCallback
EXPECT_TRUE(IsGMPStorageIsEmpty()); EXPECT_TRUE(IsGMPStorageIsEmpty());
// Generate storage data for some site. // Generate storage data for some site.
CreateDecryptor(NS_LITERAL_STRING("example1.com"),
NS_LITERAL_STRING("example2.com"),
false);
nsCOMPtr<nsIRunnable> r = NS_NewRunnableMethod( nsCOMPtr<nsIRunnable> r = NS_NewRunnableMethod(
this, &GMPStorageTest::TestClearRecentHistory3_Clear); this, &GMPStorageTest::TestClearRecentHistory3_Clear);
Expect(NS_LITERAL_CSTRING("test-storage complete"), r); Expect(NS_LITERAL_CSTRING("test-storage complete"), r);
Update(NS_LITERAL_CSTRING("test-storage"));
CreateDecryptor(NS_LITERAL_STRING("example1.com"),
NS_LITERAL_STRING("example2.com"),
false,
NS_LITERAL_CSTRING("test-storage"));
} }
class MaxMTimeFinder { class MaxMTimeFinder {
@ -775,12 +981,6 @@ class GMPStorageTest : public GMPDecryptorProxyCallback
void TestCrossOriginStorage() { void TestCrossOriginStorage() {
EXPECT_TRUE(!mDecryptor); EXPECT_TRUE(!mDecryptor);
// Open decryptor on one, origin, write a record, and test that that
// record can't be read on another origin.
CreateDecryptor(NS_LITERAL_STRING("example3.com"),
NS_LITERAL_STRING("example4.com"),
false);
// Send the decryptor the message "store recordid $time" // Send the decryptor the message "store recordid $time"
// Wait for the decrytor to send us "stored recordid $time" // Wait for the decrytor to send us "stored recordid $time"
auto t = time(0); auto t = time(0);
@ -791,7 +991,13 @@ class GMPStorageTest : public GMPDecryptorProxyCallback
nsCString update("store crossOriginTestRecordId "); nsCString update("store crossOriginTestRecordId ");
update.AppendInt((int64_t)t); update.AppendInt((int64_t)t);
Update(update);
// Open decryptor on one, origin, write a record, and test that that
// record can't be read on another origin.
CreateDecryptor(NS_LITERAL_STRING("example3.com"),
NS_LITERAL_STRING("example4.com"),
false,
update);
} }
void TestCrossOriginStorage_RecordStoredContinuation() { void TestCrossOriginStorage_RecordStoredContinuation() {
@ -799,87 +1005,102 @@ class GMPStorageTest : public GMPDecryptorProxyCallback
// and try to read the record. // and try to read the record.
Shutdown(); Shutdown();
CreateDecryptor(NS_LITERAL_STRING("example5.com"),
NS_LITERAL_STRING("example6.com"),
false);
Expect(NS_LITERAL_CSTRING("retrieve crossOriginTestRecordId succeeded (length 0 bytes)"), Expect(NS_LITERAL_CSTRING("retrieve crossOriginTestRecordId succeeded (length 0 bytes)"),
NS_NewRunnableMethod(this, &GMPStorageTest::SetFinished)); NS_NewRunnableMethod(this, &GMPStorageTest::SetFinished));
Update(NS_LITERAL_CSTRING("retrieve crossOriginTestRecordId"));
CreateDecryptor(NS_LITERAL_STRING("example5.com"),
NS_LITERAL_STRING("example6.com"),
false,
NS_LITERAL_CSTRING("retrieve crossOriginTestRecordId"));
} }
void TestPBStorage() { void TestPBStorage() {
// Open decryptor on one, origin, write a record, close decryptor,
// open another, and test that record can be read, close decryptor,
// then send pb-last-context-closed notification, then open decryptor
// and check that it can't read that data; it should have been purged.
CreateDecryptor(NS_LITERAL_STRING("pb1.com"),
NS_LITERAL_STRING("pb2.com"),
true);
// Send the decryptor the message "store recordid $time" // Send the decryptor the message "store recordid $time"
// Wait for the decrytor to send us "stored recordid $time" // Wait for the decrytor to send us "stored recordid $time"
nsCString response("stored pbdata test-pb-data"); nsCString response("stored pbdata test-pb-data");
Expect(response, NS_NewRunnableMethod(this, Expect(response, NS_NewRunnableMethod(this,
&GMPStorageTest::TestPBStorage_RecordStoredContinuation)); &GMPStorageTest::TestPBStorage_RecordStoredContinuation));
nsCString update("store pbdata test-pb-data"); // Open decryptor on one, origin, write a record, close decryptor,
Update(update); // open another, and test that record can be read, close decryptor,
// then send pb-last-context-closed notification, then open decryptor
// and check that it can't read that data; it should have been purged.
CreateDecryptor(NS_LITERAL_STRING("pb1.com"),
NS_LITERAL_STRING("pb2.com"),
true,
NS_LITERAL_CSTRING("store pbdata test-pb-data"));
} }
void TestPBStorage_RecordStoredContinuation() { void TestPBStorage_RecordStoredContinuation() {
Shutdown(); Shutdown();
CreateDecryptor(NS_LITERAL_STRING("pb1.com"),
NS_LITERAL_STRING("pb2.com"),
true);
Expect(NS_LITERAL_CSTRING("retrieve pbdata succeeded (length 12 bytes)"), Expect(NS_LITERAL_CSTRING("retrieve pbdata succeeded (length 12 bytes)"),
NS_NewRunnableMethod(this, NS_NewRunnableMethod(this,
&GMPStorageTest::TestPBStorage_RecordRetrievedContinuation)); &GMPStorageTest::TestPBStorage_RecordRetrievedContinuation));
Update(NS_LITERAL_CSTRING("retrieve pbdata"));
CreateDecryptor(NS_LITERAL_STRING("pb1.com"),
NS_LITERAL_STRING("pb2.com"),
true,
NS_LITERAL_CSTRING("retrieve pbdata"));
} }
void TestPBStorage_RecordRetrievedContinuation() { void TestPBStorage_RecordRetrievedContinuation() {
Shutdown(); Shutdown();
SimulatePBModeExit(); SimulatePBModeExit();
CreateDecryptor(NS_LITERAL_STRING("pb1.com"),
NS_LITERAL_STRING("pb2.com"),
true);
Expect(NS_LITERAL_CSTRING("retrieve pbdata succeeded (length 0 bytes)"), Expect(NS_LITERAL_CSTRING("retrieve pbdata succeeded (length 0 bytes)"),
NS_NewRunnableMethod(this, NS_NewRunnableMethod(this,
&GMPStorageTest::SetFinished)); &GMPStorageTest::SetFinished));
Update(NS_LITERAL_CSTRING("retrieve pbdata"));
CreateDecryptor(NS_LITERAL_STRING("pb1.com"),
NS_LITERAL_STRING("pb2.com"),
true,
NS_LITERAL_CSTRING("retrieve pbdata"));
}
void NextAsyncShutdownTimeoutTest(nsIRunnable* aContinuation)
{
if (mDecryptor) {
Update(NS_LITERAL_CSTRING("shutdown-mode timeout"));
Shutdown();
}
nsCOMPtr<nsIThread> thread(GetGMPThread());
thread->Dispatch(aContinuation, NS_DISPATCH_NORMAL);
} }
void CreateAsyncShutdownTimeoutGMP(const nsAString& aOrigin1, void CreateAsyncShutdownTimeoutGMP(const nsAString& aOrigin1,
const nsAString& aOrigin2) { const nsAString& aOrigin2,
CreateDecryptor(aOrigin1, aOrigin2, false); void (GMPStorageTest::*aCallback)()) {
Update(NS_LITERAL_CSTRING("shutdown-mode timeout")); nsCOMPtr<nsIRunnable> continuation(
Shutdown(); NS_NewRunnableMethodWithArg<nsCOMPtr<nsIRunnable>>(
this,
&GMPStorageTest::NextAsyncShutdownTimeoutTest,
NS_NewRunnableMethod(this, aCallback)));
CreateDecryptor(aOrigin1, aOrigin2, false, continuation);
} }
void TestAsyncShutdownTimeout() { void TestAsyncShutdownTimeout() {
// Create decryptors that timeout in their async shutdown. // Create decryptors that timeout in their async shutdown.
// If the gtest hangs on shutdown, test fails! // If the gtest hangs on shutdown, test fails!
CreateAsyncShutdownTimeoutGMP(NS_LITERAL_STRING("example7.com"), CreateAsyncShutdownTimeoutGMP(NS_LITERAL_STRING("example7.com"),
NS_LITERAL_STRING("example8.com")); NS_LITERAL_STRING("example8.com"),
&GMPStorageTest::TestAsyncShutdownTimeout2);
};
void TestAsyncShutdownTimeout2() {
CreateAsyncShutdownTimeoutGMP(NS_LITERAL_STRING("example9.com"), CreateAsyncShutdownTimeoutGMP(NS_LITERAL_STRING("example9.com"),
NS_LITERAL_STRING("example10.com")); NS_LITERAL_STRING("example10.com"),
&GMPStorageTest::TestAsyncShutdownTimeout3);
};
void TestAsyncShutdownTimeout3() {
CreateAsyncShutdownTimeoutGMP(NS_LITERAL_STRING("example11.com"), CreateAsyncShutdownTimeoutGMP(NS_LITERAL_STRING("example11.com"),
NS_LITERAL_STRING("example12.com")); NS_LITERAL_STRING("example12.com"),
SetFinished(); &GMPStorageTest::SetFinished);
}; };
void TestAsyncShutdownStorage() { void TestAsyncShutdownStorage() {
// Test that a GMP can write to storage during shutdown, and retrieve
// that written data in a subsequent session.
CreateDecryptor(NS_LITERAL_STRING("example13.com"),
NS_LITERAL_STRING("example14.com"),
false);
// Instruct the GMP to write a token (the current timestamp, so it's // Instruct the GMP to write a token (the current timestamp, so it's
// unique) during async shutdown, then shutdown the plugin, re-create // unique) during async shutdown, then shutdown the plugin, re-create
// it, and check that the token was successfully stored. // it, and check that the token was successfully stored.
@ -896,7 +1117,12 @@ class GMPStorageTest : public GMPDecryptorProxyCallback
Expect(response, NS_NewRunnableMethodWithArg<nsCString>(this, Expect(response, NS_NewRunnableMethodWithArg<nsCString>(this,
&GMPStorageTest::TestAsyncShutdownStorage_ReceivedShutdownToken, token)); &GMPStorageTest::TestAsyncShutdownStorage_ReceivedShutdownToken, token));
Update(update); // Test that a GMP can write to storage during shutdown, and retrieve
// that written data in a subsequent session.
CreateDecryptor(NS_LITERAL_STRING("example13.com"),
NS_LITERAL_STRING("example14.com"),
false,
update);
} }
void TestAsyncShutdownStorage_ReceivedShutdownToken(const nsCString& aToken) { void TestAsyncShutdownStorage_ReceivedShutdownToken(const nsCString& aToken) {
@ -907,37 +1133,39 @@ class GMPStorageTest : public GMPDecryptorProxyCallback
void TestAsyncShutdownStorage_AsyncShutdownComplete(const nsCString& aToken) { void TestAsyncShutdownStorage_AsyncShutdownComplete(const nsCString& aToken) {
// Create a new instance of the plugin, retrieve the token written // Create a new instance of the plugin, retrieve the token written
// during shutdown and verify it is correct. // during shutdown and verify it is correct.
CreateDecryptor(NS_LITERAL_STRING("example13.com"),
NS_LITERAL_STRING("example14.com"),
false);
nsCString response("retrieved shutdown-token "); nsCString response("retrieved shutdown-token ");
response.Append(aToken); response.Append(aToken);
Expect(response, Expect(response,
NS_NewRunnableMethod(this, &GMPStorageTest::SetFinished)); NS_NewRunnableMethod(this, &GMPStorageTest::SetFinished));
Update(NS_LITERAL_CSTRING("retrieve-shutdown-token"));
CreateDecryptor(NS_LITERAL_STRING("example13.com"),
NS_LITERAL_STRING("example14.com"),
false,
NS_LITERAL_CSTRING("retrieve-shutdown-token"));
} }
#if defined(XP_WIN) #if defined(XP_WIN)
void TestOutputProtection() { void TestOutputProtection() {
Shutdown(); Shutdown();
CreateDecryptor(NS_LITERAL_STRING("example15.com"),
NS_LITERAL_STRING("example16.com"),
false);
Expect(NS_LITERAL_CSTRING("OP tests completed"), Expect(NS_LITERAL_CSTRING("OP tests completed"),
NS_NewRunnableMethod(this, &GMPStorageTest::SetFinished)); NS_NewRunnableMethod(this, &GMPStorageTest::SetFinished));
Update(NS_LITERAL_CSTRING("test-op-apis"));
CreateDecryptor(NS_LITERAL_STRING("example15.com"),
NS_LITERAL_STRING("example16.com"),
false,
NS_LITERAL_CSTRING("test-op-apis"));
} }
#endif #endif
void TestPluginVoucher() { void TestPluginVoucher() {
CreateDecryptor(NS_LITERAL_STRING("example17.com"),
NS_LITERAL_STRING("example18.com"),
false);
Expect(NS_LITERAL_CSTRING("retrieved plugin-voucher: gmp-fake placeholder voucher"), Expect(NS_LITERAL_CSTRING("retrieved plugin-voucher: gmp-fake placeholder voucher"),
NS_NewRunnableMethod(this, &GMPStorageTest::SetFinished)); NS_NewRunnableMethod(this, &GMPStorageTest::SetFinished));
Update(NS_LITERAL_CSTRING("retrieve-plugin-voucher"));
CreateDecryptor(NS_LITERAL_STRING("example17.com"),
NS_LITERAL_STRING("example18.com"),
false,
NS_LITERAL_CSTRING("retrieve-plugin-voucher"));
} }
void TestGetRecordNamesInMemoryStorage() { void TestGetRecordNamesInMemoryStorage() {
@ -954,12 +1182,9 @@ class GMPStorageTest : public GMPDecryptorProxyCallback
} }
void TestGetRecordNames(bool aPrivateBrowsing) { void TestGetRecordNames(bool aPrivateBrowsing) {
CreateDecryptor(NS_LITERAL_STRING("foo.com"),
NS_LITERAL_STRING("bar.com"),
aPrivateBrowsing);
// Create a number of records of different names. // Create a number of records of different names.
const uint32_t num = 100; const uint32_t num = 100;
nsTArray<nsCString> updates(num);
for (uint32_t i = 0; i < num; i++) { for (uint32_t i = 0; i < num; i++) {
nsAutoCString response; nsAutoCString response;
response.AppendLiteral("stored data"); response.AppendLiteral("stored data");
@ -973,7 +1198,7 @@ class GMPStorageTest : public GMPDecryptorProxyCallback
mRecordNames.AppendLiteral("data"); mRecordNames.AppendLiteral("data");
AppendIntPadded(mRecordNames, i); AppendIntPadded(mRecordNames, i);
nsAutoCString update; nsCString& update = *updates.AppendElement();
update.AppendLiteral("store data"); update.AppendLiteral("store data");
AppendIntPadded(update, i); AppendIntPadded(update, i);
update.AppendLiteral(" test-data"); update.AppendLiteral(" test-data");
@ -985,8 +1210,12 @@ class GMPStorageTest : public GMPDecryptorProxyCallback
NS_NewRunnableMethod(this, &GMPStorageTest::TestGetRecordNames_QueryNames); NS_NewRunnableMethod(this, &GMPStorageTest::TestGetRecordNames_QueryNames);
} }
Expect(response, continuation); Expect(response, continuation);
Update(update);
} }
CreateDecryptor(NS_LITERAL_STRING("foo.com"),
NS_LITERAL_STRING("bar.com"),
aPrivateBrowsing,
Move(updates));
} }
void TestGetRecordNames_QueryNames() { void TestGetRecordNames_QueryNames() {
@ -1026,10 +1255,6 @@ class GMPStorageTest : public GMPDecryptorProxyCallback
MOZ_ASSERT(longRecordName.Length() < GMP_MAX_RECORD_NAME_SIZE); MOZ_ASSERT(longRecordName.Length() < GMP_MAX_RECORD_NAME_SIZE);
MOZ_ASSERT(longRecordName.Length() > 260); // Windows MAX_PATH MOZ_ASSERT(longRecordName.Length() > 260); // Windows MAX_PATH
CreateDecryptor(NS_LITERAL_STRING("fuz.com"),
NS_LITERAL_STRING("baz.com"),
false);
nsCString response("stored "); nsCString response("stored ");
response.Append(longRecordName); response.Append(longRecordName);
response.AppendLiteral(" "); response.AppendLiteral(" ");
@ -1040,7 +1265,10 @@ class GMPStorageTest : public GMPDecryptorProxyCallback
update.Append(longRecordName); update.Append(longRecordName);
update.AppendLiteral(" "); update.AppendLiteral(" ");
update.Append(data); update.Append(data);
Update(update); CreateDecryptor(NS_LITERAL_STRING("fuz.com"),
NS_LITERAL_STRING("baz.com"),
false,
update);
} }
void Expect(const nsCString& aMessage, nsIRunnable* aContinuation) { void Expect(const nsCString& aMessage, nsIRunnable* aContinuation) {
@ -1147,24 +1375,31 @@ private:
}; };
void void
GMPTestRunner::DoTest(void (GMPTestRunner::*aTestMethod)()) GMPTestRunner::DoTest(void (GMPTestRunner::*aTestMethod)(GMPTestMonitor&))
{ {
nsRefPtr<GeckoMediaPluginService> service = nsCOMPtr<nsIThread> thread(GetGMPThread());
GeckoMediaPluginService::GetGeckoMediaPluginService();
nsCOMPtr<nsIThread> thread;
service->GetThread(getter_AddRefs(thread)); GMPTestMonitor monitor;
thread->Dispatch(NS_NewRunnableMethod(this, aTestMethod), NS_DISPATCH_SYNC); thread->Dispatch(NS_NewRunnableMethodWithArg<GMPTestMonitor&>(this,
aTestMethod,
monitor),
NS_DISPATCH_NORMAL);
monitor.AwaitFinished();
} }
TEST(GeckoMediaPlugins, GMPTestCodec) { TEST(GeckoMediaPlugins, GMPTestCodec) {
nsRefPtr<GMPTestRunner> runner = new GMPTestRunner(); nsRefPtr<GMPTestRunner> runner = new GMPTestRunner();
runner->DoTest(&GMPTestRunner::RunTestGMPTestCodec); runner->DoTest(&GMPTestRunner::RunTestGMPTestCodec1);
runner->DoTest(&GMPTestRunner::RunTestGMPTestCodec2);
runner->DoTest(&GMPTestRunner::RunTestGMPTestCodec3);
} }
TEST(GeckoMediaPlugins, GMPCrossOrigin) { TEST(GeckoMediaPlugins, GMPCrossOrigin) {
nsRefPtr<GMPTestRunner> runner = new GMPTestRunner(); nsRefPtr<GMPTestRunner> runner = new GMPTestRunner();
runner->DoTest(&GMPTestRunner::RunTestGMPCrossOrigin); runner->DoTest(&GMPTestRunner::RunTestGMPCrossOrigin1);
runner->DoTest(&GMPTestRunner::RunTestGMPCrossOrigin2);
runner->DoTest(&GMPTestRunner::RunTestGMPCrossOrigin3);
runner->DoTest(&GMPTestRunner::RunTestGMPCrossOrigin4);
} }
TEST(GeckoMediaPlugins, GMPStorageGetNodeId) { TEST(GeckoMediaPlugins, GMPStorageGetNodeId) {

View File

@ -151,36 +151,57 @@ WebrtcGmpVideoEncoder::InitEncode(const webrtc::VideoCodec* aCodecSettings,
} }
} }
int32_t ret; nsCOMPtr<nsIThread> currentThread(do_GetCurrentThread());
mGMPThread->Dispatch(WrapRunnableRet(this, MOZ_ASSERT(currentThread != mGMPThread);
&WebrtcGmpVideoEncoder::InitEncode_g,
aCodecSettings,
aNumberOfCores,
aMaxPayloadSize,
&ret),
NS_DISPATCH_SYNC);
return ret; nsRefPtr<InitDoneRunnable> initDone(new InitDoneRunnable());
mGMPThread->Dispatch(WrapRunnable(this,
&WebrtcGmpVideoEncoder::InitEncode_g,
aCodecSettings,
aNumberOfCores,
aMaxPayloadSize,
initDone),
NS_DISPATCH_NORMAL);
while (!initDone->IsDone()) {
NS_ProcessNextEvent(currentThread, true);
}
return initDone->Result();
} }
int32_t void
WebrtcGmpVideoEncoder::InitEncode_g(const webrtc::VideoCodec* aCodecSettings, WebrtcGmpVideoEncoder::InitEncode_g(const webrtc::VideoCodec* aCodecSettings,
int32_t aNumberOfCores, int32_t aNumberOfCores,
uint32_t aMaxPayloadSize) uint32_t aMaxPayloadSize,
InitDoneRunnable* aInitDone)
{ {
nsTArray<nsCString> tags; nsTArray<nsCString> tags;
tags.AppendElement(NS_LITERAL_CSTRING("h264")); tags.AppendElement(NS_LITERAL_CSTRING("h264"));
if (NS_WARN_IF(NS_FAILED(mMPS->GetGMPVideoEncoder(&tags, UniquePtr<GetGMPVideoEncoderCallback> callback(
NS_LITERAL_CSTRING(""), new InitDoneCallback(this, aInitDone, aCodecSettings, aMaxPayloadSize));
&mHost, nsresult rv = mMPS->GetGMPVideoEncoder(&tags,
&mGMP)))) { NS_LITERAL_CSTRING(""),
Move(callback));
if (NS_WARN_IF(NS_FAILED(rv))) {
mMPS = nullptr; mMPS = nullptr;
mGMP = nullptr; mGMP = nullptr;
mGMPThread = nullptr; mGMPThread = nullptr;
mHost = nullptr; mHost = nullptr;
return WEBRTC_VIDEO_CODEC_ERROR; aInitDone->Dispatch(WEBRTC_VIDEO_CODEC_ERROR);
return;
} }
}
int32_t
WebrtcGmpVideoEncoder::GmpInitDone(GMPVideoEncoderProxy* aGMP,
GMPVideoHost* aHost,
const webrtc::VideoCodec* aCodecSettings,
uint32_t aMaxPayloadSize)
{
mGMP = aGMP;
mHost = aHost;
if (!mGMP || !mHost) { if (!mGMP || !mHost) {
return WEBRTC_VIDEO_CODEC_ERROR; return WEBRTC_VIDEO_CODEC_ERROR;
} }
@ -189,8 +210,6 @@ WebrtcGmpVideoEncoder::InitEncode_g(const webrtc::VideoCodec* aCodecSettings,
memset(&mCodecParams, 0, sizeof(mCodecParams)); memset(&mCodecParams, 0, sizeof(mCodecParams));
mCodecParams.mGMPApiVersion = 33; mCodecParams.mGMPApiVersion = 33;
mCodecParams.mWidth = aCodecSettings->width;
mCodecParams.mHeight = aCodecSettings->height;
mCodecParams.mStartBitrate = aCodecSettings->startBitrate; mCodecParams.mStartBitrate = aCodecSettings->startBitrate;
mCodecParams.mMinBitrate = aCodecSettings->minBitrate; mCodecParams.mMinBitrate = aCodecSettings->minBitrate;
mCodecParams.mMaxBitrate = aCodecSettings->maxBitrate; mCodecParams.mMaxBitrate = aCodecSettings->maxBitrate;
@ -206,6 +225,14 @@ WebrtcGmpVideoEncoder::InitEncode_g(const webrtc::VideoCodec* aCodecSettings,
mCodecParams.mMode = kGMPRealtimeVideo; mCodecParams.mMode = kGMPRealtimeVideo;
} }
return InitEncoderForSize(aCodecSettings->width, aCodecSettings->height);
}
int32_t
WebrtcGmpVideoEncoder::InitEncoderForSize(unsigned short aWidth, unsigned short aHeight)
{
mCodecParams.mWidth = aWidth;
mCodecParams.mHeight = aHeight;
// Pass dummy codecSpecific data for now... // Pass dummy codecSpecific data for now...
nsTArray<uint8_t> codecSpecific; nsTArray<uint8_t> codecSpecific;
@ -223,8 +250,37 @@ WebrtcGmpVideoEncoder::Encode(const webrtc::I420VideoFrame& aInputImage,
const webrtc::CodecSpecificInfo* aCodecSpecificInfo, const webrtc::CodecSpecificInfo* aCodecSpecificInfo,
const std::vector<webrtc::VideoFrameType>* aFrameTypes) const std::vector<webrtc::VideoFrameType>* aFrameTypes)
{ {
MOZ_ASSERT(mHost);
if (!mGMP) {
// destroyed via Terminate()
return WEBRTC_VIDEO_CODEC_ERROR;
}
MOZ_ASSERT(aInputImage.width() >= 0 && aInputImage.height() >= 0);
if (static_cast<uint32_t>(aInputImage.width()) != mCodecParams.mWidth ||
static_cast<uint32_t>(aInputImage.height()) != mCodecParams.mHeight) {
LOGD(("GMP Encode: resolution change from %ux%u to %dx%d",
mCodecParams.mWidth, mCodecParams.mHeight, aInputImage.width(), aInputImage.height()));
nsRefPtr<InitDoneRunnable> initDone(new InitDoneRunnable());
nsCOMPtr<nsIRunnable> task(
WrapRunnable(this,
&WebrtcGmpVideoEncoder::RegetEncoderForResolutionChange,
&aInputImage,
initDone));
mGMPThread->Dispatch(task, NS_DISPATCH_NORMAL);
nsCOMPtr<nsIThread> currentThread(do_GetCurrentThread());
while (!initDone->IsDone()) {
NS_ProcessNextEvent(currentThread, true);
}
if (initDone->Result() != WEBRTC_VIDEO_CODEC_OK) {
return initDone->Result();
}
}
int32_t ret; int32_t ret;
MOZ_ASSERT(mGMPThread);
mozilla::SyncRunnable::DispatchToThread(mGMPThread, mozilla::SyncRunnable::DispatchToThread(mGMPThread,
WrapRunnableRet(this, WrapRunnableRet(this,
&WebrtcGmpVideoEncoder::Encode_g, &WebrtcGmpVideoEncoder::Encode_g,
@ -236,49 +292,38 @@ WebrtcGmpVideoEncoder::Encode(const webrtc::I420VideoFrame& aInputImage,
return ret; return ret;
} }
void
WebrtcGmpVideoEncoder::RegetEncoderForResolutionChange(const webrtc::I420VideoFrame* aInputImage,
InitDoneRunnable* aInitDone)
{
mGMP->Close();
UniquePtr<GetGMPVideoEncoderCallback> callback(
new InitDoneForResolutionChangeCallback(this, aInitDone,
aInputImage->width(),
aInputImage->height()));
// OpenH264 codec (at least) can't handle dynamic input resolution changes
// re-init the plugin when the resolution changes
// XXX allow codec to indicate it doesn't need re-init!
nsTArray<nsCString> tags;
tags.AppendElement(NS_LITERAL_CSTRING("h264"));
if (NS_WARN_IF(NS_FAILED(mMPS->GetGMPVideoEncoder(&tags,
NS_LITERAL_CSTRING(""),
Move(callback))))) {
mGMP = nullptr;
mHost = nullptr;
aInitDone->Dispatch(WEBRTC_VIDEO_CODEC_ERROR);
}
}
int32_t int32_t
WebrtcGmpVideoEncoder::Encode_g(const webrtc::I420VideoFrame* aInputImage, WebrtcGmpVideoEncoder::Encode_g(const webrtc::I420VideoFrame* aInputImage,
const webrtc::CodecSpecificInfo* aCodecSpecificInfo, const webrtc::CodecSpecificInfo* aCodecSpecificInfo,
const std::vector<webrtc::VideoFrameType>* aFrameTypes) const std::vector<webrtc::VideoFrameType>* aFrameTypes)
{ {
MOZ_ASSERT(mHost); MOZ_ASSERT(mHost);
if (!mGMP) { MOZ_ASSERT(mGMP);
// destroyed via Terminate()
return WEBRTC_VIDEO_CODEC_ERROR;
}
MOZ_ASSERT(aInputImage->width() >= 0 && aInputImage->height() >= 0);
if (static_cast<uint32_t>(aInputImage->width()) != mCodecParams.mWidth ||
static_cast<uint32_t>(aInputImage->height()) != mCodecParams.mHeight) {
LOGD(("GMP Encode: resolution change from %ux%u to %dx%d",
mCodecParams.mWidth, mCodecParams.mHeight, aInputImage->width(), aInputImage->height()));
mGMP->Close();
// OpenH264 codec (at least) can't handle dynamic input resolution changes
// re-init the plugin when the resolution changes
// XXX allow codec to indicate it doesn't need re-init!
nsTArray<nsCString> tags;
tags.AppendElement(NS_LITERAL_CSTRING("h264"));
if (NS_WARN_IF(NS_FAILED(mMPS->GetGMPVideoEncoder(&tags,
NS_LITERAL_CSTRING(""),
&mHost,
&mGMP)))) {
mGMP = nullptr;
mHost = nullptr;
return WEBRTC_VIDEO_CODEC_ERROR;
}
mCodecParams.mWidth = aInputImage->width();
mCodecParams.mHeight = aInputImage->height();
// Pass dummy codecSpecific data for now...
nsTArray<uint8_t> codecSpecific;
GMPErr err = mGMP->InitEncode(mCodecParams, codecSpecific, this, 1, mMaxPayloadSize);
if (err != GMPNoErr) {
return WEBRTC_VIDEO_CODEC_ERROR;
}
}
GMPVideoFrame* ftmp = nullptr; GMPVideoFrame* ftmp = nullptr;
GMPErr err = mHost->CreateFrame(kGMPI420VideoFrame, &ftmp); GMPErr err = mHost->CreateFrame(kGMPI420VideoFrame, &ftmp);
@ -566,33 +611,48 @@ WebrtcGmpVideoDecoder::InitDecode(const webrtc::VideoCodec* aCodecSettings,
} }
} }
int32_t ret; nsRefPtr<InitDoneRunnable> initDone(new InitDoneRunnable());
mGMPThread->Dispatch(WrapRunnableRet(this, mGMPThread->Dispatch(WrapRunnable(this,
&WebrtcGmpVideoDecoder::InitDecode_g, &WebrtcGmpVideoDecoder::InitDecode_g,
aCodecSettings, aCodecSettings,
aNumberOfCores, aNumberOfCores,
&ret), initDone.get()),
NS_DISPATCH_SYNC); NS_DISPATCH_NORMAL);
return ret; nsCOMPtr<nsIThread> currentThread(do_GetCurrentThread());
while (!initDone->IsDone()) {
NS_ProcessNextEvent(currentThread, true);
}
return initDone->Result();
} }
int32_t void
WebrtcGmpVideoDecoder::InitDecode_g(const webrtc::VideoCodec* aCodecSettings, WebrtcGmpVideoDecoder::InitDecode_g(const webrtc::VideoCodec* aCodecSettings,
int32_t aNumberOfCores) int32_t aNumberOfCores,
InitDoneRunnable* aInitDone)
{ {
nsTArray<nsCString> tags; nsTArray<nsCString> tags;
tags.AppendElement(NS_LITERAL_CSTRING("h264")); tags.AppendElement(NS_LITERAL_CSTRING("h264"));
UniquePtr<GetGMPVideoDecoderCallback> callback(
new InitDoneCallback(this, aInitDone));
if (NS_WARN_IF(NS_FAILED(mMPS->GetGMPVideoDecoder(&tags, if (NS_WARN_IF(NS_FAILED(mMPS->GetGMPVideoDecoder(&tags,
NS_LITERAL_CSTRING(""), NS_LITERAL_CSTRING(""),
&mHost, Move(callback))))) {
&mGMP)))) {
mMPS = nullptr; mMPS = nullptr;
mGMP = nullptr; mGMP = nullptr;
mGMPThread = nullptr; mGMPThread = nullptr;
mHost = nullptr; mHost = nullptr;
return WEBRTC_VIDEO_CODEC_ERROR; aInitDone->Dispatch(WEBRTC_VIDEO_CODEC_ERROR);
} }
}
int32_t
WebrtcGmpVideoDecoder::GmpInitDone(GMPVideoDecoderProxy* aGMP,
GMPVideoHost* aHost)
{
mGMP = aGMP;
mHost = aHost;
mMPS = nullptr; mMPS = nullptr;
if (!mGMP || !mHost) { if (!mGMP || !mHost) {

View File

@ -21,6 +21,7 @@
#include <queue> #include <queue>
#include "nsThreadUtils.h" #include "nsThreadUtils.h"
#include "mozilla/Monitor.h"
#include "mozilla/Mutex.h" #include "mozilla/Mutex.h"
#include "mozIGeckoMediaPluginService.h" #include "mozIGeckoMediaPluginService.h"
@ -77,13 +78,131 @@ public:
} }
private: private:
virtual int32_t InitEncode_g(const webrtc::VideoCodec* aCodecSettings, class InitDoneRunnable : public nsRunnable
int32_t aNumberOfCores, {
uint32_t aMaxPayloadSize); public:
InitDoneRunnable()
: mInitDone(false),
mResult(WEBRTC_VIDEO_CODEC_OK),
mThread(do_GetCurrentThread())
{
}
virtual int32_t Encode_g(const webrtc::I420VideoFrame* aInputImage, NS_IMETHOD Run()
const webrtc::CodecSpecificInfo* aCodecSpecificInfo, {
const std::vector<webrtc::VideoFrameType>* aFrameTypes); MOZ_ASSERT(mThread == nsCOMPtr<nsIThread>(do_GetCurrentThread()));
mInitDone = true;
return NS_OK;
}
void Dispatch(int32_t aResult)
{
mResult = aResult;
mThread->Dispatch(this, NS_DISPATCH_NORMAL);
}
bool IsDone()
{
MOZ_ASSERT(nsCOMPtr<nsIThread>(do_GetCurrentThread()) == mThread);
return mInitDone;
}
int32_t Result()
{
return mResult;
}
private:
bool mInitDone;
int32_t mResult;
nsCOMPtr<nsIThread> mThread;
};
void InitEncode_g(const webrtc::VideoCodec* aCodecSettings,
int32_t aNumberOfCores,
uint32_t aMaxPayloadSize,
InitDoneRunnable* aInitDone);
int32_t GmpInitDone(GMPVideoEncoderProxy* aGMP, GMPVideoHost* aHost,
const webrtc::VideoCodec* aCodecSettings,
uint32_t aMaxPayloadSize);
int32_t InitEncoderForSize(unsigned short aWidth, unsigned short aHeight);
class InitDoneCallback : public GetGMPVideoEncoderCallback
{
public:
InitDoneCallback(WebrtcGmpVideoEncoder* aEncoder,
InitDoneRunnable* aInitDone,
const webrtc::VideoCodec* aCodecSettings,
uint32_t aMaxPayloadSize)
: mEncoder(aEncoder),
mInitDone(aInitDone),
mCodecSettings(aCodecSettings),
mMaxPayloadSize(aMaxPayloadSize)
{
}
virtual void Done(GMPVideoEncoderProxy* aGMP, GMPVideoHost* aHost) override
{
mEncoder->mGMP = aGMP;
mEncoder->mHost = aHost;
int32_t result;
if (aGMP || aHost) {
result = mEncoder->GmpInitDone(aGMP, aHost, mCodecSettings,
mMaxPayloadSize);
} else {
result = WEBRTC_VIDEO_CODEC_ERROR;
}
mInitDone->Dispatch(result);
}
private:
WebrtcGmpVideoEncoder* mEncoder;
nsRefPtr<InitDoneRunnable> mInitDone;
const webrtc::VideoCodec* mCodecSettings;
uint32_t mMaxPayloadSize;
};
int32_t Encode_g(const webrtc::I420VideoFrame* aInputImage,
const webrtc::CodecSpecificInfo* aCodecSpecificInfo,
const std::vector<webrtc::VideoFrameType>* aFrameTypes);
void RegetEncoderForResolutionChange(const webrtc::I420VideoFrame* aInputImage,
InitDoneRunnable* aInitDone);
class InitDoneForResolutionChangeCallback : public GetGMPVideoEncoderCallback
{
public:
InitDoneForResolutionChangeCallback(WebrtcGmpVideoEncoder* aEncoder,
InitDoneRunnable* aInitDone,
uint32_t aWidth,
uint32_t aHeight)
: mEncoder(aEncoder),
mInitDone(aInitDone),
mWidth(aWidth),
mHeight(aHeight)
{
}
virtual void Done(GMPVideoEncoderProxy* aGMP, GMPVideoHost* aHost) override
{
mEncoder->mGMP = aGMP;
mEncoder->mHost = aHost;
int32_t result;
if (aGMP && aHost) {
result = mEncoder->InitEncoderForSize(mWidth, mHeight);
} else {
result = WEBRTC_VIDEO_CODEC_ERROR;
}
mInitDone->Dispatch(result);
}
private:
WebrtcGmpVideoEncoder* mEncoder;
nsRefPtr<InitDoneRunnable> mInitDone;
uint32_t mWidth;
uint32_t mHeight;
};
virtual int32_t SetRates_g(uint32_t aNewBitRate, virtual int32_t SetRates_g(uint32_t aNewBitRate,
uint32_t aFrameRate); uint32_t aFrameRate);
@ -151,8 +270,72 @@ public:
} }
private: private:
virtual int32_t InitDecode_g(const webrtc::VideoCodec* aCodecSettings, class InitDoneRunnable : public nsRunnable
int32_t aNumberOfCores); {
public:
InitDoneRunnable()
: mInitDone(false),
mResult(WEBRTC_VIDEO_CODEC_OK),
mThread(do_GetCurrentThread())
{
}
NS_IMETHOD Run()
{
MOZ_ASSERT(mThread == nsCOMPtr<nsIThread>(do_GetCurrentThread()));
mInitDone = true;
return NS_OK;
}
void Dispatch(int32_t aResult)
{
mResult = aResult;
mThread->Dispatch(this, NS_DISPATCH_NORMAL);
}
bool IsDone()
{
MOZ_ASSERT(nsCOMPtr<nsIThread>(do_GetCurrentThread()) == mThread);
return mInitDone;
}
int32_t Result()
{
return mResult;
}
private:
bool mInitDone;
int32_t mResult;
nsCOMPtr<nsIThread> mThread;
};
void InitDecode_g(const webrtc::VideoCodec* aCodecSettings,
int32_t aNumberOfCores,
InitDoneRunnable* aInitDone);
int32_t GmpInitDone(GMPVideoDecoderProxy* aGMP, GMPVideoHost* aHost);
class InitDoneCallback : public GetGMPVideoDecoderCallback
{
public:
explicit InitDoneCallback(WebrtcGmpVideoDecoder* aDecoder,
InitDoneRunnable* aInitDone)
: mDecoder(aDecoder),
mInitDone(aInitDone)
{
}
virtual void Done(GMPVideoDecoderProxy* aGMP, GMPVideoHost* aHost)
{
int32_t result = mDecoder->GmpInitDone(aGMP, aHost);
mInitDone->Dispatch(result);
}
private:
WebrtcGmpVideoDecoder* mDecoder;
nsRefPtr<InitDoneRunnable> mInitDone;
};
virtual int32_t Decode_g(const webrtc::EncodedImage& aInputImage, virtual int32_t Decode_g(const webrtc::EncodedImage& aInputImage,
bool aMissingFrames, bool aMissingFrames,