[SCM] WebKit Debian packaging branch, debian/experimental, updated. upstream/1.3.3-9427-gc2be6fc
eric.carlson at apple.com
eric.carlson at apple.com
Wed Dec 22 14:45:42 UTC 2010
The following commit has been merged in the debian/experimental branch:
commit d041ae7b16b334ac2c52dff292ca0e2180167092
Author: eric.carlson at apple.com <eric.carlson at apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Date: Tue Oct 19 16:43:37 2010 +0000
2010-10-19 Eric Carlson <eric.carlson at apple.com>
Reviewed by Darin Adler.
https://bugs.webkit.org/show_bug.cgi?id=46763
CRASH in WebCore::ThreadTimers::sharedTimerFiredInternal
Fix crashes caused by moving and deleting <source> element(s) of active media element.
Tests: media/video-source-moved.html
media/video-source-removed.html
* html/HTMLMediaElement.cpp:
(WebCore::HTMLMediaElement::HTMLMediaElement): Add logging. Initialize selectNextSourceChild.
(WebCore::HTMLMediaElement::~HTMLMediaElement): Ditto.
(WebCore::HTMLMediaElement::insertedIntoDocument): Ditto.
(WebCore::HTMLMediaElement::removedFromDocument): Ditto.
(WebCore::HTMLMediaElement::scheduleLoad): Ditto.
(WebCore::HTMLMediaElement::setNetworkState): Deal with m_currentSourceNode being null when
the media engine signals a failure by skipping the error message and continuing as usual.
(WebCore::HTMLMediaElement::setVolume): Fix logging typo.
(WebCore::HTMLMediaElement::havePotentialSourceChild): Save and restore m_nextChildNodeToConsider
around call to selectNextSourceChild because they are both significant.
(WebCore::HTMLMediaElement::selectNextSourceChild): Use m_nextChildNodeToConsider to pick
the first node to consider. Bail immediately if it signals that we have already processed
every <source> node. Stach the node following the current source element in m_nextChildNodeToConsider
so we can resume the search even if m_currentSourceNode is removed while it is being processed.
(WebCore::HTMLMediaElement::sourceWasAdded): New, move logic from HTMLSourceElement::insertedIntoTree
here and correct it to deal with a <source> node being inserted immediately after the
current <source> node and a new <source> node being inserted at the end of the list after
all candidates have failed.
(WebCore::HTMLMediaElement::sourceWillBeRemoved): New, deal with current source node and next
potential node being removed.
* html/HTMLMediaElement.h:
(WebCore::HTMLMediaElement::sourceChildEndOfListValue): New, define sentinal value used to indicate
that all nodes have been processed.
* html/HTMLSourceElement.cpp:
(WebCore::HTMLSourceElement::HTMLSourceElement): Add logging.
(WebCore::HTMLSourceElement::insertedIntoTree): Call mediaElement->sourceWasAdded instead
of having logic here.
(WebCore::HTMLSourceElement::willRemove): New, call mediaElement->sourceWillBeRemoved
(WebCore::HTMLSourceElement::scheduleErrorEvent): Add logging.
(WebCore::HTMLSourceElement::cancelPendingErrorEvent): Add logging.
* html/HTMLSourceElement.h:
2010-10-19 Eric Carlson <eric.carlson at apple.com>
Reviewed by Darin Adler.
https://bugs.webkit.org/show_bug.cgi?id=46763
CRASH in WebCore::ThreadTimers::sharedTimerFiredInternal
Test moving and deleting <source> element(s) of active <video>.
* media/media-file.js:
(mimeTypeForExtension): New, added to make it possible to set 'type' attribute on <source>.
* media/video-source-moved-expected.txt: Added.
* media/video-source-moved.html: Added.
* media/video-source-removed-expected.txt: Added.
* media/video-source-removed.html: Added.
git-svn-id: http://svn.webkit.org/repository/webkit/trunk@70063 268f45cc-cd09-0410-ab3c-d52691b4dbfc
diff --git a/LayoutTests/ChangeLog b/LayoutTests/ChangeLog
index e4f7f0e..fbaefae 100644
--- a/LayoutTests/ChangeLog
+++ b/LayoutTests/ChangeLog
@@ -1,3 +1,20 @@
+2010-10-19 Eric Carlson <eric.carlson at apple.com>
+
+ Reviewed by Darin Adler.
+
+ https://bugs.webkit.org/show_bug.cgi?id=46763
+ CRASH in WebCore::ThreadTimers::sharedTimerFiredInternal
+
+ Test moving and deleting <source> element(s) of active <video>.
+
+ * media/media-file.js:
+ (mimeTypeForExtension): New, added to make it possible to set 'type' attribute on <source>.
+
+ * media/video-source-moved-expected.txt: Added.
+ * media/video-source-moved.html: Added.
+ * media/video-source-removed-expected.txt: Added.
+ * media/video-source-removed.html: Added.
+
2010-10-18 Antonio Gomes <agomes at rim.com>
Reviewed by Kenneth Rohde Christiansen.
diff --git a/LayoutTests/media/media-file.js b/LayoutTests/media/media-file.js
index 20e8235..c95595a 100644
--- a/LayoutTests/media/media-file.js
+++ b/LayoutTests/media/media-file.js
@@ -31,6 +31,19 @@ function findMediaFile(tagName, name) {
return "";
}
+function mimeTypeForExtension(extension) {
+ for (var i = 0; i < videoCodecs.length; ++i) {
+ if (extension == videoCodecs[i][1])
+ return videoCodecs[i][0];
+ }
+ for (var i = 0; i < audioCodecs.length; ++i) {
+ if (extension == audioCodecs[i][1])
+ return audioCodecs[i][0];
+ }
+
+ return "";
+}
+
function setSrcByTagName(tagName, src) {
var elements = document.getElementsByTagName(tagName);
if (elements) {
diff --git a/LayoutTests/media/video-source-moved-expected.txt b/LayoutTests/media/video-source-moved-expected.txt
new file mode 100644
index 0000000..62f77cf
--- /dev/null
+++ b/LayoutTests/media/video-source-moved-expected.txt
@@ -0,0 +1,34 @@
+Test to make sure a <source> moved after the media element begins processing is handled correctly.
+
+Moving previous <source> element to end of list, it should be processed again.
+EXPECTED ([object HTMLSourceElement] != 'null') OK
+<source> moved was processed a second time. OK
+
+Moving current <source> element, it should be processed again.
+EXPECTED ([object HTMLSourceElement] != 'null') OK
+<source> moved was processed a second time. OK
+
+Moving next <source> element, it should be processed again.
+EXPECTED ([object HTMLSourceElement] != 'null') OK
+<source> moved was processed a second time. OK
+
+Moving current <source> element to beginning of list, it should not be processed again.
+EXPECTED ([object HTMLSourceElement] != 'null') OK
+<source> moved was not processed OK
+
+Moving next <source> element to beginning of list, it should never processed.
+EXPECTED ([object HTMLSourceElement] != 'null') OK
+<source> moved was not processed OK
+
+<span> inserted after current <source> element before it is removed, processing should proceed normally.
+EXPECTED ([object HTMLSourceElement] != 'null') OK
+<source> moved was not processed OK
+
+<span> inserted after next <source> element before it is removed, processing should proceed normally.
+EXPECTED ([object HTMLSourceElement] != 'null') OK
+<source> moved was not processed OK
+
+PASS
+
+END OF TEST
+
diff --git a/LayoutTests/media/video-source-moved.html b/LayoutTests/media/video-source-moved.html
new file mode 100644
index 0000000..ba5a96c
--- /dev/null
+++ b/LayoutTests/media/video-source-moved.html
@@ -0,0 +1,205 @@
+<!doctype HTML>
+<html>
+ <head>
+ <title>moving <source> element test</title>
+ <script src=video-test.js></script>
+ <script src=media-file.js></script>
+ <script>
+
+ var testInfo =
+ {
+ current : -1,
+ tests :
+ [
+ { fcn : moveToEnd, errorCount : 0, moved : null, done : false, iteration : 1},
+ { fcn : moveToEnd, errorCount : 0, moved : null, done : false, iteration : 2},
+ { fcn : moveToEnd, errorCount : 0, moved : null, done : false, iteration : 3},
+ { fcn : moveEarlier, errorCount : 0, moved : null, iteration : 1 },
+ { fcn : moveEarlier, errorCount : 0, moved : null, iteration : 2 },
+ { fcn : moveEarlier, errorCount : 0, moved : null, iteration : 3 },
+ { fcn : moveEarlier, errorCount : 0, moved : null, iteration : 4 }
+ ]
+ };
+
+ function findCurrentSourceElement()
+ {
+ var sources = video.getElementsByTagName('source');
+ var currentSrc = video.currentSrc;
+ var ndx;
+ for (ndx = 0; ndx < sources.length; ++ndx) {
+ if (sources[ndx].src == currentSrc)
+ break;
+ }
+ if (ndx >= sources.length) {
+ failTest(currentSrc + " not found in <source> list");
+ return null;
+ }
+ return sources[ndx];
+ }
+
+ function moveEarlier(test, event)
+ {
+ if (test.done)
+ return;
+
+ switch (++test.errorCount)
+ {
+ case 1:
+ // Do nothing on the first error event
+ break;
+
+ case 2:
+ var current = findCurrentSourceElement();
+ switch (test.iteration)
+ {
+ case 1:
+ consoleWrite("Moving <b>current<" + "/b> <source> element to beginning of list, it should not be processed again.");
+ test.moved = video.removeChild(current);
+ break;
+ case 2:
+ consoleWrite("Moving <b>next<" + "/b> <source> element to beginning of list, it should never processed.");
+ test.moved = video.removeChild(current.nextSibling);
+ break;
+ case 3:
+ consoleWrite("<span> inserted after <b>current<" + "/b> <source> element before it is removed, processing should proceed normally.");
+ var span = document.createElement("span")
+ span.appendChild(document.createTextNode("Your browser doesn't support HTML5 video!"));
+ video.insertBefore(span, current.nextSibling);
+ test.moved = video.removeChild(current);
+ break;
+ case 4:
+ consoleWrite("<span> inserted after <b>next<" + "/b> <source> element before it is removed, processing should proceed normally.");
+ var span = document.createElement("span")
+ span.appendChild(document.createTextNode("Your browser doesn't support HTML5 video!"));
+ video.insertBefore(span, current.nextSibling.nextSibling);
+ test.moved = video.removeChild(current.nextSibling);
+ break;
+ default:
+ failTest("Malformed test data!");
+ break;
+ }
+
+ testExpected(test.moved, null, '!=');
+ video.insertBefore(test.moved, video.firstChild);
+ break;
+
+ default:
+ // We should never get an error for the element we moved.
+ if (event.target == test.moved) {
+ failTest("Error fired for <source> moved to beginning of list.");
+ test.done = true;
+ return;
+ } else if (!event.target.nextSibling) {
+ logResult(true, "<source> moved was not processed");
+ setTimeout(configureNextTest, 100);
+ }
+ break;
+ }
+ }
+
+ function moveToEnd(test, event)
+ {
+ switch (++test.errorCount)
+ {
+ case 1:
+ // Do nothing on the first error event
+ break;
+
+ case 2:
+ var current = findCurrentSourceElement();
+ switch (test.iteration)
+ {
+ case 1:
+ consoleWrite("Moving <b>previous<" + "/b> <source> element to end of list, it should be processed again.");
+ test.moved = video.removeChild(current.previousSibling);
+ break;
+ case 2:
+ consoleWrite("Moving <b>current<" + "/b> <source> element, it should be processed again.");
+ test.moved = video.removeChild(current);
+ break;
+ case 3:
+ consoleWrite("Moving <b>next<" + "/b> <source> element, it should be processed again.");
+ test.moved = video.removeChild(current.nextSibling);
+ break;
+ default:
+ failTest("Malformed test data!");
+ break;
+ }
+
+ testExpected(test.moved, null, '!=');
+ video.appendChild(test.moved);
+ break;
+
+ default:
+ if (event.target == test.moved) {
+ logResult(true, "<source> moved was processed a second time.");
+ setTimeout(configureNextTest, 100);
+ } else if (!event.target.nextSibling) {
+ // We should never reach the end of the source list since the tests stops
+ // when the error fires for the moved element.
+ failTest("Error never fired for <source> moved!");
+ }
+ break;
+ }
+ }
+
+ function runOneTest(evt)
+ {
+ var test = testInfo.tests[testInfo.current];
+ test.fcn(test, evt);
+ }
+
+ function addSource(index)
+ {
+ var source = document.createElement('source');
+ source.src = findMediaFile("video", index + "-" + Date.now());
+ source.type = mimeTypeForExtension(source.src.split('.')[1]);
+ video.appendChild(source);
+ }
+
+ function runNextTest()
+ {
+ consoleWrite("");
+ if (++testInfo.current >= testInfo.tests.length) {
+ consoleWrite("PASS<br>");
+ endTest();
+ return;
+ }
+
+ testInfo.errorCount = 0;
+ video = mediaElement = document.createElement('video');
+ document.body.appendChild(video);
+
+ // Add a bunch of source elements with bogus urls because we want to rearrange elements
+ // after the media engine begins processing sources, and we can't predict the delay
+ // between when the media element fires an 'error' event and our handler is called,
+ // but we need to guarantee that there are <source> elements that haven't been processed
+ // when we run the test.
+ for (var ndx = 1; ndx <= 10; ndx++)
+ addSource(ndx);
+ }
+
+ function configureNextTest()
+ {
+ var videos = document.querySelectorAll('video');
+ for (var ndx = 0; ndx < videos.length; ++ndx)
+ videos[ndx].parentNode.removeChild(videos[ndx]);
+ video = mediaElement = null;
+ setTimeout(runNextTest, 100);
+ }
+
+ function setup()
+ {
+ document.addEventListener("error", runOneTest, true);
+ configureNextTest();
+ }
+
+ </script>
+ </head>
+
+ <body>
+ <div>Test to make sure a <source> moved after the media element begins processing
+ is handled correctly.</div>
+ <script>setup()</script>
+ </body>
+</html>
diff --git a/LayoutTests/media/video-source-removed-expected.txt b/LayoutTests/media/video-source-removed-expected.txt
new file mode 100644
index 0000000..029d5f8
--- /dev/null
+++ b/LayoutTests/media/video-source-removed-expected.txt
@@ -0,0 +1,33 @@
+Test to make sure removing a media element's <source>(s) does not cause a crash.
+
+Removing all <source> elements with removeChild()
+-> removeChild(0)
+-> removeChild(1)
+-> removeChild(2)
+-> removeChild(3)
+-> removeChild(4)
+-> removeChild(5)
+-> removeChild(6)
+-> removeChild(7)
+-> removeChild(8)
+-> removeChild(9)
+
+Removing all <source> by setting .innerHTML
+-> video.innerHTML = ''
+
+Removing all <source> elements with replaceChild()
+-> replaceChild(0)
+-> replaceChild(1)
+-> replaceChild(2)
+-> replaceChild(3)
+-> replaceChild(4)
+-> replaceChild(5)
+-> replaceChild(6)
+-> replaceChild(7)
+-> replaceChild(8)
+-> replaceChild(9)
+
+PASS: A crash did not occur when removing <source> elements.
+
+END OF TEST
+
diff --git a/LayoutTests/media/video-source-removed.html b/LayoutTests/media/video-source-removed.html
new file mode 100644
index 0000000..316b0ee
--- /dev/null
+++ b/LayoutTests/media/video-source-removed.html
@@ -0,0 +1,92 @@
+<!doctype HTML>
+<html>
+ <head>
+ <title>crash after removing <source> test</title>
+ <script src=video-test.js></script>
+ <script src=media-file.js></script>
+ <script>
+
+ var testInfo =
+ {
+ current : -1,
+ tests : [removeChild, innerHTML, replaceChild]
+ };
+
+ function removeChild(sources)
+ {
+ consoleWrite("Removing all <source> elements with <i>removeChild()<" + "/i>");
+ for (var ndx = 0; ndx < sources.length; ++ndx) {
+ consoleWrite(" -> removeChild(" + ndx + ")");
+ video.removeChild(sources[ndx]);
+ }
+ }
+
+ function innerHTML()
+ {
+ consoleWrite("Removing all <source> by setting <i>.innerHTML<" + "/i>");
+ consoleWrite(" -> video.innerHTML = ''");
+ }
+
+ function replaceChild(sources)
+ {
+ consoleWrite("Removing all <source> elements with <i>replaceChild()<" + "/i>");
+ var span = document.createElement("span")
+ span.appendChild(document.createTextNode("Yo"));
+ for (var ndx = 0; ndx < sources.length; ++ndx) {
+ consoleWrite(" -> replaceChild(" + ndx + ")");
+ video.replaceChild(span, sources[ndx]);
+ }
+ }
+
+ function runOneTest()
+ {
+ testInfo.tests[testInfo.current](document.querySelectorAll('source'));
+ setTimeout(configureNextTest, 100);
+ }
+
+ function addSource(index)
+ {
+ source = document.createElement('source');
+ source.src = findMediaFile("video", index + "-" + Date.now());
+ source.type = mimeTypeForExtension(source.src.split('.')[1]);
+ video.appendChild(source);
+ }
+
+ function runNextTest()
+ {
+ consoleWrite("");
+ if (++testInfo.current >= testInfo.tests.length) {
+ consoleWrite("PASS: A crash did not occur when removing <source> elements.<br>");
+ endTest();
+ return;
+ }
+
+ video = mediaElement = document.createElement('video');
+ document.body.appendChild(video);
+ video.addEventListener("loadstart", runOneTest);
+
+ // Add a bunch of source elements with bogus urls because we want to remove elements
+ // after the media engine begins processing sources, and we can't predict the delay
+ // between when the media element fires an 'error' event and our handler is called,
+ // but we need to guarantee that there are <source> elements that haven't been processed
+ // when we run the test.
+ for (var ndx = 1; ndx <= 10; ndx++)
+ addSource(ndx);
+ }
+
+ function configureNextTest()
+ {
+ var videos = document.querySelectorAll('video');
+ for (var ndx = 0; ndx < videos.length; ++ndx)
+ videos[ndx].parentNode.removeChild(videos[ndx]);
+ video = mediaElement = null;
+ setTimeout(runNextTest, 100);
+ }
+ </script>
+ </head>
+
+ <body>
+ Test to make sure removing a media element's <source>(s) does not cause a crash.
+ <script>configureNextTest()</script>
+ </body>
+</html>
diff --git a/WebCore/ChangeLog b/WebCore/ChangeLog
index fc89f23..5550d79 100644
--- a/WebCore/ChangeLog
+++ b/WebCore/ChangeLog
@@ -1,3 +1,49 @@
+2010-10-19 Eric Carlson <eric.carlson at apple.com>
+
+ Reviewed by Darin Adler.
+
+ https://bugs.webkit.org/show_bug.cgi?id=46763
+ CRASH in WebCore::ThreadTimers::sharedTimerFiredInternal
+
+ Fix crashes caused by moving and deleting <source> element(s) of active media element.
+
+ Tests: media/video-source-moved.html
+ media/video-source-removed.html
+
+ * html/HTMLMediaElement.cpp:
+ (WebCore::HTMLMediaElement::HTMLMediaElement): Add logging. Initialize selectNextSourceChild.
+ (WebCore::HTMLMediaElement::~HTMLMediaElement): Ditto.
+ (WebCore::HTMLMediaElement::insertedIntoDocument): Ditto.
+ (WebCore::HTMLMediaElement::removedFromDocument): Ditto.
+ (WebCore::HTMLMediaElement::scheduleLoad): Ditto.
+ (WebCore::HTMLMediaElement::setNetworkState): Deal with m_currentSourceNode being null when
+ the media engine signals a failure by skipping the error message and continuing as usual.
+ (WebCore::HTMLMediaElement::setVolume): Fix logging typo.
+ (WebCore::HTMLMediaElement::havePotentialSourceChild): Save and restore m_nextChildNodeToConsider
+ around call to selectNextSourceChild because they are both significant.
+ (WebCore::HTMLMediaElement::selectNextSourceChild): Use m_nextChildNodeToConsider to pick
+ the first node to consider. Bail immediately if it signals that we have already processed
+ every <source> node. Stach the node following the current source element in m_nextChildNodeToConsider
+ so we can resume the search even if m_currentSourceNode is removed while it is being processed.
+ (WebCore::HTMLMediaElement::sourceWasAdded): New, move logic from HTMLSourceElement::insertedIntoTree
+ here and correct it to deal with a <source> node being inserted immediately after the
+ current <source> node and a new <source> node being inserted at the end of the list after
+ all candidates have failed.
+ (WebCore::HTMLMediaElement::sourceWillBeRemoved): New, deal with current source node and next
+ potential node being removed.
+ * html/HTMLMediaElement.h:
+ (WebCore::HTMLMediaElement::sourceChildEndOfListValue): New, define sentinal value used to indicate
+ that all nodes have been processed.
+
+ * html/HTMLSourceElement.cpp:
+ (WebCore::HTMLSourceElement::HTMLSourceElement): Add logging.
+ (WebCore::HTMLSourceElement::insertedIntoTree): Call mediaElement->sourceWasAdded instead
+ of having logic here.
+ (WebCore::HTMLSourceElement::willRemove): New, call mediaElement->sourceWillBeRemoved
+ (WebCore::HTMLSourceElement::scheduleErrorEvent): Add logging.
+ (WebCore::HTMLSourceElement::cancelPendingErrorEvent): Add logging.
+ * html/HTMLSourceElement.h:
+
2010-10-19 Luiz Agostini <luiz.agostini at openbossa.org>
Reviewed by Antti Koivisto.
diff --git a/WebCore/html/HTMLMediaElement.cpp b/WebCore/html/HTMLMediaElement.cpp
index 7c2138f..362c771 100644
--- a/WebCore/html/HTMLMediaElement.cpp
+++ b/WebCore/html/HTMLMediaElement.cpp
@@ -125,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)
@@ -155,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);
@@ -324,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();
@@ -331,6 +335,7 @@ void HTMLMediaElement::insertedIntoDocument()
void HTMLMediaElement::removedFromDocument()
{
+ LOG(Media, "HTMLMediaElement::removedFromDocument");
if (m_networkState > NETWORK_EMPTY)
pause(processingUserGesture());
if (m_isFullscreen)
@@ -370,6 +375,7 @@ void HTMLMediaElement::recalcStyle(StyleChange change)
void HTMLMediaElement::scheduleLoad()
{
+ LOG(Media, "HTMLMediaElement::scheduleLoad");
#if ENABLE(PLUGIN_PROXY_FOR_VIDEO)
createMediaPlayerProxy();
#endif
@@ -863,7 +869,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;
}
@@ -874,16 +880,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) {
- ASSERT(m_currentSourceNode);
- if (!m_currentSourceNode)
- return;
- 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();
}
@@ -1419,7 +1426,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;
@@ -1567,11 +1574,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();
}
@@ -1585,22 +1596,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;
+ 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);
@@ -1635,26 +1653,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");
diff --git a/WebCore/html/HTMLMediaElement.h b/WebCore/html/HTMLMediaElement.h
index 99232e0..e5e7ae0 100644
--- a/WebCore/html/HTMLMediaElement.h
+++ b/WebCore/html/HTMLMediaElement.h
@@ -169,6 +169,9 @@ public:
bool processingUserGesture() const;
+ void sourceWillBeRemoved(HTMLSourceElement*);
+ void sourceWasAdded(HTMLSourceElement*);
+
protected:
HTMLMediaElement(const QualifiedName&, Document*);
virtual ~HTMLMediaElement();
@@ -331,7 +334,9 @@ private:
enum LoadState { WaitingForSource, LoadingFromSrcAttr, LoadingFromSourceElement };
LoadState m_loadState;
HTMLSourceElement* m_currentSourceNode;
-
+ Node* m_nextChildNodeToConsider;
+ Node* sourceChildEndOfListValue() { return static_cast<Node*>(this); }
+
OwnPtr<MediaPlayer> m_player;
#if ENABLE(PLUGIN_PROXY_FOR_VIDEO)
RefPtr<Widget> m_proxyWidget;
diff --git a/WebCore/html/HTMLSourceElement.cpp b/WebCore/html/HTMLSourceElement.cpp
index 96c9829..59b3882 100644
--- a/WebCore/html/HTMLSourceElement.cpp
+++ b/WebCore/html/HTMLSourceElement.cpp
@@ -33,6 +33,7 @@
#include "HTMLDocument.h"
#include "HTMLMediaElement.h"
#include "HTMLNames.h"
+#include "Logging.h"
using namespace std;
@@ -44,6 +45,7 @@ inline HTMLSourceElement::HTMLSourceElement(const QualifiedName& tagName, Docume
: HTMLElement(tagName, document)
, m_errorEventTimer(this, &HTMLSourceElement::errorEventTimerFired)
{
+ LOG(Media, "HTMLSourceElement::HTMLSourceElement - %p", this);
ASSERT(hasTagName(sourceTag));
}
@@ -55,13 +57,17 @@ PassRefPtr<HTMLSourceElement> HTMLSourceElement::create(const QualifiedName& tag
void HTMLSourceElement::insertedIntoTree(bool deep)
{
HTMLElement::insertedIntoTree(deep);
- if (parentNode() && (parentNode()->hasTagName(audioTag) || parentNode()->hasTagName(videoTag))) {
- HTMLMediaElement* media = static_cast<HTMLMediaElement*>(parentNode());
- if (media->networkState() == HTMLMediaElement::NETWORK_EMPTY)
- media->scheduleLoad();
- }
+ if (parentNode() && (parentNode()->hasTagName(audioTag) || parentNode()->hasTagName(videoTag)))
+ static_cast<HTMLMediaElement*>(parentNode())->sourceWasAdded(this);
}
+void HTMLSourceElement::willRemove()
+{
+ if (parentNode() && (parentNode()->hasTagName(audioTag) || parentNode()->hasTagName(videoTag)))
+ static_cast<HTMLMediaElement*>(parentNode())->sourceWillBeRemoved(this);
+ HTMLElement::willRemove();
+}
+
void HTMLSourceElement::setSrc(const String& url)
{
setAttribute(srcAttr, url);
@@ -89,6 +95,7 @@ void HTMLSourceElement::setType(const String& type)
void HTMLSourceElement::scheduleErrorEvent()
{
+ LOG(Media, "HTMLSourceElement::scheduleErrorEvent - %p", this);
if (m_errorEventTimer.isActive())
return;
@@ -97,11 +104,13 @@ void HTMLSourceElement::scheduleErrorEvent()
void HTMLSourceElement::cancelPendingErrorEvent()
{
+ LOG(Media, "HTMLSourceElement::cancelPendingErrorEvent - %p", this);
m_errorEventTimer.stop();
}
void HTMLSourceElement::errorEventTimerFired(Timer<HTMLSourceElement>*)
{
+ LOG(Media, "HTMLSourceElement::errorEventTimerFired - %p", this);
dispatchEvent(Event::create(eventNames().errorEvent, false, true));
}
diff --git a/WebCore/html/HTMLSourceElement.h b/WebCore/html/HTMLSourceElement.h
index 8aa1d06..cc1e5d7 100644
--- a/WebCore/html/HTMLSourceElement.h
+++ b/WebCore/html/HTMLSourceElement.h
@@ -50,6 +50,7 @@ private:
HTMLSourceElement(const QualifiedName&, Document*);
virtual void insertedIntoTree(bool);
+ virtual void willRemove();
virtual bool isURLAttribute(Attribute*) const;
void errorEventTimerFired(Timer<HTMLSourceElement>*);
--
WebKit Debian packaging
More information about the Pkg-webkit-commits
mailing list