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 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 |