Bug 485288 - Update media load algorithm. r=roc a=blocking2.0

This commit is contained in:
Chris Pearce 2010-09-03 12:03:03 +12:00
Родитель 08258ac624
Коммит 3313bd2660
17 изменённых файлов: 373 добавлений и 226 удалений

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

@ -117,9 +117,6 @@ public:
virtual void UnbindFromTree(PRBool aDeep = PR_TRUE,
PRBool aNullParent = PR_TRUE);
virtual PRBool IsDoneAddingChildren();
virtual nsresult DoneAddingChildren(PRBool aHaveNotified);
/**
* Call this to reevaluate whether we should start/stop due to our owner
* document being active or inactive.
@ -149,6 +146,10 @@ public:
// resource has a decode error during metadata loading or decoding.
void DecodeError();
// Called by the video decoder object, on the main thread, when the
// resource load has been cancelled.
void LoadAborted();
// Called by the video decoder object, on the main thread,
// when the video playback has ended.
void PlaybackEnded();
@ -280,7 +281,7 @@ public:
/**
* Called when a child source element is added to this media element. This
* may queue a load() task if appropriate.
* may queue a task to run the select resource algorithm if appropriate.
*/
void NotifyAddedSource();
@ -333,8 +334,6 @@ public:
protected:
class MediaLoadListener;
class LoadNextSourceEvent;
class SelectResourceEvent;
/**
* Changes mHasPlayedOrSeeked to aValue. If mHasPlayedOrSeeked changes
@ -388,23 +387,25 @@ protected:
/**
* Attempts to load resources from the <source> children. This is a
* substep of the media selection algorith. Do not call this directly,
* substep of the resource selection algorithm. Do not call this directly,
* call QueueLoadFromSourceTask() instead.
*/
void LoadFromSourceChildren();
/**
* Sends an async event to call LoadFromSourceChildren().
* Asynchronously awaits a stable state, and then causes
* LoadFromSourceChildren() to be called on the main threads' event loop.
*/
void QueueLoadFromSourceTask();
/**
* Media selection algorithm.
* Runs the media resource selection algorithm.
*/
void SelectResource();
/**
* Sends an async event to call SelectResource().
* Asynchronously awaits a stable state, and then causes SelectResource()
* to be run on the main thread's event loop.
*/
void QueueSelectResourceTask();
@ -415,9 +416,10 @@ protected:
/**
* Selects the next <source> child from which to load a resource. Called
* during the media selection algorithm.
* during the resource selection algorithm. Stores the return value in
* mSourceLoadCandidate before returning.
*/
already_AddRefed<nsIURI> GetNextSource();
nsIContent* GetNextSource();
/**
* Changes mDelayingLoadEvent, and will call BlockOnLoad()/UnblockOnLoad()
@ -493,6 +495,17 @@ protected:
*/
void UpdatePreloadAction();
/**
* Dispatches an error event to a child source element.
*/
void DispatchAsyncSourceError(nsIContent* aSourceElement);
/**
* Resets the media element for an error condition as per aErrorCode.
* aErrorCode must be one of nsIDOMHTMLMediaError codes.
*/
void Error(PRUint16 aErrorCode);
nsRefPtr<nsMediaDecoder> mDecoder;
// A reference to the ImageContainer which contains the current frame
@ -527,18 +540,19 @@ protected:
nsMediaReadyState mReadyState;
enum LoadAlgorithmState {
// Not waiting for any src/<source>.
// No load algorithm instance is waiting for a source to be added to the
// media in order to continue loading.
NOT_WAITING,
// No src or <source> children, load is waiting at load algorithm step 1.
WAITING_FOR_SRC_OR_SOURCE,
// No src at load time, and all <source> children don't resolve or
// give network errors during fetch, waiting for more <source> children
// to be added.
// We've run the load algorithm, and we tried all source children of the
// media element, and failed to load any successfully. We're waiting for
// another source element to be added to the media element, and will try
// to load any such element when its added.
WAITING_FOR_SOURCE
};
// When the load algorithm is waiting for more src/<source>, this denotes
// what type of waiting we're doing.
// Denotes the waiting state of a load algorithm instance. When the load
// algorithm is waiting for a source element child to be added, this is set
// to WAITING_FOR_SOURCE, otherwise it's NOT_WAITING.
LoadAlgorithmState mLoadWaitStatus;
// Current audio volume
@ -550,11 +564,12 @@ protected:
// Current audio sample rate.
PRUint32 mRate;
// If we're loading a preload:none media, we'll record the URI we're
// attempting to load in mPreloadURI, and delay loading the resource until
// the user initiates a load by either playing the resource, or explicitly
// loading it.
nsCOMPtr<nsIURI> mPreloadURI;
// URI of the resource we're attempting to load. When the decoder is
// successfully initialized, we rely on it to record the URI we're playing,
// and clear mLoadingSrc. This stores the value we return in the currentSrc
// attribute until the decoder is initialized. Use GetCurrentSrc() to access
// the currentSrc attribute.
nsCOMPtr<nsIURI> mLoadingSrc;
// Stores the current preload action for this element. Initially set to
// PRELOAD_UNDEFINED, its value is changed by calling
@ -562,11 +577,15 @@ protected:
PreloadAction mPreloadAction;
// Size of the media. Updated by the decoder on the main thread if
// it changes. Defaults to a width and height of -1 if not set.
// it changes. Defaults to a width and height of -1 inot set.
nsIntSize mMediaSize;
nsRefPtr<gfxASurface> mPrintSurface;
// Reference to the source element last returned by GetNextSource().
// This is the child source element which we're trying to load from.
nsCOMPtr<nsIContent> mSourceLoadCandidate;
// An audio stream for writing audio directly from JS.
nsAutoPtr<nsAudioStream> mAudioStream;
@ -604,10 +623,6 @@ protected:
// True if the sound is muted
PRPackedBool mMuted;
// Flag to indicate if the child elements (eg. <source/>) have been
// parsed.
PRPackedBool mIsDoneAddingChildren;
// If TRUE then the media element was actively playing before the currently
// in progress seeking. If FALSE then the media element is either not seeking
// or was not actively playing before the current seek. Used to decide whether

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

@ -57,6 +57,7 @@
#include "nsXPCOMStrings.h"
#include "prlock.h"
#include "nsThreadUtils.h"
#include "nsIThreadInternal.h"
#include "nsContentUtils.h"
#include "nsFrameManager.h"
@ -88,6 +89,8 @@
#include <limits>
#include "nsIDocShellTreeItem.h"
#include "nsIAsyncVerifyRedirectCallback.h"
#include "nsIAppShell.h"
#include "nsWidgetsCID.h"
#include "nsIPrivateDOMEvent.h"
#include "nsIDOMNotifyAudioAvailableEvent.h"
@ -209,50 +212,31 @@ public:
}
};
class nsHTMLMediaElement::LoadNextSourceEvent : public nsMediaEvent {
class nsSourceErrorEventRunner : public nsMediaEvent
{
private:
nsCOMPtr<nsIContent> mSource;
public:
LoadNextSourceEvent(nsHTMLMediaElement *aElement)
: nsMediaEvent(aElement) {}
nsSourceErrorEventRunner(nsHTMLMediaElement* aElement,
nsIContent* aSource)
: nsMediaEvent(aElement),
mSource(aSource)
{
}
NS_IMETHOD Run() {
if (!IsCancelled())
mElement->LoadFromSourceChildren();
return NS_OK;
// Silently cancel if our load has been cancelled.
if (IsCancelled())
return NS_OK;
LOG_EVENT(PR_LOG_DEBUG, ("%p Dispatching simple event source error", mElement.get()));
return nsContentUtils::DispatchTrustedEvent(mElement->GetOwnerDoc(),
mSource,
NS_LITERAL_STRING("error"),
PR_TRUE,
PR_TRUE);
}
};
class nsHTMLMediaElement::SelectResourceEvent : public nsMediaEvent {
public:
SelectResourceEvent(nsHTMLMediaElement *aElement)
: nsMediaEvent(aElement) {}
NS_IMETHOD Run() {
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);
}
/**
* There is a reference cycle involving this class: MediaLoadListener
* holds a reference to the nsHTMLMediaElement, which holds a reference
@ -441,6 +425,8 @@ NS_IMETHODIMP nsHTMLMediaElement::GetCurrentSrc(nsAString & aCurrentSrc)
if (stream) {
stream->URI()->GetSpec(src);
}
} else if (mLoadingSrc) {
mLoadingSrc->GetSpec(src);
}
aCurrentSrc = NS_ConvertUTF8toUTF16(src);
@ -504,7 +490,6 @@ void nsHTMLMediaElement::AbortExistingLoads()
if (mNetworkState == nsIDOMHTMLMediaElement::NETWORK_LOADING ||
mNetworkState == nsIDOMHTMLMediaElement::NETWORK_IDLE)
{
mError = new nsMediaError(nsIDOMMediaError::MEDIA_ERR_ABORTED);
DispatchProgressEvent(NS_LITERAL_STRING("abort"));
}
@ -551,6 +536,62 @@ void nsHTMLMediaElement::NoSupportedMediaSourceError()
ChangeDelayLoadStatus(PR_FALSE);
}
typedef void (nsHTMLMediaElement::*SyncSectionFn)();
// Runs a "synchronous section", a function that must run once the event loop
// has reached a "stable state". See:
// http://www.whatwg.org/specs/web-apps/current-work/multipage/webappapis.html#synchronous-section
class nsSyncSection : public nsMediaEvent
{
private:
SyncSectionFn mClosure;
public:
nsSyncSection(nsHTMLMediaElement* aElement,
SyncSectionFn aClosure) :
nsMediaEvent(aElement),
mClosure(aClosure)
{
}
NS_IMETHOD Run() {
// Silently cancel if our load has been cancelled.
if (IsCancelled())
return NS_OK;
(mElement.get()->*mClosure)();
return NS_OK;
}
};
static NS_DEFINE_CID(kAppShellCID, NS_APPSHELL_CID);
// Asynchronously awaits a stable state, whereupon aClosure runs on the main
// thread. This adds an event which run aClosure to the appshell's list of
// sections synchronous the next time control returns to the event loop.
void AsyncAwaitStableState(nsHTMLMediaElement* aElement,
SyncSectionFn aClosure)
{
nsCOMPtr<nsIRunnable> event = new nsSyncSection(aElement, aClosure);
nsCOMPtr<nsIAppShell> appShell = do_GetService(kAppShellCID);
appShell->RunInStableState(event);
}
void nsHTMLMediaElement::QueueLoadFromSourceTask()
{
ChangeDelayLoadStatus(PR_TRUE);
mNetworkState = nsIDOMHTMLMediaElement::NETWORK_LOADING;
AsyncAwaitStableState(this, &nsHTMLMediaElement::LoadFromSourceChildren);
}
void nsHTMLMediaElement::QueueSelectResourceTask()
{
// Don't allow multiple async select resource calls to be queued.
if (mIsRunningSelectResource)
return;
mIsRunningSelectResource = PR_TRUE;
mNetworkState = nsIDOMHTMLMediaElement::NETWORK_NO_SOURCE;
AsyncAwaitStableState(this, &nsHTMLMediaElement::SelectResource);
}
/* void load (); */
NS_IMETHODIMP nsHTMLMediaElement::Load()
{
@ -591,18 +632,20 @@ static PRBool HasPotentialResource(nsIContent *aElement)
void nsHTMLMediaElement::SelectResource()
{
NS_ASSERTION(mDelayingLoadEvent, "Load event not delayed during resource selection?");
NS_ASSERTION(!mDelayingLoadEvent,
"Load event should not be delayed at start of resource selection.");
if (!HasPotentialResource(this)) {
// While the media element has neither a src attribute nor any source
// element children, wait. (This steps might wait forever.)
mNetworkState = nsIDOMHTMLMediaElement::NETWORK_NO_SOURCE;
mLoadWaitStatus = WAITING_FOR_SRC_OR_SOURCE;
// The media element has neither a src attribute nor any source
// element children, abort the load.
mNetworkState = nsIDOMHTMLMediaElement::NETWORK_EMPTY;
// This clears mDelayingLoadEvent, so AddRemoveSelfReference will be called
ChangeDelayLoadStatus(PR_FALSE);
mIsRunningSelectResource = PR_FALSE;
return;
}
ChangeDelayLoadStatus(PR_TRUE);
mNetworkState = nsIDOMHTMLMediaElement::NETWORK_LOADING;
// Load event was delayed, and still is, so no need to call
// AddRemoveSelfReference, since it must still be held
@ -617,29 +660,37 @@ void nsHTMLMediaElement::SelectResource()
if (NS_SUCCEEDED(rv)) {
LOG(PR_LOG_DEBUG, ("%p Trying load from src=%s", this, NS_ConvertUTF16toUTF8(src).get()));
mIsLoadingFromSrcAttribute = PR_TRUE;
mLoadingSrc = uri;
if (mPreloadAction == nsHTMLMediaElement::PRELOAD_NONE) {
// preload:none media, suspend the load here before we make any
// network requests.
SuspendLoad(uri);
mIsRunningSelectResource = PR_FALSE;
return;
}
rv = LoadResource(uri);
if (NS_SUCCEEDED(rv))
if (NS_SUCCEEDED(rv)) {
mIsRunningSelectResource = PR_FALSE;
return;
}
}
NoSupportedMediaSourceError();
} else {
// Otherwise, the source elements will be used.
LoadFromSourceChildren();
}
mIsRunningSelectResource = PR_FALSE;
}
void nsHTMLMediaElement::NotifyLoadError()
{
if (mIsLoadingFromSrcAttribute) {
LOG(PR_LOG_DEBUG, ("NotifyLoadError(), no supported media error"));
NoSupportedMediaSourceError();
} else {
NS_ASSERTION(mSourceLoadCandidate, "Must know the source we were loading from!");
DispatchAsyncSourceError(mSourceLoadCandidate);
QueueLoadFromSourceTask();
}
}
@ -682,18 +733,47 @@ void nsHTMLMediaElement::LoadFromSourceChildren()
{
NS_ASSERTION(mDelayingLoadEvent,
"Should delay load event (if in document) during load");
NS_ASSERTION(!mIsLoadingFromSrcAttribute,
"Must remember we're loading from source children");
while (PR_TRUE) {
nsresult rv;
nsCOMPtr<nsIURI> uri = GetNextSource();
if (!uri) {
nsIContent* child = GetNextSource();
if (!child) {
// Exhausted candidates, wait for more candidates to be appended to
// the media element.
mLoadWaitStatus = WAITING_FOR_SOURCE;
NoSupportedMediaSourceError();
mNetworkState = nsIDOMHTMLMediaElement::NETWORK_NO_SOURCE;
ChangeDelayLoadStatus(PR_FALSE);
return;
}
mNetworkState = nsIDOMHTMLMediaElement::NETWORK_LOADING;
nsCOMPtr<nsIURI> uri;
nsAutoString src,type;
// Must have src attribute.
if (!child->GetAttr(kNameSpaceID_None, nsGkAtoms::src, src)) {
DispatchAsyncSourceError(child);
continue;
}
// If we have a type attribute, it must be a supported type.
if (child->GetAttr(kNameSpaceID_None, nsGkAtoms::type, type) &&
GetCanPlay(type) == CANPLAY_NO)
{
DispatchAsyncSourceError(child);
continue;
}
LOG(PR_LOG_DEBUG, ("%p Trying load from <source>=%s type=%s", this,
NS_ConvertUTF16toUTF8(src).get(), NS_ConvertUTF16toUTF8(type).get()));
NewURIFromString(src, getter_AddRefs(uri));
if (!uri) {
DispatchAsyncSourceError(child);
continue;
}
mLoadingSrc = uri;
NS_ASSERTION(mNetworkState == nsIDOMHTMLMediaElement::NETWORK_LOADING,
"Network state should be loading");
if (mPreloadAction == nsHTMLMediaElement::PRELOAD_NONE) {
// preload:none media, suspend the load here before we make any
@ -707,6 +787,7 @@ void nsHTMLMediaElement::LoadFromSourceChildren()
return;
// If we fail to load, loop back and try loading the next resource.
DispatchAsyncSourceError(child);
}
NS_NOTREACHED("Execution should not reach here!");
}
@ -714,7 +795,6 @@ void nsHTMLMediaElement::LoadFromSourceChildren()
void nsHTMLMediaElement::SuspendLoad(nsIURI* aURI)
{
mLoadIsSuspended = PR_TRUE;
mPreloadURI = aURI;
mNetworkState = nsIDOMHTMLMediaElement::NETWORK_IDLE;
DispatchAsyncProgressEvent(NS_LITERAL_STRING("suspend"));
ChangeDelayLoadStatus(PR_FALSE);
@ -723,9 +803,8 @@ void nsHTMLMediaElement::SuspendLoad(nsIURI* aURI)
void nsHTMLMediaElement::ResumeLoad(PreloadAction aAction)
{
NS_ASSERTION(mLoadIsSuspended, "Can only resume preload if halted for one");
nsCOMPtr<nsIURI> uri = mPreloadURI;
nsCOMPtr<nsIURI> uri = mLoadingSrc;
mLoadIsSuspended = PR_FALSE;
mPreloadURI = nsnull;
mPreloadAction = aAction;
ChangeDelayLoadStatus(PR_TRUE);
mNetworkState = nsIDOMHTMLMediaElement::NETWORK_LOADING;
@ -1063,6 +1142,7 @@ NS_IMETHODIMP nsHTMLMediaElement::GetPaused(PRBool *aPaused)
NS_IMETHODIMP nsHTMLMediaElement::Pause()
{
if (mNetworkState == nsIDOMHTMLMediaElement::NETWORK_EMPTY) {
LOG(PR_LOG_DEBUG, ("Loading due to Pause()"));
nsresult rv = Load();
NS_ENSURE_SUCCESS(rv, rv);
} else if (mDecoder) {
@ -1200,7 +1280,6 @@ nsHTMLMediaElement::nsHTMLMediaElement(already_AddRefed<nsINodeInfo> aNodeInfo,
mAutoplayEnabled(PR_TRUE),
mPaused(PR_TRUE),
mMuted(PR_FALSE),
mIsDoneAddingChildren(!aFromParser),
mPlayingBeforeSeek(PR_FALSE),
mPausedForInactiveDocument(PR_FALSE),
mWaitingFired(PR_FALSE),
@ -1369,16 +1448,11 @@ nsresult nsHTMLMediaElement::SetAttr(PRInt32 aNameSpaceID, nsIAtom* aName,
aNotify);
if (NS_FAILED(rv))
return rv;
if (aNameSpaceID == kNameSpaceID_None && aName == nsGkAtoms::src) {
Load();
}
if (aNotify && aNameSpaceID == kNameSpaceID_None) {
if (aName == nsGkAtoms::src) {
if (mLoadWaitStatus == WAITING_FOR_SRC_OR_SOURCE) {
// A previous load algorithm instance is waiting on a src
// addition, resume the load. It is waiting at "step 1 of the load
// algorithm".
mLoadWaitStatus = NOT_WAITING;
QueueSelectResourceTask();
}
} else if (aName == nsGkAtoms::autoplay) {
if (aName == nsGkAtoms::autoplay) {
StopSuspendingAfterFirstFrame();
if (mReadyState == nsIDOMHTMLMediaElement::HAVE_ENOUGH_DATA) {
NotifyAutoplayDataReady();
@ -1429,15 +1503,6 @@ nsresult nsHTMLMediaElement::BindToTree(nsIDocument* aDocument, nsIContent* aPar
aParent,
aBindingParent,
aCompileEventHandlers);
if (aDocument) {
if (NS_SUCCEEDED(rv) &&
mIsDoneAddingChildren &&
mNetworkState == nsIDOMHTMLMediaElement::NETWORK_EMPTY)
{
QueueSelectResourceTask();
}
}
mIsBindingToTree = PR_FALSE;
return rv;
@ -1813,6 +1878,9 @@ nsresult nsHTMLMediaElement::FinishDecoderSetup(nsMediaDecoder* aDecoder)
{
mDecoder = aDecoder;
// Decoder has assumed ownership responsibility for remembering the URI.
mLoadingSrc = nsnull;
// Force a same-origin check before allowing events for this media resource.
mMediaSecurityVerified = PR_FALSE;
@ -1902,29 +1970,43 @@ void nsHTMLMediaElement::ResourceLoaded()
mNetworkState = nsIDOMHTMLMediaElement::NETWORK_IDLE;
AddRemoveSelfReference();
ChangeReadyState(nsIDOMHTMLMediaElement::HAVE_ENOUGH_DATA);
// The download has stopped
// Ensure a progress event is dispatched at the end of download.
DispatchAsyncProgressEvent(NS_LITERAL_STRING("progress"));
// The download has stopped.
DispatchAsyncSimpleEvent(NS_LITERAL_STRING("suspend"));
}
void nsHTMLMediaElement::NetworkError()
{
mError = new nsMediaError(nsIDOMMediaError::MEDIA_ERR_NETWORK);
mBegun = PR_FALSE;
DispatchAsyncProgressEvent(NS_LITERAL_STRING("error"));
mNetworkState = nsIDOMHTMLMediaElement::NETWORK_EMPTY;
AddRemoveSelfReference();
DispatchAsyncSimpleEvent(NS_LITERAL_STRING("emptied"));
ChangeDelayLoadStatus(PR_FALSE);
Error(nsIDOMMediaError::MEDIA_ERR_NETWORK);
}
void nsHTMLMediaElement::DecodeError()
{
mError = new nsMediaError(nsIDOMMediaError::MEDIA_ERR_DECODE);
Error(nsIDOMMediaError::MEDIA_ERR_DECODE);
}
void nsHTMLMediaElement::LoadAborted()
{
Error(nsIDOMMediaError::MEDIA_ERR_ABORTED);
}
void nsHTMLMediaElement::Error(PRUint16 aErrorCode)
{
NS_ASSERTION(aErrorCode == nsIDOMMediaError::MEDIA_ERR_DECODE ||
aErrorCode == nsIDOMMediaError::MEDIA_ERR_NETWORK ||
aErrorCode == nsIDOMMediaError::MEDIA_ERR_ABORTED,
"Only use nsIDOMMediaError codes!");
mError = new nsMediaError(aErrorCode);
mBegun = PR_FALSE;
DispatchAsyncProgressEvent(NS_LITERAL_STRING("error"));
mNetworkState = nsIDOMHTMLMediaElement::NETWORK_EMPTY;
if (mReadyState == nsIDOMHTMLMediaElement::HAVE_NOTHING) {
mNetworkState = nsIDOMHTMLMediaElement::NETWORK_EMPTY;
DispatchAsyncSimpleEvent(NS_LITERAL_STRING("emptied"));
} else {
mNetworkState = nsIDOMHTMLMediaElement::NETWORK_IDLE;
}
AddRemoveSelfReference();
DispatchAsyncSimpleEvent(NS_LITERAL_STRING("emptied"));
ChangeDelayLoadStatus(PR_FALSE);
}
@ -2211,25 +2293,6 @@ nsresult nsHTMLMediaElement::DispatchProgressEvent(const nsAString& aName)
return target->DispatchEvent(event, &dummy);
}
nsresult nsHTMLMediaElement::DoneAddingChildren(PRBool aHaveNotified)
{
if (!mIsDoneAddingChildren) {
mIsDoneAddingChildren = PR_TRUE;
UpdatePreloadAction();
if (mNetworkState == nsIDOMHTMLMediaElement::NETWORK_EMPTY) {
QueueSelectResourceTask();
}
}
return NS_OK;
}
PRBool nsHTMLMediaElement::IsDoneAddingChildren()
{
return mIsDoneAddingChildren;
}
PRBool nsHTMLMediaElement::IsPotentiallyPlaying() const
{
// TODO:
@ -2349,21 +2412,41 @@ nsHTMLMediaElement::IsNodeOfType(PRUint32 aFlags) const
return !(aFlags & ~(eCONTENT | eMEDIA));
}
void nsHTMLMediaElement::DispatchAsyncSourceError(nsIContent* aSourceElement)
{
LOG_EVENT(PR_LOG_DEBUG, ("%p Queuing simple source error event", this));
nsCOMPtr<nsIRunnable> event = new nsSourceErrorEventRunner(this, aSourceElement);
NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL);
}
void nsHTMLMediaElement::NotifyAddedSource()
{
if (mLoadWaitStatus == WAITING_FOR_SRC_OR_SOURCE) {
// 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 (!HasAttr(kNameSpaceID_None, nsGkAtoms::src) &&
mNetworkState == nsIDOMHTMLMediaElement::NETWORK_EMPTY)
{
QueueSelectResourceTask();
} else if (mLoadWaitStatus == WAITING_FOR_SOURCE) {
}
// A load was paused in the resource selection algorithm, waiting for
// a new source child to be added, resume the resource selction algorithm.
if (mLoadWaitStatus == WAITING_FOR_SOURCE) {
QueueLoadFromSourceTask();
}
}
already_AddRefed<nsIURI> nsHTMLMediaElement::GetNextSource()
nsIContent* nsHTMLMediaElement::GetNextSource()
{
nsresult rv = NS_OK;
nsCOMPtr<nsIDOMNode> thisDomNode =
do_QueryInterface(static_cast<nsGenericElement*>(this));
mSourceLoadCandidate = nsnull;
if (!mSourcePointer) {
// First time this has been run, create a selection to cover children.
mSourcePointer = do_CreateInstance("@mozilla.org/content/range;1");
@ -2397,27 +2480,13 @@ already_AddRefed<nsIURI> nsHTMLMediaElement::GetNextSource()
nsIContent* child = GetChildAt(startOffset);
// If child is a <source> element, it may be the next candidate.
// If child is a <source> element, it is the next candidate.
if (child &&
child->Tag() == nsGkAtoms::source &&
child->IsHTML())
{
nsCOMPtr<nsIURI> uri;
nsAutoString src,type;
// Must have src attribute.
if (!child->GetAttr(kNameSpaceID_None, nsGkAtoms::src, src))
continue;
// If we have a type attribute, it must be a supported type.
if (child->GetAttr(kNameSpaceID_None, nsGkAtoms::type, type) &&
GetCanPlay(type) == CANPLAY_NO)
continue;
LOG(PR_LOG_DEBUG, ("%p Trying load from <source>=%s type=%s", this,
NS_ConvertUTF16toUTF8(src).get(), NS_ConvertUTF16toUTF8(type).get()));
NewURIFromString(src, getter_AddRefs(uri));
return uri.forget();
mSourceLoadCandidate = child;
return child;
}
}
NS_NOTREACHED("Execution should not reach here!");

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

@ -411,7 +411,6 @@ void nsBuiltinDecoder::ResourceLoaded()
// Ensure the final progress event gets fired
if (mElement) {
mElement->DispatchAsyncProgressEvent(NS_LITERAL_STRING("progress"));
mElement->ResourceLoaded();
}
}
@ -566,8 +565,11 @@ void nsBuiltinDecoder::NotifyDownloadEnded(nsresult aStatus)
{
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
if (aStatus == NS_BINDING_ABORTED)
if (aStatus == NS_BINDING_ABORTED) {
// Download has been cancelled by user.
mElement->LoadAborted();
return;
}
{
MonitorAutoEnter mon(mMonitor);

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

@ -17,7 +17,6 @@
is(HTMLElement.NETWORK_EMPTY, undefined);
is(HTMLElement.NETWORK_IDLE, undefined);
is(HTMLElement.NETWORK_LOADING, undefined);
is(HTMLElement.NETWORK_LOADED, undefined);
is(HTMLElement.NETWORK_NO_SOURCE, undefined);
is(HTMLElement.HAVE_NOTHING, undefined);
is(HTMLElement.HAVE_METADATA, undefined);
@ -31,8 +30,7 @@ is(HTMLElement.MEDIA_ERR_SRC_NOT_SUPPORTED, undefined);
is(HTMLMediaElement.NETWORK_EMPTY, 0);
is(HTMLMediaElement.NETWORK_IDLE, 1);
is(HTMLMediaElement.NETWORK_LOADING, 2);
todo_is(HTMLMediaElement.NETWORK_LOADED, undefined);
is(HTMLMediaElement.NETWORK_NO_SOURCE, 4);
is(HTMLMediaElement.NETWORK_NO_SOURCE, 3);
is(HTMLMediaElement.HAVE_NOTHING, 0);
is(HTMLMediaElement.HAVE_METADATA, 1);
is(HTMLMediaElement.HAVE_CURRENT_DATA, 2);
@ -58,7 +56,6 @@ is(HTMLVideoElement.MEDIA_ERR_SRC_NOT_SUPPORTED, undefined);
is(HTMLAudioElement.NETWORK_EMPTY, undefined);
is(HTMLAudioElement.NETWORK_IDLE, undefined);
is(HTMLAudioElement.NETWORK_LOADING, undefined);
is(HTMLAudioElement.NETWORK_LOADED, undefined);
is(HTMLAudioElement.NETWORK_NO_SOURCE, undefined);
is(HTMLAudioElement.HAVE_NOTHING, undefined);
is(HTMLAudioElement.HAVE_METADATA, undefined);
@ -72,7 +69,6 @@ is(HTMLAudioElement.MEDIA_ERR_SRC_NOT_SUPPORTED, undefined);
is(HTMLSourceElement.NETWORK_EMPTY, undefined);
is(HTMLSourceElement.NETWORK_IDLE, undefined);
is(HTMLSourceElement.NETWORK_LOADING, undefined);
is(HTMLSourceElement.NETWORK_LOADED, undefined);
is(HTMLSourceElement.NETWORK_NO_SOURCE, undefined);
is(HTMLSourceElement.HAVE_NOTHING, undefined);
is(HTMLSourceElement.HAVE_METADATA, undefined);
@ -86,7 +82,6 @@ is(HTMLSourceElement.MEDIA_ERR_SRC_NOT_SUPPORTED, undefined);
is(MediaError.NETWORK_EMPTY, undefined);
is(MediaError.NETWORK_IDLE, undefined);
is(MediaError.NETWORK_LOADING, undefined);
is(MediaError.NETWORK_LOADED, undefined);
is(MediaError.NETWORK_NO_SOURCE, undefined);
is(MediaError.HAVE_NOTHING, undefined);
is(MediaError.HAVE_METADATA, undefined);
@ -100,7 +95,6 @@ is(MediaError.MEDIA_ERR_SRC_NOT_SUPPORTED, 4);
is(document.body.NETWORK_EMPTY, undefined);
is(document.body.NETWORK_IDLE, undefined);
is(document.body.NETWORK_LOADING, undefined);
is(document.body.NETWORK_LOADED, undefined);
is(document.body.NETWORK_NO_SOURCE, undefined);
is(document.body.HAVE_NOTHING, undefined);
is(document.body.HAVE_METADATA, undefined);
@ -114,8 +108,7 @@ is(document.body.MEDIA_ERR_SRC_NOT_SUPPORTED, undefined);
is(document.getElementsByTagName("video")[0].NETWORK_EMPTY, 0);
is(document.getElementsByTagName("video")[0].NETWORK_IDLE, 1);
is(document.getElementsByTagName("video")[0].NETWORK_LOADING, 2);
is(document.getElementsByTagName("video")[0].NETWORK_LOADED, 3);
is(document.getElementsByTagName("video")[0].NETWORK_NO_SOURCE, 4);
is(document.getElementsByTagName("video")[0].NETWORK_NO_SOURCE, 3);
is(document.getElementsByTagName("video")[0].HAVE_NOTHING, 0);
is(document.getElementsByTagName("video")[0].HAVE_METADATA, 1);
is(document.getElementsByTagName("video")[0].HAVE_CURRENT_DATA, 2);
@ -128,8 +121,7 @@ is(document.getElementsByTagName("video")[0].MEDIA_ERR_SRC_NOT_SUPPORTED, undefi
is(document.getElementsByTagName("audio")[0].NETWORK_EMPTY, 0);
is(document.getElementsByTagName("audio")[0].NETWORK_IDLE, 1);
is(document.getElementsByTagName("audio")[0].NETWORK_LOADING, 2);
is(document.getElementsByTagName("audio")[0].NETWORK_LOADED, 3);
is(document.getElementsByTagName("audio")[0].NETWORK_NO_SOURCE, 4);
is(document.getElementsByTagName("audio")[0].NETWORK_NO_SOURCE, 3);
is(document.getElementsByTagName("audio")[0].HAVE_NOTHING, 0);
is(document.getElementsByTagName("audio")[0].HAVE_METADATA, 1);
is(document.getElementsByTagName("audio")[0].HAVE_CURRENT_DATA, 2);
@ -142,7 +134,6 @@ is(document.getElementsByTagName("audio")[0].MEDIA_ERR_SRC_NOT_SUPPORTED, undefi
is(document.getElementsByTagName("source")[0].NETWORK_EMPTY, undefined);
is(document.getElementsByTagName("source")[0].NETWORK_IDLE, undefined);
is(document.getElementsByTagName("source")[0].NETWORK_LOADING, undefined);
is(document.getElementsByTagName("source")[0].NETWORK_LOADED, undefined);
is(document.getElementsByTagName("source")[0].NETWORK_NO_SOURCE, undefined);
is(document.getElementsByTagName("source")[0].HAVE_NOTHING, undefined);
is(document.getElementsByTagName("source")[0].HAVE_METADATA, undefined);
@ -156,7 +147,6 @@ is(document.getElementsByTagName("source")[0].MEDIA_ERR_SRC_NOT_SUPPORTED, undef
is(HTMLElement.prototype.NETWORK_EMPTY, undefined);
is(HTMLElement.prototype.NETWORK_IDLE, undefined);
is(HTMLElement.prototype.NETWORK_LOADING, undefined);
is(HTMLElement.prototype.NETWORK_LOADED, undefined);
is(HTMLElement.prototype.NETWORK_NO_SOURCE, undefined);
is(HTMLElement.prototype.HAVE_NOTHING, undefined);
is(HTMLElement.prototype.HAVE_METADATA, undefined);
@ -170,8 +160,7 @@ is(HTMLElement.prototype.MEDIA_ERR_SRC_NOT_SUPPORTED, undefined);
todo_is(HTMLMediaElement.prototype.NETWORK_EMPTY, 0, "HTMLMediaElement.prototype.NETWORK_EMPTY");
todo_is(HTMLMediaElement.prototype.NETWORK_IDLE, 1, "HTMLMediaElement.prototype.NETWORK_IDLE");
todo_is(HTMLMediaElement.prototype.NETWORK_LOADING, 2, "HTMLMediaElement.prototype.NETWORK_LOADING");
todo_is(HTMLMediaElement.prototype.NETWORK_LOADED, 3, "HTMLMediaElement.prototype.NETWORK_LOADED");
todo_is(HTMLMediaElement.prototype.NETWORK_NO_SOURCE, 4, "HTMLMediaElement.prototype.NETWORK_NO_SOURCE");
todo_is(HTMLMediaElement.prototype.NETWORK_NO_SOURCE, 3, "HTMLMediaElement.prototype.NETWORK_NO_SOURCE");
todo_is(HTMLMediaElement.prototype.HAVE_NOTHING, 0, "HTMLMediaElement.prototype.HAVE_NOTHING");
todo_is(HTMLMediaElement.prototype.HAVE_METADATA, 1, "HTMLMediaElement.prototype.HAVE_METADATA");
todo_is(HTMLMediaElement.prototype.HAVE_CURRENT_DATA, 2, "HTMLMediaElement.prototype.HAVE_CURRENT_DATA");
@ -184,8 +173,7 @@ is(HTMLMediaElement.prototype.MEDIA_ERR_SRC_NOT_SUPPORTED, undefined, "HTMLMedia
is(HTMLVideoElement.prototype.NETWORK_EMPTY, 0);
is(HTMLVideoElement.prototype.NETWORK_IDLE, 1);
is(HTMLVideoElement.prototype.NETWORK_LOADING, 2);
is(HTMLVideoElement.prototype.NETWORK_LOADED, 3);
is(HTMLVideoElement.prototype.NETWORK_NO_SOURCE, 4);
is(HTMLVideoElement.prototype.NETWORK_NO_SOURCE, 3);
is(HTMLVideoElement.prototype.HAVE_NOTHING, 0);
is(HTMLVideoElement.prototype.HAVE_METADATA, 1);
is(HTMLVideoElement.prototype.HAVE_CURRENT_DATA, 2);
@ -198,8 +186,7 @@ is(HTMLVideoElement.prototype.MEDIA_ERR_SRC_NOT_SUPPORTED, undefined);
is(HTMLAudioElement.prototype.NETWORK_EMPTY, 0);
is(HTMLAudioElement.prototype.NETWORK_IDLE, 1);
is(HTMLAudioElement.prototype.NETWORK_LOADING, 2);
is(HTMLAudioElement.prototype.NETWORK_LOADED, 3);
is(HTMLAudioElement.prototype.NETWORK_NO_SOURCE, 4);
is(HTMLAudioElement.prototype.NETWORK_NO_SOURCE, 3);
is(HTMLAudioElement.prototype.HAVE_NOTHING, 0);
is(HTMLAudioElement.prototype.HAVE_METADATA, 1);
is(HTMLAudioElement.prototype.HAVE_CURRENT_DATA, 2);
@ -212,7 +199,6 @@ is(HTMLAudioElement.prototype.MEDIA_ERR_SRC_NOT_SUPPORTED, undefined);
is(HTMLSourceElement.prototype.NETWORK_EMPTY, undefined);
is(HTMLSourceElement.prototype.NETWORK_IDLE, undefined);
is(HTMLSourceElement.prototype.NETWORK_LOADING, undefined);
is(HTMLSourceElement.prototype.NETWORK_LOADED, undefined);
is(HTMLSourceElement.prototype.NETWORK_NO_SOURCE, undefined);
is(HTMLSourceElement.prototype.HAVE_NOTHING, undefined);
is(HTMLSourceElement.prototype.HAVE_METADATA, undefined);
@ -226,7 +212,6 @@ is(HTMLSourceElement.prototype.MEDIA_ERR_SRC_NOT_SUPPORTED, undefined);
is(MediaError.prototype.NETWORK_EMPTY, undefined);
is(MediaError.prototype.NETWORK_IDLE, undefined);
is(MediaError.prototype.NETWORK_LOADING, undefined);
is(MediaError.prototype.NETWORK_LOADED, undefined);
is(MediaError.prototype.NETWORK_NO_SOURCE, undefined);
is(MediaError.prototype.HAVE_NOTHING, undefined);
is(MediaError.prototype.HAVE_METADATA, undefined);

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

@ -43,8 +43,7 @@ function startTest(test, token) {
ok(false, "Unexpected ended event");
}, false);
v.src = test.name;
v.load();
v.src = test.name; // implicitly starts a load.
}
manager.runTests(gDecodeErrorTests, startTest);

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

@ -18,6 +18,14 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=448600
<pre id="test">
<script type="application/javascript">
function filename(uri) {
return uri.substr(uri.lastIndexOf("/")+1);
}
function e(id) {
return document.getElementById(id);
}
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
var prefService = Components.classes["@mozilla.org/preferences-service;1"]
.getService(Components.interfaces.nsIPrefService);
@ -39,13 +47,19 @@ var gErrorCount = 0;
SimpleTest.waitForExplicitFinish();
function finishTest() {
is(document.getElementById('video1').currentSrc, "", 'video1.currentSrc==""');
is(document.getElementById('video2').currentSrc, "", 'video2.currentSrc==""');
is(document.getElementById('video3').currentSrc, "", 'video3.currentSrc==""');
is(e('video1').currentSrc,
"",
'video1 currentSrc should be empty when there\'s no playable typed source children');
is(filename(e('video2').currentSrc),
filename(e('video2').src),
'video2 currentSrc should match src');
is(filename(e('video3').currentSrc),
filename(e('video3').src),
'video3 currentSrc should match src');
is(gLoadError['video1'], 1, "Load error on video1");
is(gLoadError['video2'], 1, "Load error on video2");
is(gLoadError['video3'], 1, "Load error on video3");
is(gLoadError['video1'], 2, "Expect one error per invalid source child on video1");
is(gLoadError['video2'], 1, "Expect one error on video2");
is(gLoadError['video3'], 1, "Expect one error on video3");
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
@ -59,7 +73,7 @@ function videoError(event, id) {
event.stopPropagation();
gLoadError[id]++;
gErrorCount++;
if (gErrorCount == 3) {
if (gErrorCount >= 4) {
finishTest();
}
}
@ -68,7 +82,7 @@ function videoError(event, id) {
<video id="video1" onerror="videoError(event, 'video1');">
<source type="video/ogg" src="320x240.ogv"/>
<source type="audio/wave" src="r11025_u8_c1.wav"/>
<source type="audio/wave" src="r11025_u8_c1.wav" id='s2'/>
</video>
<video id="video2" src="320x240.ogv" onerror="videoError(event, 'video2');"></video>

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

@ -89,6 +89,7 @@ function createMedia(type, src) {
gMedia.addEventListener(gEventTypes[i], listener, false);
}
gMedia.src = src;
gMedia._name = src;
document.body.appendChild(gMedia);
}
@ -108,6 +109,10 @@ function test_is(a, b, msg) {
}
}
function filename(uri) {
return uri.substr(uri.lastIndexOf("/")+1);
}
function checkState() {
if (gMedia != null) {
test_ok(gMedia.networkState <= HTMLMediaElement.NETWORK_LOADING ||
@ -126,7 +131,9 @@ function checkState() {
test_ok(gMedia.error==null || gMedia.error.code==MediaError.MEDIA_ERR_SRC_NOT_SUPPORTED,
"Error code should not exist or be SRC_NOT_SUPPORTED. gMedia.error=" +
(gMedia.error ? gMedia.error.code : "null"));
test_is(gMedia.currentSrc, "", "Leaked currentSrc");
test_ok(filename(gMedia.currentSrc) == filename(gMedia._name) ||
gMedia.networkState == HTMLMediaElement.NETWORK_NO_SOURCE,
"currentSrc should match candidate uri, if we've got a valid source");
}
if (!gFinished) {
setTimeout(checkState, 1);

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

@ -89,8 +89,9 @@ var gTests = [
addSource(src, type);
},
expectedEvents: ['loadstart', 'durationchange', 'loadedmetadata', 'loadeddata']
}, {
// Test 2: video with multiple source, the first of which are bad, we should load the last.
},{
// Test 2: video with multiple source, the first of which are bad, we should load the last,
// and receive error events for failed loads on the source children.
create:
function(src, type) {
document.body.appendChild(gMedia);
@ -98,7 +99,7 @@ var gTests = [
addSource("404b", type);
addSource(src, type);
},
expectedEvents: ['loadstart', 'durationchange', 'loadedmetadata', 'loadeddata']
expectedEvents: ['loadstart', 'error', 'error', 'durationchange', 'loadedmetadata', 'loadeddata']
}, {
// Test 3: video with bad src, good <source>, ensure that <source> aren't used.
create:
@ -123,7 +124,7 @@ var gTests = [
false);
document.body.appendChild(gMedia);
},
expectedEvents: ['loadstart', 'error', 'durationchange', 'loadedmetadata', 'loadeddata']
expectedEvents: ['loadstart', 'error', 'error', 'durationchange', 'loadedmetadata', 'loadeddata']
}, {
// Test 5: video with only 1 bad source, let it fail to load, then prepend
// a good <source> to the video, it shouldn't be selected, because the

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

@ -25,7 +25,7 @@ var manager = new MediaTestManager;
function finish(evt) {
var v = evt.target;
is(v._error, false, "Shouldn't have thrown an error - " + v._name);
ok(v._error, "Should have received an error event before loaded");
v._finished = true;
v.parentNode.removeChild(v);
manager.finished(v.token);
@ -33,8 +33,7 @@ function finish(evt) {
function errorHandler(evt) {
evt.stopPropagation();
evt.target._error = true;
finish(evt);
evt.target.parentNode._error = true;
}
var extenstion = {
@ -56,6 +55,7 @@ function startTest(test, token) {
var s1 = document.createElement("source");
s1.type = test.type;
s1.src = "404." + extenstion[test.type];
s1.addEventListener("error", errorHandler, false);
v.appendChild(s1);
var s2 = document.createElement("source");
@ -63,9 +63,7 @@ function startTest(test, token) {
s2.src = test.name;
v.appendChild(s2);
v.addEventListener("error", errorHandler, false);
v.addEventListener("loadeddata", finish, false);
document.body.appendChild(v);
}

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

@ -134,8 +134,8 @@ var tests = [
v.addEventListener("loadstart", function(e){v._gotLoadStart = true;}, false);
v.addEventListener("loadedmetadata", function(e){v._gotLoadedMetaData = true;}, false);
v.addEventListener("canplaythrough", this.canplaythrough, false);
v.src = test.name;
document.body.appendChild(v); // Causes implicit load.
v.src = test.name; // Causes implicit load.
document.body.appendChild(v);
},
},
{
@ -253,6 +253,7 @@ var tests = [
ok(true, "(7) Got playback ended");
var v = e.target;
v._finished = true;
is(v._gotErrorEvent, true, "(7) Should get error event from first source load failure");
maybeFinish(v, 7);
},
@ -262,10 +263,12 @@ var tests = [
v._gotLoadedMetaData = false;
v._finished = false;
v.preload = "none";
v._gotErrorEvent = false;
v.addEventListener("loadedmetadata", function(e){v._gotLoadedMetaData = true;}, false);
v.addEventListener("loadstart", function(e){v._gotLoadStart = true;}, false);
v.addEventListener("suspend", this.suspend, false);
v.addEventListener("ended", this.ended, false);
v.addEventListener("error", function(e){v._gotErrorEvent = true; e.stopPropagation();}, false);
var s1 = document.createElement("source");
s1.src = "not-a-real-file.404"
s1.type = test.type;
@ -297,8 +300,8 @@ var tests = [
v.addEventListener("loadstart", function(e){v.preload = "metadata";}, false);
v.addEventListener("loadedmetadata", function(e){v._gotLoadedMetaData = true;}, false);
v.addEventListener("loadeddata", this.loadeddata, false);
v.src = test.name;
document.body.appendChild(v); // Causes implicit load.
v.src = test.name; // Causes implicit load.
document.body.appendChild(v);
},
},
/*{
@ -326,8 +329,8 @@ var tests = [
v.addEventListener("loadedmetadata", function(e){v._gotLoadedMetaData = true;}, false);
v.addEventListener("loadeddata", function(){v.preload = "auto"}, false);
v.addEventListener("canplaythrough", this.canplaythrough, false);
v.src = test.name;
document.body.appendChild(v); // Causes implicit load.
v.src = test.name; // Causes implicit load.
document.body.appendChild(v);
},
},*/
{
@ -348,8 +351,8 @@ var tests = [
v.addEventListener("loadstart", function(e){v.preload = "auto";}, false);
v.addEventListener("loadedmetadata", function(e){v._gotLoadedMetaData = true;}, false);
v.addEventListener("canplaythrough", this.canplaythrough, false);
v.src = test.name;
document.body.appendChild(v); // Causes implicit load.
v.src = test.name; // Causes implicit load.
document.body.appendChild(v);
},
},
{
@ -372,8 +375,8 @@ var tests = [
v.addEventListener("loadstart", function(e){v.preload = "metadata";}, false);
v.addEventListener("loadedmetadata", function(e){v._gotLoadedMetaData = true;}, false);
v.addEventListener("loadeddata", this.loadeddata, false);
v.src = test.name;
document.body.appendChild(v); // Causes implicit load.
v.src = test.name; // Causes implicit load.
document.body.appendChild(v);
},
},
{
@ -397,8 +400,8 @@ var tests = [
v.addEventListener("loadstart", function(e){v._gotLoadStart = true;}, false);
v.addEventListener("loadedmetadata", function(e){v._gotLoadedMetaData = true;}, false);
v.addEventListener("canplaythrough", this.canplaythrough, false);
v.src = test.name;
document.body.appendChild(v); // Causes implicit load.
v.src = test.name; // Causes implicit load.
document.body.appendChild(v);
v.preload = "metadata";
},
},
@ -426,8 +429,8 @@ var tests = [
v.addEventListener("loadstart", function(e){v._gotLoadStart = true;}, false);
v.addEventListener("loadedmetadata", function(e){v._gotLoadedMetaData = true;}, false);
v.addEventListener("loadeddata", this.loadeddata, false);
v.src = test.name;
document.body.appendChild(v); // Causes implicit load.
v.src = test.name; // Causes implicit load.
document.body.appendChild(v);
v.preload = "none";
},
},
@ -486,8 +489,8 @@ var tests = [
v.addEventListener("loadstart", function(e){v._gotLoadStart = true;}, false);
v.addEventListener("loadedmetadata", function(e){v._gotLoadedMetaData = true;}, false);
v.addEventListener("ended", this.ended, false);
v.src = test.name;
document.body.appendChild(v); // Causes implicit load.
v.src = test.name; // Causes implicit load.
document.body.appendChild(v);
},
},
{
@ -506,8 +509,8 @@ var tests = [
v.preload = "metadata";
v.autoplay = true;
v.addEventListener("ended", this.ended, false);
v.src = test.name;
document.body.appendChild(v); // Causes implicit load.
v.src = test.name; // Causes implicit load.
document.body.appendChild(v);
},
},
{

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

@ -33,8 +33,8 @@ if (resource != null) {
v.appendChild(s);
}
ok(v.networkState == HTMLMediaElement.NETWORK_EMPTY,
"We shouldn't start a load until the video tag is closed.");
ok(v.networkState == HTMLMediaElement.NETWORK_NO_SOURCE,
"We shouldn't start network load until the current task returns.");
</script>
</video>
</body>

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

@ -1466,7 +1466,6 @@ nsWaveDecoder::ResourceLoaded()
if (mElement) {
// Ensure the final progress event gets fired
mElement->DispatchAsyncProgressEvent(NS_LITERAL_STRING("progress"));
mElement->ResourceLoaded();
}
@ -1533,8 +1532,10 @@ nsWaveDecoder::NotifyDownloadEnded(nsresult aStatus)
{
if (NS_SUCCEEDED(aStatus)) {
ResourceLoaded();
} else if (aStatus != NS_BASE_STREAM_CLOSED &&
aStatus != NS_BINDING_ABORTED) {
} else if (aStatus == NS_BINDING_ABORTED) {
// Download has been cancelled by user.
mElement->LoadAborted();
} else if (aStatus != NS_BASE_STREAM_CLOSED) {
NetworkError();
}
UpdateReadyStateForData();

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

@ -57,7 +57,7 @@
#endif
%}
[scriptable, uuid(4355cfdb-0d70-4e5b-bb48-3ae8111ee72b)]
[scriptable, uuid(c8b1423f-1321-4324-b6b7-8548d2fdc3da)]
interface nsIDOMHTMLMediaElement : nsIDOMHTMLElement
{
// error state
@ -69,8 +69,7 @@ interface nsIDOMHTMLMediaElement : nsIDOMHTMLElement
const unsigned short NETWORK_EMPTY = 0;
const unsigned short NETWORK_IDLE = 1;
const unsigned short NETWORK_LOADING = 2;
const unsigned short NETWORK_LOADED = 3;
const unsigned short NETWORK_NO_SOURCE = 4;
const unsigned short NETWORK_NO_SOURCE = 3;
readonly attribute unsigned short networkState;
attribute DOMString preload;
readonly attribute nsIDOMTimeRanges buffered;

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

@ -515,9 +515,7 @@ nsHtml5TreeBuilder::elementPopped(PRInt32 aNamespace, nsIAtom* aName, nsIContent
// Some HTML nodes need DoneAddingChildren() called to initialize
// properly (e.g. form state restoration).
// XXX expose ElementName group here and do switch
if (aName == nsHtml5Atoms::video ||
aName == nsHtml5Atoms::audio ||
aName == nsHtml5Atoms::object ||
if (aName == nsHtml5Atoms::object ||
aName == nsHtml5Atoms::applet) {
nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement();
NS_ASSERTION(treeOp, "Tree op allocation failed.");

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

@ -38,12 +38,13 @@
* ***** END LICENSE BLOCK ***** */
#include "nsISupports.idl"
#include "nsIRunnable.idl"
/**
* Interface for the native event system layer. This interface is designed
* to be used on the main application thread only.
*/
[uuid(501403e9-a091-4780-ba55-cfd1e21287a1)]
[uuid(40bc6280-ad83-471e-b197-80ab90e2065e)]
interface nsIAppShell : nsISupports
{
/**
@ -101,4 +102,15 @@ interface nsIAppShell : nsISupports
* The current event loop nesting level.
*/
readonly attribute unsigned long eventloopNestingLevel;
/**
* Allows running of a "synchronous section", in the form of an nsIRunnable
* once the event loop has reached a "stable state". We've reached a stable
* state when the currently executing task/event has finished, see:
* http://www.whatwg.org/specs/web-apps/current-work/multipage/webappapis.html#synchronous-section
* In practice this runs aRunnable once the currently executing event
* finishes. If called multiple times per task/event, all the runnables will
* be executed, in the order in which runInStableState() was called.
*/
void runInStableState(in nsIRunnable aRunnable);
};

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

@ -324,14 +324,36 @@ nsBaseAppShell::OnProcessNextEvent(nsIThreadInternal *thr, PRBool mayWait,
thr->Dispatch(mDummyEvent, NS_DISPATCH_NORMAL);
}
// We're about to run an event, so we're in a stable state.
RunSyncSections();
return NS_OK;
}
void
nsBaseAppShell::RunSyncSections()
{
if (mSyncSections.Count() == 0) {
return;
}
// We've got synchronous sections awaiting a stable state. Run
// all the synchronous sections. Note that a synchronous section could
// add another synchronous section, so we don't remove elements from
// mSyncSections until all sections have been run, else we'll screw up
// our iteration.
for (PRUint32 i=0; i<mSyncSections.Count(); i++) {
mSyncSections[i]->Run();
}
mSyncSections.Clear();
}
// Called from the main thread
NS_IMETHODIMP
nsBaseAppShell::AfterProcessNextEvent(nsIThreadInternal *thr,
PRUint32 recursionDepth)
{
// We've just finished running an event, so we're in a stable state.
RunSyncSections();
return NS_OK;
}
@ -343,3 +365,18 @@ nsBaseAppShell::Observe(nsISupports *subject, const char *topic,
Exit();
return NS_OK;
}
NS_IMETHODIMP
nsBaseAppShell::RunInStableState(nsIRunnable* aRunnable)
{
if (!mRunning) {
// We're not running a "task"/event, so we're already in a "stable state",
// so we can run the synchronous section immediately.
aRunnable->Run();
return NS_OK;
}
// Else we're running a "task"/event, record the synchronous section, and
// run it with any others once we reach a stable state.
mSyncSections.AppendObject(aRunnable);
return NS_OK;
}

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

@ -42,6 +42,7 @@
#include "nsIThreadInternal.h"
#include "nsIObserver.h"
#include "nsIRunnable.h"
#include "nsCOMArray.h"
#include "nsCOMPtr.h"
#include "prinrval.h"
@ -99,6 +100,11 @@ protected:
private:
PRBool DoProcessNextNativeEvent(PRBool mayWait);
/**
* Runs all synchronous sections which are queued up in mSyncSections.
*/
void RunSyncSections();
nsCOMPtr<nsIRunnable> mDummyEvent;
/**
* mBlockedWait points back to a slot that controls the wait loop in
@ -119,6 +125,7 @@ private:
eEventloopOther // innermost native event loop is a native library/plugin etc
};
EventloopNestingState mEventloopNestingState;
nsCOMArray<nsIRunnable> mSyncSections;
PRPackedBool mRunning;
PRPackedBool mExiting;
/**