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 efeaceaddd63eecdb4b7d2f1a3ec83db944fcc60..6810dbc1e9c00a79e8dab69e5f3f7f802b20a7e4 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" |
@@ -72,6 +72,7 @@ |
#include "platform/MIMETypeFromURL.h" |
#include "platform/MIMETypeRegistry.h" |
#include "platform/RuntimeEnabledFeatures.h" |
+#include "platform/Task.h" |
hiroshige
2016/02/22 17:43:10
We can remove this #include if we omit new Task()'
mlamouri (slow - plz ping)
2016/02/23 16:49:00
Done.
|
#include "platform/UserGestureIndicator.h" |
#include "platform/audio/AudioBus.h" |
#include "platform/audio/AudioSourceProviderClient.h" |
@@ -730,6 +731,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) |
@@ -1244,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(); |
} |
@@ -1519,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; |
} |
@@ -1527,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. |
@@ -1540,7 +1543,7 @@ void HTMLMediaElement::setReadyState(ReadyState state) |
m_paused = false; |
invalidateCachedTime(); |
scheduleEvent(EventTypeNames::play); |
- scheduleEvent(EventTypeNames::playing); |
+ scheduleNotifyPlaying(); |
m_autoplaying = false; |
} |
} |
@@ -1932,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); |
@@ -1945,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) |
@@ -1953,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() |
@@ -1984,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(); |
@@ -2040,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(); |
@@ -3494,6 +3531,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); |
@@ -3571,6 +3609,42 @@ void HTMLMediaElement::triggerAutoplayViewportCheckForTesting() |
m_autoplayHelper.triggerAutoplayViewportCheckForTesting(); |
} |
+void HTMLMediaElement::scheduleResolvePlayPromises() |
+{ |
+ Platform::current()->currentThread()->taskRunner()->postTask( |
+ BLINK_FROM_HERE, new Task(WTF::bind(&HTMLMediaElement::resolvePlayPromises, PassRefPtrWillBeRawPtr<HTMLMediaElement>(this)))); |
hiroshige
2016/02/22 17:43:10
bind() usage looks good.
BTW, could you omit new
mlamouri (slow - plz ping)
2016/02/23 16:49:00
Done.
|
+} |
+ |
+void HTMLMediaElement::scheduleRejectPlayPromises(ExceptionCode code, const String& message) |
+{ |
+ Platform::current()->currentThread()->taskRunner()->postTask( |
+ BLINK_FROM_HERE, new Task(WTF::bind(&HTMLMediaElement::rejectPlayPromises, PassRefPtrWillBeRawPtr<HTMLMediaElement>(this), code, message))); |
hiroshige
2016/02/22 17:43:10
ditto.
mlamouri (slow - plz ping)
2016/02/23 16:49:00
Done.
|
+} |
+ |
+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)) |