Chris Pearce - Bug 479711 - Media elements should delay the document 'load' event

This commit is contained in:
Chris Pearce 2009-03-09 10:01:03 +13:00
parent 035c54d22e
commit b37dae2092
11 changed files with 181 additions and 24 deletions

View File

@ -292,6 +292,12 @@ protected:
*/
already_AddRefed<nsIURI> GetNextSource();
/**
* Changes mDelayingLoadEvent, and will call BlockOnLoad()/UnblockOnLoad()
* on the owning document, so it can delay the load event firing.
*/
void ChangeDelayLoadStatus(PRBool aDelay);
nsRefPtr<nsMediaDecoder> mDecoder;
nsCOMPtr<nsIChannel> mChannel;
@ -308,6 +314,10 @@ protected:
// when selecting a resource to load.
nsCOMPtr<nsIDOMRange> mSourcePointer;
// Points to the document whose load we're blocking. This is the document
// we're bound to when loading starts.
nsCOMPtr<nsIDocument> mLoadBlockedDoc;
// Media loading flags. See:
// http://www.whatwg.org/specs/web-apps/current-work/#video)
nsMediaNetworkState mNetworkState;
@ -392,4 +402,12 @@ protected:
// PR_TRUE if we're loading exclusively from the src attribute's resource.
PRPackedBool mIsLoadingFromSrcAttribute;
// PR_TRUE if we're delaying the "load" event. They are delayed until either
// an error occurs, or the first frame is loaded.
PRPackedBool mDelayingLoadEvent;
// PR_TRUE when we've got a task queued to call SelectResource(),
// or while we're running SelectResource().
PRPackedBool mIsRunningSelectResource;
};

View File

@ -144,20 +144,30 @@ public:
SelectResourceEvent(nsHTMLMediaElement *aElement)
: nsMediaEvent(aElement) {}
NS_IMETHOD Run() {
if (!IsCancelled())
if (!IsCancelled()) {
NS_ASSERTION(mElement->mIsRunningSelectResource,
"Should have flagged that we're running SelectResource()");
mElement->SelectResource();
mElement->mIsRunningSelectResource = PR_FALSE;
}
return NS_OK;
}
};
void nsHTMLMediaElement::QueueSelectResourceTask()
{
// Don't allow multiple async select resource calls to be queued.
if (mIsRunningSelectResource)
return;
mIsRunningSelectResource = PR_TRUE;
ChangeDelayLoadStatus(PR_TRUE);
nsCOMPtr<nsIRunnable> event = new SelectResourceEvent(this);
NS_DispatchToMainThread(event);
}
void nsHTMLMediaElement::QueueLoadFromSourceTask()
{
ChangeDelayLoadStatus(PR_TRUE);
nsCOMPtr<nsIRunnable> event = new LoadNextSourceEvent(this);
NS_DispatchToMainThread(event);
}
@ -237,6 +247,7 @@ NS_IMPL_CYCLE_COLLECTION_CLASS(nsHTMLMediaElement)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(nsHTMLMediaElement, nsGenericHTMLElement)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mSourcePointer)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mLoadBlockedDoc)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(nsHTMLMediaElement, nsGenericHTMLElement)
@ -337,6 +348,8 @@ void nsHTMLMediaElement::AbortExistingLoads()
// TODO: The current playback position must be set to 0.
DispatchSimpleEvent(NS_LITERAL_STRING("emptied"));
}
mIsRunningSelectResource = PR_FALSE;
}
void nsHTMLMediaElement::NoSupportedMediaError()
@ -344,6 +357,7 @@ void nsHTMLMediaElement::NoSupportedMediaError()
mError = new nsHTMLMediaError(nsIDOMHTMLMediaError::MEDIA_ERR_NONE_SUPPORTED);
mNetworkState = nsIDOMHTMLMediaElement::NETWORK_NO_SOURCE;
DispatchAsyncProgressEvent(NS_LITERAL_STRING("error"));
ChangeDelayLoadStatus(PR_FALSE);
}
/* void load (); */
@ -390,6 +404,7 @@ void nsHTMLMediaElement::SelectResource()
// element children, wait. (This steps might wait forever.)
mNetworkState = nsIDOMHTMLMediaElement::NETWORK_NO_SOURCE;
mLoadWaitStatus = WAITING_FOR_SRC_OR_SOURCE;
ChangeDelayLoadStatus(PR_FALSE);
return;
}
@ -426,6 +441,8 @@ void nsHTMLMediaElement::NotifyLoadError()
void nsHTMLMediaElement::LoadFromSourceChildren()
{
NS_ASSERTION(!IsInDoc() || mDelayingLoadEvent,
"Should delay load event while loading in document");
while (PR_TRUE) {
nsresult rv;
nsCOMPtr<nsIURI> uri = GetNextSource();
@ -450,6 +467,8 @@ void nsHTMLMediaElement::LoadFromSourceChildren()
nsresult nsHTMLMediaElement::LoadResource(nsIURI* aURI)
{
NS_ASSERTION(!IsInDoc() || mDelayingLoadEvent,
"Should delay load event while loading in document");
nsresult rv;
if (mChannel) {
@ -540,6 +559,8 @@ nsresult nsHTMLMediaElement::LoadWithChannel(nsIChannel *aChannel,
AbortExistingLoads();
ChangeDelayLoadStatus(PR_TRUE);
nsresult rv = InitializeDecoderForChannel(aChannel, aListener);
if (NS_FAILED(rv)) {
return rv;
@ -699,7 +720,9 @@ nsHTMLMediaElement::nsHTMLMediaElement(nsINodeInfo *aNodeInfo, PRBool aFromParse
mIsBindingToTree(PR_FALSE),
mLoadWaitStatus(NOT_WAITING),
mIsLoadingFromSrcAttribute(PR_FALSE),
mIsRunningLoadMethod(PR_FALSE)
mIsRunningLoadMethod(PR_FALSE),
mDelayingLoadEvent(PR_FALSE),
mIsRunningSelectResource(PR_FALSE)
{
}
@ -831,7 +854,6 @@ void nsHTMLMediaElement::UnbindFromTree(PRBool aDeep,
{
if (!mPaused && mNetworkState != nsIDOMHTMLMediaElement::NETWORK_EMPTY)
Pause();
nsGenericHTMLElement::UnbindFromTree(aDeep, aNullParent);
}
@ -1138,6 +1160,7 @@ void nsHTMLMediaElement::MetadataLoaded()
void nsHTMLMediaElement::FirstFrameLoaded()
{
ChangeReadyState(nsIDOMHTMLMediaElement::HAVE_CURRENT_DATA);
ChangeDelayLoadStatus(PR_FALSE);
}
void nsHTMLMediaElement::ResourceLoaded()
@ -1155,6 +1178,7 @@ void nsHTMLMediaElement::NetworkError()
DispatchAsyncProgressEvent(NS_LITERAL_STRING("error"));
mNetworkState = nsIDOMHTMLMediaElement::NETWORK_EMPTY;
DispatchAsyncSimpleEvent(NS_LITERAL_STRING("emptied"));
ChangeDelayLoadStatus(PR_FALSE);
}
void nsHTMLMediaElement::PlaybackEnded()
@ -1528,3 +1552,20 @@ already_AddRefed<nsIURI> nsHTMLMediaElement::GetNextSource()
NS_NOTREACHED("Execution should not reach here!");
return nsnull;
}
void nsHTMLMediaElement::ChangeDelayLoadStatus(PRBool aDelay) {
if (mDelayingLoadEvent == aDelay)
return;
LOG(PR_LOG_DEBUG, ("ChangeDelayLoadStatus(%d) doc=0x%p", aDelay, mLoadBlockedDoc.get()));
mDelayingLoadEvent = aDelay;
if (aDelay) {
mLoadBlockedDoc = GetOwnerDoc();
mLoadBlockedDoc->BlockOnload();
} else {
NS_ASSERTION(mLoadBlockedDoc, "Need a doc to block on");
mLoadBlockedDoc->UnblockOnload(PR_FALSE);
mLoadBlockedDoc = nsnull;
}
}

View File

@ -72,6 +72,7 @@ _TEST_FILES += \
test_contentDuration4.html \
test_contentDuration5.html \
test_contentDuration6.html \
test_delay_load.html \
test_duration1.html \
test_ended1.html \
test_ended2.html \

View File

@ -0,0 +1,104 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=479711
-->
<head>
<title>Test for Bug 479711</title>
<script type="application/javascript" src="/MochiKit/MochiKit.js"></script>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
<script>
var gLoadedDataCount = 0;
function log(msg) {
//document.getElementById('log').innerHTML += "<p>"+msg+"</p>";
//dump(msg + "\n");
}
function loadeddata() {
gLoadedDataCount++;
}
function loaded() {
is(gLoadedDataCount, 5, "onload was not delayed until after metadataloaded");
log("doc-loaded gLoadedDataCount = " + gLoadedDataCount);
SimpleTest.finish();
}
addLoadEvent(loaded);
SimpleTest.waitForExplicitFinish();
</script>
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=479711">Mozilla Bug 479711</a>
<p id="display"></p>
<div id="content" style="display: none">
</div>
<video src="320x240.ogv" onloadeddata="loadeddata();"></video>
<div id="log" style="font-size: small;"></div>
<pre id="test">
<script type="application/javascript">
/** Test for Bug 479711 **/
var gEventTypes = [ 'loadstart', 'progress', 'suspend', 'load', 'abort', 'error', 'emptied', 'stalled', 'play', 'pause', 'loadedmetadata', 'loadeddata', 'waiting', 'playing', 'canplay', 'canplaythrough', 'seeking', 'seeked', 'timeupdate', 'ended', 'ratechange', 'durationchange', 'volumechange' ];
function listener(evt) {
log('event ' + evt.target.id + " " + evt.type);
}
function createVideo(id) {
var v = document.createElement("video");
v.id = id;
for (var i=0; i<gEventTypes.length; i++) {
var t = gEventTypes[i];
v.addEventListener(t, listener, false);
}
v.addEventListener('loadeddata', loadeddata, false);
v.src = "http://localhost:8888/tests/content/media/video/test/320x240.ogv";
return v;
}
// Load, add, then remove.
var v1 = createVideo("v1");
v1.load();
document.body.appendChild(v1);
v1.parentNode.removeChild(v1);
// Load and add.
var v2 = createVideo("v2");
v2.load();
document.body.appendChild(v2);
// Load outside of doc.
var v3 = createVideo("v3");
v3.load();
// Load and move to another document.
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
var v4 = createVideo("v4");
v4.load(); // load started while in this document, this doc's load will block until
// the video's finished loading (in the other document).
var gTestWindow = window.open("", "testWindow", "width=400,height=400");
gTestWindow.document.body.appendChild(v4);
v4.addEventListener('load',
function() {
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
gTestWindow.close();
}, false);
</script>
</pre>
</body>
</html>

View File

@ -1,8 +1,7 @@
<!DOCTYPE HTML>
<html class="reftest-wait">
<html>
<body style="background:white;">
<video id="v" src="black140x100.ogv"
style="width:140px; height:300px; position:relative; left:100px;"
onloadeddata="document.documentElement.removeAttribute('class')"></video>
style="width:140px; height:300px; position:relative; left:100px;"></video>
</body>
</html>

View File

@ -1,8 +1,7 @@
<!DOCTYPE HTML>
<html class="reftest-wait">
<html>
<body style="background:white;">
<video id="v" src="black140x100.ogv"
style="width:680px; height:200px; position:relative; top:200px;"
onloadeddata="document.documentElement.removeAttribute('class')"></video>
style="width:680px; height:200px; position:relative; top:200px;"></video>
</body>
</html>

View File

@ -1,8 +1,7 @@
<!DOCTYPE HTML>
<html class="reftest-wait">
<html>
<body style="background:white;">
<video id="v" src="black140x100.ogv"
style="width:280px; height:600px; position:relative; left:200px;"
onloadeddata="document.documentElement.removeAttribute('class')"></video>
style="width:280px; height:600px; position:relative; left:200px;"></video>
</body>
</html>

View File

@ -1,7 +1,6 @@
<!DOCTYPE HTML>
<html class="reftest-wait">
<html>
<body style="background:white;">
<video id="v" src="black140x100.ogv"
onloadeddata="document.documentElement.removeAttribute('class')"></video>
<video id="v" src="black140x100.ogv"></video>
</body>
</html>

View File

@ -1,7 +1,6 @@
<!DOCTYPE HTML>
<html class="reftest-wait">
<html>
<body style="background:white;">
<video id="v" src="black140x100.ogv" style="width:0"
onloadeddata="document.documentElement.removeAttribute('class')"></video>
<video id="v" src="black140x100.ogv" style="width:0"></video>
</body>
</html>

View File

@ -1,7 +1,6 @@
<!DOCTYPE HTML>
<html class="reftest-wait">
<html>
<body style="background:white;">
<video id="v" src="black140x100.ogv" style="height:0"
onloadeddata="document.documentElement.removeAttribute('class')"></video>
<video id="v" src="black140x100.ogv" style="height:0"></video>
</body>
</html>

View File

@ -1,7 +1,6 @@
<!DOCTYPE HTML>
<html class="reftest-wait" reftest-zoom="1.5">
<html reftest-zoom="1.5">
<body style="background:white; margin:0">
<video id="v" src="black140x100.ogv"
onloadeddata="document.documentElement.removeAttribute('class')"></video>
<video id="v" src="black140x100.ogv"></video>
</body>
</html>