summaryrefslogtreecommitdiffstats
path: root/WebCore/html/HTMLMediaElement.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'WebCore/html/HTMLMediaElement.cpp')
-rw-r--r--WebCore/html/HTMLMediaElement.cpp174
1 files changed, 127 insertions, 47 deletions
diff --git a/WebCore/html/HTMLMediaElement.cpp b/WebCore/html/HTMLMediaElement.cpp
index 6399f60..3f6c286 100644
--- a/WebCore/html/HTMLMediaElement.cpp
+++ b/WebCore/html/HTMLMediaElement.cpp
@@ -34,7 +34,6 @@
#include "ClientRect.h"
#include "ClientRectList.h"
#include "ContentType.h"
-#include "CSSHelper.h"
#include "CSSPropertyNames.h"
#include "CSSValueKeywords.h"
#include "Event.h"
@@ -126,6 +125,7 @@ HTMLMediaElement::HTMLMediaElement(const QualifiedName& tagName, Document* docum
, m_lastTimeUpdateEventMovieTime(numeric_limits<float>::max())
, m_loadState(WaitingForSource)
, m_currentSourceNode(0)
+ , m_nextChildNodeToConsider(0)
, m_player(0)
#if ENABLE(PLUGIN_PROXY_FOR_VIDEO)
, m_proxyWidget(0)
@@ -137,7 +137,6 @@ HTMLMediaElement::HTMLMediaElement(const QualifiedName& tagName, Document* docum
, m_playing(false)
, m_isWaitingUntilMediaCanStart(false)
, m_shouldDelayLoadEvent(false)
- , m_isWaitingToDecrementLoadEventDelayCount(false)
, m_haveFiredLoadedData(false)
, m_inActiveDocument(true)
, m_autoplaying(true)
@@ -157,12 +156,14 @@ HTMLMediaElement::HTMLMediaElement(const QualifiedName& tagName, Document* docum
, m_loadInitiatedByUserGesture(false)
, m_completelyLoaded(false)
{
+ LOG(Media, "HTMLMediaElement::HTMLMediaElement");
document->registerForDocumentActivationCallbacks(this);
document->registerForMediaVolumeCallbacks(this);
}
HTMLMediaElement::~HTMLMediaElement()
{
+ LOG(Media, "HTMLMediaElement::~HTMLMediaElement");
if (m_isWaitingUntilMediaCanStart)
document()->removeMediaCanStartListener(this);
setShouldDelayLoadEvent(false);
@@ -326,6 +327,7 @@ RenderObject* HTMLMediaElement::createRenderer(RenderArena* arena, RenderStyle*)
void HTMLMediaElement::insertedIntoDocument()
{
+ LOG(Media, "HTMLMediaElement::removedFromDocument");
HTMLElement::insertedIntoDocument();
if (!getAttribute(srcAttr).isEmpty() && m_networkState == NETWORK_EMPTY)
scheduleLoad();
@@ -333,6 +335,7 @@ void HTMLMediaElement::insertedIntoDocument()
void HTMLMediaElement::removedFromDocument()
{
+ LOG(Media, "HTMLMediaElement::removedFromDocument");
if (m_networkState > NETWORK_EMPTY)
pause(processingUserGesture());
if (m_isFullscreen)
@@ -372,6 +375,7 @@ void HTMLMediaElement::recalcStyle(StyleChange change)
void HTMLMediaElement::scheduleLoad()
{
+ LOG(Media, "HTMLMediaElement::scheduleLoad");
#if ENABLE(PLUGIN_PROXY_FOR_VIDEO)
createMediaPlayerProxy();
#endif
@@ -400,15 +404,6 @@ void HTMLMediaElement::scheduleEvent(const AtomicString& eventName)
void HTMLMediaElement::asyncEventTimerFired(Timer<HTMLMediaElement>*)
{
- // If we are waiting to release our delay on the load event, do that first and post
- // the pending events on the next go around.
- if (m_isWaitingToDecrementLoadEventDelayCount) {
- setShouldDelayLoadEvent(false);
- if (!m_asyncEventTimer.isActive())
- m_asyncEventTimer.startOneShot(0);
- return;
- }
-
Vector<RefPtr<Event> > pendingEvents;
ExceptionCode ec = 0;
@@ -541,6 +536,7 @@ void HTMLMediaElement::prepareForLoad()
m_displayMode = Unknown;
// 1 - Abort any already-running instance of the resource selection algorithm for this element.
+ m_loadState = WaitingForSource;
m_currentSourceNode = 0;
// 2 - If there are any tasks from the media element's media element event task source in
@@ -879,7 +875,7 @@ void HTMLMediaElement::setNetworkState(MediaPlayer::NetworkState state)
LOG(Media, "HTMLMediaElement::setNetworkState(%d) - current state is %d", static_cast<int>(state), static_cast<int>(m_networkState));
if (state == MediaPlayer::Empty) {
- // just update the cached state and leave, we can't do anything
+ // Just update the cached state and leave, we can't do anything.
m_networkState = NETWORK_EMPTY;
return;
}
@@ -890,12 +886,17 @@ void HTMLMediaElement::setNetworkState(MediaPlayer::NetworkState state)
// If we failed while trying to load a <source> element, the movie was never parsed, and there are more
// <source> children, schedule the next one
if (m_readyState < HAVE_METADATA && m_loadState == LoadingFromSourceElement) {
- m_currentSourceNode->scheduleErrorEvent();
+
+ if (m_currentSourceNode)
+ m_currentSourceNode->scheduleErrorEvent();
+ else
+ LOG(Media, "HTMLMediaElement::setNetworkState - error event not sent, <source> was removed");
+
if (havePotentialSourceChild()) {
- LOG(Media, "HTMLMediaElement::setNetworkState scheduling next <source>");
+ LOG(Media, "HTMLMediaElement::setNetworkState - scheduling next <source>");
scheduleNextSourceChild();
} else {
- LOG(Media, "HTMLMediaElement::setNetworkState no more <source> elements, waiting");
+ LOG(Media, "HTMLMediaElement::setNetworkState - no more <source> elements, waiting");
waitForSourceChange();
}
@@ -1431,7 +1432,7 @@ float HTMLMediaElement::volume() const
void HTMLMediaElement::setVolume(float vol, ExceptionCode& ec)
{
- LOG(Media, "HTMLMediaElement::setControls(%f)", vol);
+ LOG(Media, "HTMLMediaElement::setVolume(%f)", vol);
if (vol < 0.0f || vol > 1.0f) {
ec = INDEX_SIZE_ERR;
@@ -1579,11 +1580,15 @@ float HTMLMediaElement::percentLoaded() const
bool HTMLMediaElement::havePotentialSourceChild()
{
- // Stash the current <source> node so we can restore it after checking
- // to see there is another potential
+ // Stash the current <source> node and next nodes so we can restore them after checking
+ // to see there is another potential.
HTMLSourceElement* currentSourceNode = m_currentSourceNode;
+ Node* nextNode = m_nextChildNodeToConsider;
+
KURL nextURL = selectNextSourceChild(0, DoNothing);
+
m_currentSourceNode = currentSourceNode;
+ m_nextChildNodeToConsider = nextNode;
return nextURL.isValid();
}
@@ -1597,22 +1602,29 @@ KURL HTMLMediaElement::selectNextSourceChild(ContentType *contentType, InvalidSo
LOG(Media, "HTMLMediaElement::selectNextSourceChild(contentType : \"%s\")", contentType ? contentType->raw().utf8().data() : "");
#endif
+ if (m_nextChildNodeToConsider == sourceChildEndOfListValue()) {
+#if !LOG_DISABLED
+ if (shouldLog)
+ LOG(Media, "HTMLMediaElement::selectNextSourceChild -> 0x0000, \"\"");
+#endif
+ return KURL();
+ }
+
KURL mediaURL;
Node* node;
- bool lookingForPreviousNode = m_currentSourceNode;
+ HTMLSourceElement* source = 0;
+ bool lookingForStartNode = m_nextChildNodeToConsider;
bool canUse = false;
for (node = firstChild(); !canUse && node; node = node->nextSibling()) {
- if (!node->hasTagName(sourceTag))
+ if (lookingForStartNode && m_nextChildNodeToConsider != node)
continue;
-
- if (lookingForPreviousNode) {
- if (m_currentSourceNode == static_cast<HTMLSourceElement*>(node))
- lookingForPreviousNode = false;
+ lookingForStartNode = false;
+
+ if (!node->hasTagName(sourceTag))
continue;
- }
- HTMLSourceElement* source = static_cast<HTMLSourceElement*>(node);
+ source = static_cast<HTMLSourceElement*>(node);
// If candidate does not have a src attribute, or if its src attribute's value is the empty string ... jump down to the failed step below
mediaURL = source->getNonEmptyURLAttribute(srcAttr);
@@ -1647,26 +1659,108 @@ KURL HTMLMediaElement::selectNextSourceChild(ContentType *contentType, InvalidSo
if (!isSafeToLoadURL(mediaURL, actionIfInvalid) || !dispatchBeforeLoadEvent(mediaURL.string()))
goto check_again;
- // Making it this far means the <source> looks reasonable
+ // Making it this far means the <source> looks reasonable.
canUse = true;
- if (contentType)
- *contentType = ContentType(source->type());
check_again:
if (!canUse && actionIfInvalid == Complain)
source->scheduleErrorEvent();
- m_currentSourceNode = static_cast<HTMLSourceElement*>(node);
}
- if (!canUse)
+ if (canUse) {
+ if (contentType)
+ *contentType = ContentType(source->type());
+ m_currentSourceNode = source;
+ m_nextChildNodeToConsider = source->nextSibling();
+ if (!m_nextChildNodeToConsider)
+ m_nextChildNodeToConsider = sourceChildEndOfListValue();
+ } else {
m_currentSourceNode = 0;
+ m_nextChildNodeToConsider = sourceChildEndOfListValue();
+ }
+
#if !LOG_DISABLED
if (shouldLog)
- LOG(Media, "HTMLMediaElement::selectNextSourceChild -> %s", canUse ? urlForLogging(mediaURL.string()).utf8().data() : "");
+ LOG(Media, "HTMLMediaElement::selectNextSourceChild -> %p, %s", m_currentSourceNode, canUse ? urlForLogging(mediaURL.string()).utf8().data() : "");
#endif
return canUse ? mediaURL : KURL();
}
+void HTMLMediaElement::sourceWasAdded(HTMLSourceElement* source)
+{
+ LOG(Media, "HTMLMediaElement::sourceWasAdded(%p)", source);
+
+#if !LOG_DISABLED
+ if (source->hasTagName(sourceTag)) {
+ KURL url = source->getNonEmptyURLAttribute(srcAttr);
+ LOG(Media, "HTMLMediaElement::sourceWasAdded - 'src' is %s", urlForLogging(url).utf8().data());
+ }
+#endif
+
+ // We should only consider a <source> element when there is not src attribute at all.
+ if (hasAttribute(srcAttr))
+ return;
+
+ // 4.8.8 - If a source element is inserted as a child of a media element that has no src
+ // attribute and whose networkState has the value NETWORK_EMPTY, the user agent must invoke
+ // the media element's resource selection algorithm.
+ if (networkState() == HTMLMediaElement::NETWORK_EMPTY) {
+ scheduleLoad();
+ return;
+ }
+
+ if (m_currentSourceNode && source == m_currentSourceNode->nextSibling()) {
+ LOG(Media, "HTMLMediaElement::sourceWasAdded - <source> inserted immediately after current source");
+ m_nextChildNodeToConsider = source;
+ return;
+ }
+
+ if (m_nextChildNodeToConsider != sourceChildEndOfListValue())
+ return;
+
+ // 4.8.9.5, resource selection algorithm, source elements section:
+ // 20 - Wait until the node after pointer is a node other than the end of the list. (This step might wait forever.)
+ // 21 - Asynchronously await a stable state...
+ // 22 - 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);
+
+ // 23 - Set the networkState back to NETWORK_LOADING.
+ m_networkState = NETWORK_LOADING;
+
+ // 24 - Jump back to the find next candidate step above.
+ m_nextChildNodeToConsider = source;
+ scheduleNextSourceChild();
+}
+
+void HTMLMediaElement::sourceWillBeRemoved(HTMLSourceElement* source)
+{
+ LOG(Media, "HTMLMediaElement::sourceWillBeRemoved(%p)", source);
+
+#if !LOG_DISABLED
+ if (source->hasTagName(sourceTag)) {
+ KURL url = source->getNonEmptyURLAttribute(srcAttr);
+ LOG(Media, "HTMLMediaElement::sourceWillBeRemoved - 'src' is %s", urlForLogging(url).utf8().data());
+ }
+#endif
+
+ if (source != m_currentSourceNode && source != m_nextChildNodeToConsider)
+ return;
+
+ if (source == m_nextChildNodeToConsider) {
+ m_nextChildNodeToConsider = m_nextChildNodeToConsider->nextSibling();
+ if (!m_nextChildNodeToConsider)
+ m_nextChildNodeToConsider = sourceChildEndOfListValue();
+ LOG(Media, "HTMLMediaElement::sourceRemoved - m_nextChildNodeToConsider set to %p", m_nextChildNodeToConsider);
+ } else if (source == m_currentSourceNode) {
+ // Clear the current source node pointer, but don't change the movie as the spec says:
+ // 4.8.8 - Dynamically modifying a source element and its attribute when the element is already
+ // inserted in a video or audio element will have no effect.
+ m_currentSourceNode = 0;
+ LOG(Media, "HTMLMediaElement::sourceRemoved - m_currentSourceNode set to 0");
+ }
+}
+
void HTMLMediaElement::mediaPlayerTimeChanged(MediaPlayer*)
{
LOG(Media, "HTMLMediaElement::mediaPlayerTimeChanged");
@@ -2003,6 +2097,7 @@ void HTMLMediaElement::userCancelledLoad()
m_player.clear();
#endif
stopPeriodicTimers();
+ m_loadState = WaitingForSource;
// 2 - Set the error attribute to a new MediaError object whose code attribute is set to MEDIA_ERR_ABORTED.
m_error = MediaError::create(MediaError::MEDIA_ERR_ABORTED);
@@ -2338,24 +2433,9 @@ void HTMLMediaElement::setShouldDelayLoadEvent(bool shouldDelay)
if (m_shouldDelayLoadEvent == shouldDelay)
return;
- // Don't decrement the load event delay if we are in the middle of a callback from
- // the media engine. The load event is sent synchronously and may trigger a script that
- // causes the document to be come inactive and that will clear the media engine, causing
- // the return to be a rough one.
- if (!shouldDelay && processingMediaPlayerCallback()) {
- m_isWaitingToDecrementLoadEventDelayCount = true;
-
- // Instead of creating yet-another-timer, reuse the async event timer which is always
- // used as a one-shot.
- if (!m_asyncEventTimer.isActive())
- m_asyncEventTimer.startOneShot(0);
- return;
- }
-
LOG(Media, "HTMLMediaElement::setShouldDelayLoadEvent(%s)", boolString(shouldDelay));
m_shouldDelayLoadEvent = shouldDelay;
- m_isWaitingToDecrementLoadEventDelayCount = false;
if (shouldDelay)
document()->incrementLoadEventDelayCount();
else