Bug 584615 - Make media progress events be 'simple' Events, not 'progress' Events - r=roc,cpearce,dolske,kinetik a=blocking2.0
☠☠ backed out by f085aacfb4d2 ☠ ☠
authorChris Double <chris.double@double.co.nz>
Fri, 10 Sep 2010 15:29:06 +1200
changeset 52452 96b74fec2915294ec65b8ed489b6503dc10c130b
parent 52451 5c303535a850e2ea8859568ebba00b98642bb67f
child 52453 963d95181b4beab18bdca39bdfcb84f87faf404c
child 52464 f085aacfb4d201d974c72885a9ad9261d89bdcca
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)
reviewersroc, cpearce, dolske, kinetik, blocking2
bugs584615
milestone2.0b6pre
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 584615 - Make media progress events be 'simple' Events, not 'progress' Events - r=roc,cpearce,dolske,kinetik a=blocking2.0
content/html/content/public/nsHTMLMediaElement.h
content/html/content/src/nsHTMLMediaElement.cpp
content/media/nsBuiltinDecoder.cpp
content/media/nsMediaDecoder.cpp
content/media/ogg/nsOggReader.cpp
content/media/test/test_progress.html
content/media/wave/nsWaveDecoder.cpp
toolkit/content/widgets/videocontrols.xml
--- a/content/html/content/public/nsHTMLMediaElement.h
+++ b/content/html/content/public/nsHTMLMediaElement.h
@@ -180,20 +180,18 @@ 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 DispatchSimpleEvent(const nsAString& aName);
-  nsresult DispatchProgressEvent(const nsAString& aName);
-  nsresult DispatchAsyncSimpleEvent(const nsAString& aName);
-  nsresult DispatchAsyncProgressEvent(const nsAString& aName);
+  nsresult DispatchEvent(const nsAString& aName);
+  nsresult DispatchAsyncEvent(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,17 +65,16 @@
 #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"
@@ -187,33 +186,30 @@ protected:
   nsRefPtr<nsHTMLMediaElement> mElement;
   PRUint32 mLoadID;
 };
 
 class nsAsyncEventRunner : public nsMediaEvent
 {
 private:
   nsString mName;
-  PRPackedBool mProgress;
 
 public:
-  nsAsyncEventRunner(const nsAString& aName, nsHTMLMediaElement* aElement, PRBool aProgress) :
-    nsMediaEvent(aElement), mName(aName), mProgress(aProgress)
+  nsAsyncEventRunner(const nsAString& aName, nsHTMLMediaElement* aElement) :
+    nsMediaEvent(aElement), mName(aName)
   {
   }
 
   NS_IMETHOD Run()
   {
     // Silently cancel if our load has been cancelled.
     if (IsCancelled())
       return NS_OK;
 
-    return mProgress ?
-      mElement->DispatchProgressEvent(mName) :
-      mElement->DispatchSimpleEvent(mName);
+    return mElement->DispatchEvent(mName);
   }
 };
 
 class nsSourceErrorEventRunner : public nsMediaEvent
 {
 private:
   nsCOMPtr<nsIContent> mSource;
 public:
@@ -485,17 +481,17 @@ void nsHTMLMediaElement::AbortExistingLo
     fireTimeUpdate = mDecoder->GetCurrentTime() != 0.0;
     mDecoder->Shutdown();
     mDecoder = nsnull;
   }
 
   if (mNetworkState == nsIDOMHTMLMediaElement::NETWORK_LOADING ||
       mNetworkState == nsIDOMHTMLMediaElement::NETWORK_IDLE)
   {
-    DispatchProgressEvent(NS_LITERAL_STRING("abort"));
+    DispatchEvent(NS_LITERAL_STRING("abort"));
   }
 
   mError = nsnull;
   mLoadedFirstFrame = PR_FALSE;
   mAutoplaying = PR_TRUE;
   mIsLoadingFromSrcAttribute = PR_FALSE;
   mSuspendedAfterFirstFrame = PR_FALSE;
   mAllowSuspendAfterFirstFrame = PR_TRUE;
@@ -508,35 +504,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.
-      DispatchAsyncSimpleEvent(NS_LITERAL_STRING("timeupdate"));
+      DispatchAsyncEvent(NS_LITERAL_STRING("timeupdate"));
     }
-    DispatchSimpleEvent(NS_LITERAL_STRING("emptied"));
+    DispatchEvent(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;
-  DispatchAsyncProgressEvent(NS_LITERAL_STRING("error"));
+  DispatchAsyncEvent(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:
@@ -644,17 +640,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
-  DispatchAsyncProgressEvent(NS_LITERAL_STRING("loadstart"));
+  DispatchAsyncEvent(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)) {
@@ -796,17 +792,17 @@ void nsHTMLMediaElement::LoadFromSourceC
   }
   NS_NOTREACHED("Execution should not reach here!");
 }
 
 void nsHTMLMediaElement::SuspendLoad(nsIURI* aURI)
 {
   mLoadIsSuspended = PR_TRUE;
   mNetworkState = nsIDOMHTMLMediaElement::NETWORK_IDLE;
-  DispatchAsyncProgressEvent(NS_LITERAL_STRING("suspend"));
+  DispatchAsyncEvent(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;
@@ -1032,17 +1028,17 @@ nsresult nsHTMLMediaElement::LoadWithCha
   ChangeDelayLoadStatus(PR_TRUE);
 
   nsresult rv = InitializeDecoderForChannel(aChannel, aListener);
   if (NS_FAILED(rv)) {
     ChangeDelayLoadStatus(PR_FALSE);
     return rv;
   }
 
-  DispatchAsyncProgressEvent(NS_LITERAL_STRING("loadstart"));
+  DispatchAsyncEvent(NS_LITERAL_STRING("loadstart"));
 
   return NS_OK;
 }
 
 NS_IMETHODIMP nsHTMLMediaElement::MozLoadFrom(nsIDOMHTMLMediaElement* aOther)
 {
   NS_ENSURE_ARG_POINTER(aOther);
 
@@ -1056,17 +1052,17 @@ NS_IMETHODIMP nsHTMLMediaElement::MozLoa
   ChangeDelayLoadStatus(PR_TRUE);
 
   nsresult rv = InitializeDecoderAsClone(other->mDecoder);
   if (NS_FAILED(rv)) {
     ChangeDelayLoadStatus(PR_FALSE);
     return rv;
   }
 
-  DispatchAsyncProgressEvent(NS_LITERAL_STRING("loadstart"));
+  DispatchAsyncEvent(NS_LITERAL_STRING("loadstart"));
 
   return NS_OK;
 }
 
 /* readonly attribute unsigned short readyState; */
 NS_IMETHODIMP nsHTMLMediaElement::GetReadyState(PRUint16 *aReadyState)
 {
   *aReadyState = mReadyState;
@@ -1156,18 +1152,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) {
-    DispatchAsyncSimpleEvent(NS_LITERAL_STRING("timeupdate"));
-    DispatchAsyncSimpleEvent(NS_LITERAL_STRING("pause"));
+    DispatchAsyncEvent(NS_LITERAL_STRING("timeupdate"));
+    DispatchAsyncEvent(NS_LITERAL_STRING("pause"));
   }
 
   return NS_OK;
 }
 
 /* attribute float volume; */
 NS_IMETHODIMP nsHTMLMediaElement::GetVolume(float *aVolume)
 {
@@ -1187,17 +1183,17 @@ NS_IMETHODIMP nsHTMLMediaElement::SetVol
   mVolume = aVolume;
 
   if (mDecoder && !mMuted) {
     mDecoder->SetVolume(mVolume);
   } else if (mAudioStream && !mMuted) {
     mAudioStream->SetVolume(mVolume);
   }
 
-  DispatchAsyncSimpleEvent(NS_LITERAL_STRING("volumechange"));
+  DispatchAsyncEvent(NS_LITERAL_STRING("volumechange"));
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsHTMLMediaElement::GetMozChannels(PRUint32 *aMozChannels)
 {
   if (!mDecoder && !mAudioStream) {
@@ -1257,17 +1253,17 @@ NS_IMETHODIMP nsHTMLMediaElement::SetMut
   mMuted = aMuted;
 
   if (mDecoder) {
     mDecoder->SetVolume(mMuted ? 0.0 : mVolume);
   } else if (mAudioStream) {
     mAudioStream->SetVolume(mMuted ? 0.0 : mVolume);
   }
 
-  DispatchAsyncSimpleEvent(NS_LITERAL_STRING("volumechange"));
+  DispatchAsyncEvent(NS_LITERAL_STRING("volumechange"));
 
   return NS_OK;
 }
 
 nsHTMLMediaElement::nsHTMLMediaElement(already_AddRefed<nsINodeInfo> aNodeInfo,
                                        PRUint32 aFromParser)
   : nsGenericHTMLElement(aNodeInfo),
     mCurrentLoadID(0),
@@ -1380,25 +1376,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) {
-    DispatchAsyncSimpleEvent(NS_LITERAL_STRING("play"));
+    DispatchAsyncEvent(NS_LITERAL_STRING("play"));
     switch (mReadyState) {
     case nsIDOMHTMLMediaElement::HAVE_METADATA:
     case nsIDOMHTMLMediaElement::HAVE_CURRENT_DATA:
-      DispatchAsyncSimpleEvent(NS_LITERAL_STRING("waiting"));
+      DispatchAsyncEvent(NS_LITERAL_STRING("waiting"));
       break;
     case nsIDOMHTMLMediaElement::HAVE_FUTURE_DATA:
     case nsIDOMHTMLMediaElement::HAVE_ENOUGH_DATA:
-      DispatchAsyncSimpleEvent(NS_LITERAL_STRING("playing"));
+      DispatchAsyncEvent(NS_LITERAL_STRING("playing"));
       break;
     }
   }
 
   mPaused = PR_FALSE;
   mAutoplaying = PR_FALSE;
   // We changed mPaused and mAutoplaying which can affect AddRemoveSelfReference
   AddRemoveSelfReference();
@@ -1944,18 +1940,18 @@ nsresult nsHTMLMediaElement::NewURIFromS
   return NS_OK;
 }
 
 void nsHTMLMediaElement::MetadataLoaded(PRUint32 aChannels, PRUint32 aRate)
 {
   mChannels = aChannels;
   mRate = aRate;
   ChangeReadyState(nsIDOMHTMLMediaElement::HAVE_METADATA);
-  DispatchAsyncSimpleEvent(NS_LITERAL_STRING("durationchange"));
-  DispatchAsyncSimpleEvent(NS_LITERAL_STRING("loadedmetadata"));
+  DispatchAsyncEvent(NS_LITERAL_STRING("durationchange"));
+  DispatchAsyncEvent(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");
@@ -1971,19 +1967,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.
-  DispatchAsyncProgressEvent(NS_LITERAL_STRING("progress"));
+  DispatchAsyncEvent(NS_LITERAL_STRING("progress"));
   // The download has stopped.
-  DispatchAsyncSimpleEvent(NS_LITERAL_STRING("suspend"));
+  DispatchAsyncEvent(NS_LITERAL_STRING("suspend"));
 }
 
 void nsHTMLMediaElement::NetworkError()
 {
   Error(nsIDOMMediaError::MEDIA_ERR_NETWORK);
 }
 
 void nsHTMLMediaElement::DecodeError()
@@ -1999,72 +1995,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;
-  DispatchAsyncProgressEvent(NS_LITERAL_STRING("error"));
+  DispatchAsyncEvent(NS_LITERAL_STRING("error"));
   if (mReadyState == nsIDOMHTMLMediaElement::HAVE_NOTHING) {
     mNetworkState = nsIDOMHTMLMediaElement::NETWORK_EMPTY;
-    DispatchAsyncSimpleEvent(NS_LITERAL_STRING("emptied"));
+    DispatchAsyncEvent(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();
 
-  DispatchAsyncSimpleEvent(NS_LITERAL_STRING("ended"));
+  DispatchAsyncEvent(NS_LITERAL_STRING("ended"));
 }
 
 void nsHTMLMediaElement::SeekStarted()
 {
-  DispatchAsyncSimpleEvent(NS_LITERAL_STRING("seeking"));
-  DispatchAsyncSimpleEvent(NS_LITERAL_STRING("timeupdate"));
+  DispatchAsyncEvent(NS_LITERAL_STRING("seeking"));
+  DispatchAsyncEvent(NS_LITERAL_STRING("timeupdate"));
 }
 
 void nsHTMLMediaElement::SeekCompleted()
 {
   mPlayingBeforeSeek = PR_FALSE;
   SetPlayedOrSeeked(PR_TRUE);
-  DispatchAsyncSimpleEvent(NS_LITERAL_STRING("seeked"));
+  DispatchAsyncEvent(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();
-    DispatchAsyncSimpleEvent(NS_LITERAL_STRING("suspend"));
+    DispatchAsyncEvent(NS_LITERAL_STRING("suspend"));
   }
 }
 
 void nsHTMLMediaElement::DownloadResumed()
 {
   if (mBegun) {
     mNetworkState = nsIDOMHTMLMediaElement::NETWORK_LOADING;
     AddRemoveSelfReference();
   }
 }
 
 void nsHTMLMediaElement::DownloadStalled()
 {
   if (mNetworkState == nsIDOMHTMLMediaElement::NETWORK_LOADING) {
-    DispatchAsyncProgressEvent(NS_LITERAL_STRING("stalled"));
+    DispatchAsyncEvent(NS_LITERAL_STRING("stalled"));
   }
 }
 
 PRBool nsHTMLMediaElement::ShouldCheckAllowOrigin()
 {
   return nsContentUtils::GetBoolPref("media.enforce_same_site_origin",
                                      PR_TRUE);
 }
@@ -2077,17 +2073,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) {
-      DispatchAsyncSimpleEvent(NS_LITERAL_STRING("waiting"));
+      DispatchAsyncEvent(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
@@ -2127,49 +2123,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) {
-    DispatchAsyncSimpleEvent(NS_LITERAL_STRING("waiting"));
+    DispatchAsyncEvent(NS_LITERAL_STRING("waiting"));
   }
 
   if (oldState < nsIDOMHTMLMediaElement::HAVE_CURRENT_DATA &&
       mReadyState >= nsIDOMHTMLMediaElement::HAVE_CURRENT_DATA &&
       !mLoadedFirstFrame)
   {
-    DispatchAsyncSimpleEvent(NS_LITERAL_STRING("loadeddata"));
+    DispatchAsyncEvent(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) {
-    DispatchAsyncSimpleEvent(NS_LITERAL_STRING("canplay"));
+    DispatchAsyncEvent(NS_LITERAL_STRING("canplay"));
   }
 
   if (mReadyState == nsIDOMHTMLMediaElement::HAVE_ENOUGH_DATA) {
     NotifyAutoplayDataReady();
   }
 
   if (oldState < nsIDOMHTMLMediaElement::HAVE_FUTURE_DATA &&
       mReadyState >= nsIDOMHTMLMediaElement::HAVE_FUTURE_DATA &&
       IsPotentiallyPlaying()) {
-    DispatchAsyncSimpleEvent(NS_LITERAL_STRING("playing"));
+    DispatchAsyncEvent(NS_LITERAL_STRING("playing"));
   }
 
   if (oldState < nsIDOMHTMLMediaElement::HAVE_ENOUGH_DATA &&
       mReadyState >= nsIDOMHTMLMediaElement::HAVE_ENOUGH_DATA) {
-    DispatchAsyncSimpleEvent(NS_LITERAL_STRING("canplaythrough"));
+    DispatchAsyncEvent(NS_LITERAL_STRING("canplaythrough"));
   }
 }
 
 PRBool nsHTMLMediaElement::CanActivateAutoplay()
 {
   return mAutoplaying &&
          mPaused &&
          HasAttr(kNameSpaceID_None, nsGkAtoms::autoplay) &&
@@ -2182,17 +2178,17 @@ void nsHTMLMediaElement::NotifyAutoplayD
     mPaused = PR_FALSE;
     // We changed mPaused which can affect AddRemoveSelfReference
     AddRemoveSelfReference();
 
     if (mDecoder) {
       SetPlayedOrSeeked(PR_TRUE);
       mDecoder->Play();
     }
-    DispatchAsyncSimpleEvent(NS_LITERAL_STRING("play"));
+    DispatchAsyncEvent(NS_LITERAL_STRING("play"));
   }
 }
 
 ImageContainer* nsHTMLMediaElement::GetImageContainer()
 {
   if (mImageContainer)
     return mImageContainer;
 
@@ -2239,77 +2235,38 @@ 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::DispatchSimpleEvent(const nsAString& aName)
+nsresult nsHTMLMediaElement::DispatchEvent(const nsAString& aName)
 {
-  LOG_EVENT(PR_LOG_DEBUG, ("%p Dispatching simple event %s", this,
+  LOG_EVENT(PR_LOG_DEBUG, ("%p Dispatching event %s", this,
                           NS_ConvertUTF16toUTF8(aName).get()));
 
   return nsContentUtils::DispatchTrustedEvent(GetOwnerDoc(),
                                               static_cast<nsIContent*>(this),
                                               aName,
                                               PR_TRUE,
                                               PR_TRUE);
 }
 
-nsresult nsHTMLMediaElement::DispatchAsyncSimpleEvent(const nsAString& aName)
+nsresult nsHTMLMediaElement::DispatchAsyncEvent(const nsAString& aName)
 {
-  LOG_EVENT(PR_LOG_DEBUG, ("%p Queuing simple event %s", this, NS_ConvertUTF16toUTF8(aName).get()));
+  LOG_EVENT(PR_LOG_DEBUG, ("%p Queuing event %s", this,
+            NS_ConvertUTF16toUTF8(aName).get()));
 
-  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);
+  nsCOMPtr<nsIRunnable> event = new nsAsyncEventRunner(aName, this);
   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 ||
@@ -2569,12 +2526,14 @@ 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) {
-    return mDecoder->GetBuffered(ranges);
+    // If GetBuffered fails we ignore the error result and just return the
+    // time ranges we found up till the error.
+    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->DispatchAsyncProgressEvent(NS_LITERAL_STRING("progress"));
+    mElement->DispatchAsyncEvent(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->DispatchSimpleEvent(NS_LITERAL_STRING("timeupdate"));
+    mElement->DispatchEvent(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->DispatchSimpleEvent(NS_LITERAL_STRING("durationchange"));
+    mElement->DispatchEvent(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->DispatchAsyncProgressEvent(NS_LITERAL_STRING("progress"));
+    mElement->DispatchAsyncEvent(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,24 +279,20 @@ 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) {
-    if (mTheoraState->Init()) {
-      gfxIntSize sz(mTheoraState->mInfo.pic_width,
-                    mTheoraState->mInfo.pic_height);
-      mDecoder->SetVideoData(sz, mTheoraState->mPixelAspectRatio, nsnull);
-    } else {
-      mTheoraState = nsnull;
-    }
+  if (mTheoraState && mTheoraState->Init()) {
+    gfxIntSize sz(mTheoraState->mInfo.pic_width,
+                  mTheoraState->mInfo.pic_height);
+    mDecoder->SetVideoData(sz, mTheoraState->mPixelAspectRatio, 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.
@@ -905,17 +901,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) {
+        if (endTime != -1 || readStartOffset == 0) {
           // 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);
@@ -1537,16 +1533,26 @@ 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;
@@ -1599,16 +1605,26 @@ 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,20 +12,16 @@
 <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);
@@ -34,19 +30,17 @@ 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->DispatchSimpleEvent(NS_LITERAL_STRING("timeupdate"));
+    mElement->DispatchEvent(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.networkState == this.video.NETWORK_LOADED)
-                        this.bufferBar.setAttribute("value", 100);
+                    if (this.video.readyState >= this.video.HAVE_METADATA)
+                        this.showBuffered();
                     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,25 +453,17 @@
                             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":
-                            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.showBuffered();
                             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
@@ -503,16 +495,20 @@
                                 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":
@@ -594,16 +590,63 @@
                         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);
                 },