Implement promise-based media playback.
parent
7fd3f667aa
commit
159202da62
|
@ -8,6 +8,7 @@
|
|||
#include "mozilla/dom/HTMLMediaElementBinding.h"
|
||||
#include "mozilla/dom/HTMLSourceElement.h"
|
||||
#include "mozilla/dom/ElementInlines.h"
|
||||
#include "mozilla/dom/Promise.h"
|
||||
#include "mozilla/ArrayUtils.h"
|
||||
#include "mozilla/MathAlgorithms.h"
|
||||
#include "mozilla/AsyncEventDispatcher.h"
|
||||
|
@ -171,6 +172,22 @@ static const unsigned short MEDIA_ERR_NETWORK = 2;
|
|||
static const unsigned short MEDIA_ERR_DECODE = 3;
|
||||
static const unsigned short MEDIA_ERR_SRC_NOT_SUPPORTED = 4;
|
||||
|
||||
static void
|
||||
ResolvePromisesWithUndefined(const nsTArray<RefPtr<Promise>>& aPromises)
|
||||
{
|
||||
for (auto& promise : aPromises) {
|
||||
promise->MaybeResolveWithUndefined();
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
RejectPromises(const nsTArray<RefPtr<Promise>>& aPromises, nsresult aError)
|
||||
{
|
||||
for (auto& promise : aPromises) {
|
||||
promise->MaybeReject(aError);
|
||||
}
|
||||
}
|
||||
|
||||
// Under certain conditions there may be no-one holding references to
|
||||
// a media element from script, DOM parent, etc, but the element may still
|
||||
// fire meaningful events in the future so we can't destroy it yet:
|
||||
|
@ -261,6 +278,75 @@ public:
|
|||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* If no error is passed while constructing an instance, the instance will
|
||||
* resolve the passed promises with undefined; otherwise, the instance will
|
||||
* reject the passed promises with the passed error.
|
||||
*
|
||||
* The constructor appends the constructed instance into the passed media
|
||||
* element's mPendingPlayPromisesRunners member and once the the runner is run
|
||||
* (whether fulfilled or canceled), it removes itself from
|
||||
* mPendingPlayPromisesRunners.
|
||||
*/
|
||||
class HTMLMediaElement::nsResolveOrRejectPendingPlayPromisesRunner : public nsMediaEvent
|
||||
{
|
||||
nsTArray<RefPtr<Promise>> mPromises;
|
||||
nsresult mError;
|
||||
|
||||
public:
|
||||
nsResolveOrRejectPendingPlayPromisesRunner(HTMLMediaElement* aElement,
|
||||
nsTArray<RefPtr<Promise>>&& aPromises,
|
||||
nsresult aError = NS_OK)
|
||||
: nsMediaEvent(aElement)
|
||||
, mPromises(Move(aPromises))
|
||||
, mError(aError)
|
||||
{
|
||||
mElement->mPendingPlayPromisesRunners.AppendElement(this);
|
||||
}
|
||||
|
||||
void ResolveOrReject()
|
||||
{
|
||||
if (NS_SUCCEEDED(mError)) {
|
||||
ResolvePromisesWithUndefined(mPromises);
|
||||
} else {
|
||||
RejectPromises(mPromises, mError);
|
||||
}
|
||||
}
|
||||
|
||||
NS_IMETHOD Run() override
|
||||
{
|
||||
if (!IsCancelled()) {
|
||||
ResolveOrReject();
|
||||
}
|
||||
|
||||
mElement->mPendingPlayPromisesRunners.RemoveElement(this);
|
||||
return NS_OK;
|
||||
}
|
||||
};
|
||||
|
||||
class HTMLMediaElement::nsNotifyAboutPlayingRunner : public nsResolveOrRejectPendingPlayPromisesRunner
|
||||
{
|
||||
public:
|
||||
nsNotifyAboutPlayingRunner(HTMLMediaElement* aElement,
|
||||
nsTArray<RefPtr<Promise>>&& aPendingPlayPromises)
|
||||
: nsResolveOrRejectPendingPlayPromisesRunner(aElement,
|
||||
Move(aPendingPlayPromises))
|
||||
{
|
||||
}
|
||||
|
||||
NS_IMETHOD Run() override
|
||||
{
|
||||
if (IsCancelled()) {
|
||||
mElement->mPendingPlayPromisesRunners.RemoveElement(this);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
mElement->DispatchEvent(NS_LITERAL_STRING("playing"));
|
||||
return nsResolveOrRejectPendingPlayPromisesRunner::Run();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
class nsSourceErrorEventRunner : public nsMediaEvent
|
||||
{
|
||||
private:
|
||||
|
@ -826,6 +912,7 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(HTMLMediaElement, nsGenericHTM
|
|||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMediaKeys)
|
||||
#endif
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSelectedVideoStreamTrack)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPendingPlayPromises)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(HTMLMediaElement, nsGenericHTMLElement)
|
||||
|
@ -853,6 +940,7 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(HTMLMediaElement, nsGenericHTMLE
|
|||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mMediaKeys)
|
||||
#endif
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mSelectedVideoStreamTrack)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mPendingPlayPromises)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(HTMLMediaElement)
|
||||
|
@ -1044,6 +1132,14 @@ void HTMLMediaElement::AbortExistingLoads()
|
|||
// with a different load ID to silently be cancelled.
|
||||
mCurrentLoadID++;
|
||||
|
||||
// Immediately reject or resolve the already-dispatched
|
||||
// nsResolveOrRejectPendingPlayPromisesRunners. These runners won't be
|
||||
// executed again later since the mCurrentLoadID had been changed.
|
||||
for (auto& runner : mPendingPlayPromisesRunners) {
|
||||
runner->ResolveOrReject();
|
||||
}
|
||||
mPendingPlayPromisesRunners.Clear();
|
||||
|
||||
if (mChannelLoader) {
|
||||
mChannelLoader->Cancel();
|
||||
mChannelLoader = nullptr;
|
||||
|
@ -1107,7 +1203,10 @@ void HTMLMediaElement::AbortExistingLoads()
|
|||
NS_ASSERTION(!mDecoder && !mSrcStream, "How did someone setup a new stream/decoder already?");
|
||||
// ChangeNetworkState() will call UpdateAudioChannelPlayingState()
|
||||
// indirectly which depends on mPaused. So we need to update mPaused first.
|
||||
mPaused = true;
|
||||
if (!mPaused) {
|
||||
mPaused = true;
|
||||
RejectPromises(TakePendingPlayPromises(), NS_ERROR_DOM_MEDIA_ABORT_ERR);
|
||||
}
|
||||
ChangeNetworkState(nsIDOMHTMLMediaElement::NETWORK_EMPTY);
|
||||
ChangeReadyState(nsIDOMHTMLMediaElement::HAVE_NOTHING);
|
||||
|
||||
|
@ -1149,6 +1248,7 @@ void HTMLMediaElement::NoSupportedMediaSourceError(const nsACString& aErrorDetai
|
|||
mErrorSink->SetError(MEDIA_ERR_SRC_NOT_SUPPORTED, aErrorDetails);
|
||||
ChangeDelayLoadStatus(false);
|
||||
UpdateAudioChannelPlayingState();
|
||||
RejectPromises(TakePendingPlayPromises(), NS_ERROR_DOM_MEDIA_NOT_SUPPORTED_ERR);
|
||||
}
|
||||
|
||||
typedef void (HTMLMediaElement::*SyncSectionFn)();
|
||||
|
@ -1953,14 +2053,7 @@ HTMLMediaElement::Seek(double aTime,
|
|||
// aTime should be non-NaN.
|
||||
MOZ_ASSERT(!mozilla::IsNaN(aTime));
|
||||
|
||||
nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(OwnerDoc()->GetInnerWindow());
|
||||
|
||||
if (!global) {
|
||||
aRv.Throw(NS_ERROR_UNEXPECTED);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
RefPtr<Promise> promise = Promise::Create(global, aRv);
|
||||
RefPtr<Promise> promise = CreateDOMPromise(aRv);
|
||||
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return nullptr;
|
||||
|
@ -2208,6 +2301,7 @@ HTMLMediaElement::Pause(ErrorResult& aRv)
|
|||
if (!oldPaused) {
|
||||
FireTimeUpdate(false);
|
||||
DispatchAsyncEvent(NS_LITERAL_STRING("pause"));
|
||||
AsyncRejectPendingPlayPromises(NS_ERROR_DOM_MEDIA_ABORT_ERR);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3200,36 +3294,86 @@ HTMLMediaElement::NotifyXPCOMShutdown()
|
|||
ShutdownDecoder();
|
||||
}
|
||||
|
||||
void
|
||||
already_AddRefed<Promise>
|
||||
HTMLMediaElement::Play(ErrorResult& aRv)
|
||||
{
|
||||
if (!IsAllowedToPlay()) {
|
||||
MaybeDoLoad();
|
||||
return;
|
||||
|
||||
// A blocked media element will be resumed later, so we return a pending
|
||||
// promise which might be resolved/rejected depends on the result of
|
||||
// resuming the blocked media element.
|
||||
RefPtr<Promise> promise = CreateDOMPromise(aRv);
|
||||
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
mPendingPlayPromises.AppendElement(promise);
|
||||
return promise.forget();
|
||||
}
|
||||
|
||||
nsresult rv = PlayInternal();
|
||||
if (NS_FAILED(rv)) {
|
||||
aRv.Throw(rv);
|
||||
}
|
||||
RefPtr<Promise> promise = PlayInternal(aRv);
|
||||
|
||||
OpenUnsupportedMediaWithExternalAppIfNeeded();
|
||||
|
||||
return promise.forget();
|
||||
}
|
||||
|
||||
nsresult
|
||||
HTMLMediaElement::PlayInternal()
|
||||
already_AddRefed<Promise>
|
||||
HTMLMediaElement::PlayInternal(ErrorResult& aRv)
|
||||
{
|
||||
MOZ_ASSERT(!aRv.Failed());
|
||||
|
||||
// 4.8.12.8
|
||||
// When the play() method on a media element is invoked, the user agent must
|
||||
// run the following steps.
|
||||
|
||||
// 4.8.12.8 - Step 1:
|
||||
// If the media element is not allowed to play, return a promise rejected
|
||||
// with a "NotAllowedError" DOMException and abort these steps.
|
||||
if (!IsAllowedToPlay()) {
|
||||
// NOTE: for promise-based-play, will return a rejected promise here.
|
||||
aRv.Throw(NS_ERROR_DOM_MEDIA_NOT_ALLOWED_ERR);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// 4.8.12.8 - Step 2:
|
||||
// If the media element's error attribute is not null and its code
|
||||
// attribute has the value MEDIA_ERR_SRC_NOT_SUPPORTED, return a promise
|
||||
// rejected with a "NotSupportedError" DOMException and abort these steps.
|
||||
if (GetError() && GetError()->Code() == MEDIA_ERR_SRC_NOT_SUPPORTED) {
|
||||
aRv.Throw(NS_ERROR_DOM_MEDIA_NOT_SUPPORTED_ERR);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// 4.8.12.8 - Step 3:
|
||||
// Let promise be a new promise and append promise to the list of pending
|
||||
// play promises.
|
||||
RefPtr<Promise> promise = CreateDOMPromise(aRv);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return nullptr;
|
||||
}
|
||||
mPendingPlayPromises.AppendElement(promise);
|
||||
|
||||
// Play was not blocked so assume user interacted with the element.
|
||||
mHasUserInteraction = true;
|
||||
|
||||
StopSuspendingAfterFirstFrame();
|
||||
SetPlayedOrSeeked(true);
|
||||
|
||||
// 4.8.12.8 - Step 4:
|
||||
// If the media element's networkState attribute has the value NETWORK_EMPTY,
|
||||
// invoke the media element's resource selection algorithm.
|
||||
MaybeDoLoad();
|
||||
if (mSuspendedForPreloadNone) {
|
||||
ResumeLoad(PRELOAD_ENOUGH);
|
||||
}
|
||||
|
||||
// 4.8.12.8 - Step 5:
|
||||
// If the playback has ended and the direction of playback is forwards,
|
||||
// seek to the earliest possible position of the media resource.
|
||||
|
||||
// Even if we just did Load() or ResumeLoad(), we could already have a decoder
|
||||
// here if we managed to clone an existing decoder.
|
||||
if (mDecoder) {
|
||||
|
@ -3239,7 +3383,14 @@ HTMLMediaElement::PlayInternal()
|
|||
if (!mPausedForInactiveDocumentOrChannel) {
|
||||
nsresult rv = mDecoder->Play();
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
// We don't need to remove the _promise_ from _mPendingPlayPromises_ here.
|
||||
// If something wrong between |mPendingPlayPromises.AppendElement(promise);|
|
||||
// and here, the _promise_ should already have been rejected. Otherwise,
|
||||
// the _promise_ won't be returned to JS at all, so just leave it in the
|
||||
// _mPendingPlayPromises_ and let it be resolved/rejected with the
|
||||
// following actions and the promise-resolution won't be observed at all.
|
||||
aRv.Throw(rv);
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3248,7 +3399,7 @@ HTMLMediaElement::PlayInternal()
|
|||
mCurrentPlayRangeStart = CurrentTime();
|
||||
}
|
||||
|
||||
bool oldPaused = mPaused;
|
||||
const bool oldPaused = mPaused;
|
||||
mPaused = false;
|
||||
mAutoplaying = false;
|
||||
SetAudioChannelSuspended(nsISuspendedTypes::NONE_SUSPENDED);
|
||||
|
@ -3265,8 +3416,27 @@ HTMLMediaElement::PlayInternal()
|
|||
// media, the event would be pending until media is resumed.
|
||||
// TODO: If the playback has ended, then the user agent must set
|
||||
// seek to the effective start.
|
||||
|
||||
// 4.8.12.8 - Step 6:
|
||||
// If the media element's paused attribute is true, run the following steps:
|
||||
if (oldPaused) {
|
||||
// 6.1. Change the value of paused to false. (Already done.)
|
||||
// This step is uplifted because the "block-media-playback" feature needs
|
||||
// the mPaused to be false before UpdateAudioChannelPlayingState() being
|
||||
// called.
|
||||
|
||||
// 6.2. If the show poster flag is true, set the element's show poster flag
|
||||
// to false and run the time marches on steps.
|
||||
|
||||
// 6.3. Queue a task to fire a simple event named play at the element.
|
||||
DispatchAsyncEvent(NS_LITERAL_STRING("play"));
|
||||
|
||||
// 6.4. If the media element's readyState attribute has the value
|
||||
// HAVE_NOTHING, HAVE_METADATA, or HAVE_CURRENT_DATA, queue a task to
|
||||
// fire a simple event named waiting at the element.
|
||||
// Otherwise, the media element's readyState attribute has the value
|
||||
// HAVE_FUTURE_DATA or HAVE_ENOUGH_DATA: notify about playing for the
|
||||
// element.
|
||||
switch (mReadyState) {
|
||||
case nsIDOMHTMLMediaElement::HAVE_NOTHING:
|
||||
DispatchAsyncEvent(NS_LITERAL_STRING("waiting"));
|
||||
|
@ -3279,12 +3449,20 @@ HTMLMediaElement::PlayInternal()
|
|||
case nsIDOMHTMLMediaElement::HAVE_FUTURE_DATA:
|
||||
case nsIDOMHTMLMediaElement::HAVE_ENOUGH_DATA:
|
||||
FireTimeUpdate(false);
|
||||
DispatchAsyncEvent(NS_LITERAL_STRING("playing"));
|
||||
NotifyAboutPlaying();
|
||||
break;
|
||||
}
|
||||
} else if (mReadyState >= nsIDOMHTMLMediaElement::HAVE_FUTURE_DATA) {
|
||||
// 7. Otherwise, if the media element's readyState attribute has the value
|
||||
// HAVE_FUTURE_DATA or HAVE_ENOUGH_DATA, take pending play promises and
|
||||
// queue a task to resolve pending play promises with the result.
|
||||
AsyncResolvePendingPlayPromises();
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
// 8. Set the media element's autoplaying flag to false. (Already done.)
|
||||
|
||||
// 9. Return promise.
|
||||
return promise.forget();
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -3302,9 +3480,10 @@ NS_IMETHODIMP HTMLMediaElement::Play()
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult rv = PlayInternal();
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
ErrorResult rv;
|
||||
RefPtr<Promise> toBeIgnored = PlayInternal(rv);
|
||||
if (rv.Failed()) {
|
||||
return rv.StealNSResult();
|
||||
}
|
||||
|
||||
OpenUnsupportedMediaWithExternalAppIfNeeded();
|
||||
|
@ -4463,7 +4642,12 @@ void HTMLMediaElement::PlaybackEnded()
|
|||
return;
|
||||
}
|
||||
|
||||
Pause();
|
||||
FireTimeUpdate(false);
|
||||
|
||||
if (!mPaused) {
|
||||
Pause();
|
||||
AsyncRejectPendingPlayPromises(NS_ERROR_DOM_MEDIA_ABORT_ERR);
|
||||
}
|
||||
|
||||
if (mSrcStream) {
|
||||
// A MediaStream that goes from inactive to active shall be eligible for
|
||||
|
@ -4471,7 +4655,6 @@ void HTMLMediaElement::PlaybackEnded()
|
|||
mAutoplaying = true;
|
||||
}
|
||||
|
||||
FireTimeUpdate(false);
|
||||
DispatchAsyncEvent(NS_LITERAL_STRING("ended"));
|
||||
}
|
||||
|
||||
|
@ -4865,7 +5048,7 @@ void HTMLMediaElement::ChangeReadyState(nsMediaReadyState aState)
|
|||
DispatchAsyncEvent(NS_LITERAL_STRING("canplay"));
|
||||
if (!mPaused) {
|
||||
mWaitingForKey = NOT_WAITING_FOR_KEY;
|
||||
DispatchAsyncEvent(NS_LITERAL_STRING("playing"));
|
||||
NotifyAboutPlaying();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -5111,7 +5294,14 @@ nsresult HTMLMediaElement::DispatchAsyncEvent(const nsAString& aName)
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIRunnable> event = new nsAsyncEventRunner(aName, this);
|
||||
nsCOMPtr<nsIRunnable> event;
|
||||
|
||||
if (aName.EqualsLiteral("playing")) {
|
||||
event = new nsNotifyAboutPlayingRunner(this, TakePendingPlayPromises());
|
||||
} else {
|
||||
event = new nsAsyncEventRunner(aName, this);
|
||||
}
|
||||
|
||||
NS_DispatchToMainThread(event);
|
||||
|
||||
return NS_OK;
|
||||
|
@ -6601,5 +6791,64 @@ HTMLMediaElement::MarkAsContentSource(CallerAPI aAPI)
|
|||
}
|
||||
}
|
||||
|
||||
nsTArray<RefPtr<Promise>>
|
||||
HTMLMediaElement::TakePendingPlayPromises()
|
||||
{
|
||||
return Move(mPendingPlayPromises);
|
||||
}
|
||||
|
||||
void
|
||||
HTMLMediaElement::NotifyAboutPlaying()
|
||||
{
|
||||
// Stick to the DispatchAsyncEvent() call path for now because we want to
|
||||
// trigger some telemetry-related codes in the DispatchAsyncEvent() method.
|
||||
DispatchAsyncEvent(NS_LITERAL_STRING("playing"));
|
||||
}
|
||||
|
||||
already_AddRefed<Promise>
|
||||
HTMLMediaElement::CreateDOMPromise(ErrorResult& aRv) const
|
||||
{
|
||||
nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(OwnerDoc()->GetInnerWindow());
|
||||
|
||||
if (!global) {
|
||||
aRv.Throw(NS_ERROR_UNEXPECTED);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return Promise::Create(global, aRv);
|
||||
}
|
||||
|
||||
void
|
||||
HTMLMediaElement::AsyncResolvePendingPlayPromises()
|
||||
{
|
||||
if (mShuttingDown) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIRunnable> event
|
||||
= new nsResolveOrRejectPendingPlayPromisesRunner(this,
|
||||
TakePendingPlayPromises());
|
||||
|
||||
NS_DispatchToMainThread(event);
|
||||
}
|
||||
|
||||
void
|
||||
HTMLMediaElement::AsyncRejectPendingPlayPromises(nsresult aError)
|
||||
{
|
||||
if (mShuttingDown) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIRunnable> event
|
||||
= new nsResolveOrRejectPendingPlayPromisesRunner(this,
|
||||
TakePendingPlayPromises(),
|
||||
aError);
|
||||
|
||||
NS_DispatchToMainThread(event);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
|
|
@ -16,7 +16,6 @@
|
|||
#include "DecoderTraits.h"
|
||||
#include "nsIAudioChannelAgent.h"
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/dom/Promise.h"
|
||||
#include "mozilla/dom/TextTrackManager.h"
|
||||
#include "mozilla/WeakPtr.h"
|
||||
#include "MediaDecoder.h"
|
||||
|
@ -78,6 +77,7 @@ namespace dom {
|
|||
|
||||
class MediaError;
|
||||
class MediaSource;
|
||||
class Promise;
|
||||
class TextTrackList;
|
||||
class AudioTrackList;
|
||||
class VideoTrackList;
|
||||
|
@ -549,7 +549,7 @@ public:
|
|||
SetHTMLBoolAttr(nsGkAtoms::loop, aValue, aRv);
|
||||
}
|
||||
|
||||
void Play(ErrorResult& aRv);
|
||||
already_AddRefed<Promise> Play(ErrorResult& aRv);
|
||||
|
||||
void Pause(ErrorResult& aRv);
|
||||
|
||||
|
@ -835,7 +835,7 @@ protected:
|
|||
nsTArray<Pair<nsString, RefPtr<MediaInputPort>>> mTrackPorts;
|
||||
};
|
||||
|
||||
nsresult PlayInternal();
|
||||
already_AddRefed<Promise> PlayInternal(ErrorResult& aRv);
|
||||
|
||||
/** Use this method to change the mReadyState member, so required
|
||||
* events can be fired.
|
||||
|
@ -981,6 +981,7 @@ protected:
|
|||
void AbortExistingLoads();
|
||||
|
||||
/**
|
||||
* These are the dedicated media source failure steps.
|
||||
* Called when all potential resources are exhausted. Changes network
|
||||
* state to NETWORK_NO_SOURCE, and sends error event with code
|
||||
* MEDIA_ERR_SRC_NOT_SUPPORTED.
|
||||
|
@ -1285,6 +1286,8 @@ protected:
|
|||
void MaybeNotifyMediaResumed(SuspendTypes aSuspend);
|
||||
|
||||
class nsAsyncEventRunner;
|
||||
class nsNotifyAboutPlayingRunner;
|
||||
class nsResolveOrRejectPendingPlayPromisesRunner;
|
||||
using nsGenericHTMLElement::DispatchEvent;
|
||||
// For nsAsyncEventRunner.
|
||||
nsresult DispatchEvent(const nsAString& aName);
|
||||
|
@ -1293,6 +1296,24 @@ protected:
|
|||
// triggers play() after loaded fail. eg. preload the data before start play.
|
||||
void OpenUnsupportedMediaWithExternalAppIfNeeded() const;
|
||||
|
||||
// This method moves the mPendingPlayPromises into a temperate object. So the
|
||||
// mPendingPlayPromises is cleared after this method call.
|
||||
nsTArray<RefPtr<Promise>> TakePendingPlayPromises();
|
||||
|
||||
// This method snapshots the mPendingPlayPromises by TakePendingPlayPromises()
|
||||
// and queues a task to resolve them.
|
||||
void AsyncResolvePendingPlayPromises();
|
||||
|
||||
// This method snapshots the mPendingPlayPromises by TakePendingPlayPromises()
|
||||
// and queues a task to reject them.
|
||||
void AsyncRejectPendingPlayPromises(nsresult aError);
|
||||
|
||||
// This method snapshots the mPendingPlayPromises by TakePendingPlayPromises()
|
||||
// and queues a task to resolve them also to dispatch a "playing" event.
|
||||
void NotifyAboutPlaying();
|
||||
|
||||
already_AddRefed<Promise> CreateDOMPromise(ErrorResult& aRv) const;
|
||||
|
||||
// The current decoder. Load() has been called on this decoder.
|
||||
// At most one of mDecoder and mSrcStream can be non-null.
|
||||
RefPtr<MediaDecoder> mDecoder;
|
||||
|
@ -1684,6 +1705,17 @@ private:
|
|||
Visibility mVisibilityState;
|
||||
|
||||
UniquePtr<ErrorSink> mErrorSink;
|
||||
|
||||
// A list of pending play promises. The elements are pushed during the play()
|
||||
// method call and are resolved/rejected during further playback steps.
|
||||
nsTArray<RefPtr<Promise>> mPendingPlayPromises;
|
||||
|
||||
// A list of already-dispatched but not yet run
|
||||
// nsResolveOrRejectPendingPlayPromisesRunners.
|
||||
// Runners whose Run() method is called remove themselves from this list.
|
||||
// We keep track of these because the load algorithm resolves/rejects all
|
||||
// already-dispatched pending play promises.
|
||||
nsTArray<nsResolveOrRejectPendingPlayPromisesRunner*> mPendingPlayPromisesRunners;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
|
|
|
@ -68,7 +68,7 @@ interface HTMLMediaElement : HTMLElement {
|
|||
[SetterThrows]
|
||||
attribute boolean loop;
|
||||
[Throws]
|
||||
void play();
|
||||
Promise<void> play();
|
||||
[Throws]
|
||||
void pause();
|
||||
|
||||
|
|
|
@ -5427,13 +5427,7 @@ pref("dom.node.rootNode.enabled", true);
|
|||
// Default media volume
|
||||
pref("media.default_volume", "1.0");
|
||||
|
||||
// Once bug 1276272 is resolved, we will trun this preference to default ON in
|
||||
// non-release channels.
|
||||
#ifdef RELEASE_OR_BETA
|
||||
pref("media.seekToNextFrame.enabled", false);
|
||||
#else
|
||||
pref("media.seekToNextFrame.enabled", true);
|
||||
#endif
|
||||
|
||||
// return the maximum number of cores that navigator.hardwareCurrency returns
|
||||
pref("dom.maxHardwareConcurrency", 16);
|
||||
|
|
Loading…
Reference in New Issue