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
Родитель da9888206a
Коммит 4b32f2905e
17 изменённых файлов: 373 добавлений и 226 удалений

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

@ -117,9 +117,6 @@ public:
virtual void UnbindFromTree(PRBool aDeep = PR_TRUE, virtual void UnbindFromTree(PRBool aDeep = PR_TRUE,
PRBool aNullParent = 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 * Call this to reevaluate whether we should start/stop due to our owner
* document being active or inactive. * document being active or inactive.
@ -149,6 +146,10 @@ public:
// resource has a decode error during metadata loading or decoding. // resource has a decode error during metadata loading or decoding.
void DecodeError(); 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, // Called by the video decoder object, on the main thread,
// when the video playback has ended. // when the video playback has ended.
void PlaybackEnded(); void PlaybackEnded();
@ -280,7 +281,7 @@ public:
/** /**
* Called when a child source element is added to this media element. This * 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(); void NotifyAddedSource();
@ -333,8 +334,6 @@ public:
protected: protected:
class MediaLoadListener; class MediaLoadListener;
class LoadNextSourceEvent;
class SelectResourceEvent;
/** /**
* Changes mHasPlayedOrSeeked to aValue. If mHasPlayedOrSeeked changes * Changes mHasPlayedOrSeeked to aValue. If mHasPlayedOrSeeked changes
@ -388,23 +387,25 @@ protected:
/** /**
* Attempts to load resources from the <source> children. This is a * 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. * call QueueLoadFromSourceTask() instead.
*/ */
void LoadFromSourceChildren(); 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(); void QueueLoadFromSourceTask();
/** /**
* Media selection algorithm. * Runs the media resource selection algorithm.
*/ */
void SelectResource(); 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(); void QueueSelectResourceTask();
@ -415,9 +416,10 @@ protected:
/** /**
* Selects the next <source> child from which to load a resource. Called * 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() * Changes mDelayingLoadEvent, and will call BlockOnLoad()/UnblockOnLoad()
@ -493,6 +495,17 @@ protected:
*/ */
void UpdatePreloadAction(); 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; nsRefPtr<nsMediaDecoder> mDecoder;
// A reference to the ImageContainer which contains the current frame // A reference to the ImageContainer which contains the current frame
@ -527,18 +540,19 @@ protected:
nsMediaReadyState mReadyState; nsMediaReadyState mReadyState;
enum LoadAlgorithmState { 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, NOT_WAITING,
// No src or <source> children, load is waiting at load algorithm step 1. // We've run the load algorithm, and we tried all source children of the
WAITING_FOR_SRC_OR_SOURCE, // media element, and failed to load any successfully. We're waiting for
// No src at load time, and all <source> children don't resolve or // another source element to be added to the media element, and will try
// give network errors during fetch, waiting for more <source> children // to load any such element when its added.
// to be added.
WAITING_FOR_SOURCE WAITING_FOR_SOURCE
}; };
// When the load algorithm is waiting for more src/<source>, this denotes // Denotes the waiting state of a load algorithm instance. When the load
// what type of waiting we're doing. // 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; LoadAlgorithmState mLoadWaitStatus;
// Current audio volume // Current audio volume
@ -550,11 +564,12 @@ protected:
// Current audio sample rate. // Current audio sample rate.
PRUint32 mRate; PRUint32 mRate;
// If we're loading a preload:none media, we'll record the URI we're // URI of the resource we're attempting to load. When the decoder is
// attempting to load in mPreloadURI, and delay loading the resource until // successfully initialized, we rely on it to record the URI we're playing,
// the user initiates a load by either playing the resource, or explicitly // and clear mLoadingSrc. This stores the value we return in the currentSrc
// loading it. // attribute until the decoder is initialized. Use GetCurrentSrc() to access
nsCOMPtr<nsIURI> mPreloadURI; // the currentSrc attribute.
nsCOMPtr<nsIURI> mLoadingSrc;
// Stores the current preload action for this element. Initially set to // Stores the current preload action for this element. Initially set to
// PRELOAD_UNDEFINED, its value is changed by calling // PRELOAD_UNDEFINED, its value is changed by calling
@ -562,11 +577,15 @@ protected:
PreloadAction mPreloadAction; PreloadAction mPreloadAction;
// Size of the media. Updated by the decoder on the main thread if // 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; nsIntSize mMediaSize;
nsRefPtr<gfxASurface> mPrintSurface; 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. // An audio stream for writing audio directly from JS.
nsAutoPtr<nsAudioStream> mAudioStream; nsAutoPtr<nsAudioStream> mAudioStream;
@ -604,10 +623,6 @@ protected:
// True if the sound is muted // True if the sound is muted
PRPackedBool mMuted; 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 // 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 // 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 // or was not actively playing before the current seek. Used to decide whether

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

@ -57,6 +57,7 @@
#include "nsXPCOMStrings.h" #include "nsXPCOMStrings.h"
#include "prlock.h" #include "prlock.h"
#include "nsThreadUtils.h" #include "nsThreadUtils.h"
#include "nsIThreadInternal.h"
#include "nsContentUtils.h" #include "nsContentUtils.h"
#include "nsFrameManager.h" #include "nsFrameManager.h"
@ -88,6 +89,8 @@
#include <limits> #include <limits>
#include "nsIDocShellTreeItem.h" #include "nsIDocShellTreeItem.h"
#include "nsIAsyncVerifyRedirectCallback.h" #include "nsIAsyncVerifyRedirectCallback.h"
#include "nsIAppShell.h"
#include "nsWidgetsCID.h"
#include "nsIPrivateDOMEvent.h" #include "nsIPrivateDOMEvent.h"
#include "nsIDOMNotifyAudioAvailableEvent.h" #include "nsIDOMNotifyAudioAvailableEvent.h"
@ -209,50 +212,31 @@ public:
} }
}; };
class nsHTMLMediaElement::LoadNextSourceEvent : public nsMediaEvent { class nsSourceErrorEventRunner : public nsMediaEvent
{
private:
nsCOMPtr<nsIContent> mSource;
public: public:
LoadNextSourceEvent(nsHTMLMediaElement *aElement) nsSourceErrorEventRunner(nsHTMLMediaElement* aElement,
: nsMediaEvent(aElement) {} nsIContent* aSource)
: nsMediaEvent(aElement),
mSource(aSource)
{
}
NS_IMETHOD Run() { NS_IMETHOD Run() {
if (!IsCancelled()) // Silently cancel if our load has been cancelled.
mElement->LoadFromSourceChildren(); if (IsCancelled())
return NS_OK; 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 * There is a reference cycle involving this class: MediaLoadListener
* holds a reference to the nsHTMLMediaElement, which holds a reference * holds a reference to the nsHTMLMediaElement, which holds a reference
@ -441,6 +425,8 @@ NS_IMETHODIMP nsHTMLMediaElement::GetCurrentSrc(nsAString & aCurrentSrc)
if (stream) { if (stream) {
stream->URI()->GetSpec(src); stream->URI()->GetSpec(src);
} }
} else if (mLoadingSrc) {
mLoadingSrc->GetSpec(src);
} }
aCurrentSrc = NS_ConvertUTF8toUTF16(src); aCurrentSrc = NS_ConvertUTF8toUTF16(src);
@ -504,7 +490,6 @@ void nsHTMLMediaElement::AbortExistingLoads()
if (mNetworkState == nsIDOMHTMLMediaElement::NETWORK_LOADING || if (mNetworkState == nsIDOMHTMLMediaElement::NETWORK_LOADING ||
mNetworkState == nsIDOMHTMLMediaElement::NETWORK_IDLE) mNetworkState == nsIDOMHTMLMediaElement::NETWORK_IDLE)
{ {
mError = new nsMediaError(nsIDOMMediaError::MEDIA_ERR_ABORTED);
DispatchProgressEvent(NS_LITERAL_STRING("abort")); DispatchProgressEvent(NS_LITERAL_STRING("abort"));
} }
@ -551,6 +536,62 @@ void nsHTMLMediaElement::NoSupportedMediaSourceError()
ChangeDelayLoadStatus(PR_FALSE); 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 (); */ /* void load (); */
NS_IMETHODIMP nsHTMLMediaElement::Load() NS_IMETHODIMP nsHTMLMediaElement::Load()
{ {
@ -591,18 +632,20 @@ static PRBool HasPotentialResource(nsIContent *aElement)
void nsHTMLMediaElement::SelectResource() 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)) { if (!HasPotentialResource(this)) {
// While the media element has neither a src attribute nor any source // The media element has neither a src attribute nor any source
// element children, wait. (This steps might wait forever.) // element children, abort the load.
mNetworkState = nsIDOMHTMLMediaElement::NETWORK_NO_SOURCE; mNetworkState = nsIDOMHTMLMediaElement::NETWORK_EMPTY;
mLoadWaitStatus = WAITING_FOR_SRC_OR_SOURCE;
// This clears mDelayingLoadEvent, so AddRemoveSelfReference will be called // This clears mDelayingLoadEvent, so AddRemoveSelfReference will be called
ChangeDelayLoadStatus(PR_FALSE); ChangeDelayLoadStatus(PR_FALSE);
mIsRunningSelectResource = PR_FALSE;
return; return;
} }
ChangeDelayLoadStatus(PR_TRUE);
mNetworkState = nsIDOMHTMLMediaElement::NETWORK_LOADING; mNetworkState = nsIDOMHTMLMediaElement::NETWORK_LOADING;
// Load event was delayed, and still is, so no need to call // Load event was delayed, and still is, so no need to call
// AddRemoveSelfReference, since it must still be held // AddRemoveSelfReference, since it must still be held
@ -617,29 +660,37 @@ void nsHTMLMediaElement::SelectResource()
if (NS_SUCCEEDED(rv)) { if (NS_SUCCEEDED(rv)) {
LOG(PR_LOG_DEBUG, ("%p Trying load from src=%s", this, NS_ConvertUTF16toUTF8(src).get())); LOG(PR_LOG_DEBUG, ("%p Trying load from src=%s", this, NS_ConvertUTF16toUTF8(src).get()));
mIsLoadingFromSrcAttribute = PR_TRUE; mIsLoadingFromSrcAttribute = PR_TRUE;
mLoadingSrc = uri;
if (mPreloadAction == nsHTMLMediaElement::PRELOAD_NONE) { if (mPreloadAction == nsHTMLMediaElement::PRELOAD_NONE) {
// preload:none media, suspend the load here before we make any // preload:none media, suspend the load here before we make any
// network requests. // network requests.
SuspendLoad(uri); SuspendLoad(uri);
mIsRunningSelectResource = PR_FALSE;
return; return;
} }
rv = LoadResource(uri); rv = LoadResource(uri);
if (NS_SUCCEEDED(rv)) if (NS_SUCCEEDED(rv)) {
mIsRunningSelectResource = PR_FALSE;
return; return;
} }
}
NoSupportedMediaSourceError(); NoSupportedMediaSourceError();
} else { } else {
// Otherwise, the source elements will be used. // Otherwise, the source elements will be used.
LoadFromSourceChildren(); LoadFromSourceChildren();
} }
mIsRunningSelectResource = PR_FALSE;
} }
void nsHTMLMediaElement::NotifyLoadError() void nsHTMLMediaElement::NotifyLoadError()
{ {
if (mIsLoadingFromSrcAttribute) { if (mIsLoadingFromSrcAttribute) {
LOG(PR_LOG_DEBUG, ("NotifyLoadError(), no supported media error"));
NoSupportedMediaSourceError(); NoSupportedMediaSourceError();
} else { } else {
NS_ASSERTION(mSourceLoadCandidate, "Must know the source we were loading from!");
DispatchAsyncSourceError(mSourceLoadCandidate);
QueueLoadFromSourceTask(); QueueLoadFromSourceTask();
} }
} }
@ -682,18 +733,47 @@ void nsHTMLMediaElement::LoadFromSourceChildren()
{ {
NS_ASSERTION(mDelayingLoadEvent, NS_ASSERTION(mDelayingLoadEvent,
"Should delay load event (if in document) during load"); "Should delay load event (if in document) during load");
NS_ASSERTION(!mIsLoadingFromSrcAttribute,
"Must remember we're loading from source children");
while (PR_TRUE) { while (PR_TRUE) {
nsresult rv; nsresult rv;
nsCOMPtr<nsIURI> uri = GetNextSource(); nsIContent* child = GetNextSource();
if (!uri) { if (!child) {
// Exhausted candidates, wait for more candidates to be appended to // Exhausted candidates, wait for more candidates to be appended to
// the media element. // the media element.
mLoadWaitStatus = WAITING_FOR_SOURCE; mLoadWaitStatus = WAITING_FOR_SOURCE;
NoSupportedMediaSourceError(); mNetworkState = nsIDOMHTMLMediaElement::NETWORK_NO_SOURCE;
ChangeDelayLoadStatus(PR_FALSE);
return; 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) { if (mPreloadAction == nsHTMLMediaElement::PRELOAD_NONE) {
// preload:none media, suspend the load here before we make any // preload:none media, suspend the load here before we make any
@ -707,6 +787,7 @@ void nsHTMLMediaElement::LoadFromSourceChildren()
return; return;
// If we fail to load, loop back and try loading the next resource. // If we fail to load, loop back and try loading the next resource.
DispatchAsyncSourceError(child);
} }
NS_NOTREACHED("Execution should not reach here!"); NS_NOTREACHED("Execution should not reach here!");
} }
@ -714,7 +795,6 @@ void nsHTMLMediaElement::LoadFromSourceChildren()
void nsHTMLMediaElement::SuspendLoad(nsIURI* aURI) void nsHTMLMediaElement::SuspendLoad(nsIURI* aURI)
{ {
mLoadIsSuspended = PR_TRUE; mLoadIsSuspended = PR_TRUE;
mPreloadURI = aURI;
mNetworkState = nsIDOMHTMLMediaElement::NETWORK_IDLE; mNetworkState = nsIDOMHTMLMediaElement::NETWORK_IDLE;
DispatchAsyncProgressEvent(NS_LITERAL_STRING("suspend")); DispatchAsyncProgressEvent(NS_LITERAL_STRING("suspend"));
ChangeDelayLoadStatus(PR_FALSE); ChangeDelayLoadStatus(PR_FALSE);
@ -723,9 +803,8 @@ void nsHTMLMediaElement::SuspendLoad(nsIURI* aURI)
void nsHTMLMediaElement::ResumeLoad(PreloadAction aAction) void nsHTMLMediaElement::ResumeLoad(PreloadAction aAction)
{ {
NS_ASSERTION(mLoadIsSuspended, "Can only resume preload if halted for one"); NS_ASSERTION(mLoadIsSuspended, "Can only resume preload if halted for one");
nsCOMPtr<nsIURI> uri = mPreloadURI; nsCOMPtr<nsIURI> uri = mLoadingSrc;
mLoadIsSuspended = PR_FALSE; mLoadIsSuspended = PR_FALSE;
mPreloadURI = nsnull;
mPreloadAction = aAction; mPreloadAction = aAction;
ChangeDelayLoadStatus(PR_TRUE); ChangeDelayLoadStatus(PR_TRUE);
mNetworkState = nsIDOMHTMLMediaElement::NETWORK_LOADING; mNetworkState = nsIDOMHTMLMediaElement::NETWORK_LOADING;
@ -1063,6 +1142,7 @@ NS_IMETHODIMP nsHTMLMediaElement::GetPaused(PRBool *aPaused)
NS_IMETHODIMP nsHTMLMediaElement::Pause() NS_IMETHODIMP nsHTMLMediaElement::Pause()
{ {
if (mNetworkState == nsIDOMHTMLMediaElement::NETWORK_EMPTY) { if (mNetworkState == nsIDOMHTMLMediaElement::NETWORK_EMPTY) {
LOG(PR_LOG_DEBUG, ("Loading due to Pause()"));
nsresult rv = Load(); nsresult rv = Load();
NS_ENSURE_SUCCESS(rv, rv); NS_ENSURE_SUCCESS(rv, rv);
} else if (mDecoder) { } else if (mDecoder) {
@ -1200,7 +1280,6 @@ nsHTMLMediaElement::nsHTMLMediaElement(already_AddRefed<nsINodeInfo> aNodeInfo,
mAutoplayEnabled(PR_TRUE), mAutoplayEnabled(PR_TRUE),
mPaused(PR_TRUE), mPaused(PR_TRUE),
mMuted(PR_FALSE), mMuted(PR_FALSE),
mIsDoneAddingChildren(!aFromParser),
mPlayingBeforeSeek(PR_FALSE), mPlayingBeforeSeek(PR_FALSE),
mPausedForInactiveDocument(PR_FALSE), mPausedForInactiveDocument(PR_FALSE),
mWaitingFired(PR_FALSE), mWaitingFired(PR_FALSE),
@ -1369,16 +1448,11 @@ nsresult nsHTMLMediaElement::SetAttr(PRInt32 aNameSpaceID, nsIAtom* aName,
aNotify); aNotify);
if (NS_FAILED(rv)) if (NS_FAILED(rv))
return rv; return rv;
if (aNotify && aNameSpaceID == kNameSpaceID_None) { if (aNameSpaceID == kNameSpaceID_None && aName == nsGkAtoms::src) {
if (aName == nsGkAtoms::src) { Load();
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 (aNotify && aNameSpaceID == kNameSpaceID_None) {
if (aName == nsGkAtoms::autoplay) {
StopSuspendingAfterFirstFrame(); StopSuspendingAfterFirstFrame();
if (mReadyState == nsIDOMHTMLMediaElement::HAVE_ENOUGH_DATA) { if (mReadyState == nsIDOMHTMLMediaElement::HAVE_ENOUGH_DATA) {
NotifyAutoplayDataReady(); NotifyAutoplayDataReady();
@ -1429,15 +1503,6 @@ nsresult nsHTMLMediaElement::BindToTree(nsIDocument* aDocument, nsIContent* aPar
aParent, aParent,
aBindingParent, aBindingParent,
aCompileEventHandlers); aCompileEventHandlers);
if (aDocument) {
if (NS_SUCCEEDED(rv) &&
mIsDoneAddingChildren &&
mNetworkState == nsIDOMHTMLMediaElement::NETWORK_EMPTY)
{
QueueSelectResourceTask();
}
}
mIsBindingToTree = PR_FALSE; mIsBindingToTree = PR_FALSE;
return rv; return rv;
@ -1813,6 +1878,9 @@ nsresult nsHTMLMediaElement::FinishDecoderSetup(nsMediaDecoder* aDecoder)
{ {
mDecoder = 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. // Force a same-origin check before allowing events for this media resource.
mMediaSecurityVerified = PR_FALSE; mMediaSecurityVerified = PR_FALSE;
@ -1902,29 +1970,43 @@ void nsHTMLMediaElement::ResourceLoaded()
mNetworkState = nsIDOMHTMLMediaElement::NETWORK_IDLE; mNetworkState = nsIDOMHTMLMediaElement::NETWORK_IDLE;
AddRemoveSelfReference(); AddRemoveSelfReference();
ChangeReadyState(nsIDOMHTMLMediaElement::HAVE_ENOUGH_DATA); 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")); DispatchAsyncSimpleEvent(NS_LITERAL_STRING("suspend"));
} }
void nsHTMLMediaElement::NetworkError() void nsHTMLMediaElement::NetworkError()
{ {
mError = new nsMediaError(nsIDOMMediaError::MEDIA_ERR_NETWORK); Error(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);
} }
void nsHTMLMediaElement::DecodeError() 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; mBegun = PR_FALSE;
DispatchAsyncProgressEvent(NS_LITERAL_STRING("error")); DispatchAsyncProgressEvent(NS_LITERAL_STRING("error"));
if (mReadyState == nsIDOMHTMLMediaElement::HAVE_NOTHING) {
mNetworkState = nsIDOMHTMLMediaElement::NETWORK_EMPTY; mNetworkState = nsIDOMHTMLMediaElement::NETWORK_EMPTY;
AddRemoveSelfReference();
DispatchAsyncSimpleEvent(NS_LITERAL_STRING("emptied")); DispatchAsyncSimpleEvent(NS_LITERAL_STRING("emptied"));
} else {
mNetworkState = nsIDOMHTMLMediaElement::NETWORK_IDLE;
}
AddRemoveSelfReference();
ChangeDelayLoadStatus(PR_FALSE); ChangeDelayLoadStatus(PR_FALSE);
} }
@ -2211,25 +2293,6 @@ nsresult nsHTMLMediaElement::DispatchProgressEvent(const nsAString& aName)
return target->DispatchEvent(event, &dummy); 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 PRBool nsHTMLMediaElement::IsPotentiallyPlaying() const
{ {
// TODO: // TODO:
@ -2349,21 +2412,41 @@ nsHTMLMediaElement::IsNodeOfType(PRUint32 aFlags) const
return !(aFlags & ~(eCONTENT | eMEDIA)); 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() 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(); 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(); QueueLoadFromSourceTask();
} }
} }
already_AddRefed<nsIURI> nsHTMLMediaElement::GetNextSource() nsIContent* nsHTMLMediaElement::GetNextSource()
{ {
nsresult rv = NS_OK; nsresult rv = NS_OK;
nsCOMPtr<nsIDOMNode> thisDomNode = nsCOMPtr<nsIDOMNode> thisDomNode =
do_QueryInterface(static_cast<nsGenericElement*>(this)); do_QueryInterface(static_cast<nsGenericElement*>(this));
mSourceLoadCandidate = nsnull;
if (!mSourcePointer) { if (!mSourcePointer) {
// First time this has been run, create a selection to cover children. // First time this has been run, create a selection to cover children.
mSourcePointer = do_CreateInstance("@mozilla.org/content/range;1"); mSourcePointer = do_CreateInstance("@mozilla.org/content/range;1");
@ -2397,27 +2480,13 @@ already_AddRefed<nsIURI> nsHTMLMediaElement::GetNextSource()
nsIContent* child = GetChildAt(startOffset); 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 && if (child &&
child->Tag() == nsGkAtoms::source && child->Tag() == nsGkAtoms::source &&
child->IsHTML()) child->IsHTML())
{ {
nsCOMPtr<nsIURI> uri; mSourceLoadCandidate = child;
nsAutoString src,type; return child;
// 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();
} }
} }
NS_NOTREACHED("Execution should not reach here!"); NS_NOTREACHED("Execution should not reach here!");

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

@ -411,7 +411,6 @@ void nsBuiltinDecoder::ResourceLoaded()
// Ensure the final progress event gets fired // Ensure the final progress event gets fired
if (mElement) { if (mElement) {
mElement->DispatchAsyncProgressEvent(NS_LITERAL_STRING("progress"));
mElement->ResourceLoaded(); mElement->ResourceLoaded();
} }
} }
@ -566,8 +565,11 @@ void nsBuiltinDecoder::NotifyDownloadEnded(nsresult aStatus)
{ {
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread."); 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; return;
}
{ {
MonitorAutoEnter mon(mMonitor); MonitorAutoEnter mon(mMonitor);

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

@ -17,7 +17,6 @@
is(HTMLElement.NETWORK_EMPTY, undefined); is(HTMLElement.NETWORK_EMPTY, undefined);
is(HTMLElement.NETWORK_IDLE, undefined); is(HTMLElement.NETWORK_IDLE, undefined);
is(HTMLElement.NETWORK_LOADING, undefined); is(HTMLElement.NETWORK_LOADING, undefined);
is(HTMLElement.NETWORK_LOADED, undefined);
is(HTMLElement.NETWORK_NO_SOURCE, undefined); is(HTMLElement.NETWORK_NO_SOURCE, undefined);
is(HTMLElement.HAVE_NOTHING, undefined); is(HTMLElement.HAVE_NOTHING, undefined);
is(HTMLElement.HAVE_METADATA, 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_EMPTY, 0);
is(HTMLMediaElement.NETWORK_IDLE, 1); is(HTMLMediaElement.NETWORK_IDLE, 1);
is(HTMLMediaElement.NETWORK_LOADING, 2); is(HTMLMediaElement.NETWORK_LOADING, 2);
todo_is(HTMLMediaElement.NETWORK_LOADED, undefined); is(HTMLMediaElement.NETWORK_NO_SOURCE, 3);
is(HTMLMediaElement.NETWORK_NO_SOURCE, 4);
is(HTMLMediaElement.HAVE_NOTHING, 0); is(HTMLMediaElement.HAVE_NOTHING, 0);
is(HTMLMediaElement.HAVE_METADATA, 1); is(HTMLMediaElement.HAVE_METADATA, 1);
is(HTMLMediaElement.HAVE_CURRENT_DATA, 2); 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_EMPTY, undefined);
is(HTMLAudioElement.NETWORK_IDLE, undefined); is(HTMLAudioElement.NETWORK_IDLE, undefined);
is(HTMLAudioElement.NETWORK_LOADING, undefined); is(HTMLAudioElement.NETWORK_LOADING, undefined);
is(HTMLAudioElement.NETWORK_LOADED, undefined);
is(HTMLAudioElement.NETWORK_NO_SOURCE, undefined); is(HTMLAudioElement.NETWORK_NO_SOURCE, undefined);
is(HTMLAudioElement.HAVE_NOTHING, undefined); is(HTMLAudioElement.HAVE_NOTHING, undefined);
is(HTMLAudioElement.HAVE_METADATA, 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_EMPTY, undefined);
is(HTMLSourceElement.NETWORK_IDLE, undefined); is(HTMLSourceElement.NETWORK_IDLE, undefined);
is(HTMLSourceElement.NETWORK_LOADING, undefined); is(HTMLSourceElement.NETWORK_LOADING, undefined);
is(HTMLSourceElement.NETWORK_LOADED, undefined);
is(HTMLSourceElement.NETWORK_NO_SOURCE, undefined); is(HTMLSourceElement.NETWORK_NO_SOURCE, undefined);
is(HTMLSourceElement.HAVE_NOTHING, undefined); is(HTMLSourceElement.HAVE_NOTHING, undefined);
is(HTMLSourceElement.HAVE_METADATA, 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_EMPTY, undefined);
is(MediaError.NETWORK_IDLE, undefined); is(MediaError.NETWORK_IDLE, undefined);
is(MediaError.NETWORK_LOADING, undefined); is(MediaError.NETWORK_LOADING, undefined);
is(MediaError.NETWORK_LOADED, undefined);
is(MediaError.NETWORK_NO_SOURCE, undefined); is(MediaError.NETWORK_NO_SOURCE, undefined);
is(MediaError.HAVE_NOTHING, undefined); is(MediaError.HAVE_NOTHING, undefined);
is(MediaError.HAVE_METADATA, 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_EMPTY, undefined);
is(document.body.NETWORK_IDLE, undefined); is(document.body.NETWORK_IDLE, undefined);
is(document.body.NETWORK_LOADING, undefined); is(document.body.NETWORK_LOADING, undefined);
is(document.body.NETWORK_LOADED, undefined);
is(document.body.NETWORK_NO_SOURCE, undefined); is(document.body.NETWORK_NO_SOURCE, undefined);
is(document.body.HAVE_NOTHING, undefined); is(document.body.HAVE_NOTHING, undefined);
is(document.body.HAVE_METADATA, 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_EMPTY, 0);
is(document.getElementsByTagName("video")[0].NETWORK_IDLE, 1); is(document.getElementsByTagName("video")[0].NETWORK_IDLE, 1);
is(document.getElementsByTagName("video")[0].NETWORK_LOADING, 2); is(document.getElementsByTagName("video")[0].NETWORK_LOADING, 2);
is(document.getElementsByTagName("video")[0].NETWORK_LOADED, 3); is(document.getElementsByTagName("video")[0].NETWORK_NO_SOURCE, 3);
is(document.getElementsByTagName("video")[0].NETWORK_NO_SOURCE, 4);
is(document.getElementsByTagName("video")[0].HAVE_NOTHING, 0); is(document.getElementsByTagName("video")[0].HAVE_NOTHING, 0);
is(document.getElementsByTagName("video")[0].HAVE_METADATA, 1); is(document.getElementsByTagName("video")[0].HAVE_METADATA, 1);
is(document.getElementsByTagName("video")[0].HAVE_CURRENT_DATA, 2); 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_EMPTY, 0);
is(document.getElementsByTagName("audio")[0].NETWORK_IDLE, 1); is(document.getElementsByTagName("audio")[0].NETWORK_IDLE, 1);
is(document.getElementsByTagName("audio")[0].NETWORK_LOADING, 2); is(document.getElementsByTagName("audio")[0].NETWORK_LOADING, 2);
is(document.getElementsByTagName("audio")[0].NETWORK_LOADED, 3); is(document.getElementsByTagName("audio")[0].NETWORK_NO_SOURCE, 3);
is(document.getElementsByTagName("audio")[0].NETWORK_NO_SOURCE, 4);
is(document.getElementsByTagName("audio")[0].HAVE_NOTHING, 0); is(document.getElementsByTagName("audio")[0].HAVE_NOTHING, 0);
is(document.getElementsByTagName("audio")[0].HAVE_METADATA, 1); is(document.getElementsByTagName("audio")[0].HAVE_METADATA, 1);
is(document.getElementsByTagName("audio")[0].HAVE_CURRENT_DATA, 2); 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_EMPTY, undefined);
is(document.getElementsByTagName("source")[0].NETWORK_IDLE, undefined); is(document.getElementsByTagName("source")[0].NETWORK_IDLE, undefined);
is(document.getElementsByTagName("source")[0].NETWORK_LOADING, 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].NETWORK_NO_SOURCE, undefined);
is(document.getElementsByTagName("source")[0].HAVE_NOTHING, undefined); is(document.getElementsByTagName("source")[0].HAVE_NOTHING, undefined);
is(document.getElementsByTagName("source")[0].HAVE_METADATA, 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_EMPTY, undefined);
is(HTMLElement.prototype.NETWORK_IDLE, undefined); is(HTMLElement.prototype.NETWORK_IDLE, undefined);
is(HTMLElement.prototype.NETWORK_LOADING, undefined); is(HTMLElement.prototype.NETWORK_LOADING, undefined);
is(HTMLElement.prototype.NETWORK_LOADED, undefined);
is(HTMLElement.prototype.NETWORK_NO_SOURCE, undefined); is(HTMLElement.prototype.NETWORK_NO_SOURCE, undefined);
is(HTMLElement.prototype.HAVE_NOTHING, undefined); is(HTMLElement.prototype.HAVE_NOTHING, undefined);
is(HTMLElement.prototype.HAVE_METADATA, 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_EMPTY, 0, "HTMLMediaElement.prototype.NETWORK_EMPTY");
todo_is(HTMLMediaElement.prototype.NETWORK_IDLE, 1, "HTMLMediaElement.prototype.NETWORK_IDLE"); 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_LOADING, 2, "HTMLMediaElement.prototype.NETWORK_LOADING");
todo_is(HTMLMediaElement.prototype.NETWORK_LOADED, 3, "HTMLMediaElement.prototype.NETWORK_LOADED"); todo_is(HTMLMediaElement.prototype.NETWORK_NO_SOURCE, 3, "HTMLMediaElement.prototype.NETWORK_NO_SOURCE");
todo_is(HTMLMediaElement.prototype.NETWORK_NO_SOURCE, 4, "HTMLMediaElement.prototype.NETWORK_NO_SOURCE");
todo_is(HTMLMediaElement.prototype.HAVE_NOTHING, 0, "HTMLMediaElement.prototype.HAVE_NOTHING"); 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_METADATA, 1, "HTMLMediaElement.prototype.HAVE_METADATA");
todo_is(HTMLMediaElement.prototype.HAVE_CURRENT_DATA, 2, "HTMLMediaElement.prototype.HAVE_CURRENT_DATA"); 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_EMPTY, 0);
is(HTMLVideoElement.prototype.NETWORK_IDLE, 1); is(HTMLVideoElement.prototype.NETWORK_IDLE, 1);
is(HTMLVideoElement.prototype.NETWORK_LOADING, 2); is(HTMLVideoElement.prototype.NETWORK_LOADING, 2);
is(HTMLVideoElement.prototype.NETWORK_LOADED, 3); is(HTMLVideoElement.prototype.NETWORK_NO_SOURCE, 3);
is(HTMLVideoElement.prototype.NETWORK_NO_SOURCE, 4);
is(HTMLVideoElement.prototype.HAVE_NOTHING, 0); is(HTMLVideoElement.prototype.HAVE_NOTHING, 0);
is(HTMLVideoElement.prototype.HAVE_METADATA, 1); is(HTMLVideoElement.prototype.HAVE_METADATA, 1);
is(HTMLVideoElement.prototype.HAVE_CURRENT_DATA, 2); 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_EMPTY, 0);
is(HTMLAudioElement.prototype.NETWORK_IDLE, 1); is(HTMLAudioElement.prototype.NETWORK_IDLE, 1);
is(HTMLAudioElement.prototype.NETWORK_LOADING, 2); is(HTMLAudioElement.prototype.NETWORK_LOADING, 2);
is(HTMLAudioElement.prototype.NETWORK_LOADED, 3); is(HTMLAudioElement.prototype.NETWORK_NO_SOURCE, 3);
is(HTMLAudioElement.prototype.NETWORK_NO_SOURCE, 4);
is(HTMLAudioElement.prototype.HAVE_NOTHING, 0); is(HTMLAudioElement.prototype.HAVE_NOTHING, 0);
is(HTMLAudioElement.prototype.HAVE_METADATA, 1); is(HTMLAudioElement.prototype.HAVE_METADATA, 1);
is(HTMLAudioElement.prototype.HAVE_CURRENT_DATA, 2); 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_EMPTY, undefined);
is(HTMLSourceElement.prototype.NETWORK_IDLE, undefined); is(HTMLSourceElement.prototype.NETWORK_IDLE, undefined);
is(HTMLSourceElement.prototype.NETWORK_LOADING, undefined); is(HTMLSourceElement.prototype.NETWORK_LOADING, undefined);
is(HTMLSourceElement.prototype.NETWORK_LOADED, undefined);
is(HTMLSourceElement.prototype.NETWORK_NO_SOURCE, undefined); is(HTMLSourceElement.prototype.NETWORK_NO_SOURCE, undefined);
is(HTMLSourceElement.prototype.HAVE_NOTHING, undefined); is(HTMLSourceElement.prototype.HAVE_NOTHING, undefined);
is(HTMLSourceElement.prototype.HAVE_METADATA, 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_EMPTY, undefined);
is(MediaError.prototype.NETWORK_IDLE, undefined); is(MediaError.prototype.NETWORK_IDLE, undefined);
is(MediaError.prototype.NETWORK_LOADING, undefined); is(MediaError.prototype.NETWORK_LOADING, undefined);
is(MediaError.prototype.NETWORK_LOADED, undefined);
is(MediaError.prototype.NETWORK_NO_SOURCE, undefined); is(MediaError.prototype.NETWORK_NO_SOURCE, undefined);
is(MediaError.prototype.HAVE_NOTHING, undefined); is(MediaError.prototype.HAVE_NOTHING, undefined);
is(MediaError.prototype.HAVE_METADATA, undefined); is(MediaError.prototype.HAVE_METADATA, undefined);

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

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

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

@ -18,6 +18,14 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=448600
<pre id="test"> <pre id="test">
<script type="application/javascript"> <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"); netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
var prefService = Components.classes["@mozilla.org/preferences-service;1"] var prefService = Components.classes["@mozilla.org/preferences-service;1"]
.getService(Components.interfaces.nsIPrefService); .getService(Components.interfaces.nsIPrefService);
@ -39,13 +47,19 @@ var gErrorCount = 0;
SimpleTest.waitForExplicitFinish(); SimpleTest.waitForExplicitFinish();
function finishTest() { function finishTest() {
is(document.getElementById('video1').currentSrc, "", 'video1.currentSrc==""'); is(e('video1').currentSrc,
is(document.getElementById('video2').currentSrc, "", 'video2.currentSrc==""'); "",
is(document.getElementById('video3').currentSrc, "", 'video3.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['video1'], 2, "Expect one error per invalid source child on video1");
is(gLoadError['video2'], 1, "Load error on video2"); is(gLoadError['video2'], 1, "Expect one error on video2");
is(gLoadError['video3'], 1, "Load error on video3"); is(gLoadError['video3'], 1, "Expect one error on video3");
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect"); netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
@ -59,7 +73,7 @@ function videoError(event, id) {
event.stopPropagation(); event.stopPropagation();
gLoadError[id]++; gLoadError[id]++;
gErrorCount++; gErrorCount++;
if (gErrorCount == 3) { if (gErrorCount >= 4) {
finishTest(); finishTest();
} }
} }
@ -68,7 +82,7 @@ function videoError(event, id) {
<video id="video1" onerror="videoError(event, 'video1');"> <video id="video1" onerror="videoError(event, 'video1');">
<source type="video/ogg" src="320x240.ogv"/> <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>
<video id="video2" src="320x240.ogv" onerror="videoError(event, 'video2');"></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.addEventListener(gEventTypes[i], listener, false);
} }
gMedia.src = src; gMedia.src = src;
gMedia._name = src;
document.body.appendChild(gMedia); 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() { function checkState() {
if (gMedia != null) { if (gMedia != null) {
test_ok(gMedia.networkState <= HTMLMediaElement.NETWORK_LOADING || 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, 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=" + "Error code should not exist or be SRC_NOT_SUPPORTED. gMedia.error=" +
(gMedia.error ? gMedia.error.code : "null")); (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) { if (!gFinished) {
setTimeout(checkState, 1); setTimeout(checkState, 1);

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

@ -89,8 +89,9 @@ var gTests = [
addSource(src, type); addSource(src, type);
}, },
expectedEvents: ['loadstart', 'durationchange', 'loadedmetadata', 'loadeddata'] 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: create:
function(src, type) { function(src, type) {
document.body.appendChild(gMedia); document.body.appendChild(gMedia);
@ -98,7 +99,7 @@ var gTests = [
addSource("404b", type); addSource("404b", type);
addSource(src, 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. // Test 3: video with bad src, good <source>, ensure that <source> aren't used.
create: create:
@ -123,7 +124,7 @@ var gTests = [
false); false);
document.body.appendChild(gMedia); 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 // 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 // a good <source> to the video, it shouldn't be selected, because the

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

@ -25,7 +25,7 @@ var manager = new MediaTestManager;
function finish(evt) { function finish(evt) {
var v = evt.target; 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._finished = true;
v.parentNode.removeChild(v); v.parentNode.removeChild(v);
manager.finished(v.token); manager.finished(v.token);
@ -33,8 +33,7 @@ function finish(evt) {
function errorHandler(evt) { function errorHandler(evt) {
evt.stopPropagation(); evt.stopPropagation();
evt.target._error = true; evt.target.parentNode._error = true;
finish(evt);
} }
var extenstion = { var extenstion = {
@ -56,6 +55,7 @@ function startTest(test, token) {
var s1 = document.createElement("source"); var s1 = document.createElement("source");
s1.type = test.type; s1.type = test.type;
s1.src = "404." + extenstion[test.type]; s1.src = "404." + extenstion[test.type];
s1.addEventListener("error", errorHandler, false);
v.appendChild(s1); v.appendChild(s1);
var s2 = document.createElement("source"); var s2 = document.createElement("source");
@ -63,9 +63,7 @@ function startTest(test, token) {
s2.src = test.name; s2.src = test.name;
v.appendChild(s2); v.appendChild(s2);
v.addEventListener("error", errorHandler, false);
v.addEventListener("loadeddata", finish, false); v.addEventListener("loadeddata", finish, false);
document.body.appendChild(v); document.body.appendChild(v);
} }

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

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

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

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

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

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

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

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

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

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

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

@ -38,12 +38,13 @@
* ***** END LICENSE BLOCK ***** */ * ***** END LICENSE BLOCK ***** */
#include "nsISupports.idl" #include "nsISupports.idl"
#include "nsIRunnable.idl"
/** /**
* Interface for the native event system layer. This interface is designed * Interface for the native event system layer. This interface is designed
* to be used on the main application thread only. * to be used on the main application thread only.
*/ */
[uuid(501403e9-a091-4780-ba55-cfd1e21287a1)] [uuid(40bc6280-ad83-471e-b197-80ab90e2065e)]
interface nsIAppShell : nsISupports interface nsIAppShell : nsISupports
{ {
/** /**
@ -101,4 +102,15 @@ interface nsIAppShell : nsISupports
* The current event loop nesting level. * The current event loop nesting level.
*/ */
readonly attribute unsigned long eventloopNestingLevel; 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); thr->Dispatch(mDummyEvent, NS_DISPATCH_NORMAL);
} }
// We're about to run an event, so we're in a stable state.
RunSyncSections();
return NS_OK; 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 // Called from the main thread
NS_IMETHODIMP NS_IMETHODIMP
nsBaseAppShell::AfterProcessNextEvent(nsIThreadInternal *thr, nsBaseAppShell::AfterProcessNextEvent(nsIThreadInternal *thr,
PRUint32 recursionDepth) PRUint32 recursionDepth)
{ {
// We've just finished running an event, so we're in a stable state.
RunSyncSections();
return NS_OK; return NS_OK;
} }
@ -343,3 +365,18 @@ nsBaseAppShell::Observe(nsISupports *subject, const char *topic,
Exit(); Exit();
return NS_OK; 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 "nsIThreadInternal.h"
#include "nsIObserver.h" #include "nsIObserver.h"
#include "nsIRunnable.h" #include "nsIRunnable.h"
#include "nsCOMArray.h"
#include "nsCOMPtr.h" #include "nsCOMPtr.h"
#include "prinrval.h" #include "prinrval.h"
@ -99,6 +100,11 @@ protected:
private: private:
PRBool DoProcessNextNativeEvent(PRBool mayWait); PRBool DoProcessNextNativeEvent(PRBool mayWait);
/**
* Runs all synchronous sections which are queued up in mSyncSections.
*/
void RunSyncSections();
nsCOMPtr<nsIRunnable> mDummyEvent; nsCOMPtr<nsIRunnable> mDummyEvent;
/** /**
* mBlockedWait points back to a slot that controls the wait loop in * 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 eEventloopOther // innermost native event loop is a native library/plugin etc
}; };
EventloopNestingState mEventloopNestingState; EventloopNestingState mEventloopNestingState;
nsCOMArray<nsIRunnable> mSyncSections;
PRPackedBool mRunning; PRPackedBool mRunning;
PRPackedBool mExiting; PRPackedBool mExiting;
/** /**