From 4b32f2905e68bac45cf3e70f5e48e3531aa8e4cd Mon Sep 17 00:00:00 2001 From: Chris Pearce Date: Fri, 3 Sep 2010 12:03:03 +1200 Subject: [PATCH] Bug 485288 - Update media load algorithm. r=roc a=blocking2.0 --- .../html/content/public/nsHTMLMediaElement.h | 75 +++-- .../html/content/src/nsHTMLMediaElement.cpp | 315 +++++++++++------- content/media/nsBuiltinDecoder.cpp | 6 +- content/media/test/test_constants.html | 27 +- content/media/test/test_decode_error.html | 3 +- content/media/test/test_decoder_disable.html | 30 +- content/media/test/test_info_leak.html | 9 +- content/media/test/test_load.html | 9 +- content/media/test/test_load_candidates.html | 8 +- content/media/test/test_preload_actions.html | 39 ++- content/media/test/test_source_write.html | 4 +- content/media/wave/nsWaveDecoder.cpp | 7 +- .../html/nsIDOMHTMLMediaElement.idl | 5 +- parser/html/nsHtml5TreeBuilderCppSupplement.h | 4 +- widget/public/nsIAppShell.idl | 14 +- widget/src/xpwidgets/nsBaseAppShell.cpp | 37 ++ widget/src/xpwidgets/nsBaseAppShell.h | 7 + 17 files changed, 373 insertions(+), 226 deletions(-) diff --git a/content/html/content/public/nsHTMLMediaElement.h b/content/html/content/public/nsHTMLMediaElement.h index 1fae80cafc0..ee8b3400051 100644 --- a/content/html/content/public/nsHTMLMediaElement.h +++ b/content/html/content/public/nsHTMLMediaElement.h @@ -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 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 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 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 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/. + // 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 children, load is waiting at load algorithm step 1. - WAITING_FOR_SRC_OR_SOURCE, - // No src at load time, and all children don't resolve or - // give network errors during fetch, waiting for more 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/, 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 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 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 mPrintSurface; + // Reference to the source element last returned by GetNextSource(). + // This is the child source element which we're trying to load from. + nsCOMPtr mSourceLoadCandidate; + // An audio stream for writing audio directly from JS. nsAutoPtr mAudioStream; @@ -604,10 +623,6 @@ protected: // True if the sound is muted PRPackedBool mMuted; - // Flag to indicate if the child elements (eg. ) 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 diff --git a/content/html/content/src/nsHTMLMediaElement.cpp b/content/html/content/src/nsHTMLMediaElement.cpp index 6289ffaf392..782a548ba62 100644 --- a/content/html/content/src/nsHTMLMediaElement.cpp +++ b/content/html/content/src/nsHTMLMediaElement.cpp @@ -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 #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 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 event = new SelectResourceEvent(this); - NS_DispatchToMainThread(event); -} - -void nsHTMLMediaElement::QueueLoadFromSourceTask() -{ - ChangeDelayLoadStatus(PR_TRUE); - nsCOMPtr 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 event = new nsSyncSection(aElement, aClosure); + nsCOMPtr 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 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 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 =%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 uri = mPreloadURI; + nsCOMPtr 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 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 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 nsHTMLMediaElement::GetNextSource() +nsIContent* nsHTMLMediaElement::GetNextSource() { nsresult rv = NS_OK; nsCOMPtr thisDomNode = do_QueryInterface(static_cast(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 nsHTMLMediaElement::GetNextSource() nsIContent* child = GetChildAt(startOffset); - // If child is a element, it may be the next candidate. + // If child is a element, it is the next candidate. if (child && child->Tag() == nsGkAtoms::source && child->IsHTML()) { - nsCOMPtr 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 =%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!"); diff --git a/content/media/nsBuiltinDecoder.cpp b/content/media/nsBuiltinDecoder.cpp index faeb6aa177a..964b2d54a0f 100644 --- a/content/media/nsBuiltinDecoder.cpp +++ b/content/media/nsBuiltinDecoder.cpp @@ -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); diff --git a/content/media/test/test_constants.html b/content/media/test/test_constants.html index 9bff765e620..c459bd961cf 100644 --- a/content/media/test/test_constants.html +++ b/content/media/test/test_constants.html @@ -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); diff --git a/content/media/test/test_decode_error.html b/content/media/test/test_decode_error.html index 8c75b664216..e2324cf8daf 100644 --- a/content/media/test/test_decode_error.html +++ b/content/media/test/test_decode_error.html @@ -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); diff --git a/content/media/test/test_decoder_disable.html b/content/media/test/test_decoder_disable.html index 0eb2715fd3d..7967036e010 100644 --- a/content/media/test/test_decoder_disable.html +++ b/content/media/test/test_decoder_disable.html @@ -18,6 +18,14 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=448600
 
 
 
diff --git a/content/media/wave/nsWaveDecoder.cpp b/content/media/wave/nsWaveDecoder.cpp
index b54376fd4a0..91e1088c656 100644
--- a/content/media/wave/nsWaveDecoder.cpp
+++ b/content/media/wave/nsWaveDecoder.cpp
@@ -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();
diff --git a/dom/interfaces/html/nsIDOMHTMLMediaElement.idl b/dom/interfaces/html/nsIDOMHTMLMediaElement.idl
index d9a5cb05a0f..0a1280421eb 100644
--- a/dom/interfaces/html/nsIDOMHTMLMediaElement.idl
+++ b/dom/interfaces/html/nsIDOMHTMLMediaElement.idl
@@ -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;
diff --git a/parser/html/nsHtml5TreeBuilderCppSupplement.h b/parser/html/nsHtml5TreeBuilderCppSupplement.h
index 1f4311eb8da..2e49a61cc2d 100644
--- a/parser/html/nsHtml5TreeBuilderCppSupplement.h
+++ b/parser/html/nsHtml5TreeBuilderCppSupplement.h
@@ -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.");
diff --git a/widget/public/nsIAppShell.idl b/widget/public/nsIAppShell.idl
index d0227831407..ec4abec74dd 100644
--- a/widget/public/nsIAppShell.idl
+++ b/widget/public/nsIAppShell.idl
@@ -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);
 };
diff --git a/widget/src/xpwidgets/nsBaseAppShell.cpp b/widget/src/xpwidgets/nsBaseAppShell.cpp
index 1320eefb403..fe489d8e8c2 100644
--- a/widget/src/xpwidgets/nsBaseAppShell.cpp
+++ b/widget/src/xpwidgets/nsBaseAppShell.cpp
@@ -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; iRun();
+  }
+  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;
+}
diff --git a/widget/src/xpwidgets/nsBaseAppShell.h b/widget/src/xpwidgets/nsBaseAppShell.h
index bb991e77204..6924ab8e098 100644
--- a/widget/src/xpwidgets/nsBaseAppShell.h
+++ b/widget/src/xpwidgets/nsBaseAppShell.h
@@ -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 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 mSyncSections;
   PRPackedBool mRunning;
   PRPackedBool mExiting;
   /**