Index: Source/WebCore/html/HTMLMediaElement.cpp |
=================================================================== |
--- Source/WebCore/html/HTMLMediaElement.cpp (revision 112578) |
+++ Source/WebCore/html/HTMLMediaElement.cpp (working copy) |
@@ -190,8 +190,6 @@ |
, m_lastTimeUpdateEventWallTime(0) |
, m_lastTimeUpdateEventMovieTime(numeric_limits<float>::max()) |
, m_loadState(WaitingForSource) |
- , m_currentSourceNode(0) |
- , m_nextChildNodeToConsider(0) |
#if ENABLE(PLUGIN_PROXY_FOR_VIDEO) |
, m_proxyWidget(0) |
#endif |
@@ -548,6 +546,8 @@ |
void HTMLMediaElement::loadTimerFired(Timer<HTMLMediaElement>*) |
{ |
+ RefPtr<HTMLMediaElement> protect(this); // loadNextSourceChild may fire 'beforeload', which can make arbitrary DOM mutations. |
+ |
if (m_pendingLoadFlags & MediaResource) { |
if (m_loadState == LoadingFromSourceElement) |
loadNextSourceChild(); |
@@ -604,6 +604,8 @@ |
void HTMLMediaElement::load(ExceptionCode& ec) |
{ |
+ RefPtr<HTMLMediaElement> protect(this); // loadInternal may result in a 'beforeload' event, which can make arbitrary DOM mutations. |
+ |
LOG(Media, "HTMLMediaElement::load()"); |
if (userGestureRequiredForLoad() && !ScriptController::processingUserGesture()) |
@@ -757,7 +759,7 @@ |
// source element child in tree order. |
if (node) { |
mode = children; |
- m_nextChildNodeToConsider = 0; |
+ m_nextChildNodeToConsider = node; |
m_currentSourceNode = 0; |
} else { |
// Otherwise the media element has neither a src attribute nor a source element |
@@ -2451,8 +2453,8 @@ |
{ |
// 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; |
+ RefPtr<HTMLSourceElement> currentSourceNode = m_currentSourceNode; |
+ RefPtr<Node> nextNode = m_nextChildNodeToConsider; |
KURL nextURL = selectNextSourceChild(0, DoNothing); |
@@ -2471,7 +2473,7 @@ |
LOG(Media, "HTMLMediaElement::selectNextSourceChild"); |
#endif |
- if (m_nextChildNodeToConsider == sourceChildEndOfListValue()) { |
+ if (!m_nextChildNodeToConsider) { |
#if !LOG_DISABLED |
if (shouldLog) |
LOG(Media, "HTMLMediaElement::selectNextSourceChild -> 0x0000, \"\""); |
@@ -2482,16 +2484,24 @@ |
KURL mediaURL; |
Node* node; |
HTMLSourceElement* source = 0; |
+ String type; |
bool lookingForStartNode = m_nextChildNodeToConsider; |
- bool canUse = false; |
+ bool canUseSourceElement = false; |
+ bool okToLoadSourceURL; |
- for (node = firstChild(); !canUse && node; node = node->nextSibling()) { |
+ NodeVector potentialSourceNodes; |
+ getChildNodes(this, potentialSourceNodes); |
+ |
+ for (unsigned i = 0; !canUseSourceElement && i < potentialSourceNodes.size(); ++i) { |
+ node = potentialSourceNodes[i].get(); |
if (lookingForStartNode && m_nextChildNodeToConsider != node) |
continue; |
lookingForStartNode = false; |
- |
+ |
if (!node->hasTagName(sourceTag)) |
continue; |
+ if (node->parentNode() != this) |
+ continue; |
source = static_cast<HTMLSourceElement*>(node); |
@@ -2525,34 +2535,41 @@ |
} |
// Is it safe to load this url? |
- if (!isSafeToLoadURL(mediaURL, actionIfInvalid) || !dispatchBeforeLoadEvent(mediaURL.string())) |
+ okToLoadSourceURL = isSafeToLoadURL(mediaURL, actionIfInvalid) && dispatchBeforeLoadEvent(mediaURL.string()); |
+ |
+ // A 'beforeload' event handler can mutate the DOM, so check to see if the source element is still a child node. |
+ if (node->parentNode() != this) { |
+ LOG(Media, "HTMLMediaElement::selectNextSourceChild : 'beforeload' removed current element"); |
+ source = 0; |
goto check_again; |
+ } |
+ if (!okToLoadSourceURL) |
+ goto check_again; |
+ |
// Making it this far means the <source> looks reasonable. |
- canUse = true; |
+ canUseSourceElement = true; |
check_again: |
- if (!canUse && actionIfInvalid == Complain) |
+ if (!canUseSourceElement && actionIfInvalid == Complain && source) |
source->scheduleErrorEvent(); |
} |
- if (canUse) { |
+ if (canUseSourceElement) { |
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(); |
+ m_nextChildNodeToConsider = 0; |
} |
#if !LOG_DISABLED |
if (shouldLog) |
- LOG(Media, "HTMLMediaElement::selectNextSourceChild -> %p, %s", m_currentSourceNode, canUse ? urlForLogging(mediaURL).utf8().data() : ""); |
+ LOG(Media, "HTMLMediaElement::selectNextSourceChild -> %p, %s", m_currentSourceNode.get(), canUseSourceElement ? urlForLogging(mediaURL).utf8().data() : ""); |
#endif |
- return canUse ? mediaURL : KURL(); |
+ return canUseSourceElement ? mediaURL : KURL(); |
} |
void HTMLMediaElement::sourceWasAdded(HTMLSourceElement* source) |
@@ -2584,20 +2601,20 @@ |
return; |
} |
- if (m_nextChildNodeToConsider != sourceChildEndOfListValue()) |
+ if (m_nextChildNodeToConsider) |
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 |
+ // 21. Wait until the node after pointer is a node other than the end of the list. (This step might wait forever.) |
+ // 22. Asynchronously await a stable state... |
+ // 23. 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. |
+ // 24. Set the networkState back to NETWORK_LOADING. |
m_networkState = NETWORK_LOADING; |
- // 24 - Jump back to the find next candidate step above. |
+ // 25. Jump back to the find next candidate step above. |
m_nextChildNodeToConsider = source; |
scheduleNextSourceChild(); |
} |
@@ -2619,8 +2636,8 @@ |
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); |
+ m_nextChildNodeToConsider = 0; |
+ LOG(Media, "HTMLMediaElement::sourceRemoved - m_nextChildNodeToConsider set to %p", m_nextChildNodeToConsider.get()); |
} 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 |
@@ -3212,6 +3229,8 @@ |
void HTMLMediaElement::getPluginProxyParams(KURL& url, Vector<String>& names, Vector<String>& values) |
{ |
+ RefPtr<HTMLMediaElement> protect(this); // selectNextSourceChild may fire 'beforeload', which can make arbitrary DOM mutations. |
+ |
Frame* frame = document()->frame(); |
if (isVideo()) { |