b=465458 Media selection algorithm changed in r2403 of HTML5 spec r=doublec sr=roc

--HG--
extra : rebase_source : 09a091ebb5c7a42480a423ac304aacedfa286c4c
This commit is contained in:
Chris Pearce 2009-02-20 15:49:00 +13:00
Родитель 36a2d9ec15
Коммит f95265dfc7
11 изменённых файлов: 554 добавлений и 159 удалений

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

@ -48,14 +48,30 @@ typedef PRUint16 nsMediaNetworkState;
typedef PRUint16 nsMediaReadyState;
// Object representing a single execution of the media load algorithm.
// Used by implicit load events so that they can be cancelled when Load()
// is executed.
// Note: When bug 465458 lands, all events are expected to do this, not
// just implicit load events.
// Used by asynchronous events so that they can be cancelled when Load()
// is executed. Holds the list of candidate resources which the media
// element attempts to load, and provides iteration over that list.
class nsMediaLoad : public nsISupports
{
public:
NS_DECL_ISUPPORTS
nsMediaLoad() : mPosition(0) {}
~nsMediaLoad() {}
// Appends a candidate resource to the candidate list. List is populated
// by nsHTMLMediaElement::GenerateCandidates().
void AddCandidate(nsIURI *aURI);
// Returns the next candidate in the list, or null if at the end.
already_AddRefed<nsIURI> GetNextCandidate();
PRBool HasMoreCandidates() { return mPosition < mCandidates.Count(); }
private:
// Candidate resource URIs.
nsCOMArray<nsIURI> mCandidates;
// Index/iterator position in mCandidates.
PRInt32 mPosition;
};
class nsHTMLMediaElement : public nsGenericHTMLElement
@ -213,22 +229,17 @@ public:
virtual PRBool IsNodeOfType(PRUint32 aFlags) const;
/**
* Queues an event to call Load().
*/
void QueueLoadTask();
/**
* Returns the current nsMediaLoad object. Implicit load events store a
* Returns the current nsMediaLoad object. Asynchronous events store a
* reference to the nsMediaLoad object that was current when they were
* enqueued, and if it has changed when they come to fire, they consider
* themselves cancelled, and don't fire.
* Note: When bug 465458 lands, all events are expected to do this, not
* just implicit load events.
*/
nsMediaLoad* GetCurrentMediaLoad() { return mCurrentLoad; }
protected:
class nsMediaLoadListener;
class MediaLoadListener;
class LoadNextCandidateEvent;
/**
* Figure out which resource to load (either the 'src' attribute or a
@ -248,10 +259,12 @@ protected:
nsIStreamListener **aListener);
/**
* Execute the initial steps of the load algorithm that ensure existing
* loads are aborted and the element is emptied. Returns true if aborted,
* false if emptied.
* loads are aborted, the element is emptied, and a new load object is
* created. Returns true if the current load aborts due to a new load being
* started in event handlers triggered during this function call.
*/
PRBool AbortExistingLoads();
/**
* Create a URI for the given aURISpec string.
*/
@ -263,6 +276,25 @@ protected:
*/
void NoSupportedMediaError();
// Performs "the candidate loop" step in the load algorithm. Attempts to
// load candidates in the candidate media resource list until a channel opens
// to a resource, then it returns. If the resource download fails, it will
// set a callback to this function. Do not call this directly, call
// QueueLoadNextCandidateTask() instead.
void LoadNextCandidate();
// Populates the candidate resource list in mCurrentLoad.
void GenerateCandidates();
// Enqueues an event to call Load() on the main thread. This will begin
// the process of attempting to load candidate media resources from the
// candidate resource list.
void QueueLoadTask();
// Enqueues an event to resume trying to open resources in the candidate
// loop.
void QueueLoadNextCandidateTask();
nsRefPtr<nsMediaDecoder> mDecoder;
nsCOMPtr<nsIChannel> mChannel;
@ -270,7 +302,8 @@ protected:
// Error attribute
nsCOMPtr<nsIDOMHTMLMediaError> mError;
// The current media load object.
// The current media load object. Stores the list of candidate media
// resources that we are attempting to load.
nsRefPtr<nsMediaLoad> mCurrentLoad;
// Media loading flags. See:

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

@ -83,54 +83,92 @@
#include "nsWaveDecoder.h"
#endif
class nsAsyncEventRunner : public nsRunnable
class nsMediaEvent : public nsRunnable
{
public:
nsMediaEvent(nsHTMLMediaElement* aElement) :
mElement(aElement),
mCurrentLoad(mElement->GetCurrentMediaLoad()) {}
~nsMediaEvent() {}
NS_IMETHOD Run() = 0;
protected:
PRBool IsCancelled() {
return mElement->GetCurrentMediaLoad() != mCurrentLoad;
}
nsCOMPtr<nsHTMLMediaElement> mElement;
nsRefPtr<nsMediaLoad> mCurrentLoad;
};
class nsAsyncEventRunner : public nsMediaEvent
{
private:
nsString mName;
nsCOMPtr<nsHTMLMediaElement> mElement;
PRPackedBool mProgress;
public:
nsAsyncEventRunner(const nsAString& aName, nsHTMLMediaElement* aElement, PRBool aProgress) :
mName(aName), mElement(aElement), mProgress(aProgress)
nsMediaEvent(aElement), mName(aName), mProgress(aProgress)
{
}
NS_IMETHOD Run() {
// Silently cancel if our load has been cancelled.
if (IsCancelled())
return NS_OK;
return mProgress ?
mElement->DispatchProgressEvent(mName) :
mElement->DispatchSimpleEvent(mName);
}
};
// Asynchronous runner which invokes Load() on the main thread.
class nsMediaLoadEvent : public nsRunnable
{
class nsMediaLoadEvent : public nsMediaEvent {
public:
nsMediaLoadEvent(nsHTMLMediaElement *aMedia)
: mMedia(aMedia), mCurrentLoad(mMedia->GetCurrentMediaLoad()) {}
~nsMediaLoadEvent() {}
nsMediaLoadEvent(nsHTMLMediaElement *aElement)
: nsMediaEvent(aElement) {}
NS_IMETHOD Run() {
// Only run the task if it's not been cancelled.
if (mMedia && mMedia->GetCurrentMediaLoad() == mCurrentLoad)
mMedia->Load();
if (!IsCancelled())
mElement->Load();
return NS_OK;
}
private:
nsCOMPtr<nsHTMLMediaElement> mMedia;
nsRefPtr<nsMediaLoad> mCurrentLoad;
};
class nsHTMLMediaElement::nsMediaLoadListener : public nsIStreamListener
class nsHTMLMediaElement::LoadNextCandidateEvent : public nsMediaEvent {
public:
LoadNextCandidateEvent(nsHTMLMediaElement *aElement)
: nsMediaEvent(aElement) {}
NS_IMETHOD Run() {
if (!IsCancelled())
mElement->LoadNextCandidate();
return NS_OK;
}
};
void nsHTMLMediaElement::QueueLoadTask()
{
nsCOMPtr<nsIRunnable> event = new nsMediaLoadEvent(this);
NS_DispatchToMainThread(event);
}
void nsHTMLMediaElement::QueueLoadNextCandidateTask()
{
nsCOMPtr<nsIRunnable> event = new LoadNextCandidateEvent(this);
NS_DispatchToMainThread(event);
}
class nsHTMLMediaElement::MediaLoadListener : public nsIStreamListener
{
NS_DECL_ISUPPORTS
NS_DECL_NSIREQUESTOBSERVER
NS_DECL_NSISTREAMLISTENER
public:
nsMediaLoadListener(nsHTMLMediaElement* aElement)
MediaLoadListener(nsHTMLMediaElement* aElement)
: mElement(aElement)
{
NS_ABORT_IF_FALSE(mElement, "Must pass an element to call back");
@ -141,9 +179,9 @@ private:
nsCOMPtr<nsIStreamListener> mNextListener;
};
NS_IMPL_ISUPPORTS2(nsHTMLMediaElement::nsMediaLoadListener, nsIRequestObserver, nsIStreamListener)
NS_IMPL_ISUPPORTS2(nsHTMLMediaElement::MediaLoadListener, nsIRequestObserver, nsIStreamListener)
NS_IMETHODIMP nsHTMLMediaElement::nsMediaLoadListener::OnStartRequest(nsIRequest* aRequest, nsISupports* aContext)
NS_IMETHODIMP nsHTMLMediaElement::MediaLoadListener::OnStartRequest(nsIRequest* aRequest, nsISupports* aContext)
{
nsresult rv = NS_OK;
@ -157,7 +195,9 @@ NS_IMETHODIMP nsHTMLMediaElement::nsMediaLoadListener::OnStartRequest(nsIRequest
// If InitializeDecoderForChannel() returned an error, fire a network
// error.
if (NS_FAILED(rv) && !mNextListener && mElement) {
mElement->NetworkError();
// Load failed, attempt to load the next candidate resource. If there
// are none, this will trigger a MEDIA_ERR_NONE_SUPPORTED error.
mElement->QueueLoadNextCandidateTask();
}
// If InitializeDecoderForChannel did not return a listener (but may
// have otherwise succeeded), we abort the connection since we aren't
@ -172,7 +212,7 @@ NS_IMETHODIMP nsHTMLMediaElement::nsMediaLoadListener::OnStartRequest(nsIRequest
return rv;
}
NS_IMETHODIMP nsHTMLMediaElement::nsMediaLoadListener::OnStopRequest(nsIRequest* aRequest, nsISupports* aContext,
NS_IMETHODIMP nsHTMLMediaElement::MediaLoadListener::OnStopRequest(nsIRequest* aRequest, nsISupports* aContext,
nsresult aStatus)
{
if (mNextListener) {
@ -181,7 +221,7 @@ NS_IMETHODIMP nsHTMLMediaElement::nsMediaLoadListener::OnStopRequest(nsIRequest*
return NS_OK;
}
NS_IMETHODIMP nsHTMLMediaElement::nsMediaLoadListener::OnDataAvailable(nsIRequest* aRequest, nsISupports* aContext,
NS_IMETHODIMP nsHTMLMediaElement::MediaLoadListener::OnDataAvailable(nsIRequest* aRequest, nsISupports* aContext,
nsIInputStream* aStream, PRUint32 aOffset,
PRUint32 aCount)
{
@ -189,6 +229,22 @@ NS_IMETHODIMP nsHTMLMediaElement::nsMediaLoadListener::OnDataAvailable(nsIReques
return mNextListener->OnDataAvailable(aRequest, aContext, aStream, aOffset, aCount);
}
void
nsMediaLoad::AddCandidate(nsIURI *aURI)
{
mCandidates.AppendObject(aURI);
}
already_AddRefed<nsIURI>
nsMediaLoad::GetNextCandidate()
{
if (mPosition == mCandidates.Count())
return nsnull;
nsCOMPtr<nsIURI> uri = mCandidates.ObjectAt(mPosition);
mPosition++;
return uri.forget();
}
NS_IMPL_ISUPPORTS0(nsMediaLoad)
// nsIDOMHTMLMediaElement
@ -240,6 +296,11 @@ NS_IMETHODIMP nsHTMLMediaElement::GetNetworkState(PRUint16 *aNetworkState)
PRBool nsHTMLMediaElement::AbortExistingLoads()
{
// Set a new load object. This will cause events which were enqueued
// with a differnet load object to silently be cancelled.
mCurrentLoad = new nsMediaLoad();
nsRefPtr<nsMediaLoad> currentLoad = mCurrentLoad;
if (mDecoder) {
mDecoder->Shutdown();
mDecoder = nsnull;
@ -249,7 +310,10 @@ PRBool nsHTMLMediaElement::AbortExistingLoads()
mBegun = PR_FALSE;
mError = new nsHTMLMediaError(nsIDOMHTMLMediaError::MEDIA_ERR_ABORTED);
DispatchProgressEvent(NS_LITERAL_STRING("abort"));
return PR_TRUE;
if (mCurrentLoad != currentLoad) {
// A new load was triggered in handler, bail out of this load.
return PR_TRUE;
}
}
mError = nsnull;
@ -265,13 +329,18 @@ PRBool nsHTMLMediaElement::AbortExistingLoads()
// TODO: The current playback position must be set to 0.
// TODO: The currentLoop DOM attribute must be set to 0.
DispatchSimpleEvent(NS_LITERAL_STRING("emptied"));
if (mCurrentLoad != currentLoad) {
// A new load was triggered in handler, bail out of this load.
return PR_TRUE;
}
}
return PR_FALSE;
}
void nsHTMLMediaElement::NoSupportedMediaError()
{
NS_ASSERTION(mCurrentLoad && !mCurrentLoad->HasMoreCandidates(),
"Should have exhausted all candidates");
mError = new nsHTMLMediaError(nsIDOMHTMLMediaError::MEDIA_ERR_NONE_SUPPORTED);
mBegun = PR_FALSE;
DispatchAsyncProgressEvent(NS_LITERAL_STRING("error"));
@ -279,21 +348,9 @@ void nsHTMLMediaElement::NoSupportedMediaError()
DispatchAsyncSimpleEvent(NS_LITERAL_STRING("emptied"));
}
void nsHTMLMediaElement::QueueLoadTask()
{
nsCOMPtr<nsIRunnable> event = new nsMediaLoadEvent(this);
NS_DispatchToMainThread(event);
}
/* void load (); */
NS_IMETHODIMP nsHTMLMediaElement::Load()
{
// Set a new load object. This will cause implicit load events which were
// enqueued before with a different load object to silently be cancelled.
// Note: When bug 465458 lands, all events are expected to do this, not
// just implicit load events.
mCurrentLoad = new nsMediaLoad();
if (AbortExistingLoads())
return NS_OK;
@ -301,99 +358,101 @@ NS_IMETHODIMP nsHTMLMediaElement::Load()
mBegun = PR_TRUE;
DispatchAsyncProgressEvent(NS_LITERAL_STRING("loadstart"));
nsCOMPtr<nsIURI> uri;
nsresult rv = PickMediaElement(getter_AddRefs(uri));
if (NS_FAILED(rv)) {
NoSupportedMediaError();
return NS_OK;
}
GenerateCandidates();
if (mChannel) {
mChannel->Cancel(NS_BINDING_ABORTED);
mChannel = nsnull;
}
PRInt16 shouldLoad = nsIContentPolicy::ACCEPT;
rv = NS_CheckContentLoadPolicy(nsIContentPolicy::TYPE_MEDIA,
uri,
NodePrincipal(),
this,
EmptyCString(), // mime type
nsnull, // extra
&shouldLoad,
nsContentUtils::GetContentPolicy(),
nsContentUtils::GetSecurityManager());
if (NS_FAILED(rv) || NS_CP_REJECTED(shouldLoad)) {
NoSupportedMediaError();
return NS_OK;
}
rv = NS_NewChannel(getter_AddRefs(mChannel),
uri,
nsnull,
nsnull,
nsnull,
nsIRequest::LOAD_NORMAL);
if (NS_FAILED(rv)) {
NetworkError();
return NS_OK;
}
// 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.
nsCOMPtr<nsIStreamListener> loadListener = new nsMediaLoadListener(this);
NS_ENSURE_TRUE(loadListener, NS_ERROR_OUT_OF_MEMORY);
nsCOMPtr<nsIStreamListener> listener;
if (ShouldCheckAllowOrigin()) {
listener = new nsCrossSiteListenerProxy(loadListener,
NodePrincipal(),
mChannel,
PR_FALSE,
&rv);
NS_ENSURE_TRUE(listener, NS_ERROR_OUT_OF_MEMORY);
if (NS_FAILED(rv)) {
NoSupportedMediaError();
return NS_OK;
}
} else {
rv = nsContentUtils::GetSecurityManager()->
CheckLoadURIWithPrincipal(NodePrincipal(),
uri,
nsIScriptSecurityManager::STANDARD);
if (NS_FAILED(rv)) {
NoSupportedMediaError();
return NS_OK;
}
listener = loadListener;
}
nsCOMPtr<nsIHttpChannel> hc = do_QueryInterface(mChannel);
if (hc) {
// Use a byte range request from the start of the resource.
// This enables us to detect if the stream supports byte range
// requests, and therefore seeking, early.
hc->SetRequestHeader(NS_LITERAL_CSTRING("Range"),
NS_LITERAL_CSTRING("bytes=0-"),
PR_FALSE);
}
rv = mChannel->AsyncOpen(listener, nsnull);
if (NS_FAILED(rv)) {
// OnStartRequest is guaranteed to be called if the open succeeds. If
// the open failed, the listener's OnStartRequest will never be called,
// so we need to break the element->channel->listener->element reference
// cycle here. The channel holds the only reference to the listener,
// and is useless now anyway, so drop our reference to it to allow it to
// be destroyed.
mChannel = nsnull;
NetworkError();
return NS_OK;
}
QueueLoadNextCandidateTask();
return NS_OK;
}
void nsHTMLMediaElement::LoadNextCandidate()
{
NS_ASSERTION(mCurrentLoad, "Need a load object");
while (mCurrentLoad->HasMoreCandidates()) {
nsresult rv;
nsCOMPtr<nsIURI> uri = mCurrentLoad->GetNextCandidate();
if (mChannel) {
mChannel->Cancel(NS_BINDING_ABORTED);
mChannel = nsnull;
}
PRInt16 shouldLoad = nsIContentPolicy::ACCEPT;
rv = NS_CheckContentLoadPolicy(nsIContentPolicy::TYPE_MEDIA,
uri,
NodePrincipal(),
this,
EmptyCString(), // mime type
nsnull, // extra
&shouldLoad,
nsContentUtils::GetContentPolicy(),
nsContentUtils::GetSecurityManager());
if (NS_FAILED(rv) || NS_CP_REJECTED(shouldLoad)) continue;
rv = NS_NewChannel(getter_AddRefs(mChannel),
uri,
nsnull,
nsnull,
nsnull,
nsIRequest::LOAD_NORMAL);
if (NS_FAILED(rv)) continue;
// 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.
nsCOMPtr<nsIStreamListener> loadListener = new MediaLoadListener(this);
if (!loadListener) continue;
nsCOMPtr<nsIStreamListener> listener;
if (ShouldCheckAllowOrigin()) {
listener = new nsCrossSiteListenerProxy(loadListener,
NodePrincipal(),
mChannel,
PR_FALSE,
&rv);
if (!listener || NS_FAILED(rv)) continue;
} else {
rv = nsContentUtils::GetSecurityManager()->
CheckLoadURIWithPrincipal(NodePrincipal(),
uri,
nsIScriptSecurityManager::STANDARD);
if (NS_FAILED(rv)) continue;
listener = loadListener;
}
nsCOMPtr<nsIHttpChannel> hc = do_QueryInterface(mChannel);
if (hc) {
// Use a byte range request from the start of the resource.
// This enables us to detect if the stream supports byte range
// requests, and therefore seeking, early.
hc->SetRequestHeader(NS_LITERAL_CSTRING("Range"),
NS_LITERAL_CSTRING("bytes=0-"),
PR_FALSE);
}
rv = mChannel->AsyncOpen(listener, nsnull);
if (NS_FAILED(rv)) {
// OnStartRequest is guaranteed to be called if the open succeeds. If
// the open failed, the listener's OnStartRequest will never be called,
// so we need to break the element->channel->listener->element reference
// cycle here. The channel holds the only reference to the listener,
// and is useless now anyway, so drop our reference to it to allow it to
// be destroyed.
mChannel = nsnull;
continue;
}
// Else the channel must be open and starting to download. If it encounters
// a non-catestrophic failure, it will set a new task to continue loading
// the candidates.
return;
}
// Must have exhausted all candidates.
NoSupportedMediaError();
}
nsresult nsHTMLMediaElement::LoadWithChannel(nsIChannel *aChannel,
nsIStreamListener **aListener)
{
@ -981,6 +1040,57 @@ nsresult nsHTMLMediaElement::NewURIFromString(const nsAutoString& aURISpec, nsIU
return NS_OK;
}
// Implements 'generate the list of potential media resources' as per:
// http://www.whatwg.org/specs/web-apps/current-work/multipage/video.html#generate-the-list-of-potential-media-resources
void nsHTMLMediaElement::GenerateCandidates()
{
NS_ASSERTION(mCurrentLoad, "Need a nsMediaLoad Object");
nsCOMPtr<nsIURI> uri;
nsresult res;
nsAutoString src;
// If we have a src attribute on the media element, just use that only.
if (GetAttr(kNameSpaceID_None, nsGkAtoms::src, src)) {
res = NewURIFromString(src, getter_AddRefs(uri));
if (NS_SUCCEEDED(res)) {
mCurrentLoad->AddCandidate(uri);
}
return;
}
PRUint32 count = GetChildCount();
for (PRUint32 i = 0; i < count; ++i) {
nsIContent* child = GetChildAt(i);
NS_ASSERTION(child, "GetChildCount lied!");
nsCOMPtr<nsIContent> source = do_QueryInterface(child);
// Only check source element children.
if (!source ||
source->Tag() != nsGkAtoms::source ||
!source->IsNodeOfType(nsINode::eHTML))
continue;
nsAutoString type;
// Must have src attribute.
if (!source->GetAttr(kNameSpaceID_None, nsGkAtoms::src, src))
continue;
// If we have a type attribyte, it must be valid.
if (source->GetAttr(kNameSpaceID_None, nsGkAtoms::type, type) &&
GetCanPlay(type) == CANPLAY_NO)
continue;
res = NewURIFromString(src, getter_AddRefs(uri));
if (NS_SUCCEEDED(res)) {
mCurrentLoad->AddCandidate(uri);
}
}
}
nsresult nsHTMLMediaElement::PickMediaElement(nsIURI** aURI)
{
NS_ENSURE_ARG_POINTER(aURI);

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

@ -328,7 +328,7 @@ protected:
void HandleAudioData(FrameData* aFrame, OggPlayAudioData* aAudioData, int aSize);
// These methods can only be called on the decoding thread.
void LoadOggHeaders();
void LoadOggHeaders(nsChannelReader* aReader);
// Initializes and opens the audio stream. Called from the decode
// thread only. Must be called with the decode monitor held.
@ -909,7 +909,7 @@ nsresult nsOggDecodeStateMachine::Run()
case DECODER_STATE_DECODING_METADATA:
mon.Exit();
LoadOggHeaders();
LoadOggHeaders(reader);
mon.Enter();
if (mState == DECODER_STATE_DECODING_METADATA) {
@ -1170,10 +1170,10 @@ nsresult nsOggDecodeStateMachine::Run()
return NS_OK;
}
void nsOggDecodeStateMachine::LoadOggHeaders()
void nsOggDecodeStateMachine::LoadOggHeaders(nsChannelReader* aReader)
{
LOG(PR_LOG_DEBUG, ("Loading Ogg Headers"));
mPlayer = oggplay_open_with_reader(mDecoder->GetReader());
mPlayer = oggplay_open_with_reader(aReader);
if (mPlayer) {
LOG(PR_LOG_DEBUG, ("There are %d tracks", oggplay_get_num_tracks(mPlayer)));

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

@ -62,6 +62,8 @@ ifdef MOZ_OGG
_TEST_FILES += \
test_access_control.html \
file_access_controls.html \
test_async_abort_events.html \
test_async_load_source_append.html \
test_bug448534.html \
test_bug461281.html \
test_bug468190.html \
@ -77,6 +79,7 @@ _TEST_FILES += \
test_ended2.html \
test_error_on_404.html \
test_onloadedmetadata.html \
test_load_candidates.html \
test_load_coalescing.html \
test_play.html \
test_progress1.html \

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

@ -117,7 +117,6 @@ function nextTest() {
//dump("gTestNum == gTests.length\n");
if (!gTestedRemoved) {
// Repeat all tests with element removed from doc, should get same result.
gVideo.parentNode.removeChild(gVideo);
gTestedRemoved = true;
gTestNum = 0;
} else {
@ -130,6 +129,9 @@ function nextTest() {
gExpectedResult = gTests[gTestNum].result;
gTestDescription = gTests[gTestNum].description;
//dump("Starting test " + gTestNum + " at " + gTests[gTestNum].url + " expecting:" + gExpectedResult + "\n");
if (gVideo && gVideo.parentNode)
gVideo.parentNode.removeChild(gVideo);
gVideo = createVideo();
gVideo.src = gTests[gTestNum].url;

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

@ -0,0 +1,81 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=478299
-->
<head>
<title>Test for Bug 478299</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"/>
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=478299">Mozilla Bug 478299</a>
<p id="display"></p>
<div id="content" style="display: none">
</div>
<pre id="test">
<script type="application/javascript">
/** Test for Bug 478299 **/
// This tests that we bail out of a load if we do another load in an abort handler.
// See: https://bugzilla.mozilla.org/show_bug.cgi?id=465458#c4
var v = null;
var gAbortCount = 0;
var gErrorCount = 0;
var gEmptiedCount = 0;
var gLoadedDataCount = 0;
SimpleTest.waitForExplicitFinish();
function errorListener() {
gErrorCount++;
}
function emptiedListener() {
gEmptiedCount++;
}
function loadedDataListener() {
gLoadedDataCount++;
is(gLoadedDataCount, 1, "Should only get 1 load");
is(gAbortCount, 1, "Should only get 1 abort");
is(gEmptiedCount, 1, "Should only get 1 emptied");
is(gErrorCount, 0, "Shouldn't get any errors");
SimpleTest.finish();
}
function abortListener() {
gAbortCount++;
if (gAbortCount == 1) {
v.src = '320x240.ogv';
v.load();
}
}
v = document.createElement('video');
v.addEventListener('error', errorListener, false);
v.addEventListener('emptied', emptiedListener, false);
v.addEventListener('loadeddata', loadedDataListener, false);
v.addEventListener('abort', abortListener, false);
is(v.networkState, HTMLMediaElement.NETWORK_EMPTY, "NW State invalid.");
v.load();
ok(v.networkState > HTMLMediaElement.NETWORK_EMPTY, "NW State invalid.");
// Cause another load. This will abort the previous load, causing 'abort' event to fire.
// That will trigger the abort handler.
// Abort handler adds 'src' attribute, and starts a new load. This aborts the previous load,
// so it won't fire emptied for the first abort. The second abort then causes 'abort' and
// 'emptied' events. The load should then succeed, causing 'loadeddata' event.
v.load();
</script>
</pre>
</body>
</html>

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

@ -0,0 +1,89 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=478299
-->
<head>
<title>Test for Bug 478299</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"/>
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=478299">Mozilla Bug 478299</a>
<p id="display"></p>
<div id="content" style="display: none">
</div>
<pre id="test">
<script type="application/javascript">
/** Test for Bug 478299 **/
// This tests that we only uses sources added before a load is triggered in an async load.
// See: https://bugzilla.mozilla.org/show_bug.cgi?id=465458#c4
var gLoadStartsCount = 0;
var gLoadedCount = 0;
var gAbortCount = 0;
var gEmptiedCount = 0;
var gErrorCount = 0;
function errorHandler(e) {
gErrorCount++;
is(gLoadStartsCount, 1, "Should have received 1 loadstart.");
is(gLoadedCount, 0, "Should not have loaded");
is(gAbortCount, 0, "Shouldn't have aborted any loads");
is(gErrorCount, 1, "Shouldn't have received errors");
is(gEmptiedCount, 0, "Shouldn't have reEmptiedCount should be 1");
SimpleTest.finish();
}
function abortHandler() {
gAbortCount++;
}
function startHandler() {
gLoadStartsCount++;
}
function loadedHandler() {
gLoadedCount++;
}
function emptiedHandler() {
gEmptiedCount++;
}
function volumeChangeHandler() {
gVolumeChanged++;
}
var v = document.createElement('video');
v.addEventListener('error', errorHandler, false);
v.addEventListener('abort', abortHandler, false);
v.addEventListener('loadeddata', loadedHandler, false);
v.addEventListener('loadstart', startHandler, false);
v.addEventListener('emptied', emptiedHandler, false);
// Create a source to a 404. Expect error on load.
var s1 = document.createElement('source');
s1.type = "application/ogg";
s1.src = "404.ogv";
v.appendChild(s1);
v.load();
// Create a source to a valid resource.
var s2 = document.createElement('source');
s2.type = "application/ogg";
s2.src = "320x240.ogv";
// We expect that this source will not be considered during load.
v.appendChild(s2);
SimpleTest.waitForExplicitFinish();
</script>
</pre>
</body>
</html>

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

@ -13,19 +13,49 @@ 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' 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[
ok($("a1").networkState == HTMLMediaElement.NETWORK_EMPTY, "audio a1 with bogus child should not be loading");
ok($("a2").networkState >= HTMLMediaElement.NETWORK_LOADING, "audio a2 with valid child should be loading");
ok($("a3").networkState >= HTMLMediaElement.NETWORK_LOADING, "audio a3 with valid child should be loading");
ok($("a4").networkState == HTMLMediaElement.NETWORK_EMPTY, "audio a4 with bogus child should not be loading");
var gExpectedResult = {
'a1' : 'error',
'a2' : 'loaded',
'a3' : 'loaded',
'a4' : 'error',
};
var gResultCount = 0;
function onError(event, id) {
event.stopPropagation();
is('error', gExpectedResult[id], 'unexpected error loading ' + id);
gResultCount++;
dump('error('+id+') expected ' + gExpectedResult[id] + ' gResultCount=' + gResultCount + '\n');
if (gResultCount == 4)
SimpleTest.finish();
}
function onMetaData(id) {
is('loaded', gExpectedResult[id], 'unexpected loadedmetadata loading ' + id);
gResultCount++;
dump('onMetaData('+id+') expected ' + gExpectedResult[id] + ' gResultCount=' + gResultCount + '\n');
if (gResultCount == 4)
SimpleTest.finish();
}
SimpleTest.waitForExplicitFinish();
]]>
</script>
<audio id='a1' onerror="onError(event, 'a1');" onloadedmetadata="onMetaData('a1');"><sauce type="audio/x-wav" src="r11025_s16_c1.wav"/></audio>
<audio id='a2' onerror="onError(event, 'a2');" onloadedmetadata="onMetaData('a2');"><source type="audio/x-wav" src="r11025_s16_c1.wav"/></audio>
<audio id='a3' onerror="onError(event, 'a3');" onloadedmetadata="onMetaData('a3');"><html:source type="audio/x-wav" src="r11025_s16_c1.wav"/></audio>
<audio id='a4' onerror="onError(event, 'a4');" onloadedmetadata="onMetaData('a4');"><svg:source type="audio/x-wav" src="r11025_s16_c1.wav"/></audio>
<pre id="test">
</pre>
</body>
</html>

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

@ -0,0 +1,48 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=465458
-->
<head>
<title>Test for Bug 465458</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"/>
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=465458">Mozilla Bug 465458</a>
<p id="display"></p>
<div id="content" style="display: none">
</div>
<pre id="test">
<script type="application/javascript">
/** Test for Bug 465458 **/
var gError = false;
function finish() {
is(gError, false, "Shouldn't have thrown an error");
SimpleTest.finish();
}
function errorHandler(e) {
e.stopPropagation();
gError = true;
finish();
}
SimpleTest.waitForExplicitFinish();
</script>
</pre>
<video onerror="errorHandler(event)" id="v" onloadeddata="finish();">
<source src="404.ogv" type="application/ogg"></source>
<source src="320x240.ogv" type="application/ogg"></source>
</video>
</body>
</html>

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

@ -14,9 +14,6 @@ var v1 = document.getElementById('v1');
var a1 = document.getElementById('a1');
var passed = true;
is(v1.networkState, 0);
is(a1.networkState, 0);
try {
v1.networkState = 0;
a1.networkState = 0;

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

@ -49,7 +49,9 @@ v1.addEventListener('loadeddata', function() { loaded(v1); }, false);
a1.addEventListener('loadeddata', function() { loaded(a1); }, false);
v1.appendChild(newSource());
v1.load();
a1.appendChild(newSource());
a1.load();
SimpleTest.waitForExplicitFinish();
</script>