Commit 3e858e50 authored by fs@opera.com's avatar fs@opera.com

Transition networkState back to NETWORK_IDLE on fetch if preload=none

When a media element is marked with the preload hint 'none', a load is not
started until data is explicitly requested. The progress timer is however
still started, which means that a 'stalled' event will be fired after ~3s.
This is confusing, since loading never actually started in the first place.
Fix this by implementing (the "optional") step 3 from the 'resource fetch
algorithm'.

BUG=382505
BUG=284413

Review URL: https://codereview.chromium.org/327553002

git-svn-id: svn://svn.chromium.org/blink/trunk@176199 bbb929c8-8fbe-4397-9dbb-9b2b20218538
parent 17bd319a
Test to see if a media element with preload=none dispatches a 'stalled' event.
EVENT(suspend)
END OF TEST
<!DOCTYPE html>
<script src=media-file.js></script>
<script src=video-test.js></script>
<script>
function runTest()
{
findMediaElement();
waitForEventAndEnd("suspend");
waitForEventAndFail("stalled");
consoleWrite("");
video.src = findMediaFile("video", "content/test");
}
</script>
<body onload="runTest()">
<p>Test to see if a media element with preload=none dispatches a 'stalled' event.</p>
<video preload=none></video>
</body>
......@@ -247,6 +247,8 @@ HTMLMediaElement::HTMLMediaElement(const QualifiedName& tagName, Document& docum
, m_lastTimeUpdateEventWallTime(0)
, m_lastTimeUpdateEventMovieTime(std::numeric_limits<double>::max())
, m_loadState(WaitingForSource)
, m_deferredLoadState(NotDeferred)
, m_deferredLoadTimer(this, &HTMLMediaElement::deferredLoadTimerFired)
, m_webLayer(0)
, m_preload(MediaPlayer::Auto)
, m_displayMode(Unknown)
......@@ -271,7 +273,6 @@ HTMLMediaElement::HTMLMediaElement(const QualifiedName& tagName, Document& docum
, m_closedCaptionsVisible(false)
, m_completelyLoaded(false)
, m_havePreparedToPlay(false)
, m_delayingLoadForPreloadNone(false)
, m_tracksAreReady(true)
, m_haveVisibleTextTrack(false)
, m_processingPreferenceChange(false)
......@@ -641,6 +642,7 @@ void HTMLMediaElement::prepareForLoad()
// 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;
......@@ -896,7 +898,7 @@ void HTMLMediaElement::loadResource(const KURL& url, ContentType& contentType, c
if (!m_havePreparedToPlay && !autoplay() && m_preload == MediaPlayer::None) {
WTF_LOG(Media, "HTMLMediaElement::loadResource : Delaying load because preload == 'none'");
m_delayingLoadForPreloadNone = true;
deferLoad();
} else {
startPlayerLoad();
}
......@@ -940,19 +942,79 @@ void HTMLMediaElement::setPlayerPreload()
{
m_player->setPreload(m_preload);
if (m_delayingLoadForPreloadNone && m_preload != MediaPlayer::None)
startDelayedLoad();
if (loadIsDeferred() && m_preload != MediaPlayer::None)
startDeferredLoad();
}
void HTMLMediaElement::startDelayedLoad()
bool HTMLMediaElement::loadIsDeferred() const
{
ASSERT(m_delayingLoadForPreloadNone);
return m_deferredLoadState != NotDeferred;
}
void HTMLMediaElement::deferLoad()
{
// This implements the "optional" step 3 from the resource fetch algorithm.
ASSERT(!m_deferredLoadTimer.isActive());
ASSERT(m_deferredLoadState == NotDeferred);
// 1. Set the networkState to NETWORK_IDLE.
// 2. Queue a task to fire a simple event named suspend at the element.
changeNetworkStateFromLoadingToIdle();
// 3. Queue a task to set the element's delaying-the-load-event
// flag to false. This stops delaying the load event.
m_deferredLoadTimer.startOneShot(0, FROM_HERE);
// 4. Wait for the task to be run.
m_deferredLoadState = WaitingForStopDelayingLoadEventTask;
// Continued in executeDeferredLoad().
}
void HTMLMediaElement::cancelDeferredLoad()
{
m_deferredLoadTimer.stop();
m_deferredLoadState = NotDeferred;
}
m_delayingLoadForPreloadNone = false;
void HTMLMediaElement::executeDeferredLoad()
{
ASSERT(m_deferredLoadState >= WaitingForTrigger);
// resource fetch algorithm step 3 - continued from deferLoad().
// 5. Wait for an implementation-defined event (e.g. the user requesting that the media element begin playback).
// This is assumed to be whatever 'event' ended up calling this method.
cancelDeferredLoad();
// 6. Set the element's delaying-the-load-event flag back to true (this
// delays the load event again, in case it hasn't been fired yet).
setShouldDelayLoadEvent(true);
// 7. Set the networkState to NETWORK_LOADING.
m_networkState = NETWORK_LOADING;
startProgressEventTimer();
startPlayerLoad();
}
void HTMLMediaElement::startDeferredLoad()
{
if (m_deferredLoadState == WaitingForTrigger) {
executeDeferredLoad();
return;
}
ASSERT(m_deferredLoadState == WaitingForStopDelayingLoadEventTask);
m_deferredLoadState = ExecuteOnStopDelayingLoadEventTask;
}
void HTMLMediaElement::deferredLoadTimerFired(Timer<HTMLMediaElement>*)
{
setShouldDelayLoadEvent(false);
if (m_deferredLoadState == ExecuteOnStopDelayingLoadEventTask) {
executeDeferredLoad();
return;
}
ASSERT(m_deferredLoadState == WaitingForStopDelayingLoadEventTask);
m_deferredLoadState = WaitingForTrigger;
}
WebMediaPlayer::LoadType HTMLMediaElement::loadType() const
{
if (m_mediaSource)
......@@ -1582,11 +1644,13 @@ void HTMLMediaElement::setNetworkState(MediaPlayer::NetworkState state)
void HTMLMediaElement::changeNetworkStateFromLoadingToIdle()
{
ASSERT(m_player);
m_progressEventTimer.stop();
// Schedule one last progress event so we guarantee that at least one is fired
// for files that load very quickly.
scheduleEvent(EventTypeNames::progress);
if (m_player->didLoadingProgress())
scheduleEvent(EventTypeNames::progress);
scheduleEvent(EventTypeNames::suspend);
m_networkState = NETWORK_IDLE;
}
......@@ -1750,8 +1814,8 @@ void HTMLMediaElement::prepareToPlay()
return;
m_havePreparedToPlay = true;
if (m_delayingLoadForPreloadNone)
startDelayedLoad();
if (loadIsDeferred())
startDeferredLoad();
}
void HTMLMediaElement::seek(double time, ExceptionState& exceptionState)
......@@ -3139,7 +3203,7 @@ void HTMLMediaElement::clearMediaPlayer(int flags)
closeMediaSource();
m_delayingLoadForPreloadNone = false;
cancelDeferredLoad();
#if ENABLE(WEB_AUDIO)
if (m_audioSourceNode)
......
......@@ -364,7 +364,6 @@ private:
void loadResource(const KURL&, ContentType&, const String& keySystem);
void startPlayerLoad();
void setPlayerPreload();
void startDelayedLoad();
blink::WebMediaPlayer::LoadType loadType() const;
void scheduleNextSourceChild();
void loadNextSourceChild();
......@@ -382,6 +381,14 @@ private:
void mediaLoadingFailed(MediaPlayer::NetworkState);
// deferred loading (preload=none)
bool loadIsDeferred() const;
void deferLoad();
void cancelDeferredLoad();
void startDeferredLoad();
void executeDeferredLoad();
void deferredLoadTimerFired(Timer<HTMLMediaElement>*);
void updateActiveTextTrackCues(double);
HTMLTrackElement* showingTrackWithSameKind(HTMLTrackElement*) const;
......@@ -461,6 +468,22 @@ private:
RefPtrWillBeMember<HTMLSourceElement> m_currentSourceNode;
RefPtrWillBeMember<Node> m_nextChildNodeToConsider;
// "Deferred loading" state (for preload=none).
enum DeferredLoadState {
// The load is not deferred.
NotDeferred,
// The load is deferred, and waiting for the task to set the
// delaying-the-load-event flag (to false).
WaitingForStopDelayingLoadEventTask,
// The load is the deferred, and waiting for a triggering event.
WaitingForTrigger,
// The load is deferred, and waiting for the task to set the
// delaying-the-load-event flag, after which the load will be executed.
ExecuteOnStopDelayingLoadEventTask
};
DeferredLoadState m_deferredLoadState;
Timer<HTMLMediaElement> m_deferredLoadTimer;
OwnPtr<MediaPlayer> m_player;
blink::WebLayer* m_webLayer;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment