| Index: third_party/WebKit/Source/core/html/HTMLMediaElement.cpp
|
| diff --git a/third_party/WebKit/Source/core/html/HTMLMediaElement.cpp b/third_party/WebKit/Source/core/html/HTMLMediaElement.cpp
|
| index a10491cfbac6f7463e57e54240a6abec84fb8f2e..013dfee6ae2bc3a9629e605a74d8d16f175c6e4a 100644
|
| --- a/third_party/WebKit/Source/core/html/HTMLMediaElement.cpp
|
| +++ b/third_party/WebKit/Source/core/html/HTMLMediaElement.cpp
|
| @@ -29,11 +29,11 @@
|
| #include "bindings/core/v8/ExceptionStatePlaceholder.h"
|
| #include "bindings/core/v8/ScriptController.h"
|
| #include "bindings/core/v8/ScriptEventListener.h"
|
| +#include "bindings/core/v8/ScriptPromiseResolver.h"
|
| #include "core/HTMLNames.h"
|
| #include "core/css/MediaList.h"
|
| #include "core/dom/Attribute.h"
|
| #include "core/dom/ElementTraversal.h"
|
| -#include "core/dom/ExceptionCode.h"
|
| #include "core/dom/Fullscreen.h"
|
| #include "core/dom/shadow/ShadowRoot.h"
|
| #include "core/events/Event.h"
|
| @@ -733,6 +733,8 @@ void HTMLMediaElement::prepareForLoad()
|
| // one of the task queues, then remove those tasks.
|
| cancelPendingEventsAndCallbacks();
|
|
|
| + rejectPlayPromises(AbortError, "The play() request was interrupted by a new load request.");
|
| +
|
| // 3 - If the media element's networkState is set to NETWORK_LOADING or NETWORK_IDLE, queue
|
| // a task to fire a simple event named abort at the media element.
|
| if (m_networkState == NETWORK_LOADING || m_networkState == NETWORK_IDLE)
|
| @@ -1245,33 +1247,33 @@ void HTMLMediaElement::noneSupported()
|
| m_loadState = WaitingForSource;
|
| m_currentSourceNode = nullptr;
|
|
|
| - // 4.8.10.5
|
| - // 6 - Reaching this step indicates that the media resource failed to load or that the given
|
| - // URL could not be resolved. In one atomic operation, run the following steps:
|
| + // 4.8.13.5
|
| + // The dedicated media source failure steps are the following steps:
|
|
|
| - // 6.1 - Set the error attribute to a new MediaError object whose code attribute is set to
|
| + // 1 - Set the error attribute to a new MediaError object whose code attribute is set to
|
| // MEDIA_ERR_SRC_NOT_SUPPORTED.
|
| m_error = MediaError::create(MediaError::MEDIA_ERR_SRC_NOT_SUPPORTED);
|
|
|
| - // 6.2 - Forget the media element's media-resource-specific text tracks.
|
| + // 2 - Forget the media element's media-resource-specific text tracks.
|
| forgetResourceSpecificTracks();
|
|
|
| - // 6.3 - Set the element's networkState attribute to the NETWORK_NO_SOURCE value.
|
| + // 3 - Set the element's networkState attribute to the NETWORK_NO_SOURCE value.
|
| setNetworkState(NETWORK_NO_SOURCE);
|
|
|
| - // 7 - Queue a task to fire a simple event named error at the media element.
|
| + // 4 - Set the element's show poster flag to true.
|
| + updateDisplayState();
|
| +
|
| + // 5 - Fire a simple event named error at the media element.
|
| scheduleEvent(EventTypeNames::error);
|
|
|
| + // 6 - Reject pending play promises with NotSupportedError.
|
| + scheduleRejectPlayPromises(NotSupportedError, "Failed to load because no supported source was found.");
|
| +
|
| closeMediaSource();
|
|
|
| - // 8 - Set the element's delaying-the-load-event flag to false. This stops delaying the load event.
|
| + // 7 - Set the element's delaying-the-load-event flag to false. This stops delaying the load event.
|
| setShouldDelayLoadEvent(false);
|
|
|
| - // 9 - Abort these steps. Until the load() method is invoked or the src attribute is changed,
|
| - // the element won't attempt to load another resource.
|
| -
|
| - updateDisplayState();
|
| -
|
| if (layoutObject())
|
| layoutObject()->updateFromElement();
|
| }
|
| @@ -1520,7 +1522,7 @@ void HTMLMediaElement::setReadyState(ReadyState state)
|
| if (m_readyState == HAVE_FUTURE_DATA && oldState <= HAVE_CURRENT_DATA && tracksAreReady) {
|
| scheduleEvent(EventTypeNames::canplay);
|
| if (isPotentiallyPlaying)
|
| - scheduleEvent(EventTypeNames::playing);
|
| + scheduleNotifyPlaying();
|
| shouldUpdateDisplayState = true;
|
| }
|
|
|
| @@ -1528,7 +1530,7 @@ void HTMLMediaElement::setReadyState(ReadyState state)
|
| if (oldState <= HAVE_CURRENT_DATA) {
|
| scheduleEvent(EventTypeNames::canplay);
|
| if (isPotentiallyPlaying)
|
| - scheduleEvent(EventTypeNames::playing);
|
| + scheduleNotifyPlaying();
|
| }
|
|
|
| // Check for autoplay, and record metrics about it if needed.
|
| @@ -1541,7 +1543,7 @@ void HTMLMediaElement::setReadyState(ReadyState state)
|
| m_paused = false;
|
| invalidateCachedTime();
|
| scheduleEvent(EventTypeNames::play);
|
| - scheduleEvent(EventTypeNames::playing);
|
| + scheduleNotifyPlaying();
|
| m_autoplaying = false;
|
| }
|
| }
|
| @@ -1933,7 +1935,32 @@ WebMediaPlayer::Preload HTMLMediaElement::effectivePreloadType() const
|
| return autoplay() ? WebMediaPlayer::PreloadAuto : preloadType();
|
| }
|
|
|
| -void HTMLMediaElement::play()
|
| +ScriptPromise HTMLMediaElement::playForBindings(ScriptState* scriptState)
|
| +{
|
| + Nullable<ExceptionCode> code = play();
|
| + if (!code.isNull()) {
|
| + String message;
|
| + switch (code.get()) {
|
| + case NotAllowedError:
|
| + message = "play() can only be initiated by a user gesture.";
|
| + break;
|
| + case NotSupportedError:
|
| + message = "The element has no supported sources.";
|
| + break;
|
| + default:
|
| + ASSERT_NOT_REACHED();
|
| + }
|
| + return ScriptPromise::rejectWithDOMException(scriptState, DOMException::create(code.get(), message));
|
| + }
|
| +
|
| + ScriptPromiseResolver* resolver = ScriptPromiseResolver::create(scriptState);
|
| + ScriptPromise promise = resolver->promise();
|
| +
|
| + m_playResolvers.append(resolver);
|
| + return promise;
|
| +}
|
| +
|
| +Nullable<ExceptionCode> HTMLMediaElement::play()
|
| {
|
| WTF_LOG(Media, "HTMLMediaElement::play(%p)", this);
|
|
|
| @@ -1946,7 +1973,7 @@ void HTMLMediaElement::play()
|
| recordAutoplayMetric(PlayMethodFailed);
|
| String message = ExceptionMessages::failedToExecute("play", "HTMLMediaElement", "API can only be initiated by a user gesture.");
|
| document().addConsoleMessage(ConsoleMessage::create(JSMessageSource, WarningMessageLevel, message));
|
| - return;
|
| + return NotAllowedError;
|
| }
|
| } else if (m_userGestureRequiredForPlay) {
|
| if (m_autoplayMediaCounted)
|
| @@ -1954,7 +1981,12 @@ void HTMLMediaElement::play()
|
| m_userGestureRequiredForPlay = false;
|
| }
|
|
|
| + if (m_error && m_error->code() == MediaError::MEDIA_ERR_SRC_NOT_SUPPORTED)
|
| + return NotSupportedError;
|
| +
|
| playInternal();
|
| +
|
| + return nullptr;
|
| }
|
|
|
| void HTMLMediaElement::playInternal()
|
| @@ -1985,8 +2017,11 @@ void HTMLMediaElement::playInternal()
|
| if (m_readyState <= HAVE_CURRENT_DATA)
|
| scheduleEvent(EventTypeNames::waiting);
|
| else if (m_readyState >= HAVE_FUTURE_DATA)
|
| - scheduleEvent(EventTypeNames::playing);
|
| + scheduleNotifyPlaying();
|
| + } else if (m_readyState >= HAVE_FUTURE_DATA) {
|
| + scheduleResolvePlayPromises();
|
| }
|
| +
|
| m_autoplaying = false;
|
|
|
| updatePlayState();
|
| @@ -2041,6 +2076,7 @@ void HTMLMediaElement::pauseInternal()
|
| m_paused = true;
|
| scheduleTimeupdateEvent(false);
|
| scheduleEvent(EventTypeNames::pause);
|
| + scheduleRejectPlayPromises(AbortError, "The play() request was interrupted by a call to pause().");
|
| }
|
|
|
| updatePlayState();
|
| @@ -3491,6 +3527,7 @@ DEFINE_TRACE(HTMLMediaElement)
|
| visitor->trace(m_cueTimeline);
|
| visitor->trace(m_textTracks);
|
| visitor->trace(m_textTracksWhenResourceSelectionBegan);
|
| + visitor->trace(m_playResolvers);
|
| visitor->trace(m_audioSourceProvider);
|
| visitor->template registerWeakMembers<HTMLMediaElement, &HTMLMediaElement::clearWeakMembers>(this);
|
| visitor->trace(m_autoplayHelper);
|
| @@ -3568,6 +3605,42 @@ void HTMLMediaElement::triggerAutoplayViewportCheckForTesting()
|
| m_autoplayHelper.triggerAutoplayViewportCheckForTesting();
|
| }
|
|
|
| +void HTMLMediaElement::scheduleResolvePlayPromises()
|
| +{
|
| + Platform::current()->currentThread()->taskRunner()->postTask(
|
| + BLINK_FROM_HERE, WTF::bind(&HTMLMediaElement::resolvePlayPromises, PassRefPtrWillBeRawPtr<HTMLMediaElement>(this)));
|
| +}
|
| +
|
| +void HTMLMediaElement::scheduleRejectPlayPromises(ExceptionCode code, const String& message)
|
| +{
|
| + Platform::current()->currentThread()->taskRunner()->postTask(
|
| + BLINK_FROM_HERE, WTF::bind(&HTMLMediaElement::rejectPlayPromises, PassRefPtrWillBeRawPtr<HTMLMediaElement>(this), code, message));
|
| +}
|
| +
|
| +void HTMLMediaElement::scheduleNotifyPlaying()
|
| +{
|
| + scheduleEvent(EventTypeNames::playing);
|
| + scheduleResolvePlayPromises();
|
| +}
|
| +
|
| +void HTMLMediaElement::resolvePlayPromises()
|
| +{
|
| + for (auto& resolver: m_playResolvers)
|
| + resolver->resolve();
|
| +
|
| + m_playResolvers.clear();
|
| +}
|
| +
|
| +void HTMLMediaElement::rejectPlayPromises(ExceptionCode code, const String& message)
|
| +{
|
| + ASSERT(code == AbortError || code == NotSupportedError);
|
| +
|
| + for (auto& resolver: m_playResolvers)
|
| + resolver->reject(DOMException::create(code, message));
|
| +
|
| + m_playResolvers.clear();
|
| +}
|
| +
|
| void HTMLMediaElement::clearWeakMembers(Visitor* visitor)
|
| {
|
| if (!Heap::isHeapObjectAlive(m_audioSourceNode))
|
|
|