Chromium Code Reviews| 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 7a3c5bb811ac9c53744959f8ea10062c0685cee2..fff9ef427dbd9c01f4d1e274b9c29cf19431d847 100644 |
| --- a/third_party/WebKit/Source/core/html/HTMLMediaElement.cpp |
| +++ b/third_party/WebKit/Source/core/html/HTMLMediaElement.cpp |
| @@ -29,6 +29,7 @@ |
| #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" |
| @@ -44,7 +45,6 @@ |
| #include "core/html/HTMLMediaSource.h" |
| #include "core/html/HTMLSourceElement.h" |
| #include "core/html/HTMLTrackElement.h" |
| -#include "core/html/MediaError.h" |
| #include "core/html/MediaFragmentURIParser.h" |
| #include "core/html/TimeRanges.h" |
| #include "core/html/shadow/MediaControls.h" |
| @@ -70,6 +70,7 @@ |
| #include "platform/MIMETypeFromURL.h" |
| #include "platform/MIMETypeRegistry.h" |
| #include "platform/RuntimeEnabledFeatures.h" |
| +#include "platform/Task.h" |
| #include "platform/UserGestureIndicator.h" |
| #include "platform/audio/AudioBus.h" |
| #include "platform/audio/AudioSourceProviderClient.h" |
| @@ -726,6 +727,8 @@ void HTMLMediaElement::prepareForLoad() |
| // one of the task queues, then remove those tasks. |
| cancelPendingEventsAndCallbacks(); |
| + rejectPlayPromises(MediaError::MEDIA_ERR_ABORTED); |
| + |
| // 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) |
| @@ -1254,12 +1257,15 @@ void HTMLMediaElement::noneSupported() |
| // 6.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. |
| + // 5 - Queue a task to fire a simple event named error at the media element. |
| scheduleEvent(EventTypeNames::error); |
| + // 6 - For each promise in the list of pending play romises, reject it with NotSupportedError. |
|
philipj_slow
2016/02/02 09:56:33
Can you sweep your CL and update step numbers and
mlamouri (slow - plz ping)
2016/02/03 19:28:57
Done.
|
| + scheduleRejectPlayPromises(MediaError::MEDIA_ERR_SRC_NOT_SUPPORTED); |
| + |
| 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, |
| @@ -1517,7 +1523,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; |
| } |
| @@ -1525,7 +1531,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. |
| @@ -1538,7 +1544,7 @@ void HTMLMediaElement::setReadyState(ReadyState state) |
| m_paused = false; |
| invalidateCachedTime(); |
| scheduleEvent(EventTypeNames::play); |
| - scheduleEvent(EventTypeNames::playing); |
| + scheduleNotifyPlaying(); |
| m_autoplaying = false; |
| } |
| } |
| @@ -1930,7 +1936,7 @@ WebMediaPlayer::Preload HTMLMediaElement::effectivePreloadType() const |
| return autoplay() ? WebMediaPlayer::PreloadAuto : preloadType(); |
| } |
| -void HTMLMediaElement::play() |
| +ScriptPromise HTMLMediaElement::play(ScriptState* scriptState) |
| { |
| WTF_LOG(Media, "HTMLMediaElement::play(%p)", this); |
| @@ -1943,7 +1949,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 ScriptPromise::rejectWithDOMException(scriptState, DOMException::create(NotAllowedError, "play() must be initiated by a user gesture.")); |
| } |
| } else if (m_userGestureRequiredForPlay) { |
| if (m_autoplayMediaCounted) |
| @@ -1951,13 +1957,24 @@ void HTMLMediaElement::play() |
| m_userGestureRequiredForPlay = false; |
| } |
| + ScriptPromiseResolver* resolver = ScriptPromiseResolver::create(scriptState); |
| + ScriptPromise promise = resolver->promise(); |
| + |
| + m_playResolvers.append(resolver); |
| playInternal(); |
| + |
| + return promise; |
| } |
| void HTMLMediaElement::playInternal() |
| { |
| WTF_LOG(Media, "HTMLMediaElement::playInternal(%p)", this); |
| + if (m_error && m_error->code() == MediaError::MEDIA_ERR_SRC_NOT_SUPPORTED) { |
|
philipj_slow
2016/02/02 09:56:33
This should be together right after the user gestu
mlamouri (slow - plz ping)
2016/02/03 19:28:57
I don't think there is actually any difference in
philipj_slow
2016/02/04 10:54:20
New structure solves it nicely. I don't think "we
mlamouri (slow - plz ping)
2016/02/18 17:06:06
Done. (for me to keep track)
|
| + rejectPlayPromises(MediaError::MEDIA_ERR_SRC_NOT_SUPPORTED); |
| + return; |
| + } |
| + |
| // Always return the buffering strategy to normal when not paused, |
| // regardless of the cause. (In contrast with aggressive buffering which is |
| // only enabled by pause(), not pauseInternal().) |
| @@ -1982,8 +1999,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(); |
| @@ -2038,6 +2058,7 @@ void HTMLMediaElement::pauseInternal() |
| m_paused = true; |
| scheduleTimeupdateEvent(false); |
| scheduleEvent(EventTypeNames::pause); |
| + scheduleRejectPlayPromises(MediaError::MEDIA_ERR_ABORTED); |
| } |
| updatePlayState(); |
| @@ -2207,7 +2228,7 @@ void HTMLMediaElement::scheduleTimeupdateEvent(bool periodicEvent) |
| void HTMLMediaElement::togglePlayState() |
| { |
| if (paused()) |
| - play(); |
| + playInternal(); |
|
philipj_slow
2016/02/02 09:56:33
Hmm, so you don't have a ScriptState here, but if
mlamouri (slow - plz ping)
2016/02/03 19:28:57
Done.
|
| else |
| pause(); |
| } |
| @@ -3487,6 +3508,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); |
| @@ -3564,6 +3586,58 @@ void HTMLMediaElement::triggerAutoplayViewportCheckForTesting() |
| m_autoplayHelper.triggerAutoplayViewportCheckForTesting(); |
| } |
| +void HTMLMediaElement::scheduleResolvePlayPromises() |
| +{ |
| + Platform::current()->currentThread()->taskRunner()->postTask( |
| + BLINK_FROM_HERE, new Task(WTF::bind(&HTMLMediaElement::resolvePlayPromises, this))); |
|
philipj_slow
2016/02/02 09:56:33
So this is much nicer than the Timer thing used el
mlamouri (slow - plz ping)
2016/02/03 19:28:57
I do not know how oilpan would handle that. Worth
philipj_slow
2016/02/04 10:54:20
Please do, this looks like a potential crash.
mlamouri (slow - plz ping)
2016/02/18 17:06:06
I asked haraken@. He gave me a hint and I'm adding
|
| +} |
| + |
| +void HTMLMediaElement::scheduleRejectPlayPromises(MediaError::Code errorCode) |
| +{ |
| + Platform::current()->currentThread()->taskRunner()->postTask( |
| + BLINK_FROM_HERE, new Task(WTF::bind(&HTMLMediaElement::rejectPlayPromises, this, errorCode))); |
| +} |
| + |
| +void HTMLMediaElement::scheduleNotifyPlaying() |
| +{ |
| + scheduleEvent(EventTypeNames::playing); |
|
philipj_slow
2016/02/02 09:56:33
https://html.spec.whatwg.org/#notify-about-playing
mlamouri (slow - plz ping)
2016/02/03 19:28:57
AFAICT, with the current infra we have in Blink, w
philipj_slow
2016/02/04 10:54:20
It is indeed hard to fix without revamping the med
mlamouri (slow - plz ping)
2016/02/18 17:06:06
Done.
|
| + scheduleResolvePlayPromises(); |
| +} |
| + |
| +void HTMLMediaElement::resolvePlayPromises() |
| +{ |
| + for (auto& resolver: m_playResolvers) |
| + resolver->resolve(); |
| + |
| + m_playResolvers.clear(); |
| +} |
| + |
| +void HTMLMediaElement::rejectPlayPromises(MediaError::Code errorCode) |
| +{ |
| + ExceptionCode code; |
| + String message; |
| + |
| + switch (errorCode) { |
| + case MediaError::MEDIA_ERR_ABORTED: |
| + code = AbortError; |
| + message = "The play() command was interrupted by another command like load() or pause()."; |
|
philipj_slow
2016/02/02 09:56:33
I think it'd be best to just pass code and message
mlamouri (slow - plz ping)
2016/02/03 19:28:57
Done.
|
| + break; |
| + case MediaError::MEDIA_ERR_SRC_NOT_SUPPORTED: |
| + code = NotSupportedError; |
| + message = "Failed to load because no supported source was found."; |
|
philipj_slow
2016/02/02 09:56:33
It would be good to split this too, to distinguish
mlamouri (slow - plz ping)
2016/02/03 19:28:57
Done.
|
| + break; |
| + case MediaError::MEDIA_ERR_DECODE: |
| + case MediaError::MEDIA_ERR_NETWORK: |
| + ASSERT_NOT_REACHED(); |
| + break; |
| + } |
| + |
| + for (auto& resolver: m_playResolvers) |
| + resolver->reject(DOMException::create(code, message)); |
| + |
| + m_playResolvers.clear(); |
| +} |
| + |
| void HTMLMediaElement::clearWeakMembers(Visitor* visitor) |
| { |
| if (!Heap::isHeapObjectAlive(m_audioSourceNode)) |