Bug 476731 - Media element should fire error event when src is a 404; r=chris.double sr=roc

This commit is contained in:
Chris Pearce 2009-02-15 17:26:32 +01:00
Родитель 8d0f59aa64
Коммит 346544976a
23 изменённых файлов: 155 добавлений и 80 удалений

Просмотреть файл

@ -222,6 +222,12 @@ protected:
*/
nsresult NewURIFromString(const nsAutoString& aURISpec, nsIURI** aURI);
/**
* Does step 12 of the media load() algorithm, sends error/emptied events to
* to the media element, and reset network/begun state.
*/
void NoSupportedMediaError();
nsRefPtr<nsMediaDecoder> mDecoder;
nsCOMPtr<nsIChannel> mChannel;

Просмотреть файл

@ -125,18 +125,23 @@ NS_IMPL_ISUPPORTS2(nsHTMLMediaElement::nsMediaLoadListener, nsIRequestObserver,
NS_IMETHODIMP nsHTMLMediaElement::nsMediaLoadListener::OnStartRequest(nsIRequest* aRequest, nsISupports* aContext)
{
nsresult rv;
nsresult rv = NS_OK;
nsCOMPtr<nsIChannel> channel = do_QueryInterface(aRequest);
if (channel &&
mElement &&
NS_SUCCEEDED(mElement->InitializeDecoderForChannel(channel, getter_AddRefs(mNextListener))) &&
NS_SUCCEEDED(rv = mElement->InitializeDecoderForChannel(channel, getter_AddRefs(mNextListener))) &&
mNextListener) {
rv = mNextListener->OnStartRequest(aRequest, aContext);
} else {
// If InitializeDecoderForChannel did not return a listener, we abort
// the connection since we aren't interested in keeping the channel
// alive ourselves.
// If InitializeDecoderForChannel() returned an error, fire a network
// error.
if (NS_FAILED(rv) && !mNextListener && mElement) {
mElement->NetworkError();
}
// If InitializeDecoderForChannel did not return a listener (but may
// have otherwise succeeded), we abort the connection since we aren't
// interested in keeping the channel alive ourselves.
rv = NS_BINDING_ABORTED;
}
@ -243,16 +248,31 @@ PRBool nsHTMLMediaElement::AbortExistingLoads()
return PR_FALSE;
}
void nsHTMLMediaElement::NoSupportedMediaError()
{
mError = new nsHTMLMediaError(nsIDOMHTMLMediaError::MEDIA_ERR_NONE_SUPPORTED);
mBegun = PR_FALSE;
DispatchAsyncProgressEvent(NS_LITERAL_STRING("error"));
mNetworkState = nsIDOMHTMLMediaElement::NETWORK_EMPTY;
DispatchAsyncSimpleEvent(NS_LITERAL_STRING("emptied"));
}
/* void load (); */
NS_IMETHODIMP nsHTMLMediaElement::Load()
{
if (AbortExistingLoads())
return NS_OK;
mNetworkState = nsIDOMHTMLMediaElement::NETWORK_LOADING;
mBegun = PR_TRUE;
DispatchAsyncProgressEvent(NS_LITERAL_STRING("loadstart"));
nsCOMPtr<nsIURI> uri;
nsresult rv = PickMediaElement(getter_AddRefs(uri));
if (NS_FAILED(rv))
if (NS_FAILED(rv)) {
NoSupportedMediaError();
return rv;
}
if (mChannel) {
mChannel->Cancel(NS_BINDING_ABORTED);
@ -270,6 +290,7 @@ NS_IMETHODIMP nsHTMLMediaElement::Load()
nsContentUtils::GetContentPolicy(),
nsContentUtils::GetSecurityManager());
if (NS_FAILED(rv) || NS_CP_REJECTED(shouldLoad)) {
NoSupportedMediaError();
return NS_ERROR_CONTENT_BLOCKED;
}
@ -279,8 +300,10 @@ NS_IMETHODIMP nsHTMLMediaElement::Load()
nsnull,
nsnull,
nsIRequest::LOAD_NORMAL);
NS_ENSURE_SUCCESS(rv, rv);
if (NS_FAILED(rv)) {
NetworkError();
return rv;
}
// The listener holds a strong reference to us. This creates a reference
// cycle which is manually broken in the listener's OnStartRequest method
// after it is finished with the element.
@ -295,14 +318,19 @@ NS_IMETHODIMP nsHTMLMediaElement::Load()
PR_FALSE,
&rv);
NS_ENSURE_TRUE(listener, NS_ERROR_OUT_OF_MEMORY);
NS_ENSURE_SUCCESS(rv, rv);
if (NS_FAILED(rv)) {
NoSupportedMediaError();
return rv;
}
} else {
rv = nsContentUtils::GetSecurityManager()->
CheckLoadURIWithPrincipal(NodePrincipal(),
uri,
nsIScriptSecurityManager::STANDARD);
NS_ENSURE_SUCCESS(rv, rv);
if (NS_FAILED(rv)) {
NoSupportedMediaError();
return rv;
}
listener = loadListener;
}
@ -325,15 +353,10 @@ NS_IMETHODIMP nsHTMLMediaElement::Load()
// and is useless now anyway, so drop our reference to it to allow it to
// be destroyed.
mChannel = nsnull;
NetworkError();
return rv;
}
mNetworkState = nsIDOMHTMLMediaElement::NETWORK_LOADING;
mBegun = PR_TRUE;
DispatchAsyncProgressEvent(NS_LITERAL_STRING("loadstart"));
return NS_OK;
}
@ -593,7 +616,9 @@ nsresult nsHTMLMediaElement::SetAttr(PRInt32 aNameSpaceID, nsIAtom* aName,
nsGenericHTMLElement::SetAttr(aNameSpaceID, aName, aPrefix, aValue,
aNotify);
if (aNotify && aNameSpaceID == kNameSpaceID_None) {
if (aName == nsGkAtoms::src) {
if (aName == nsGkAtoms::src &&
IsInDoc() &&
mNetworkState == nsIDOMHTMLMediaElement::NETWORK_EMPTY) {
Load();
}
}
@ -1172,9 +1197,6 @@ nsresult nsHTMLMediaElement::DispatchAsyncProgressEvent(const nsAString& aName)
nsresult nsHTMLMediaElement::DispatchProgressEvent(const nsAString& aName)
{
if (!mDecoder)
return NS_OK;
nsCOMPtr<nsIDOMDocumentEvent> docEvent(do_QueryInterface(GetOwnerDoc()));
nsCOMPtr<nsIDOMEventTarget> target(do_QueryInterface(static_cast<nsIContent*>(this)));
NS_ENSURE_TRUE(docEvent && target, NS_ERROR_INVALID_ARG);
@ -1186,9 +1208,15 @@ nsresult nsHTMLMediaElement::DispatchProgressEvent(const nsAString& aName)
nsCOMPtr<nsIDOMProgressEvent> progressEvent(do_QueryInterface(event));
NS_ENSURE_TRUE(progressEvent, NS_ERROR_FAILURE);
nsMediaDecoder::Statistics stats = mDecoder->GetStatistics();
PRInt64 totalBytes = 0;
PRUint64 downloadPosition = 0;
if (mDecoder) {
nsMediaDecoder::Statistics stats = mDecoder->GetStatistics();
totalBytes = stats.mTotalBytes;
downloadPosition = stats.mDownloadPosition;
}
rv = progressEvent->InitProgressEvent(aName, PR_TRUE, PR_TRUE,
stats.mTotalBytes >= 0, stats.mDownloadPosition, stats.mTotalBytes);
totalBytes >= 0, downloadPosition, totalBytes);
NS_ENSURE_SUCCESS(rv, rv);
PRBool dummy;

Просмотреть файл

@ -74,6 +74,7 @@ _TEST_FILES += \
test_duration1.html \
test_ended1.html \
test_ended2.html \
test_error_on_404.html \
test_onloadedmetadata.html \
test_play.html \
test_progress1.html \

Просмотреть файл

@ -1,55 +1,69 @@
<html>
<head>
</head>
<body onload="setTimeout(load, 0);" onunload="done()">
<script>
// Page URL: http://example.org/tests/content/media/video/test/file_access_controls.html
var gTests = [
{
// Test 0
url: "redirect.sjs?http://example.com/tests/content/media/video/test/320x240.ogv",
result: "error",
description: "Won't load when redirected to different domain",
},{
// Test 1
url: "redirect.sjs?http://example.com/tests/content/media/video/test/320x240.allow-origin.ogv",
result: "loaded",
description: "Can load when redirected to different domain with allow-origin",
},{
// Test 2
url: "redirect.sjs?http://test1.example.org/tests/content/media/video/test/320x240.ogv",
result: "error",
description: "Won't load when redirected to subdomain",
},{
// Test 3
url: "redirect.sjs?http://test1.example.org/tests/content/media/video/test/320x240.allow-origin.ogv",
result: "loaded",
description: "Can load when redirected to subdomain with allow-origin",
},{
// Test 4
url: "redirect.sjs?http://example.org/tests/content/media/video/test/320x240.ogv",
result: "loaded",
description: "Can load when redirected to same domain",
},{
// Test 5
url: "http://example.org/tests/content/media/video/test/320x240.ogv",
result: "loaded",
description: "Can load from same domain"
},{
// Test 6
url: "http://example.org:8000/tests/content/media/video/test/320x240.ogv",
result: "error",
description: "Won't load from differnet port on same domain"
},{
// Test 7
url: "http://example.org:8000/tests/content/media/video/test/320x240.allow-origin.ogv",
result: "loaded",
description: "Can load from different port on same domain with allow-origin",
},{
// Test 8
url: "http://example.com/tests/content/media/video/test/320x240.ogv",
result: "error",
description: "Won't load cross domain",
},{
// Test 9
url: "http://example.com/tests/content/media/video/test/320x240.allow-origin.ogv",
result: "loaded",
description: "Can load cross domain with allow-origin",
},{
// Test 10
url: "http://test1.example.org/tests/content/media/video/test/320x240.allow-origin.ogv",
result: "loaded",
description: "Can load from subdomain with allow-origin",
},{
// Test 11
url: "http://test1.example.org/tests/content/media/video/test/320x240.ogv",
result: "error",
description: "Won't load from subdomain",
@ -59,24 +73,31 @@ var gTests = [
var gTestNum = 0;
var gExpectedResult = null;
var gTestDescription = null;
var video = null;
var gVideo = null;
var gTestedRemoved = false;
var gOldPref;
function result(code) {
dump((gTestNum - 1) + ": " + code + "\n");
//dump((gTestNum - 1) + ": " + code + "\n");
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
opener.is(code, gExpectedResult, gTestDescription);
nextTest();
}
function createVideo() {
var v = document.createElement('video');
v.addEventListener('loadeddata', function(){result('loaded');}, false);
v.addEventListener('error', function(){result('error');}, false);
v.id = 'video';
return v;
}
function load() {
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
opener.is(window.location.href,
"http://example.org/tests/content/media/video/test/file_access_controls.html",
"We must be on a example.org:80");
video = document.getElementById('video');
// Ensure access control check pref is on.
// media.enforce_same_site_origin
var prefService = Components.classes["@mozilla.org/preferences-service;1"]
@ -90,14 +111,17 @@ function load() {
}
function nextTest() {
//dump("nextTest() called, gTestNum="+gTestNum+" gTestedRemoved="+gTestedRemoved+"\n");
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
if (gTestNum == gTests.length) {
//dump("gTestNum == gTests.length\n");
if (!gTestedRemoved) {
// Repeat all tests with element removed from doc, should get same result.
video.parentNode.removeChild(video);
gVideo.parentNode.removeChild(gVideo);
gTestedRemoved = true;
gTestNum = 0;
} else {
//dump("Exiting...\n");
// We're done, exit the test.
window.close();
return;
@ -105,9 +129,16 @@ function nextTest() {
}
gExpectedResult = gTests[gTestNum].result;
gTestDescription = gTests[gTestNum].description;
dump("Starting test " + gTestNum + " at " + gTests[gTestNum].url + "\n");
video.src = gTests[gTestNum].url;
video.load();
//dump("Starting test " + gTestNum + " at " + gTests[gTestNum].url + " expecting:" + gExpectedResult + "\n");
gVideo = createVideo();
gVideo.src = gTests[gTestNum].url;
if (!gTestedRemoved) {
document.body.appendChild(gVideo);
// Will cause load() to be invoked.
} else {
gVideo.load();
}
gTestNum++;
}
@ -122,16 +153,6 @@ function done() {
}
</script>
</head>
<body onload="load();" onunload="done()">
<!-- Change onloadedfirstframe to onloadeddata after bug 462570 lands -->
<video id="video"
onloadeddata="result('loaded');"
onerror="result('error');">
</video>
</body>
</html>

Просмотреть файл

@ -7,8 +7,8 @@
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<video id='v1'></video><audio id='a1'></audio>
<video id='v2' autoplay></video><audio id='a2' autoplay></audio>
<video id='v1' onerror="event.stopPropagation();"></video><audio id='a1' onerror="event.stopPropagation();"></audio>
<video id='v2' onerror="event.stopPropagation();" autoplay></video><audio id='a2' onerror="event.stopPropagation();"autoplay></audio>
<pre id="test">
<script class="testbody" type="text/javascript">
var v1 = document.getElementById('v1');

Просмотреть файл

@ -13,10 +13,10 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=463162
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=463162">Mozilla Bug 463162</a>
<audio id='a1'><sauce type="audio/x-wav" src="r11025_s16_c1.wav"/></audio>
<audio id='a2'><source type="audio/x-wav" src="r11025_s16_c1.wav"/></audio>
<audio id='a3'><html:source type="audio/x-wav" src="r11025_s16_c1.wav"/></audio>
<audio id='a4'><svg:source type="audio/x-wav" src="r11025_s16_c1.wav"/></audio>
<audio id='a1' onerror="event.stopPropagation();"><sauce type="audio/x-wav" src="r11025_s16_c1.wav"/></audio>
<audio id='a2' onerror="event.stopPropagation();"><source type="audio/x-wav" src="r11025_s16_c1.wav"/></audio>
<audio id='a3' onerror="event.stopPropagation();"><html:source type="audio/x-wav" src="r11025_s16_c1.wav"/></audio>
<audio id='a4' onerror="event.stopPropagation();"><svg:source type="audio/x-wav" src="r11025_s16_c1.wav"/></audio>
<pre id="test">
<script class="testbody" type="text/javascript">
<![CDATA[

Просмотреть файл

@ -16,7 +16,7 @@ a Bug 469247</a>
<div id="content" style="display: none">
</div>
<video id="v"></video>
<video id="v" onerror="event.stopPropagation();"></video>
<pre id="test">
<script type="application/javascript">

Просмотреть файл

@ -16,7 +16,7 @@ a Bug 469247</a>
<div id="content" style="display: none">
</div>
<video id="v"></video>
<video id="v" onerror="event.stopPropagation();"></video>
<pre id="test">
<script src="can_play_type_ogg.js"></script>

Просмотреть файл

@ -16,7 +16,7 @@ a Bug 469247</a>
<div id="content" style="display: none">
</div>
<video id="v"></video>
<video id="v" onerror="event.stopPropagation();"></video>
<pre id="test">
<script src="can_play_type_wave.js"></script>

Просмотреть файл

@ -16,7 +16,7 @@ a Bug 469247</a>
<div id="content" style="display: none">
</div>
<video id="v"></video>
<video id="v" onerror="event.stopPropagation();"></video>
<pre id="test">
<script src="can_play_type_ogg.js"></script>

Просмотреть файл

@ -16,7 +16,7 @@ a Bug 469247</a>
<div id="content" style="display: none">
</div>
<video id="v"></video>
<video id="v" onerror="event.stopPropagation();"></video>
<pre id="test">
<script src="can_play_type_wave.js"></script>

Просмотреть файл

@ -11,7 +11,7 @@
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<video><source></video><audio><source></audio>
<video onerror="event.stopPropagation();"><source></video><audio onerror="event.stopPropagation();"><source></audio>
<pre id="test">
<script class="testbody" type="text/javascript">
is(HTMLElement.NETWORK_EMPTY, undefined);

Просмотреть файл

@ -7,8 +7,8 @@
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<video id='v1'></video><audio id='a1'></audio>
<video id='v2' controls></video><audio id='a2' controls></audio>
<video id='v1' onerror="event.stopPropagation();"></video><audio id='a1' onerror="event.stopPropagation();"></audio>
<video id='v2' onerror="event.stopPropagation();" controls></video><audio id='a2' onerror="event.stopPropagation();" controls></audio>
<pre id="test">
<script class="testbody" type="text/javascript">
var v1 = document.getElementById('v1');

Просмотреть файл

@ -7,7 +7,7 @@
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<video id='v1'></video><audio id='a1'></audio>
<video id='v1' onerror="event.stopPropagation();"></video><audio id='a1' onerror="event.stopPropagation();"></audio>
<pre id="test">
<script class="testbody" type="text/javascript">
var v1 = document.getElementById('v1');

Просмотреть файл

@ -28,25 +28,25 @@ var gOldWavePref = branch.getBoolPref("wave.enabled");
branch.setBoolPref("ogg.enabled", false);
branch.setBoolPref("wave.enabled", false);
</script>
var gLoadError = new Object();
<video id="video1">
<source type="video/ogg" src="320x240.ogv"/>
<source type="audio/wave" src="r11025_u8_c1.wav"/>
</video>
gLoadError['video1'] = 0;
gLoadError['video2'] = 0;
gLoadError['video3'] = 0;
<video id="video2" src="320x240.ogv"></video>
<video id="video3" src="r11025_u8_c1.wav"></video>
<script>
var gErrorCount = 0;
SimpleTest.waitForExplicitFinish();
function doTest() {
is(document.getElementById('video1').currentSrc, "");
is(document.getElementById('video2').currentSrc, "");
is(document.getElementById('video3').currentSrc, "");
function finishTest() {
is(document.getElementById('video1').currentSrc, "", 'video1.currentSrc==""');
is(document.getElementById('video2').currentSrc, "", 'video2.currentSrc==""');
is(document.getElementById('video3').currentSrc, "", 'video3.currentSrc==""');
is(gLoadError['video1'], 1, "Load error on video1");
is(gLoadError['video2'], 1, "Load error on video2");
is(gLoadError['video3'], 1, "Load error on video3");
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
branch.setBoolPref("ogg.enabled", gOldOggPref);
@ -55,12 +55,26 @@ function doTest() {
SimpleTest.finish();
}
// Using a timeout here sucks, but we currently have no way
// to be notified that the element couldn't load. At least if this timeout
// happens "too early", the test will pass instead of failing.
setTimeout(doTest, 1000);
function videoError(event, id) {
event.stopPropagation();
gLoadError[id]++;
gErrorCount++;
if (gErrorCount == 3) {
finishTest();
}
}
</script>
<video id="video1" onerror="videoError(event, 'video1');">
<source type="video/ogg" src="320x240.ogv"/>
<source type="audio/wave" src="r11025_u8_c1.wav"/>
</video>
<video id="video2" src="320x240.ogv" onerror="videoError(event, 'video2');"></video>
<video id="video3" src="r11025_u8_c1.wav" onerror="videoError(event, 'video3');"></video>
</pre>
</body>
</html>

Просмотреть файл

@ -15,6 +15,8 @@ function maketest(expect_load, attach_media, name, type, check_metadata) {
return function (testNum) {
var e = document.createElement('video');
e.addEventListener('error', function(e) { e.stopPropagation();}, false);
if (expect_load) {
// this could be loadedmetadata, but needs bug 466410 fixed
e.addEventListener('loadeddata', function () {

Просмотреть файл

@ -7,7 +7,7 @@
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<video id='v1'></video><audio id='a1'></audio>
<video id='v1' onerror="event.stopPropagation();"></video><audio id='a1' onerror="event.stopPropagation();"></audio>
<pre id="test">
<script class="testbody" type="text/javascript">
var v1 = document.getElementById('v1');

Просмотреть файл

@ -7,7 +7,7 @@
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<video id='v1'></video><audio id='a1'></audio>
<video id='v1' onerror="event.stopPropagation();"></video><audio id='a1' onerror="event.stopPropagation();"></audio>
<pre id="test">
<script class="testbody" type="text/javascript">
var v1 = document.getElementById('v1');

Просмотреть файл

@ -8,7 +8,7 @@
</head>
<body>
<pre id="test">
<video id="v"></video>
<video id="v" onerror="event.stopPropagation();"></video>
<script>
SimpleTest.waitForExplicitFinish();

Просмотреть файл

@ -7,7 +7,7 @@
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<video id='v1'></video><audio id='a1'></audio>
<video id='v1' onerror="event.stopPropagation();"></video><audio id='a1' onerror="event.stopPropagation();"></audio>
<pre id="test">
<script class="testbody" type="text/javascript">
var v1 = document.getElementById('v1');

Просмотреть файл

@ -7,7 +7,7 @@
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<video id='v'></video>
<video id='v' onerror="event.stopPropagation();"></video>
<pre id="test">
<script class="testbody" type="text/javascript">
// http://www.whatwg.org/specs/web-apps/current-work/#dom-media-seek

Просмотреть файл

@ -8,7 +8,7 @@
</head>
<body>
<video id='v1'></video><audio id='a1'></audio>
<video id='v1' onerror="event.stopPropagation();"></video><audio id='a1' onerror="event.stopPropagation();"></audio>
<pre id="test">
<script class="testbody" type="text/javascript">

Просмотреть файл

@ -37,7 +37,7 @@
* ***** END LICENSE BLOCK ***** */
#include "domstubs.idl"
[scriptable, uuid(9BEA222F-13CF-42AD-A14E-E01F4F66B833)]
[scriptable, uuid(7bd8c29f-8a76-453f-9373-79f820f2dc01)]
interface nsIDOMHTMLMediaError : nsISupports
{
/* The download of the media resource was aborted by
@ -52,5 +52,8 @@ interface nsIDOMHTMLMediaError : nsISupports
the media resource */
const unsigned short MEDIA_ERR_DECODE = 3;
/* No suitable media resource could be found */
const unsigned short MEDIA_ERR_NONE_SUPPORTED = 4;
readonly attribute unsigned short code;
};