Bug 1395922 - [P3] Make HTMLMediaElement::SetMediaKeys asynchronously.
MozReview-Commit-ID: 5M8CTHMsmIh
--- a/dom/html/HTMLMediaElement.cpp
+++ b/dom/html/HTMLMediaElement.cpp
@@ -1485,16 +1485,17 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTextTrackManager)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAudioTrackList)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mVideoTrackList)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMediaKeys)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mIncomingMediaKeys)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSelectedVideoStreamTrack)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPendingPlayPromises)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSeekDOMPromise)
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSetMediaKeysDOMPromise)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(HTMLMediaElement, nsGenericHTMLElement)
tmp->RemoveMutationObserver(tmp);
if (tmp->mSrcStream) {
// Need to EndMediaStreamPlayback to clear mSrcStream and make sure everything
// gets unhooked correctly.
tmp->EndSrcMediaStreamPlayback();
@@ -1515,16 +1516,17 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_IN
NS_IMPL_CYCLE_COLLECTION_UNLINK(mTextTrackManager)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mAudioTrackList)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mVideoTrackList)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mMediaKeys)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mIncomingMediaKeys)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mSelectedVideoStreamTrack)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mPendingPlayPromises)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mSeekDOMPromise)
+ NS_IMPL_CYCLE_COLLECTION_UNLINK(mSetMediaKeysDOMPromise)
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(HTMLMediaElement)
NS_INTERFACE_MAP_ENTRY(nsIDOMHTMLMediaElement)
NS_INTERFACE_MAP_END_INHERITING(nsGenericHTMLElement)
// nsIDOMHTMLMediaElement
NS_IMPL_URI_ATTR(HTMLMediaElement, Src, src)
@@ -1720,16 +1722,17 @@ HTMLMediaElement::OnChannelRedirect(nsIC
MOZ_ASSERT(mChannelLoader);
return mChannelLoader->Redirect(aChannel, aNewChannel, aFlags);
}
void HTMLMediaElement::ShutdownDecoder()
{
RemoveMediaElementFromURITable();
NS_ASSERTION(mDecoder, "Must have decoder to shut down");
+ mSetCDMRequest.DisconnectIfExists();
mWaitingForKeyListener.DisconnectIfExists();
if (mMediaSource) {
mMediaSource->CompletePendingTransactions();
}
mDecoder->Shutdown();
mDecoder = nullptr;
}
@@ -7015,137 +7018,175 @@ HTMLMediaElement::GetMediaKeys() const
bool
HTMLMediaElement::ContainsRestrictedContent()
{
return GetMediaKeys() != nullptr;
}
void
+HTMLMediaElement::SetCDMProxyFailure(const MediaResult& aResult)
+{
+ LOG(LogLevel::Debug, ("%s", __func__));
+ MOZ_ASSERT(mSetMediaKeysDOMPromise);
+
+ ResetSetMediaKeysTempVariables();
+
+ mSetMediaKeysDOMPromise->MaybeReject(aResult.Code(), aResult.Message());
+}
+
+void
HTMLMediaElement::RemoveMediaKeys()
{
LOG(LogLevel::Debug, ("%s", __func__));
// 5.2.3 Stop using the CDM instance represented by the mediaKeys attribute
// to decrypt media data and remove the association with the media element.
mMediaKeys->Unbind();
mMediaKeys = nullptr;
}
bool
-HTMLMediaElement::TryRemoveMediaKeysAssociation(DetailedPromise* aPromise)
+HTMLMediaElement::TryRemoveMediaKeysAssociation()
{
MOZ_ASSERT(mMediaKeys);
LOG(LogLevel::Debug, ("%s", __func__));
// 5.2.1 If the user agent or CDM do not support removing the association,
// let this object's attaching media keys value be false and reject promise
// with a new DOMException whose name is NotSupportedError.
// 5.2.2 If the association cannot currently be removed, let this object's
// attaching media keys value be false and reject promise with a new
// DOMException whose name is InvalidStateError.
if (mDecoder) {
- // We don't support swapping out the MediaKeys once we've started to
- // setup the playback pipeline. Note this also means we don't need to worry
- // about handling disassociating the MediaKeys from the MediaDecoder.
- aPromise->MaybeReject(
- NS_ERROR_DOM_INVALID_STATE_ERR,
- NS_LITERAL_CSTRING(
- "Can't change MediaKeys on HTMLMediaElement after load has started"));
+ RefPtr<HTMLMediaElement> self = this;
+ mDecoder->SetCDMProxy(nullptr)
+ ->Then(mAbstractMainThread,
+ __func__,
+ [self]() {
+ self->mSetCDMRequest.Complete();
+
+ self->RemoveMediaKeys();
+ if (self->AttachNewMediaKeys()) {
+ // No incoming MediaKeys object or MediaDecoder is not created yet.
+ self->MakeAssociationWithCDMResolved();
+ }
+ },
+ [self](const MediaResult& aResult) {
+ self->mSetCDMRequest.Complete();
+ // 5.2.4 If the preceding step failed, let this object's attaching media
+ // keys value be false and reject promise with a new DOMException whose
+ // name is the appropriate error name.
+ self->SetCDMProxyFailure(aResult);
+ })
+ ->Track(mSetCDMRequest);
return false;
}
+
RemoveMediaKeys();
-
- // 5.2.4 If the preceding step failed, let this object's attaching media
- // keys value be false and reject promise with a new DOMException whose
- // name is the appropriate error name.
return true;
}
bool
-HTMLMediaElement::DetachExistingMediaKeys(DetailedPromise* aPromise)
+HTMLMediaElement::DetachExistingMediaKeys()
{
LOG(LogLevel::Debug, ("%s", __func__));
+ MOZ_ASSERT(mSetMediaKeysDOMPromise);
// 5.1 If mediaKeys is not null, CDM instance represented by mediaKeys is
// already in use by another media element, and the user agent is unable
// to use it with this element, let this object's attaching media keys
// value be false and reject promise with a new DOMException whose name
// is QuotaExceededError.
if (mIncomingMediaKeys && mIncomingMediaKeys->IsBoundToMediaElement()) {
- aPromise->MaybeReject(
+ SetCDMProxyFailure(MediaResult(
NS_ERROR_DOM_QUOTA_EXCEEDED_ERR,
- NS_LITERAL_CSTRING(
- "MediaKeys object is already bound to another HTMLMediaElement"));
+ "MediaKeys object is already bound to another HTMLMediaElement"));
return false;
}
// 5.2 If the mediaKeys attribute is not null, run the following steps:
if (mMediaKeys) {
- return TryRemoveMediaKeysAssociation(aPromise);
+ return TryRemoveMediaKeysAssociation();
}
return true;
}
void
-HTMLMediaElement::MakeAssociationWithCDMResolved(DetailedPromise* aPromise)
+HTMLMediaElement::MakeAssociationWithCDMResolved()
{
LOG(LogLevel::Debug, ("%s", __func__));
+ MOZ_ASSERT(mSetMediaKeysDOMPromise);
// 5.4 Set the mediaKeys attribute to mediaKeys.
mMediaKeys = mIncomingMediaKeys;
// 5.5 Let this object's attaching media keys value be false.
ResetSetMediaKeysTempVariables();
// 5.6 Resolve promise.
- aPromise->MaybeResolveWithUndefined();
+ mSetMediaKeysDOMPromise->MaybeResolveWithUndefined();
+ mSetMediaKeysDOMPromise = nullptr;
}
bool
HTMLMediaElement::TryMakeAssociationWithCDM(CDMProxy* aProxy)
{
+ LOG(LogLevel::Debug, ("%s", __func__));
MOZ_ASSERT(aProxy);
- LOG(LogLevel::Debug, ("%s", __func__));
+
// 5.3.3 Queue a task to run the "Attempt to Resume Playback If Necessary"
// algorithm on the media element.
// Note: Setting the CDMProxy on the MediaDecoder will unblock playback.
if (mDecoder) {
- mDecoder->SetCDMProxy(aProxy);
+ // CDMProxy is set asynchronously in MediaFormatReader, once it's done,
+ // HTMLMediaElement should resolve or reject the DOM promise.
+ RefPtr<HTMLMediaElement> self = this;
+ mDecoder->SetCDMProxy(aProxy)
+ ->Then(mAbstractMainThread,
+ __func__,
+ [self]() {
+ self->mSetCDMRequest.Complete();
+ self->MakeAssociationWithCDMResolved();
+ },
+ [self](const MediaResult& aResult) {
+ self->mSetCDMRequest.Complete();
+ self->SetCDMProxyFailure(aResult);
+ })
+ ->Track(mSetCDMRequest);
+ return false;
}
return true;
}
bool
-HTMLMediaElement::AttachNewMediaKeys(DetailedPromise* aPromise)
+HTMLMediaElement::AttachNewMediaKeys()
{
LOG(LogLevel::Debug,
("%s incoming MediaKeys(%p)", __func__, mIncomingMediaKeys.get()));
+ MOZ_ASSERT(mSetMediaKeysDOMPromise);
// 5.3. If mediaKeys is not null, run the following steps:
if (mIncomingMediaKeys) {
auto cdmProxy = mIncomingMediaKeys->GetCDMProxy();
if (!cdmProxy) {
- aPromise->MaybeReject(
+ SetCDMProxyFailure(MediaResult(
NS_ERROR_DOM_INVALID_STATE_ERR,
- NS_LITERAL_CSTRING(
- "CDM crashed before binding MediaKeys object to HTMLMediaElement"));
+ "CDM crashed before binding MediaKeys object to HTMLMediaElement"));
return false;
}
// 5.3.1 Associate the CDM instance represented by mediaKeys with the
// media element for decrypting media data.
if (NS_FAILED(mIncomingMediaKeys->Bind(this))) {
// 5.3.2 If the preceding step failed, run the following steps:
// 5.3.2.1 Set the mediaKeys attribute to null.
mMediaKeys = nullptr;
// 5.3.2.2 Let this object's attaching media keys value be false.
- ResetSetMediaKeysTempVariables();
// 5.3.2.3 Reject promise with a new DOMException whose name is
// the appropriate error name.
- aPromise->MaybeReject(
- NS_ERROR_DOM_INVALID_STATE_ERR,
- NS_LITERAL_CSTRING(
- "Failed to bind MediaKeys object to HTMLMediaElement"));
+ SetCDMProxyFailure(
+ MediaResult(NS_ERROR_DOM_INVALID_STATE_ERR,
+ "Failed to bind MediaKeys object to HTMLMediaElement"));
return false;
}
return TryMakeAssociationWithCDM(cdmProxy);
}
return true;
}
void
@@ -7181,48 +7222,41 @@ HTMLMediaElement::SetMediaKeys(mozilla::
// 1. If mediaKeys and the mediaKeys attribute are the same object,
// return a resolved promise.
if (mMediaKeys == aMediaKeys) {
promise->MaybeResolveWithUndefined();
return promise.forget();
}
- // Note: Our attaching code is synchronous, so we can skip the following steps.
-
// 2. If this object's attaching media keys value is true, return a
// promise rejected with a new DOMException whose name is InvalidStateError.
if (mAttachingMediaKey) {
promise->MaybeReject(
NS_ERROR_DOM_INVALID_STATE_ERR,
NS_LITERAL_CSTRING("A MediaKeys object is in attaching operation."));
return promise.forget();
}
// 3. Let this object's attaching media keys value be true.
mAttachingMediaKey = true;
mIncomingMediaKeys = aMediaKeys;
// 4. Let promise be a new promise.
+ mSetMediaKeysDOMPromise = promise;
+
// 5. Run the following steps in parallel:
- // 5.1 & 5.2
- if (!DetachExistingMediaKeys(promise)) {
- ResetSetMediaKeysTempVariables();
- return promise.forget();
- }
-
- // 5.3
- if (!AttachNewMediaKeys(promise)) {
- ResetSetMediaKeysTempVariables();
+ // 5.1 & 5.2 & 5.3
+ if (!DetachExistingMediaKeys() || !AttachNewMediaKeys()) {
return promise.forget();
}
// 5.4, 5.5, 5.6
- MakeAssociationWithCDMResolved(promise);
+ MakeAssociationWithCDMResolved();
// 6. Return promise.
return promise.forget();
}
EventHandlerNonNull*
HTMLMediaElement::GetOnencrypted()
{
--- a/dom/html/HTMLMediaElement.h
+++ b/dom/html/HTMLMediaElement.h
@@ -7,16 +7,17 @@
#define mozilla_dom_HTMLMediaElement_h
#include "nsAutoPtr.h"
#include "nsIDOMHTMLMediaElement.h"
#include "nsGenericHTMLElement.h"
#include "MediaEventSource.h"
#include "SeekTarget.h"
#include "MediaDecoderOwner.h"
+#include "MediaPromiseDefs.h"
#include "nsCycleCollectionParticipant.h"
#include "nsIObserver.h"
#include "mozilla/CORSMode.h"
#include "DecoderTraits.h"
#include "nsIAudioChannelAgent.h"
#include "mozilla/Attributes.h"
#include "mozilla/dom/TextTrackManager.h"
#include "mozilla/WeakPtr.h"
@@ -1327,22 +1328,23 @@ protected:
const nsAttrValue* aValue,
const nsAttrValue* aOldValue,
nsIPrincipal* aMaybeScriptedPrincipal,
bool aNotify) override;
virtual nsresult OnAttrSetButNotChanged(int32_t aNamespaceID, nsAtom* aName,
const nsAttrValueOrString& aValue,
bool aNotify) override;
- bool DetachExistingMediaKeys(DetailedPromise* aPromise);
- bool TryRemoveMediaKeysAssociation(DetailedPromise* aPromise);
+ bool DetachExistingMediaKeys();
+ bool TryRemoveMediaKeysAssociation();
void RemoveMediaKeys();
- bool AttachNewMediaKeys(DetailedPromise* aPromise);
+ bool AttachNewMediaKeys();
bool TryMakeAssociationWithCDM(CDMProxy* aProxy);
- void MakeAssociationWithCDMResolved(DetailedPromise* aPromise);
+ void MakeAssociationWithCDMResolved();
+ void SetCDMProxyFailure(const MediaResult& aResult);
void ResetSetMediaKeysTempVariables();
// The current decoder. Load() has been called on this decoder.
// At most one of mDecoder and mSrcStream can be non-null.
RefPtr<MediaDecoder> mDecoder;
// The DocGroup-specific nsISerialEventTarget of this HTML element on the main
// thread.
@@ -1538,18 +1540,21 @@ protected:
nsCOMPtr<nsITimer> mProgressTimer;
// Timer used to simulate video-suspend.
nsCOMPtr<nsITimer> mVideoDecodeSuspendTimer;
// Encrypted Media Extension media keys.
RefPtr<MediaKeys> mMediaKeys;
RefPtr<MediaKeys> mIncomingMediaKeys;
+ // The dom promise is used for HTMLMediaElement::SetMediaKeys.
+ RefPtr<DetailedPromise> mSetMediaKeysDOMPromise;
// Used to indicate if the MediaKeys attaching operation is on-going or not.
bool mAttachingMediaKey;
+ MozPromiseRequestHolder<SetCDMPromise> mSetCDMRequest;
// Stores the time at the start of the current 'played' range.
double mCurrentPlayRangeStart;
// True if loadeddata has been fired.
bool mLoadedDataFired;
// Indicates whether current playback is a result of user action
--- a/dom/media/MediaDecoder.cpp
+++ b/dom/media/MediaDecoder.cpp
@@ -1418,28 +1418,25 @@ MediaDecoder::CanPlayThrough()
bool val = CanPlayThroughImpl();
if (val != mCanPlayThrough) {
mCanPlayThrough = val;
mDecoderStateMachine->DispatchCanPlayThrough(val);
}
return val;
}
-void
+RefPtr<SetCDMPromise>
MediaDecoder::SetCDMProxy(CDMProxy* aProxy)
{
MOZ_ASSERT(NS_IsMainThread());
- RefPtr<CDMProxy> proxy = aProxy;
- RefPtr<MediaFormatReader> reader = mReader;
- nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(
- "MediaFormatReader::SetCDMProxy",
- [reader, proxy]() {
- reader->SetCDMProxy(proxy);
- });
- mReader->OwnerThread()->Dispatch(r.forget());
+ return InvokeAsync<RefPtr<CDMProxy>>(mReader->OwnerThread(),
+ mReader.get(),
+ __func__,
+ &MediaFormatReader::SetCDMProxy,
+ aProxy);
}
bool
MediaDecoder::IsOpusEnabled()
{
return Preferences::GetBool("media.opus.enabled");
}
--- a/dom/media/MediaDecoder.h
+++ b/dom/media/MediaDecoder.h
@@ -7,16 +7,17 @@
#if !defined(MediaDecoder_h_)
#define MediaDecoder_h_
#include "DecoderDoctorDiagnostics.h"
#include "MediaContainerType.h"
#include "MediaDecoderOwner.h"
#include "MediaEventSource.h"
#include "MediaMetadataManager.h"
+#include "MediaPromiseDefs.h"
#include "MediaResource.h"
#include "MediaStatistics.h"
#include "MediaStreamGraph.h"
#include "SeekTarget.h"
#include "TimeUnits.h"
#include "mozilla/Atomics.h"
#include "mozilla/CDMProxy.h"
#include "mozilla/MozPromise.h"
@@ -350,17 +351,17 @@ private:
MediaDecoderOwner* GetOwner() const;
AbstractThread* AbstractMainThread() const
{
return mAbstractMainThread;
}
- void SetCDMProxy(CDMProxy* aProxy);
+ RefPtr<SetCDMPromise> SetCDMProxy(CDMProxy* aProxy);
void EnsureTelemetryReported();
static bool IsOggEnabled();
static bool IsOpusEnabled();
static bool IsWaveEnabled();
static bool IsWebMEnabled();