diff options
Diffstat (limited to 'WebCore/html/HTMLMediaElement.cpp')
-rw-r--r-- | WebCore/html/HTMLMediaElement.cpp | 174 |
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 |