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 7beecb9dd4be9a28665ac847fe9e6f45f67b085f..84655683fddcfb89e582c0599efac03d7c3f88f1 100644 |
--- a/third_party/WebKit/Source/core/html/HTMLMediaElement.cpp |
+++ b/third_party/WebKit/Source/core/html/HTMLMediaElement.cpp |
@@ -434,8 +434,8 @@ HTMLMediaElement::HTMLMediaElement(const QualifiedName& tagName, Document& docum |
, m_audioTracks(AudioTrackList::create(*this)) |
, m_videoTracks(VideoTrackList::create(*this)) |
, m_textTracks(nullptr) |
- , m_playPromiseResolveTask(CancellableTaskFactory::create(this, &HTMLMediaElement::resolvePlayPromises)) |
- , m_playPromiseRejectTask(CancellableTaskFactory::create(this, &HTMLMediaElement::rejectPlayPromises)) |
+ , m_playPromiseResolveTask(CancellableTaskFactory::create(this, &HTMLMediaElement::resolveScheduledPlayPromises)) |
+ , m_playPromiseRejectTask(CancellableTaskFactory::create(this, &HTMLMediaElement::rejectScheduledPlayPromises)) |
, m_audioSourceNode(nullptr) |
, m_autoplayHelperClient(AutoplayHelperClientImpl::create(this)) |
, m_autoplayHelper(AutoplayExperimentHelper::create(m_autoplayHelperClient.get())) |
@@ -2021,8 +2021,20 @@ WebMediaPlayer::Preload HTMLMediaElement::effectivePreloadType() const |
ScriptPromise HTMLMediaElement::playForBindings(ScriptState* scriptState) |
{ |
+ // We have to share the same logic for internal and external callers. The |
+ // internal callers do not want to receive a Promise back but when ::play() |
+ // is called, |m_playPromiseResolvers| needs to be populated. What this code |
+ // does is to populate |m_playPromiseResolvers| before calling ::play() and |
+ // remove the Promise if ::play() failed. |
+ ScriptPromiseResolver* resolver = ScriptPromiseResolver::create(scriptState); |
+ ScriptPromise promise = resolver->promise(); |
+ m_playPromiseResolvers.append(resolver); |
+ |
Nullable<ExceptionCode> code = play(); |
if (!code.isNull()) { |
+ DCHECK(!m_playPromiseResolvers.isEmpty()); |
+ m_playPromiseResolvers.removeLast(); |
+ |
String message; |
switch (code.get()) { |
case NotAllowedError: |
@@ -2037,10 +2049,6 @@ ScriptPromise HTMLMediaElement::playForBindings(ScriptState* scriptState) |
return ScriptPromise::rejectWithDOMException(scriptState, DOMException::create(code.get(), message)); |
} |
- ScriptPromiseResolver* resolver = ScriptPromiseResolver::create(scriptState); |
- ScriptPromise promise = resolver->promise(); |
- |
- m_playResolvers.append(resolver); |
return promise; |
} |
@@ -3602,7 +3610,9 @@ DEFINE_TRACE(HTMLMediaElement) |
visitor->trace(m_cueTimeline); |
visitor->trace(m_textTracks); |
visitor->trace(m_textTracksWhenResourceSelectionBegan); |
- visitor->trace(m_playResolvers); |
+ visitor->trace(m_playPromiseResolvers); |
+ visitor->trace(m_playPromiseResolveList); |
+ visitor->trace(m_playPromiseRejectList); |
visitor->trace(m_audioSourceProvider); |
visitor->trace(m_autoplayHelperClient); |
visitor->trace(m_autoplayHelper); |
@@ -3699,10 +3709,19 @@ void HTMLMediaElement::triggerAutoplayViewportCheckForTesting() |
void HTMLMediaElement::scheduleResolvePlayPromises() |
{ |
- // Per spec, if there are two tasks in the queue, the first task will remove |
- // all the pending promises making the second task useless unless a promise |
- // can be added between the first and second task being run which is not |
- // possible at the moment. |
+ // TODO(mlamouri): per spec, we should create a new task but we can't create |
+ // a new cancellable task without cancelling the previous one. There are two |
+ // approaches then: cancel the previous task and create a new one with the |
+ // appended promise list or append the new promise to the current list. The |
+ // former approach is prefered because it might be the less observable |
fs
2016/06/06 09:13:24
Nit: preferred (also below)
mlamouri (slow - plz ping)
2016/06/06 16:00:55
Done.
|
+ // change. |
+ DCHECK(m_playPromiseResolveList.isEmpty() || m_playPromiseResolveTask->isPending()); |
+ if (m_playPromiseResolvers.isEmpty()) |
+ return; |
+ |
+ m_playPromiseResolveList.appendVector(m_playPromiseResolvers); |
+ m_playPromiseResolvers.clear(); |
+ |
if (m_playPromiseResolveTask->isPending()) |
return; |
@@ -3711,10 +3730,19 @@ void HTMLMediaElement::scheduleResolvePlayPromises() |
void HTMLMediaElement::scheduleRejectPlayPromises(ExceptionCode code) |
{ |
- // Per spec, if there are two tasks in the queue, the first task will remove |
- // all the pending promises making the second task useless unless a promise |
- // can be added between the first and second task being run which is not |
- // possible at the moment. |
+ // TODO(mlamouri): per spec, we should create a new task but we can't create |
+ // a new cancellable task without cancelling the previous one. There are two |
+ // approaches then: cancel the previous task and create a new one with the |
+ // appended promise list or append the new promise to the current list. The |
+ // former approach is prefered because it might be the less observable |
whywhat
2016/06/06 13:08:57
nit: the code seems to be doing the latter, not fo
mlamouri (slow - plz ping)
2016/06/06 16:00:55
Done.
|
+ // change. |
+ DCHECK(m_playPromiseRejectList.isEmpty() || m_playPromiseRejectTask->isPending()); |
+ if (m_playPromiseResolvers.isEmpty()) |
+ return; |
+ |
+ m_playPromiseRejectList.appendVector(m_playPromiseResolvers); |
+ m_playPromiseResolvers.clear(); |
+ |
if (m_playPromiseRejectTask->isPending()) |
return; |
@@ -3730,15 +3758,15 @@ void HTMLMediaElement::scheduleNotifyPlaying() |
scheduleResolvePlayPromises(); |
} |
-void HTMLMediaElement::resolvePlayPromises() |
+void HTMLMediaElement::resolveScheduledPlayPromises() |
{ |
- for (auto& resolver: m_playResolvers) |
+ for (auto& resolver: m_playPromiseResolveList) |
resolver->resolve(); |
- m_playResolvers.clear(); |
+ m_playPromiseResolveList.clear(); |
} |
-void HTMLMediaElement::rejectPlayPromises() |
+void HTMLMediaElement::rejectScheduledPlayPromises() |
{ |
// TODO(mlamouri): the message is generated based on the code because |
// arguments can't be passed to a cancellable task. In order to save space |
@@ -3752,12 +3780,19 @@ void HTMLMediaElement::rejectPlayPromises() |
void HTMLMediaElement::rejectPlayPromises(ExceptionCode code, const String& message) |
{ |
+ m_playPromiseRejectList.appendVector(m_playPromiseResolvers); |
+ m_playPromiseResolvers.clear(); |
+ rejectPlayPromisesInternal(code, message); |
+} |
+ |
+void HTMLMediaElement::rejectPlayPromisesInternal(ExceptionCode code, const String& message) |
whywhat
2016/06/06 13:08:57
nit: seems like this method could be inlined
mlamouri (slow - plz ping)
2016/06/06 16:00:55
No. I forgot to use it in `rejectScheduledPlayProm
|
+{ |
DCHECK(code == AbortError || code == NotSupportedError); |
- for (auto& resolver: m_playResolvers) |
+ for (auto& resolver: m_playPromiseRejectList) |
resolver->reject(DOMException::create(code, message)); |
- m_playResolvers.clear(); |
+ m_playPromiseRejectList.clear(); |
} |
EnumerationHistogram& HTMLMediaElement::showControlsHistogram() const |