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 5a14ea40e1bed286a555021ffa380d58d79e775f..542bc0236722e043c9f6ab855df929afb8aaf5e7 100644 |
| --- a/third_party/WebKit/Source/core/html/HTMLMediaElement.cpp |
| +++ b/third_party/WebKit/Source/core/html/HTMLMediaElement.cpp |
| @@ -35,6 +35,7 @@ |
| #include "core/dom/Attribute.h" |
| #include "core/dom/ElementTraversal.h" |
| #include "core/dom/Fullscreen.h" |
| +#include "core/dom/Microtask.h" |
| #include "core/dom/shadow/ShadowRoot.h" |
| #include "core/events/Event.h" |
| #include "core/frame/LocalFrame.h" |
| @@ -268,6 +269,56 @@ String preloadTypeToString(WebMediaPlayer::Preload preloadType) |
| } // anonymous namespace |
| +class HTMLMediaElement::Task : public WebTaskRunner::Task { |
|
philipj_slow
2016/03/23 07:27:18
It looks like it should be possible to use Cancell
Srirama
2016/03/29 11:26:29
I haven't tried that, do you want me to give a try
philipj_slow
2016/03/29 12:39:32
Yeah, sure, I wouldn't assume that the ScriptState
Srirama
2016/03/30 11:35:39
Done.
|
| +public: |
| + static PassOwnPtr<Task> create(HTMLMediaElement* mediaElement) |
| + { |
| + return adoptPtr(new Task(mediaElement)); |
| + } |
| + |
| + Task(HTMLMediaElement* mediaElement) |
| + : m_mediaElement(mediaElement) |
| + , m_weakFactory(this) |
| + { |
| + v8::Isolate* isolate = V8PerIsolateData::mainThreadIsolate(); |
| + v8::HandleScope scope(isolate); |
| + if (ScriptState::hasCurrentScriptState(isolate)) |
| + m_scriptState = ScriptState::current(isolate); |
| + else |
| + m_scriptState = ScriptState::forMainWorld(m_mediaElement->document().frame()); |
| + } |
| + |
| + void run() override |
| + { |
| + WTF_LOG(Media, "HTMLMediaElement::Task::run(%p)", m_mediaElement.get()); |
| + if (!m_mediaElement) |
| + return; |
| + |
| + if (m_scriptState->contextIsValid()) { |
| + ScriptState::Scope scope(m_scriptState.get()); |
|
philipj_slow
2016/03/24 04:43:05
What break if there is no ScriptState::Scope here?
Srirama
2016/03/29 11:26:29
Tested locally by removing and no cases are failin
|
| + m_mediaElement->continueResourceSelectionAlgorithm(); |
| + } else { |
| + m_mediaElement->continueResourceSelectionAlgorithm(); |
| + } |
| + } |
| + |
| + void clearTaskInfo() |
| + { |
| + m_mediaElement = nullptr; |
| + m_scriptState.clear(); |
| + } |
| + |
| + WeakPtr<Task> createWeakPtr() |
| + { |
| + return m_weakFactory.createWeakPtr(); |
| + } |
| + |
| +private: |
| + RawPtrWillBeWeakPersistent<HTMLMediaElement> m_mediaElement; |
| + RefPtr<ScriptState> m_scriptState; |
| + WeakPtrFactory<Task> m_weakFactory; |
| +}; |
| + |
| void HTMLMediaElement::recordAutoplayMetric(AutoplayMetrics metric) |
| { |
| DEFINE_STATIC_LOCAL(EnumerationHistogram, autoplayHistogram, ("Blink.MediaElement.Autoplay", NumberOfAutoplayMetrics)); |
| @@ -310,7 +361,7 @@ bool HTMLMediaElement::isMediaStreamURL(const String& url) |
| HTMLMediaElement::HTMLMediaElement(const QualifiedName& tagName, Document& document) |
| : HTMLElement(tagName, document) |
| , ActiveDOMObject(&document) |
| - , m_loadTimer(this, &HTMLMediaElement::loadTimerFired) |
| + , m_textTrackLoadTimer(this, &HTMLMediaElement::textTrackLoadTimerFired) |
| , m_progressEventTimer(this, &HTMLMediaElement::progressEventTimerFired) |
| , m_playbackProgressTimer(this, &HTMLMediaElement::playbackProgressTimerFired) |
| , m_audioTracksTimer(this, &HTMLMediaElement::audioTracksTimerFired) |
| @@ -335,7 +386,6 @@ HTMLMediaElement::HTMLMediaElement(const QualifiedName& tagName, Document& docum |
| , m_displayMode(Unknown) |
| , m_cachedTime(std::numeric_limits<double>::quiet_NaN()) |
| , m_fragmentEndTime(std::numeric_limits<double>::quiet_NaN()) |
| - , m_pendingActionFlags(0) |
| , m_userGestureRequiredForPlay(false) |
| , m_playing(false) |
| , m_shouldDelayLoadEvent(false) |
| @@ -398,6 +448,9 @@ HTMLMediaElement::~HTMLMediaElement() |
| closeMediaSource(); |
| + if (m_pendingTask) |
| + m_pendingTask->clearTaskInfo(); |
| + |
| removeElementFromDocumentMap(this, &document()); |
| // Destroying the player may cause a resource load to be canceled, |
| @@ -600,17 +653,8 @@ void HTMLMediaElement::scheduleTextTrackResourceLoad() |
| { |
| WTF_LOG(Media, "HTMLMediaElement::scheduleTextTrackResourceLoad(%p)", this); |
| - m_pendingActionFlags |= LoadTextTrackResource; |
| - |
| - if (!m_loadTimer.isActive()) |
| - m_loadTimer.startOneShot(0, BLINK_FROM_HERE); |
| -} |
| - |
| -void HTMLMediaElement::scheduleNextSourceChild() |
| -{ |
| - // Schedule the timer to try the next <source> element WITHOUT resetting state ala invokeLoadAlgorithm. |
| - m_pendingActionFlags |= LoadMediaResource; |
| - m_loadTimer.startOneShot(0, BLINK_FROM_HERE); |
| + if (!m_textTrackLoadTimer.isActive()) |
| + m_textTrackLoadTimer.startOneShot(0, BLINK_FROM_HERE); |
| } |
| void HTMLMediaElement::scheduleEvent(const AtomicString& eventName) |
| @@ -626,19 +670,10 @@ void HTMLMediaElement::scheduleEvent(PassRefPtrWillBeRawPtr<Event> event) |
| m_asyncEventQueue->enqueueEvent(event); |
| } |
| -void HTMLMediaElement::loadTimerFired(Timer<HTMLMediaElement>*) |
| +void HTMLMediaElement::textTrackLoadTimerFired(Timer<HTMLMediaElement>*) |
| { |
| - if (m_pendingActionFlags & LoadTextTrackResource) |
| - honorUserPreferencesForAutomaticTextTrackSelection(); |
| - |
| - if (m_pendingActionFlags & LoadMediaResource) { |
| - if (m_loadState == LoadingFromSourceElement) |
| - loadNextSourceChild(); |
| - else |
| - loadInternal(); |
| - } |
| - |
| - m_pendingActionFlags = 0; |
| + WTF_LOG(Media, "HTMLMediaElement::textTrackLoadTimerFired(%p)", this); |
| + honorUserPreferencesForAutomaticTextTrackSelection(); |
| } |
| MediaError* HTMLMediaElement::error() const |
| @@ -735,10 +770,7 @@ void HTMLMediaElement::invokeLoadAlgorithm() |
| // Perform the cleanup required for the resource load algorithm to run. |
| stopPeriodicTimers(); |
| - m_loadTimer.stop(); |
| cancelDeferredLoad(); |
| - // FIXME: Figure out appropriate place to reset LoadTextTrackResource if necessary and set m_pendingActionFlags to 0 here. |
| - m_pendingActionFlags &= ~LoadMediaResource; |
| m_sentEndEvent = false; |
| m_sentStalledEvent = false; |
| m_haveFiredLoadedData = false; |
| @@ -747,6 +779,10 @@ void HTMLMediaElement::invokeLoadAlgorithm() |
| // 1 - Abort any already-running instance of the resource selection algorithm for this element. |
| m_loadState = WaitingForSource; |
| m_currentSourceNode = nullptr; |
| + if (m_pendingTask) { |
| + m_pendingTask->clearTaskInfo(); |
| + m_pendingTask.clear(); |
| + } |
| // 2 - If there are any tasks from the media element's media element event task source in |
| // one of the task queues, then remove those tasks. |
| @@ -833,9 +869,29 @@ void HTMLMediaElement::invokeResourceSelectionAlgorithm() |
| mediaControls()->reset(); |
| // 4 - Await a stable state, allowing the task that invoked this algorithm to continue |
| - // TODO(srirama.m): Remove scheduleNextSourceChild() and post a microtask instead. |
| - // See http://crbug.com/593289 for more details. |
| - scheduleNextSourceChild(); |
| + enqueueMicrotask(); |
| +} |
| + |
| +void HTMLMediaElement::continueResourceSelectionAlgorithm() |
| +{ |
| + m_pendingTask.clear(); |
| + |
| + if (m_textTrackLoadTimer.isActive()) { |
|
Srirama
2016/03/18 07:38:11
This is required to keep the order of honorUser...
philipj_slow
2016/03/23 07:12:02
Hmm, which test case is that?
Srirama
2016/03/29 11:26:29
Sorry, couldn't remember exactly which one is fail
|
| + m_textTrackLoadTimer.stop(); |
| + honorUserPreferencesForAutomaticTextTrackSelection(); |
| + } |
| + |
| + if (m_loadState == LoadingFromSourceElement) |
|
philipj_slow
2016/03/24 04:43:05
This doesn't look right. The spec decides whether
Srirama
2016/03/29 11:26:29
Hmm, probably this can simply be loadInternal() ba
|
| + loadNextSourceChild(); |
| + else |
| + loadInternal(); |
| +} |
| + |
| +void HTMLMediaElement::enqueueMicrotask() |
| +{ |
| + OwnPtr<Task> task = Task::create(this); |
| + m_pendingTask = task->createWeakPtr(); |
| + Microtask::enqueueMicrotask(task.release()); |
| } |
| void HTMLMediaElement::loadInternal() |
| @@ -1372,7 +1428,7 @@ void HTMLMediaElement::mediaLoadingFailed(WebMediaPlayer::NetworkState error) |
| if (havePotentialSourceChild()) { |
| WTF_LOG(Media, "HTMLMediaElement::setNetworkState(%p) - scheduling next <source>", this); |
| - scheduleNextSourceChild(); |
| + enqueueMicrotask(); |
|
philipj_slow
2016/03/24 04:43:05
I guess this is the "Await a stable state" after "
Srirama
2016/03/29 11:26:29
Probably some more refactoring is needed before i
philipj_slow
2016/03/29 12:39:32
Which bits do you mean for "m_loadState is already
Srirama
2016/03/29 12:49:12
This is actually part of mediaLoadingFailed() and
philipj_slow
2016/03/29 12:57:51
Oh OK. Yeah in that case it would obviously make n
Srirama
2016/03/30 11:35:39
Done. Tried removing WaitingForSource blindly and
|
| } else { |
| WTF_LOG(Media, "HTMLMediaElement::setNetworkState(%p) - no more <source> elements, waiting", this); |
| waitForSourceChange(); |
| @@ -2772,7 +2828,7 @@ void HTMLMediaElement::sourceWasAdded(HTMLSourceElement* source) |
| // 25. Jump back to the find next candidate step above. |
| m_nextChildNodeToConsider = source; |
| - scheduleNextSourceChild(); |
| + enqueueMicrotask(); |
|
philipj_slow
2016/03/24 04:43:05
This should actually synchronously jump to the "Fi
Srirama
2016/03/29 11:26:29
This looks a bit confusing, this function sourceWa
philipj_slow
2016/03/29 12:39:32
Yes, the spec expresses this as a single big algor
Srirama
2016/03/30 11:35:39
I thought loadNextSourceChild() is more appropriat
|
| } |
| void HTMLMediaElement::sourceWasRemoved(HTMLSourceElement* source) |
| @@ -3110,9 +3166,8 @@ void HTMLMediaElement::clearMediaPlayer() |
| } |
| stopPeriodicTimers(); |
| - m_loadTimer.stop(); |
| + m_textTrackLoadTimer.stop(); |
| - m_pendingActionFlags = 0; |
| m_loadState = WaitingForSource; |
| // We can't cast if we don't have a media player. |
| @@ -3186,6 +3241,10 @@ bool HTMLMediaElement::hasPendingActivity() const |
| if (m_asyncEventQueue->hasPendingEvents()) |
| return true; |
| + // Wait for any pending microtask to be fired. |
| + if (m_pendingTask) |
| + return true; |
| + |
| return false; |
| } |