зеркало из https://github.com/mozilla/pjs.git
Bug 485288 - Update media load algorithm. r=roc a=blocking2.0
This commit is contained in:
Родитель
da9888206a
Коммит
4b32f2905e
|
@ -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;
|
||||
/**
|
||||
|
|
Загрузка…
Ссылка в новой задаче