merge backout a=orange
authorMarco Bonardo <mbonardo@mozilla.com>
Sat, 11 Sep 2010 11:18:55 +0200
changeset 52465 73ab2c3c5ad9610af6375e94a938efa567c2e520
parent 52463 b49a60a88ef293e73db089a048230b3072219358 (current diff)
parent 52464 f085aacfb4d201d974c72885a9ad9261d89bdcca (diff)
child 52466 bf347202194887c0b5e7f3dac7996a5e6e6cf56a
push id1
push userroot
push dateTue, 26 Apr 2011 22:38:44 +0000
treeherdermozilla-beta@bfdb6e623a36 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersorange
milestone2.0b6pre
first release with
nightly linux32
73ab2c3c5ad9 / 4.0b6pre / 20100911030141 / files
nightly linux64
73ab2c3c5ad9 / 4.0b6pre / 20100911030639 / files
nightly mac
73ab2c3c5ad9 / 4.0b6pre / 20100911030647 / files
nightly win32
73ab2c3c5ad9 / 4.0b6pre / 20100911041541 / files
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
merge backout a=orange
content/html/content/public/nsHTMLMediaElement.h
content/html/content/src/nsHTMLMediaElement.cpp
content/media/nsBuiltinDecoder.cpp
content/media/nsMediaDecoder.cpp
content/media/wave/nsWaveDecoder.cpp
--- a/content/html/content/public/nsHTMLMediaElement.h
+++ b/content/html/content/public/nsHTMLMediaElement.h
@@ -180,18 +180,20 @@ public:
   // ImageContainer containing the video data.
   ImageContainer* GetImageContainer();
 
   // Called by the video frame to get the print surface, if this is
   // a static document and we're not actually playing video
   gfxASurface* GetPrintSurface() { return mPrintSurface; }
 
   // Dispatch events
-  nsresult DispatchEvent(const nsAString& aName);
-  nsresult DispatchAsyncEvent(const nsAString& aName);
+  nsresult DispatchSimpleEvent(const nsAString& aName);
+  nsresult DispatchProgressEvent(const nsAString& aName);
+  nsresult DispatchAsyncSimpleEvent(const nsAString& aName);
+  nsresult DispatchAsyncProgressEvent(const nsAString& aName);
   nsresult DispatchAudioAvailableEvent(float* aFrameBuffer,
                                        PRUint32 aFrameBufferLength,
                                        PRUint64 aTime);
 
   // Called by the decoder when some data has been downloaded or
   // buffering/seeking has ended. aNextFrameAvailable is true when
   // the data for the next frame is available. This method will
   // decide whether to set the ready state to HAVE_CURRENT_DATA,
--- a/content/html/content/src/nsHTMLMediaElement.cpp
+++ b/content/html/content/src/nsHTMLMediaElement.cpp
@@ -65,16 +65,17 @@
 #include "nsIXPConnect.h"
 #include "jsapi.h"
 
 #include "nsIRenderingContext.h"
 #include "nsITimer.h"
 
 #include "nsEventDispatcher.h"
 #include "nsIDOMDocumentEvent.h"
+#include "nsIDOMProgressEvent.h"
 #include "nsMediaError.h"
 #include "nsICategoryManager.h"
 #include "nsCharSeparatedTokenizer.h"
 #include "nsMediaStream.h"
 
 #include "nsIDOMHTMLVideoElement.h"
 #include "nsIContentPolicy.h"
 #include "nsContentPolicyUtils.h"
@@ -186,30 +187,33 @@ protected:
   nsRefPtr<nsHTMLMediaElement> mElement;
   PRUint32 mLoadID;
 };
 
 class nsAsyncEventRunner : public nsMediaEvent
 {
 private:
   nsString mName;
+  PRPackedBool mProgress;
 
 public:
-  nsAsyncEventRunner(const nsAString& aName, nsHTMLMediaElement* aElement) :
-    nsMediaEvent(aElement), mName(aName)
+  nsAsyncEventRunner(const nsAString& aName, nsHTMLMediaElement* aElement, PRBool aProgress) :
+    nsMediaEvent(aElement), mName(aName), mProgress(aProgress)
   {
   }
 
   NS_IMETHOD Run()
   {
     // Silently cancel if our load has been cancelled.
     if (IsCancelled())
       return NS_OK;
 
-    return mElement->DispatchEvent(mName);
+    return mProgress ?
+      mElement->DispatchProgressEvent(mName) :
+      mElement->DispatchSimpleEvent(mName);
   }
 };
 
 class nsSourceErrorEventRunner : public nsMediaEvent
 {
 private:
   nsCOMPtr<nsIContent> mSource;
 public:
@@ -481,17 +485,17 @@ void nsHTMLMediaElement::AbortExistingLo
     fireTimeUpdate = mDecoder->GetCurrentTime() != 0.0;
     mDecoder->Shutdown();
     mDecoder = nsnull;
   }
 
   if (mNetworkState == nsIDOMHTMLMediaElement::NETWORK_LOADING ||
       mNetworkState == nsIDOMHTMLMediaElement::NETWORK_IDLE)
   {
-    DispatchEvent(NS_LITERAL_STRING("abort"));
+    DispatchProgressEvent(NS_LITERAL_STRING("abort"));
   }
 
   mError = nsnull;
   mLoadedFirstFrame = PR_FALSE;
   mAutoplaying = PR_TRUE;
   mIsLoadingFromSrcAttribute = PR_FALSE;
   mSuspendedAfterFirstFrame = PR_FALSE;
   mAllowSuspendAfterFirstFrame = PR_TRUE;
@@ -504,35 +508,35 @@ void nsHTMLMediaElement::AbortExistingLo
     ChangeReadyState(nsIDOMHTMLMediaElement::HAVE_NOTHING);
     mPaused = PR_TRUE;
 
     if (fireTimeUpdate) {
       // Since we destroyed the decoder above, the current playback position
       // will now be reported as 0. The playback position was non-zero when
       // we destroyed the decoder, so fire a timeupdate event so that the
       // change will be reflected in the controls.
-      DispatchAsyncEvent(NS_LITERAL_STRING("timeupdate"));
+      DispatchAsyncSimpleEvent(NS_LITERAL_STRING("timeupdate"));
     }
-    DispatchEvent(NS_LITERAL_STRING("emptied"));
+    DispatchSimpleEvent(NS_LITERAL_STRING("emptied"));
   }
 
   // We may have changed mPaused, mAutoplaying, mNetworkState and other
   // things which can affect AddRemoveSelfReference
   AddRemoveSelfReference();
 
   mIsRunningSelectResource = PR_FALSE;
 }
 
 void nsHTMLMediaElement::NoSupportedMediaSourceError()
 {
   NS_ASSERTION(mDelayingLoadEvent, "Load event not delayed during source selection?");
 
   mError = new nsMediaError(nsIDOMMediaError::MEDIA_ERR_SRC_NOT_SUPPORTED);
   mNetworkState = nsIDOMHTMLMediaElement::NETWORK_NO_SOURCE;
-  DispatchAsyncEvent(NS_LITERAL_STRING("error"));
+  DispatchAsyncProgressEvent(NS_LITERAL_STRING("error"));
   // This clears mDelayingLoadEvent, so AddRemoveSelfReference will be called
   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:
@@ -640,17 +644,17 @@ void nsHTMLMediaElement::SelectResource(
     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
-  DispatchAsyncEvent(NS_LITERAL_STRING("loadstart"));
+  DispatchAsyncProgressEvent(NS_LITERAL_STRING("loadstart"));
 
   nsAutoString src;
   nsCOMPtr<nsIURI> uri;
 
   // If we have a 'src' attribute, use that exclusively.
   if (GetAttr(kNameSpaceID_None, nsGkAtoms::src, src)) {
     nsresult rv = NewURIFromString(src, getter_AddRefs(uri));
     if (NS_SUCCEEDED(rv)) {
@@ -792,17 +796,17 @@ void nsHTMLMediaElement::LoadFromSourceC
   }
   NS_NOTREACHED("Execution should not reach here!");
 }
 
 void nsHTMLMediaElement::SuspendLoad(nsIURI* aURI)
 {
   mLoadIsSuspended = PR_TRUE;
   mNetworkState = nsIDOMHTMLMediaElement::NETWORK_IDLE;
-  DispatchAsyncEvent(NS_LITERAL_STRING("suspend"));
+  DispatchAsyncProgressEvent(NS_LITERAL_STRING("suspend"));
   ChangeDelayLoadStatus(PR_FALSE);
 }
 
 void nsHTMLMediaElement::ResumeLoad(PreloadAction aAction)
 {
   NS_ASSERTION(mLoadIsSuspended, "Can only resume preload if halted for one");
   nsCOMPtr<nsIURI> uri = mLoadingSrc;
   mLoadIsSuspended = PR_FALSE;
@@ -1028,17 +1032,17 @@ nsresult nsHTMLMediaElement::LoadWithCha
   ChangeDelayLoadStatus(PR_TRUE);
 
   nsresult rv = InitializeDecoderForChannel(aChannel, aListener);
   if (NS_FAILED(rv)) {
     ChangeDelayLoadStatus(PR_FALSE);
     return rv;
   }
 
-  DispatchAsyncEvent(NS_LITERAL_STRING("loadstart"));
+  DispatchAsyncProgressEvent(NS_LITERAL_STRING("loadstart"));
 
   return NS_OK;
 }
 
 NS_IMETHODIMP nsHTMLMediaElement::MozLoadFrom(nsIDOMHTMLMediaElement* aOther)
 {
   NS_ENSURE_ARG_POINTER(aOther);
 
@@ -1052,17 +1056,17 @@ NS_IMETHODIMP nsHTMLMediaElement::MozLoa
   ChangeDelayLoadStatus(PR_TRUE);
 
   nsresult rv = InitializeDecoderAsClone(other->mDecoder);
   if (NS_FAILED(rv)) {
     ChangeDelayLoadStatus(PR_FALSE);
     return rv;
   }
 
-  DispatchAsyncEvent(NS_LITERAL_STRING("loadstart"));
+  DispatchAsyncProgressEvent(NS_LITERAL_STRING("loadstart"));
 
   return NS_OK;
 }
 
 /* readonly attribute unsigned short readyState; */
 NS_IMETHODIMP nsHTMLMediaElement::GetReadyState(PRUint16 *aReadyState)
 {
   *aReadyState = mReadyState;
@@ -1152,18 +1156,18 @@ NS_IMETHODIMP nsHTMLMediaElement::Pause(
 
   PRBool oldPaused = mPaused;
   mPaused = PR_TRUE;
   mAutoplaying = PR_FALSE;
   // We changed mPaused and mAutoplaying which can affect AddRemoveSelfReference
   AddRemoveSelfReference();
 
   if (!oldPaused) {
-    DispatchAsyncEvent(NS_LITERAL_STRING("timeupdate"));
-    DispatchAsyncEvent(NS_LITERAL_STRING("pause"));
+    DispatchAsyncSimpleEvent(NS_LITERAL_STRING("timeupdate"));
+    DispatchAsyncSimpleEvent(NS_LITERAL_STRING("pause"));
   }
 
   return NS_OK;
 }
 
 /* attribute float volume; */
 NS_IMETHODIMP nsHTMLMediaElement::GetVolume(float *aVolume)
 {
@@ -1183,17 +1187,17 @@ NS_IMETHODIMP nsHTMLMediaElement::SetVol
   mVolume = aVolume;
 
   if (mDecoder && !mMuted) {
     mDecoder->SetVolume(mVolume);
   } else if (mAudioStream && !mMuted) {
     mAudioStream->SetVolume(mVolume);
   }
 
-  DispatchAsyncEvent(NS_LITERAL_STRING("volumechange"));
+  DispatchAsyncSimpleEvent(NS_LITERAL_STRING("volumechange"));
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsHTMLMediaElement::GetMozChannels(PRUint32 *aMozChannels)
 {
   if (!mDecoder && !mAudioStream) {
@@ -1253,17 +1257,17 @@ NS_IMETHODIMP nsHTMLMediaElement::SetMut
   mMuted = aMuted;
 
   if (mDecoder) {
     mDecoder->SetVolume(mMuted ? 0.0 : mVolume);
   } else if (mAudioStream) {
     mAudioStream->SetVolume(mMuted ? 0.0 : mVolume);
   }
 
-  DispatchAsyncEvent(NS_LITERAL_STRING("volumechange"));
+  DispatchAsyncSimpleEvent(NS_LITERAL_STRING("volumechange"));
 
   return NS_OK;
 }
 
 nsHTMLMediaElement::nsHTMLMediaElement(already_AddRefed<nsINodeInfo> aNodeInfo,
                                        PRUint32 aFromParser)
   : nsGenericHTMLElement(aNodeInfo),
     mCurrentLoadID(0),
@@ -1376,25 +1380,25 @@ NS_IMETHODIMP nsHTMLMediaElement::Play()
       NS_ENSURE_SUCCESS(rv, rv);
     }
   }
 
   // TODO: If the playback has ended, then the user agent must set
   // seek to the effective start.
   // TODO: The playback rate must be set to the default playback rate.
   if (mPaused) {
-    DispatchAsyncEvent(NS_LITERAL_STRING("play"));
+    DispatchAsyncSimpleEvent(NS_LITERAL_STRING("play"));
     switch (mReadyState) {
     case nsIDOMHTMLMediaElement::HAVE_METADATA:
     case nsIDOMHTMLMediaElement::HAVE_CURRENT_DATA:
-      DispatchAsyncEvent(NS_LITERAL_STRING("waiting"));
+      DispatchAsyncSimpleEvent(NS_LITERAL_STRING("waiting"));
       break;
     case nsIDOMHTMLMediaElement::HAVE_FUTURE_DATA:
     case nsIDOMHTMLMediaElement::HAVE_ENOUGH_DATA:
-      DispatchAsyncEvent(NS_LITERAL_STRING("playing"));
+      DispatchAsyncSimpleEvent(NS_LITERAL_STRING("playing"));
       break;
     }
   }
 
   mPaused = PR_FALSE;
   mAutoplaying = PR_FALSE;
   // We changed mPaused and mAutoplaying which can affect AddRemoveSelfReference
   AddRemoveSelfReference();
@@ -1940,18 +1944,18 @@ nsresult nsHTMLMediaElement::NewURIFromS
   return NS_OK;
 }
 
 void nsHTMLMediaElement::MetadataLoaded(PRUint32 aChannels, PRUint32 aRate)
 {
   mChannels = aChannels;
   mRate = aRate;
   ChangeReadyState(nsIDOMHTMLMediaElement::HAVE_METADATA);
-  DispatchAsyncEvent(NS_LITERAL_STRING("durationchange"));
-  DispatchAsyncEvent(NS_LITERAL_STRING("loadedmetadata"));
+  DispatchAsyncSimpleEvent(NS_LITERAL_STRING("durationchange"));
+  DispatchAsyncSimpleEvent(NS_LITERAL_STRING("loadedmetadata"));
 }
 
 void nsHTMLMediaElement::FirstFrameLoaded(PRBool aResourceFullyLoaded)
 {
   ChangeReadyState(nsIDOMHTMLMediaElement::HAVE_CURRENT_DATA);
   ChangeDelayLoadStatus(PR_FALSE);
 
   NS_ASSERTION(!mSuspendedAfterFirstFrame, "Should not have already suspended");
@@ -1967,19 +1971,19 @@ void nsHTMLMediaElement::FirstFrameLoade
 
 void nsHTMLMediaElement::ResourceLoaded()
 {
   mBegun = PR_FALSE;
   mNetworkState = nsIDOMHTMLMediaElement::NETWORK_IDLE;
   AddRemoveSelfReference();
   ChangeReadyState(nsIDOMHTMLMediaElement::HAVE_ENOUGH_DATA);
   // Ensure a progress event is dispatched at the end of download.
-  DispatchAsyncEvent(NS_LITERAL_STRING("progress"));
+  DispatchAsyncProgressEvent(NS_LITERAL_STRING("progress"));
   // The download has stopped.
-  DispatchAsyncEvent(NS_LITERAL_STRING("suspend"));
+  DispatchAsyncSimpleEvent(NS_LITERAL_STRING("suspend"));
 }
 
 void nsHTMLMediaElement::NetworkError()
 {
   Error(nsIDOMMediaError::MEDIA_ERR_NETWORK);
 }
 
 void nsHTMLMediaElement::DecodeError()
@@ -1995,72 +1999,72 @@ void nsHTMLMediaElement::LoadAborted()
 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;
-  DispatchAsyncEvent(NS_LITERAL_STRING("error"));
+  DispatchAsyncProgressEvent(NS_LITERAL_STRING("error"));
   if (mReadyState == nsIDOMHTMLMediaElement::HAVE_NOTHING) {
     mNetworkState = nsIDOMHTMLMediaElement::NETWORK_EMPTY;
-    DispatchAsyncEvent(NS_LITERAL_STRING("emptied"));
+    DispatchAsyncSimpleEvent(NS_LITERAL_STRING("emptied"));
   } else {
     mNetworkState = nsIDOMHTMLMediaElement::NETWORK_IDLE;
   }
   AddRemoveSelfReference();
   ChangeDelayLoadStatus(PR_FALSE);
 }
 
 void nsHTMLMediaElement::PlaybackEnded()
 {
   NS_ASSERTION(mDecoder->IsEnded(), "Decoder fired ended, but not in ended state");
   // We changed the state of IsPlaybackEnded which can affect AddRemoveSelfReference
   AddRemoveSelfReference();
 
-  DispatchAsyncEvent(NS_LITERAL_STRING("ended"));
+  DispatchAsyncSimpleEvent(NS_LITERAL_STRING("ended"));
 }
 
 void nsHTMLMediaElement::SeekStarted()
 {
-  DispatchAsyncEvent(NS_LITERAL_STRING("seeking"));
-  DispatchAsyncEvent(NS_LITERAL_STRING("timeupdate"));
+  DispatchAsyncSimpleEvent(NS_LITERAL_STRING("seeking"));
+  DispatchAsyncSimpleEvent(NS_LITERAL_STRING("timeupdate"));
 }
 
 void nsHTMLMediaElement::SeekCompleted()
 {
   mPlayingBeforeSeek = PR_FALSE;
   SetPlayedOrSeeked(PR_TRUE);
-  DispatchAsyncEvent(NS_LITERAL_STRING("seeked"));
+  DispatchAsyncSimpleEvent(NS_LITERAL_STRING("seeked"));
   // We changed whether we're seeking so we need to AddRemoveSelfReference
   AddRemoveSelfReference();
 }
 
 void nsHTMLMediaElement::DownloadSuspended()
 {
   if (mBegun) {
     mNetworkState = nsIDOMHTMLMediaElement::NETWORK_IDLE;
     AddRemoveSelfReference();
-    DispatchAsyncEvent(NS_LITERAL_STRING("suspend"));
+    DispatchAsyncSimpleEvent(NS_LITERAL_STRING("suspend"));
   }
 }
 
 void nsHTMLMediaElement::DownloadResumed()
 {
   if (mBegun) {
     mNetworkState = nsIDOMHTMLMediaElement::NETWORK_LOADING;
     AddRemoveSelfReference();
   }
 }
 
 void nsHTMLMediaElement::DownloadStalled()
 {
   if (mNetworkState == nsIDOMHTMLMediaElement::NETWORK_LOADING) {
-    DispatchAsyncEvent(NS_LITERAL_STRING("stalled"));
+    DispatchAsyncProgressEvent(NS_LITERAL_STRING("stalled"));
   }
 }
 
 PRBool nsHTMLMediaElement::ShouldCheckAllowOrigin()
 {
   return nsContentUtils::GetBoolPref("media.enforce_same_site_origin",
                                      PR_TRUE);
 }
@@ -2073,17 +2077,17 @@ void nsHTMLMediaElement::UpdateReadyStat
     // a chance to run.
     // The arrival of more data can't change us out of this readyState.
     return;
   }
 
   if (aNextFrame != NEXT_FRAME_AVAILABLE) {
     ChangeReadyState(nsIDOMHTMLMediaElement::HAVE_CURRENT_DATA);
     if (!mWaitingFired && aNextFrame == NEXT_FRAME_UNAVAILABLE_BUFFERING) {
-      DispatchAsyncEvent(NS_LITERAL_STRING("waiting"));
+      DispatchAsyncSimpleEvent(NS_LITERAL_STRING("waiting"));
       mWaitingFired = PR_TRUE;
     }
     return;
   }
 
   // Now see if we should set HAVE_ENOUGH_DATA.
   // If it's something we don't know the size of, then we can't
   // make a real estimate, so we go straight to HAVE_ENOUGH_DATA once
@@ -2123,49 +2127,49 @@ void nsHTMLMediaElement::ChangeReadyStat
     return;
   }
 
   LOG(PR_LOG_DEBUG, ("%p Ready state changed to %s", this, gReadyStateToString[aState]));
 
   // Handle raising of "waiting" event during seek (see 4.8.10.9)
   if (mPlayingBeforeSeek &&
       oldState < nsIDOMHTMLMediaElement::HAVE_FUTURE_DATA) {
-    DispatchAsyncEvent(NS_LITERAL_STRING("waiting"));
+    DispatchAsyncSimpleEvent(NS_LITERAL_STRING("waiting"));
   }
 
   if (oldState < nsIDOMHTMLMediaElement::HAVE_CURRENT_DATA &&
       mReadyState >= nsIDOMHTMLMediaElement::HAVE_CURRENT_DATA &&
       !mLoadedFirstFrame)
   {
-    DispatchAsyncEvent(NS_LITERAL_STRING("loadeddata"));
+    DispatchAsyncSimpleEvent(NS_LITERAL_STRING("loadeddata"));
     mLoadedFirstFrame = PR_TRUE;
   }
 
   if (mReadyState == nsIDOMHTMLMediaElement::HAVE_CURRENT_DATA) {
     mWaitingFired = PR_FALSE;
   }
 
   if (oldState < nsIDOMHTMLMediaElement::HAVE_FUTURE_DATA &&
       mReadyState >= nsIDOMHTMLMediaElement::HAVE_FUTURE_DATA) {
-    DispatchAsyncEvent(NS_LITERAL_STRING("canplay"));
+    DispatchAsyncSimpleEvent(NS_LITERAL_STRING("canplay"));
   }
 
   if (mReadyState == nsIDOMHTMLMediaElement::HAVE_ENOUGH_DATA) {
     NotifyAutoplayDataReady();
   }
 
   if (oldState < nsIDOMHTMLMediaElement::HAVE_FUTURE_DATA &&
       mReadyState >= nsIDOMHTMLMediaElement::HAVE_FUTURE_DATA &&
       IsPotentiallyPlaying()) {
-    DispatchAsyncEvent(NS_LITERAL_STRING("playing"));
+    DispatchAsyncSimpleEvent(NS_LITERAL_STRING("playing"));
   }
 
   if (oldState < nsIDOMHTMLMediaElement::HAVE_ENOUGH_DATA &&
       mReadyState >= nsIDOMHTMLMediaElement::HAVE_ENOUGH_DATA) {
-    DispatchAsyncEvent(NS_LITERAL_STRING("canplaythrough"));
+    DispatchAsyncSimpleEvent(NS_LITERAL_STRING("canplaythrough"));
   }
 }
 
 PRBool nsHTMLMediaElement::CanActivateAutoplay()
 {
   return mAutoplaying &&
          mPaused &&
          HasAttr(kNameSpaceID_None, nsGkAtoms::autoplay) &&
@@ -2178,17 +2182,17 @@ void nsHTMLMediaElement::NotifyAutoplayD
     mPaused = PR_FALSE;
     // We changed mPaused which can affect AddRemoveSelfReference
     AddRemoveSelfReference();
 
     if (mDecoder) {
       SetPlayedOrSeeked(PR_TRUE);
       mDecoder->Play();
     }
-    DispatchAsyncEvent(NS_LITERAL_STRING("play"));
+    DispatchAsyncSimpleEvent(NS_LITERAL_STRING("play"));
   }
 }
 
 ImageContainer* nsHTMLMediaElement::GetImageContainer()
 {
   if (mImageContainer)
     return mImageContainer;
 
@@ -2235,38 +2239,77 @@ nsresult nsHTMLMediaElement::DispatchAud
                                                     PR_TRUE, PR_TRUE, frameBuffer.forget(), aFrameBufferLength,
                                                     (float)aTime / MS_PER_SECOND, mAllowAudioData);
   NS_ENSURE_SUCCESS(rv, rv);
 
   PRBool dummy;
   return target->DispatchEvent(event, &dummy);
 }
 
-nsresult nsHTMLMediaElement::DispatchEvent(const nsAString& aName)
+nsresult nsHTMLMediaElement::DispatchSimpleEvent(const nsAString& aName)
 {
-  LOG_EVENT(PR_LOG_DEBUG, ("%p Dispatching event %s", this,
+  LOG_EVENT(PR_LOG_DEBUG, ("%p Dispatching simple event %s", this,
                           NS_ConvertUTF16toUTF8(aName).get()));
 
   return nsContentUtils::DispatchTrustedEvent(GetOwnerDoc(),
                                               static_cast<nsIContent*>(this),
                                               aName,
                                               PR_TRUE,
                                               PR_TRUE);
 }
 
-nsresult nsHTMLMediaElement::DispatchAsyncEvent(const nsAString& aName)
+nsresult nsHTMLMediaElement::DispatchAsyncSimpleEvent(const nsAString& aName)
 {
-  LOG_EVENT(PR_LOG_DEBUG, ("%p Queuing event %s", this,
-            NS_ConvertUTF16toUTF8(aName).get()));
+  LOG_EVENT(PR_LOG_DEBUG, ("%p Queuing simple event %s", this, NS_ConvertUTF16toUTF8(aName).get()));
 
-  nsCOMPtr<nsIRunnable> event = new nsAsyncEventRunner(aName, this);
+  nsCOMPtr<nsIRunnable> event = new nsAsyncEventRunner(aName, this, PR_FALSE);
+  NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL);
+  return NS_OK;
+}
+
+nsresult nsHTMLMediaElement::DispatchAsyncProgressEvent(const nsAString& aName)
+{
+  LOG_EVENT(PR_LOG_DEBUG, ("%p Queuing progress event %s", this, NS_ConvertUTF16toUTF8(aName).get()));
+
+  nsCOMPtr<nsIRunnable> event = new nsAsyncEventRunner(aName, this, PR_TRUE);
   NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL);
   return NS_OK;
 }
 
+nsresult nsHTMLMediaElement::DispatchProgressEvent(const nsAString& aName)
+{
+  nsCOMPtr<nsIDOMDocumentEvent> docEvent(do_QueryInterface(GetOwnerDoc()));
+  nsCOMPtr<nsIDOMEventTarget> target(do_QueryInterface(static_cast<nsIContent*>(this)));
+  NS_ENSURE_TRUE(docEvent && target, NS_ERROR_INVALID_ARG);
+
+  nsCOMPtr<nsIDOMEvent> event;
+  nsresult rv = docEvent->CreateEvent(NS_LITERAL_STRING("ProgressEvent"), getter_AddRefs(event));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsCOMPtr<nsIDOMProgressEvent> progressEvent(do_QueryInterface(event));
+  NS_ENSURE_TRUE(progressEvent, NS_ERROR_FAILURE);
+
+  PRInt64 totalBytes = 0;
+  PRUint64 downloadPosition = 0;
+  if (mDecoder) {
+    nsMediaDecoder::Statistics stats = mDecoder->GetStatistics();
+    totalBytes = stats.mTotalBytes;
+    downloadPosition = stats.mDownloadPosition;
+  }
+  rv = progressEvent->InitProgressEvent(aName, PR_TRUE, PR_TRUE,
+    totalBytes >= 0, downloadPosition, totalBytes);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  LOG_EVENT(PR_LOG_DEBUG, ("%p Dispatching progress event %s", this,
+                          NS_ConvertUTF16toUTF8(aName).get()));
+
+  PRBool dummy;
+  return target->DispatchEvent(event, &dummy);
+}
+
 PRBool nsHTMLMediaElement::IsPotentiallyPlaying() const
 {
   // TODO:
   //   playback has not stopped due to errors,
   //   and the element has not paused for user interaction
   return
     !mPaused &&
     (mReadyState == nsIDOMHTMLMediaElement::HAVE_ENOUGH_DATA ||
@@ -2526,14 +2569,12 @@ nsHTMLMediaElement::CopyInnerTo(nsGeneri
   return rv;
 }
 
 nsresult nsHTMLMediaElement::GetBuffered(nsIDOMTimeRanges** aBuffered)
 {
   nsTimeRanges* ranges = new nsTimeRanges();
   NS_ADDREF(*aBuffered = ranges);
   if (mReadyState >= nsIDOMHTMLMediaElement::HAVE_CURRENT_DATA && mDecoder) {
-    // If GetBuffered fails we ignore the error result and just return the
-    // time ranges we found up till the error.
-    mDecoder->GetBuffered(ranges);
+    return mDecoder->GetBuffered(ranges);
   }
   return NS_OK;
 }
--- a/content/media/nsBuiltinDecoder.cpp
+++ b/content/media/nsBuiltinDecoder.cpp
@@ -354,17 +354,17 @@ void nsBuiltinDecoder::MetadataLoaded(PR
   }
 
   if (!mResourceLoaded) {
     StartProgress();
   }
   else if (mElement) {
     // Resource was loaded during metadata loading, when progress
     // events are being ignored. Fire the final progress event.
-    mElement->DispatchAsyncEvent(NS_LITERAL_STRING("progress"));
+    mElement->DispatchAsyncProgressEvent(NS_LITERAL_STRING("progress"));
   }
 
   // Only inform the element of FirstFrameLoaded if not doing a load() in order
   // to fulfill a seek, otherwise we'll get multiple loadedfirstframe events.
   MonitorAutoEnter mon(mMonitor);
   PRBool resourceIsLoaded = !mResourceLoaded && mStream &&
     mStream->IsDataCachedToEndOfStream(mDecoderPosition);
   if (mElement && notifyElement) {
@@ -768,32 +768,32 @@ void nsBuiltinDecoder::PlaybackPositionC
 
   // Invalidate the frame so any video data is displayed.
   // Do this before the timeupdate event so that if that
   // event runs JavaScript that queries the media size, the
   // frame has reflowed and the size updated beforehand.
   Invalidate();
 
   if (mElement && lastTime != mCurrentTime) {
-    mElement->DispatchEvent(NS_LITERAL_STRING("timeupdate"));
+    mElement->DispatchSimpleEvent(NS_LITERAL_STRING("timeupdate"));
   }
 }
 
 void nsBuiltinDecoder::DurationChanged()
 {
   NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
   MonitorAutoEnter mon(mMonitor);
   PRInt64 oldDuration = mDuration;
   mDuration = mDecoderStateMachine ? mDecoderStateMachine->GetDuration() : -1;
   // Duration has changed so we should recompute playback rate
   UpdatePlaybackRate();
 
   if (mElement && oldDuration != mDuration) {
     LOG(PR_LOG_DEBUG, ("%p duration changed to %lldms", this, mDuration));
-    mElement->DispatchEvent(NS_LITERAL_STRING("durationchange"));
+    mElement->DispatchSimpleEvent(NS_LITERAL_STRING("durationchange"));
   }
 }
 
 void nsBuiltinDecoder::SetDuration(PRInt64 aDuration)
 {
   NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
   mDuration = aDuration;
 
--- a/content/media/nsMediaDecoder.cpp
+++ b/content/media/nsMediaDecoder.cpp
@@ -195,17 +195,17 @@ void nsMediaDecoder::Progress(PRBool aTi
   }
 
   // If PROGRESS_MS has passed since the last progress event fired and more
   // data has arrived since then, fire another progress event.
   if ((mProgressTime.IsNull() ||
        now - mProgressTime >= TimeDuration::FromMilliseconds(PROGRESS_MS)) &&
       !mDataTime.IsNull() &&
       now - mDataTime <= TimeDuration::FromMilliseconds(PROGRESS_MS)) {
-    mElement->DispatchAsyncEvent(NS_LITERAL_STRING("progress"));
+    mElement->DispatchAsyncProgressEvent(NS_LITERAL_STRING("progress"));
     mProgressTime = now;
   }
 
   if (!mDataTime.IsNull() &&
       now - mDataTime >= TimeDuration::FromMilliseconds(STALL_MS)) {
     mElement->DownloadStalled();
     // Null it out
     mDataTime = TimeStamp();
--- a/content/media/ogg/nsOggReader.cpp
+++ b/content/media/ogg/nsOggReader.cpp
@@ -279,20 +279,24 @@ nsresult nsOggReader::ReadMetadata()
       s->Deactivate();
     }
   }
 
   // Initialize the first Theora and Vorbis bitstreams. According to the
   // Theora spec these can be considered the 'primary' bitstreams for playback.
   // Extract the metadata needed from these streams.
   // Set a default callback period for if we have no video data
-  if (mTheoraState && mTheoraState->Init()) {
-    gfxIntSize sz(mTheoraState->mInfo.pic_width,
-                  mTheoraState->mInfo.pic_height);
-    mDecoder->SetVideoData(sz, mTheoraState->mPixelAspectRatio, nsnull);
+  if (mTheoraState) {
+    if (mTheoraState->Init()) {
+      gfxIntSize sz(mTheoraState->mInfo.pic_width,
+                    mTheoraState->mInfo.pic_height);
+      mDecoder->SetVideoData(sz, mTheoraState->mPixelAspectRatio, nsnull);
+    } else {
+      mTheoraState = nsnull;
+    }
   }
   if (mVorbisState) {
     mVorbisState->Init();
   }
 
   if (!HasAudio() && !HasVideo() && mSkeletonState) {
     // We have a skeleton track, but no audio or video, may as well disable
     // the skeleton, we can't do anything useful with this media.
@@ -901,17 +905,17 @@ PRInt64 nsOggReader::FindEndTime(PRInt64
   PRBool mustBackOff = PR_FALSE;
   while (PR_TRUE) {
     ogg_page page;    
     int ret = ogg_sync_pageseek(aState, &page);
     if (ret == 0) {
       // We need more data if we've not encountered a page we've seen before,
       // or we've read to the end of file.
       if (mustBackOff || readHead == aEndOffset) {
-        if (endTime != -1 || readStartOffset == 0) {
+        if (endTime != -1) {
           // We have encountered a page before, or we're at the end of file.
           break;
         }
         mustBackOff = PR_FALSE;
         prevChecksumAfterSeek = checksumAfterSeek;
         checksumAfterSeek = 0;
         ogg_sync_reset(aState);
         readStartOffset = NS_MAX(static_cast<PRInt64>(0), readStartOffset - step);
@@ -1533,26 +1537,16 @@ nsresult nsOggReader::SeekBisection(PRIn
   SEEK_LOG(PR_LOG_DEBUG, ("Seek complete in %d bisections.", hops));
 
   return NS_OK;
 }
 
 nsresult nsOggReader::GetBuffered(nsTimeRanges* aBuffered, PRInt64 aStartTime)
 {
   NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
-
-  // HasAudio and HasVideo are not used here as they take a lock and cause
-  // a deadlock. Accessing mInfo doesn't require a lock - it doesn't change
-  // after metadata is read and GetBuffered isn't called before metadata is
-  // read.
-  if (!mInfo.mHasVideo && !mInfo.mHasAudio) {
-    // No need to search through the file if there are no audio or video tracks
-    return NS_OK;
-  }
-
   nsMediaStream* stream = mDecoder->GetCurrentStream();
 
   // Traverse across the buffered byte ranges, determining the time ranges
   // they contain. nsMediaStream::GetNextCachedData(offset) returns -1 when
   // offset is after the end of the media stream, or there's no more cached
   // data after the offset. This loop will run until we've checked every
   // buffered range in the media, in increasing order of offset.
   ogg_sync_state state;
@@ -1605,26 +1599,16 @@ nsresult nsOggReader::GetBuffered(nsTime
 
       PRUint32 serial = ogg_page_serialno(&page);
       nsOggCodecState* codecState = nsnull;
       mCodecStates.Get(serial, &codecState);
       if (codecState && codecState->mActive) {
         startTime = codecState->Time(granulepos) - aStartTime;
         NS_ASSERTION(startTime > 0, "Must have positive start time");
       }
-      else if(codecState) {
-        // Page is for an inactive stream, skip it.
-        startOffset += page.header_len + page.body_len;
-        continue;
-      }
-      else {
-        // Page is for a stream we don't know about (possibly a chained
-        // ogg), return an error.
-        return PAGE_SYNC_ERROR;
-      }
     }
 
     if (startTime != -1) {
       // We were able to find a start time for that range, see if we can
       // find an end time.
       PRInt64 endTime = FindEndTime(endOffset, PR_TRUE, &state);
       if (endTime != -1) {
         endTime -= aStartTime;
--- a/content/media/test/test_progress.html
+++ b/content/media/test/test_progress.html
@@ -12,16 +12,20 @@
 <script src="use_large_cache.js"></script>
 <script class="testbody" type="text/javascript">
 
 var manager = new MediaTestManager;
 
 function do_progress(e) {
   var v = e.target;
   ok(!v._finished, "Check no progress events after completed for " + v._name);
+  ok(e.lengthComputable, "Check progress lengthComputable for " + v._name);
+  v._last_progress_total = e.loaded;
+  ok(e.loaded <= e.total, "Check progress in bounds: " + e.loaded + " for " + v._name);
+  is(e.total, v._size, "Check progress total for " + v._name);
 }
 
 function do_ended(e) {
   var v = e.target;
   ok(!v._finished, "Only one ended event for " + v._name);
   v._finished = true;
   v.parentNode.removeChild(v);
   manager.finished(v.token);
@@ -30,17 +34,19 @@ function do_ended(e) {
 function startTest(test, token) {
   var type = /^video/.test(test.type) ? "video" : "audio";
   var v = document.createElement(type);
   v.token = token;
   manager.started(token);
   v.src = test.name;
   v.autoplay = true;
   v._name = test.name;
+  v._size = test.size;
   v._finished = false;
+  v._last_progress_total = 0;
   v.addEventListener("ended", do_ended, false);
   v.addEventListener("progress", do_progress, false);
   document.body.appendChild(v);
 }
 
 manager.runTests(gProgressTests, startTest);
 
 </script>
--- a/content/media/wave/nsWaveDecoder.cpp
+++ b/content/media/wave/nsWaveDecoder.cpp
@@ -1673,17 +1673,17 @@ nsWaveDecoder::PlaybackPositionChanged()
   float lastTime = mCurrentTime;
 
   if (mPlaybackStateMachine) {
     mCurrentTime = mPlaybackStateMachine->GetTimeForPositionChange();
   }
 
   if (mElement && lastTime != mCurrentTime) {
     UpdateReadyStateForData();
-    mElement->DispatchEvent(NS_LITERAL_STRING("timeupdate"));
+    mElement->DispatchSimpleEvent(NS_LITERAL_STRING("timeupdate"));
   }
 }
 
 void
 nsWaveDecoder::SetDuration(PRInt64 /* aDuration */)
 {
   // Ignored by the wave decoder since we can compute the
   // duration directly from the wave data itself.
--- a/toolkit/content/widgets/videocontrols.xml
+++ b/toolkit/content/widgets/videocontrols.xml
@@ -363,18 +363,18 @@
                     // If the first frame hasn't loaded, kick off a throbber fade-in.
                     if (this.video.readyState >= this.video.HAVE_CURRENT_DATA)
                         this.firstFrameShown = true;
 
                     // We can't determine the exact buffering status, but do know if it's
                     // fully loaded. (If it's still loading, it will fire a progress event
                     // and we'll figure out the exact state then.)
                     this.bufferBar.setAttribute("max", 100);
-                    if (this.video.readyState >= this.video.HAVE_METADATA)
-                        this.showBuffered();
+                    if (this.video.networkState == this.video.NETWORK_LOADED)
+                        this.bufferBar.setAttribute("value", 100);
                     else
                         this.bufferBar.setAttribute("value", 0);
 
                     // Set the current status icon.
                     if (this.video.error) {
                         this.statusIcon.setAttribute("type", "error");
                         this.setupStatusFader(true);
                     } else {
@@ -453,17 +453,25 @@
                             this.isAudioOnly = (this.video instanceof HTMLAudioElement);
                             this.setPlayButtonState(true);
                             break;
                         case "durationchange":
                             var duration = Math.round(this.video.duration * 1000); // in ms
                             this.durationChange(duration);
                             break;
                         case "progress":
-                            this.showBuffered();
+                            var loaded = aEvent.loaded;
+                            var total = aEvent.total;
+                            this.log("+++ load, " + loaded + " of " + total);
+                            // When the source is streaming, the value of .total is -1. Set the
+                            // progress bar to the maximum, since it's not useful.
+                            if (total == -1)
+                                total = loaded;
+                            this.bufferBar.max = total;
+                            this.bufferBar.value = loaded;
                             this.setupStatusFader();
                             break;
                         case "suspend":
                             this.setupStatusFader();
                             break;
                         case "timeupdate":
                             var currentTime = Math.round(this.video.currentTime * 1000); // in ms
                             var duration = Math.round(this.video.duration * 1000); // in ms
@@ -495,20 +503,16 @@
                                 return;
                             this.lastTimeUpdate = currentTime;
                             this.showPosition(currentTime, duration);
                             break;
                         case "emptied":
                             this.bufferBar.value = 0;
                             break;
                         case "seeking":
-                            this.showBuffered();
-                            this.statusIcon.setAttribute("type", "throbber");
-                            this.setupStatusFader();
-                            break;
                         case "waiting":
                             this.statusIcon.setAttribute("type", "throbber");
                             this.setupStatusFader();
                             break;
                         case "seeked":
                         case "playing":
                         case "canplay":
                         case "canplaythrough":
@@ -590,63 +594,16 @@
                         duration = this.maxCurrentTimeSeen;
                         this.durationChange(duration);
                     }
 
                     this.log("time update @ " + currentTime + "ms of " + duration + "ms");
                     this.scrubber.value = currentTime;
                 },
 
-                showBuffered : function() {
-                    function bsearch(haystack, needle, cmp) {
-                        var length = haystack.length;
-                        var low = 0;
-                        var high = length;
-                        while (high - low > 1) {
-                            var probe = low + ((high - low) >> 1);
-                            var r = cmp(haystack, probe, needle);
-                            if (r == 0) {
-                                low = probe;
-                                break;
-                            } else if (r > 0) {
-                                low = probe + 1;
-                            } else {
-                                high = probe;
-                            }
-                        }
-                        return low < length ? low : -1;
-                    }
-
-                    function bufferedCompare(buffered, i, time) {
-                        if (time > buffered.end(i)) {
-                            return 1;
-                        } else if (time >= buffered.start(i)) {
-                            return 0;
-                        }
-                        return -1;
-                    }
-
-                    var duration = Math.round(this.video.duration * 1000);
-                    if (isNaN(duration))
-                        duration = this.maxCurrentTimeSeen;
-
-                    // Find the range that the current play position is in and use that
-                    // range for bufferBar.  At some point we may support multiple ranges
-                    // displayed in the bar.
-                    var currentTime = this.video.currentTime;
-                    var buffered = this.video.buffered;
-                    var index = bsearch(buffered, currentTime, bufferedCompare);
-                    var endTime = 0;
-                    if (index >= 0) {
-                        endTime = Math.round(buffered.end(index) * 1000);
-                    }
-                    this.bufferBar.max = duration;
-                    this.bufferBar.value = endTime;
-                },
-
                 onVolumeMouseInOut : function (event) {
                     // Ignore events caused by transitions between mute button and volumeStack,
                     // or between nodes inside these two elements.
                     if (this.isEventWithin(event, this.muteButton, this.volumeStack))
                         return;
                     var isMouseOver = (event.type == "mouseover");
                     this.startFade(this.volumeStack, isMouseOver);
                 },