Bug 1395922 - [P3] Make HTMLMediaElement::SetMediaKeys asynchronously. r=cpearce

MozReview-Commit-ID: 5M8CTHMsmIh
This commit is contained in:
Kilik Kuo
2017-11-03 20:14:17 +08:00
parent d7a3e22409
commit 853f877070
4 changed files with 93 additions and 56 deletions

View File

@@ -1489,6 +1489,7 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(HTMLMediaElement, nsGenericHTM
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)
@@ -1519,6 +1520,7 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(HTMLMediaElement, nsGenericHTMLE
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)
@@ -1724,6 +1726,7 @@ void HTMLMediaElement::ShutdownDecoder()
{
RemoveMediaElementFromURITable();
NS_ASSERTION(mDecoder, "Must have decoder to shut down");
mSetCDMRequest.DisconnectIfExists();
mWaitingForKeyListener.DisconnectIfExists();
if (mMediaSource) {
mMediaSource->CompletePendingTransactions();
@@ -7018,6 +7021,17 @@ 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()
{
@@ -7029,7 +7043,7 @@ HTMLMediaElement::RemoveMediaKeys()
}
bool
HTMLMediaElement::TryRemoveMediaKeysAssociation(DetailedPromise* aPromise)
HTMLMediaElement::TryRemoveMediaKeysAssociation()
{
MOZ_ASSERT(mMediaKeys);
LOG(LogLevel::Debug, ("%s", __func__));
@@ -7040,88 +7054,117 @@ HTMLMediaElement::TryRemoveMediaKeysAssociation(DetailedPromise* aPromise)
// 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.
RemoveMediaKeys();
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)
{
MOZ_ASSERT(aProxy);
LOG(LogLevel::Debug, ("%s", __func__));
MOZ_ASSERT(aProxy);
// 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;
}
@@ -7133,13 +7176,11 @@ HTMLMediaElement::AttachNewMediaKeys(DetailedPromise* aPromise)
// 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);
@@ -7185,8 +7226,6 @@ HTMLMediaElement::SetMediaKeys(mozilla::dom::MediaKeys* aMediaKeys,
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) {
@@ -7201,22 +7240,17 @@ HTMLMediaElement::SetMediaKeys(mozilla::dom::MediaKeys* aMediaKeys,
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();