From bf0e67458a7ef24d3c07a0e93bd5a089921447da Mon Sep 17 00:00:00 2001 From: Chris Pearce Date: Thu, 9 Mar 2017 17:34:18 +1300 Subject: [PATCH] Bug 1315850 - Shutdown ChromiumCDMParent. r=gerald MozReview-Commit-ID: E82ETFS90eH --HG-- extra : rebase_source : 6f0d72f76e313b55f7c905d5878c63b8d7292b1b --- dom/media/gmp/ChromiumCDMParent.cpp | 185 ++++++++++++++++++++++++++-- dom/media/gmp/ChromiumCDMParent.h | 6 + dom/media/gmp/ChromiumCDMProxy.cpp | 48 ++++++++ 3 files changed, 227 insertions(+), 12 deletions(-) diff --git a/dom/media/gmp/ChromiumCDMParent.cpp b/dom/media/gmp/ChromiumCDMParent.cpp index 19c9ead58a5f..58ea345f9d65 100644 --- a/dom/media/gmp/ChromiumCDMParent.cpp +++ b/dom/media/gmp/ChromiumCDMParent.cpp @@ -6,6 +6,7 @@ #include "ChromiumCDMParent.h" #include "mozilla/gmp/GMPTypes.h" #include "GMPContentChild.h" +#include "GMPContentParent.h" #include "mozilla/Unused.h" #include "ChromiumCDMProxy.h" #include "mozilla/dom/MediaKeyMessageEventBinding.h" @@ -33,6 +34,9 @@ ChromiumCDMParent::Init(ChromiumCDMProxy* aProxy, bool aAllowPersistentState) { GMP_LOG("ChromiumCDMParent::Init(this=%p)", this); + if (!aProxy) { + return false; + } mProxy = aProxy; return SendInit(aAllowDistinctiveIdentifier, aAllowPersistentState); } @@ -45,6 +49,12 @@ ChromiumCDMParent::CreateSession(uint32_t aCreateSessionToken, const nsTArray& aInitData) { GMP_LOG("ChromiumCDMParent::CreateSession(this=%p)", this); + if (mIsShutdown) { + RejectPromise(aPromiseId, + NS_ERROR_DOM_INVALID_STATE_ERR, + NS_LITERAL_CSTRING("CDM is shutdown.")); + return; + } if (!SendCreateSessionAndGenerateRequest( aPromiseId, aSessionType, aInitDataType, aInitData)) { RejectPromise( @@ -61,6 +71,12 @@ ChromiumCDMParent::SetServerCertificate(uint32_t aPromiseId, const nsTArray& aCert) { GMP_LOG("ChromiumCDMParent::SetServerCertificate(this=%p)", this); + if (mIsShutdown) { + RejectPromise(aPromiseId, + NS_ERROR_DOM_INVALID_STATE_ERR, + NS_LITERAL_CSTRING("CDM is shutdown.")); + return; + } if (!SendSetServerCertificate(aPromiseId, aCert)) { RejectPromise( aPromiseId, @@ -75,6 +91,12 @@ ChromiumCDMParent::UpdateSession(const nsCString& aSessionId, const nsTArray& aResponse) { GMP_LOG("ChromiumCDMParent::UpdateSession(this=%p)", this); + if (mIsShutdown) { + RejectPromise(aPromiseId, + NS_ERROR_DOM_INVALID_STATE_ERR, + NS_LITERAL_CSTRING("CDM is shutdown.")); + return; + } if (!SendUpdateSession(aPromiseId, aSessionId, aResponse)) { RejectPromise( aPromiseId, @@ -88,6 +110,12 @@ ChromiumCDMParent::CloseSession(const nsCString& aSessionId, uint32_t aPromiseId) { GMP_LOG("ChromiumCDMParent::CloseSession(this=%p)", this); + if (mIsShutdown) { + RejectPromise(aPromiseId, + NS_ERROR_DOM_INVALID_STATE_ERR, + NS_LITERAL_CSTRING("CDM is shutdown.")); + return; + } if (!SendCloseSession(aPromiseId, aSessionId)) { RejectPromise( aPromiseId, @@ -101,6 +129,12 @@ ChromiumCDMParent::RemoveSession(const nsCString& aSessionId, uint32_t aPromiseId) { GMP_LOG("ChromiumCDMParent::RemoveSession(this=%p)", this); + if (mIsShutdown) { + RejectPromise(aPromiseId, + NS_ERROR_DOM_INVALID_STATE_ERR, + NS_LITERAL_CSTRING("CDM is shutdown.")); + return; + } if (!SendRemoveSession(aPromiseId, aSessionId)) { RejectPromise( aPromiseId, @@ -135,6 +169,10 @@ InitCDMInputBuffer(gmp::CDMInputBuffer& aBuffer, MediaRawData* aSample) RefPtr ChromiumCDMParent::Decrypt(MediaRawData* aSample) { + if (mIsShutdown) { + return DecryptPromise::CreateAndReject(DecryptResult(GenericErr, aSample), + __func__); + } CDMInputBuffer buffer; if (!InitCDMInputBuffer(buffer, aSample)) { return DecryptPromise::CreateAndReject(DecryptResult(GenericErr, aSample), @@ -156,7 +194,12 @@ ChromiumCDMParent::Decrypt(MediaRawData* aSample) ipc::IPCResult ChromiumCDMParent::Recv__delete__() { + MOZ_ASSERT(mIsShutdown); GMP_LOG("ChromiumCDMParent::Recv__delete__(this=%p)", this); + if (mContentParent) { + mContentParent->ChromiumCDMDestroyed(this); + mContentParent = nullptr; + } return IPC_OK(); } @@ -169,7 +212,7 @@ ChromiumCDMParent::RecvOnResolveNewSessionPromise(const uint32_t& aPromiseId, this, aPromiseId, aSessionId.get()); - if (!mProxy) { + if (!mProxy || mIsShutdown) { return IPC_OK(); } @@ -199,7 +242,9 @@ ChromiumCDMParent::ResolvePromise(uint32_t aPromiseId) GMP_LOG( "ChromiumCDMParent::ResolvePromise(this=%p, pid=%u)", this, aPromiseId); - if (!mProxy) { + // Note: The MediaKeys rejects all pending DOM promises when it + // initiates shutdown. + if (!mProxy || mIsShutdown) { return; } NS_DispatchToMainThread(NewRunnableMethod( @@ -247,7 +292,9 @@ ChromiumCDMParent::RejectPromise(uint32_t aPromiseId, { GMP_LOG( "ChromiumCDMParent::RejectPromise(this=%p, pid=%u)", this, aPromiseId); - if (!mProxy) { + // Note: The MediaKeys rejects all pending DOM promises when it + // initiates shutdown. + if (!mProxy || mIsShutdown) { return; } NS_DispatchToMainThread(NewRunnableMethod( @@ -291,7 +338,7 @@ ChromiumCDMParent::RecvOnSessionMessage(const nsCString& aSessionId, GMP_LOG("ChromiumCDMParent::RecvOnSessionMessage(this=%p, sid=%s)", this, aSessionId.get()); - if (!mProxy) { + if (!mProxy || mIsShutdown) { return IPC_OK(); } RefPtr proxy = mProxy; @@ -334,7 +381,7 @@ ChromiumCDMParent::RecvOnSessionKeysChange( nsTArray&& aKeysInfo) { GMP_LOG("ChromiumCDMParent::RecvOnSessionKeysChange(this=%p)", this); - if (!mProxy) { + if (!mProxy || mIsShutdown) { return IPC_OK(); } bool keyStatusesChange = false; @@ -364,7 +411,7 @@ ChromiumCDMParent::RecvOnExpirationChange(const nsCString& aSessionId, GMP_LOG("ChromiumCDMParent::RecvOnExpirationChange(this=%p) time=%lf", this, aSecondsSinceEpoch); - if (!mProxy) { + if (!mProxy || mIsShutdown) { return IPC_OK(); } NS_DispatchToMainThread(NewRunnableMethod( @@ -379,7 +426,7 @@ ipc::IPCResult ChromiumCDMParent::RecvOnSessionClosed(const nsCString& aSessionId) { GMP_LOG("ChromiumCDMParent::RecvOnSessionClosed(this=%p)", this); - if (!mProxy) { + if (!mProxy || mIsShutdown) { return IPC_OK(); } NS_DispatchToMainThread( @@ -396,7 +443,7 @@ ChromiumCDMParent::RecvOnLegacySessionError(const nsCString& aSessionId, const nsCString& aMessage) { GMP_LOG("ChromiumCDMParent::RecvOnLegacySessionError(this=%p)", this); - if (!mProxy) { + if (!mProxy || mIsShutdown) { return IPC_OK(); } NS_DispatchToMainThread( @@ -432,6 +479,10 @@ ChromiumCDMParent::RecvDecrypted(const uint32_t& aId, this, aId, aStatus); + if (mIsShutdown) { + MOZ_ASSERT(mDecrypts.IsEmpty()); + return IPC_OK(); + } for (size_t i = 0; i < mDecrypts.Length(); i++) { if (mDecrypts[i]->mId == aId) { mDecrypts[i]->PostResult(ToDecryptStatus(aStatus), aData); @@ -445,7 +496,7 @@ ChromiumCDMParent::RecvDecrypted(const uint32_t& aId, ipc::IPCResult ChromiumCDMParent::RecvDecoded(const CDMVideoFrame& aFrame) { - if (mDecodePromise.IsEmpty()) { + if (mIsShutdown || mDecodePromise.IsEmpty()) { return IPC_OK(); } VideoData::YCbCrBuffer b; @@ -505,6 +556,10 @@ ChromiumCDMParent::RecvDecoded(const CDMVideoFrame& aFrame) ipc::IPCResult ChromiumCDMParent::RecvDecodeFailed(const uint32_t& aStatus) { + if (mIsShutdown) { + MOZ_ASSERT(mDecodePromise.IsEmpty()); + return IPC_OK(); + } mDecodePromise.RejectIfExists( MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR, RESULT_DETAIL("ChromiumCDMParent::RecvDecodeFailed")), @@ -516,7 +571,7 @@ ipc::IPCResult ChromiumCDMParent::RecvShutdown() { GMP_LOG("ChromiumCDMParent::RecvShutdown(this=%p)", this); - // TODO: SendDestroy(), call Terminated. + Shutdown(); return IPC_OK(); } @@ -524,6 +579,26 @@ void ChromiumCDMParent::ActorDestroy(ActorDestroyReason aWhy) { GMP_LOG("ChromiumCDMParent::ActorDestroy(this=%p, reason=%d)", this, aWhy); + MOZ_ASSERT(!mActorDestroyed); + mActorDestroyed = true; + if (!mIsShutdown) { + // Plugin crash. + MOZ_ASSERT(aWhy == AbnormalShutdown); + Shutdown(); + } + MOZ_ASSERT(mIsShutdown); + RefPtr kungFuDeathGrip(this); + if (mContentParent) { + mContentParent->ChromiumCDMDestroyed(this); + mContentParent = nullptr; + } + bool abnormalShutdown = (aWhy == AbnormalShutdown); + if (abnormalShutdown && mProxy) { + RefPtr task = + NewRunnableMethod(mProxy, &ChromiumCDMProxy::Terminated); + NS_DispatchToMainThread(task); + } + MaybeDisconnect(abnormalShutdown); } RefPtr @@ -532,6 +607,13 @@ ChromiumCDMParent::InitializeVideoDecoder( const VideoInfo& aInfo, RefPtr aImageContainer) { + if (mIsShutdown) { + return MediaDataDecoder::InitPromise::CreateAndReject( + MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR, + RESULT_DETAIL("ChromiumCDMParent is shutdown")), + __func__); + } + if (!SendInitializeVideoDecoder(aConfig)) { return MediaDataDecoder::InitPromise::CreateAndReject( MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR, @@ -539,6 +621,7 @@ ChromiumCDMParent::InitializeVideoDecoder( __func__); } + mVideoDecoderInitialized = true; mImageContainer = aImageContainer; mVideoInfo = aInfo; @@ -551,9 +634,14 @@ ChromiumCDMParent::RecvOnDecoderInitDone(const uint32_t& aStatus) GMP_LOG("ChromiumCDMParent::RecvOnDecoderInitDone(this=%p, status=%u)", this, aStatus); + if (mIsShutdown) { + MOZ_ASSERT(mInitVideoDecoderPromise.IsEmpty()); + return IPC_OK(); + } if (aStatus == static_cast(cdm::kSuccess)) { mInitVideoDecoderPromise.ResolveIfExists(TrackInfo::kVideoTrack, __func__); } else { + mVideoDecoderInitialized = false; mInitVideoDecoderPromise.RejectIfExists( MediaResult( NS_ERROR_DOM_MEDIA_FATAL_ERR, @@ -566,6 +654,13 @@ ChromiumCDMParent::RecvOnDecoderInitDone(const uint32_t& aStatus) RefPtr ChromiumCDMParent::DecryptAndDecodeFrame(MediaRawData* aSample) { + if (mIsShutdown) { + return MediaDataDecoder::DecodePromise::CreateAndReject( + MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR, + RESULT_DETAIL("ChromiumCDMParent is shutdown")), + __func__); + } + CDMInputBuffer buffer; if (!InitCDMInputBuffer(buffer, aSample)) { @@ -592,6 +687,13 @@ ChromiumCDMParent::DecryptAndDecodeFrame(MediaRawData* aSample) RefPtr ChromiumCDMParent::FlushVideoDecoder() { + if (mIsShutdown) { + return MediaDataDecoder::FlushPromise::CreateAndReject( + MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR, + RESULT_DETAIL("ChromiumCDMParent is shutdown")), + __func__); + } + mDecodePromise.RejectIfExists(NS_ERROR_DOM_MEDIA_CANCELED, __func__); if (!SendResetVideoDecoder()) { return MediaDataDecoder::FlushPromise::CreateAndReject( @@ -604,7 +706,11 @@ ChromiumCDMParent::FlushVideoDecoder() ipc::IPCResult ChromiumCDMParent::RecvResetVideoDecoderComplete() { - mFlushDecoderPromise.Resolve(true, __func__); + if (mIsShutdown) { + MOZ_ASSERT(mFlushDecoderPromise.IsEmpty()); + return IPC_OK(); + } + mFlushDecoderPromise.ResolveIfExists(true, __func__); return IPC_OK(); } @@ -612,6 +718,12 @@ RefPtr ChromiumCDMParent::Drain() { MOZ_ASSERT(mDecodePromise.IsEmpty(), "Must wait for decoding to complete"); + if (mIsShutdown) { + return MediaDataDecoder::DecodePromise::CreateAndReject( + MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR, + RESULT_DETAIL("ChromiumCDMParent is shutdown")), + __func__); + } RefPtr p = mDecodePromise.Ensure(__func__); if (!SendDrain()) { @@ -623,21 +735,70 @@ ChromiumCDMParent::Drain() ipc::IPCResult ChromiumCDMParent::RecvDrainComplete() { + if (mIsShutdown) { + MOZ_ASSERT(mDecodePromise.IsEmpty()); + return IPC_OK(); + } mDecodePromise.ResolveIfExists(MediaDataDecoder::DecodedData(), __func__); return IPC_OK(); } RefPtr ChromiumCDMParent::ShutdownVideoDecoder() { + if (mIsShutdown || !mVideoDecoderInitialized) { + return ShutdownPromise::CreateAndResolve(true, __func__); + } mInitVideoDecoderPromise.RejectIfExists(NS_ERROR_DOM_MEDIA_CANCELED, __func__); - MOZ_ASSERT(mDecodePromise.IsEmpty()); + mDecodePromise.RejectIfExists(NS_ERROR_DOM_MEDIA_CANCELED, __func__); MOZ_ASSERT(mFlushDecoderPromise.IsEmpty()); if (!SendDeinitializeVideoDecoder()) { return ShutdownPromise::CreateAndResolve(true, __func__); } + mVideoDecoderInitialized = false; return ShutdownPromise::CreateAndResolve(true, __func__); } +void +ChromiumCDMParent::Shutdown() +{ + GMP_LOG("ChromiumCDMParent::Shutdown(this=%p)", this); + + if (mIsShutdown) { + return; + } + mIsShutdown = true; + + for (RefPtr& decrypt : mDecrypts) { + decrypt->PostResult(AbortedErr); + } + mDecrypts.Clear(); + + if (mVideoDecoderInitialized && !mActorDestroyed) { + Unused << SendDeinitializeVideoDecoder(); + mVideoDecoderInitialized = false; + } + + // Note: MediaKeys rejects all outstanding promises when it initiates shutdown. + mPromiseToCreateSessionToken.Clear(); + + mInitVideoDecoderPromise.RejectIfExists( + MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR, + RESULT_DETAIL("ChromiumCDMParent is shutdown")), + __func__); + mDecodePromise.RejectIfExists( + MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR, + RESULT_DETAIL("ChromiumCDMParent is shutdown")), + __func__); + mFlushDecoderPromise.RejectIfExists( + MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR, + RESULT_DETAIL("ChromiumCDMParent is shutdown")), + __func__); + + if (!mActorDestroyed) { + Unused << SendDestroy(); + } +} + } // namespace gmp } // namespace mozilla diff --git a/dom/media/gmp/ChromiumCDMParent.h b/dom/media/gmp/ChromiumCDMParent.h index 64ee30005ee1..c9ecaaee6277 100644 --- a/dom/media/gmp/ChromiumCDMParent.h +++ b/dom/media/gmp/ChromiumCDMParent.h @@ -75,6 +75,8 @@ public: RefPtr ShutdownVideoDecoder(); + void Shutdown(); + protected: ~ChromiumCDMParent() {} @@ -135,6 +137,10 @@ protected: uint64_t mLastStreamOffset = 0; MozPromiseHolder mFlushDecoderPromise; + + bool mIsShutdown = false; + bool mVideoDecoderInitialized = false; + bool mActorDestroyed = false; }; } // namespace gmp diff --git a/dom/media/gmp/ChromiumCDMProxy.cpp b/dom/media/gmp/ChromiumCDMProxy.cpp index 522022f4a8d1..16400cc5a898 100644 --- a/dom/media/gmp/ChromiumCDMProxy.cpp +++ b/dom/media/gmp/ChromiumCDMProxy.cpp @@ -202,6 +202,13 @@ ChromiumCDMProxy::CreateSession(uint32_t aCreateSessionToken, uint32_t initDataType = ToCDMInitDataType(aInitDataType); RefPtr cdm = GetCDMParent(); + if (!cdm) { + RejectPromise(aPromiseId, + NS_ERROR_DOM_INVALID_STATE_ERR, + NS_LITERAL_CSTRING("Null CDM in CreateSession")); + return; + } + mGMPThread->Dispatch( NewRunnableMethod cdm = GetCDMParent(); + if (!cdm) { + RejectPromise(aPromiseId, + NS_ERROR_DOM_INVALID_STATE_ERR, + NS_LITERAL_CSTRING("Null CDM in SetServerCertificate")); + return; + } + mGMPThread->Dispatch(NewRunnableMethod>( cdm, &gmp::ChromiumCDMParent::SetServerCertificate, @@ -255,6 +269,12 @@ ChromiumCDMProxy::UpdateSession(const nsAString& aSessionId, aResponse.Length()); RefPtr cdm = GetCDMParent(); + if (!cdm) { + RejectPromise(aPromiseId, + NS_ERROR_DOM_INVALID_STATE_ERR, + NS_LITERAL_CSTRING("Null CDM in UpdateSession")); + return; + } mGMPThread->Dispatch( NewRunnableMethod>( cdm, @@ -274,6 +294,12 @@ ChromiumCDMProxy::CloseSession(const nsAString& aSessionId, aPromiseId); RefPtr cdm = GetCDMParent(); + if (!cdm) { + RejectPromise(aPromiseId, + NS_ERROR_DOM_INVALID_STATE_ERR, + NS_LITERAL_CSTRING("Null CDM in CloseSession")); + return; + } mGMPThread->Dispatch(NewRunnableMethod( cdm, &gmp::ChromiumCDMParent::CloseSession, @@ -291,6 +317,12 @@ ChromiumCDMProxy::RemoveSession(const nsAString& aSessionId, aPromiseId); RefPtr cdm = GetCDMParent(); + if (!cdm) { + RejectPromise(aPromiseId, + NS_ERROR_DOM_INVALID_STATE_ERR, + NS_LITERAL_CSTRING("Null CDM in RemoveSession")); + return; + } mGMPThread->Dispatch(NewRunnableMethod( cdm, &gmp::ChromiumCDMParent::RemoveSession, @@ -301,7 +333,19 @@ ChromiumCDMProxy::RemoveSession(const nsAString& aSessionId, void ChromiumCDMProxy::Shutdown() { + MOZ_ASSERT(NS_IsMainThread()); EME_LOG("ChromiumCDMProxy::Shutdown()"); + mKeys.Clear(); + RefPtr cdm; + { + MutexAutoLock lock(mCDMMutex); + cdm.swap(mCDM); + } + if (cdm) { + nsCOMPtr task = + NewRunnableMethod(mCDM, &gmp::ChromiumCDMParent::Shutdown); + mGMPThread->Dispatch(task.forget()); + } } void @@ -491,6 +535,10 @@ RefPtr ChromiumCDMProxy::Decrypt(MediaRawData* aSample) { RefPtr cdm = GetCDMParent(); + if (!cdm) { + return DecryptPromise::CreateAndReject(DecryptResult(AbortedErr, aSample), + __func__); + } RefPtr sample = aSample; return InvokeAsync( mGMPThread, __func__, [cdm, sample]() { return cdm->Decrypt(sample); });