Backing out part of bug 703379 to fix build failure of nsRawReader
authorRobert O'Callahan <robert@ocallahan.org>
Thu, 24 Nov 2011 12:30:03 +1300
changeset 81519 e6be0cc7a77061228a4454c62c918aae01580e45
parent 81514 fe83d9a8de5d27e0a44ed325a4e48ff3e5ee924c
child 81520 1ebbe65a5949f0d9a6a722508ef8ae3c1bce2202
push id628
push userclegnitto@mozilla.com
push dateWed, 21 Dec 2011 14:41:57 +0000
treeherdermozilla-aurora@24a61ad789e8 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs703379
milestone11.0a1
Backing out part of bug 703379 to fix build failure of nsRawReader
content/html/content/public/nsHTMLMediaElement.h
content/html/content/src/nsHTMLMediaElement.cpp
content/media/nsBuiltinDecoder.cpp
content/media/nsBuiltinDecoder.h
content/media/nsBuiltinDecoderStateMachine.cpp
content/media/nsMediaDecoder.cpp
content/media/nsMediaDecoder.h
content/media/nsMediaStream.h
content/media/ogg/nsOggReader.cpp
content/media/test/Makefile.in
content/media/test/dynamic_resource.sjs
content/media/test/manifest.js
content/media/test/test_load_same_resource.html
content/media/test/test_preload_actions.html
content/media/wave/nsWaveReader.cpp
content/media/webm/nsWebMReader.cpp
--- a/content/html/content/public/nsHTMLMediaElement.h
+++ b/content/html/content/public/nsHTMLMediaElement.h
@@ -365,49 +365,32 @@ protected:
    * Create a decoder for the given aMIMEType. Returns null if we
    * were unable to create the decoder.
    */
   already_AddRefed<nsMediaDecoder> CreateDecoder(const nsACString& aMIMEType);
 
   /**
    * Initialize a decoder as a clone of an existing decoder in another
    * element.
-   * mLoadingSrc must already be set.
    */
   nsresult InitializeDecoderAsClone(nsMediaDecoder* aOriginal);
 
   /**
    * Initialize a decoder to load the given channel. The decoder's stream
    * listener is returned via aListener.
-   * mLoadingSrc must already be set.
    */
   nsresult InitializeDecoderForChannel(nsIChannel *aChannel,
                                        nsIStreamListener **aListener);
 
   /**
    * Finish setting up the decoder after Load() has been called on it.
-   * Called by InitializeDecoderForChannel/InitializeDecoderAsClone.
    */
   nsresult FinishDecoderSetup(nsMediaDecoder* aDecoder);
 
   /**
-   * Call this after setting up mLoadingSrc and mDecoder.
-   */
-  void AddMediaElementToURITable();
-  /**
-   * Call this before clearing mLoadingSrc.
-   */
-  void RemoveMediaElementFromURITable();
-  /**
-   * Call this to find a media element with the same NodePrincipal and mLoadingSrc
-   * set to aURI, and with a decoder on which Load() has been called.
-   */
-  nsHTMLMediaElement* LookupMediaElementURITable(nsIURI* aURI);
-
-  /**
    * Execute the initial steps of the load algorithm that ensure existing
    * loads are aborted, the element is emptied, and a new load ID is
    * created.
    */
   void AbortExistingLoads();
 
   /**
    * Create a URI for the given aURISpec string.
@@ -443,17 +426,17 @@ protected:
    * Asynchronously awaits a stable state, and then causes SelectResource()
    * to be run on the main thread's event loop.
    */
   void QueueSelectResourceTask();
 
   /**
    * The resource-fetch algorithm step of the load algorithm.
    */
-  nsresult LoadResource();
+  nsresult LoadResource(nsIURI* aURI);
 
   /**
    * Selects the next <source> child from which to load a resource. Called
    * during the resource selection algorithm. Stores the return value in
    * mSourceLoadCandidate before returning.
    */
   nsIContent* GetNextSource();
 
@@ -504,21 +487,21 @@ protected:
     PRELOAD_UNDEFINED = 0, // not determined - used only for initialization
     PRELOAD_NONE = 1,      // do not preload
     PRELOAD_METADATA = 2,  // preload only the metadata (and first frame)
     PRELOAD_ENOUGH = 3     // preload enough data to allow uninterrupted
                            // playback
   };
 
   /**
-   * Suspends the load of mLoadingSrc, so that it can be resumed later
+   * Suspends the load of resource at aURI, so that it can be resumed later
    * by ResumeLoad(). This is called when we have a media with a 'preload'
    * attribute value of 'none', during the resource selection algorithm.
    */
-  void SuspendLoad();
+  void SuspendLoad(nsIURI* aURI);
 
   /**
    * Resumes a previously suspended load (suspended by SuspendLoad(uri)).
    * Will continue running the resource selection algorithm.
    * Sets mPreloadAction to aAction.
    */
   void ResumeLoad(PreloadAction aAction);
 
@@ -547,17 +530,16 @@ protected:
    **/
   void GetCurrentSpec(nsCString& aString);
 
   /**
    * Process any media fragment entries in the URI
    */
   void ProcessMediaFragmentURI();
 
-  // The current decoder. Load() has been called on this decoder.
   nsRefPtr<nsMediaDecoder> mDecoder;
 
   // A reference to the ImageContainer which contains the current frame
   // of video to display.
   nsRefPtr<ImageContainer> mImageContainer;
 
   // Holds a reference to the first channel we open to the media resource.
   // Once the decoder is created, control over the channel passes to the
@@ -610,21 +592,21 @@ protected:
   double mVolume;
 
   // Current number of audio channels.
   PRUint32 mChannels;
 
   // Current audio sample rate.
   PRUint32 mRate;
 
-  // URI of the resource we're attempting to load. This stores the value we
-  // return in the currentSrc attribute. Use GetCurrentSrc() to access the
-  // currentSrc attribute.
-  // This is always the original URL we're trying to load --- before
-  // redirects etc.
+  // URI of the resource we're attempting to load. When the decoder is
+  // successfully initialized, we rely on it to record the URI we're playing,
+  // and clear mLoadingSrc. This stores the value we return in the currentSrc
+  // attribute until the decoder is initialized. Use GetCurrentSrc() to access
+  // the currentSrc attribute.
   nsCOMPtr<nsIURI> mLoadingSrc;
   
   // Stores the current preload action for this element. Initially set to
   // PRELOAD_UNDEFINED, its value is changed by calling
   // UpdatePreloadAction().
   PreloadAction mPreloadAction;
 
   // Size of the media. Updated by the decoder on the main thread if
--- a/content/html/content/src/nsHTMLMediaElement.cpp
+++ b/content/html/content/src/nsHTMLMediaElement.cpp
@@ -84,17 +84,16 @@
 #include "nsIDocShellTreeItem.h"
 #include "nsIAsyncVerifyRedirectCallback.h"
 #include "nsIAppShell.h"
 #include "nsWidgetsCID.h"
 
 #include "nsIPrivateDOMEvent.h"
 #include "nsIDOMNotifyAudioAvailableEvent.h"
 #include "nsMediaFragmentURIParser.h"
-#include "nsURIHashKey.h"
 
 #ifdef MOZ_OGG
 #include "nsOggDecoder.h"
 #endif
 #ifdef MOZ_WAVE
 #include "nsWaveDecoder.h"
 #endif
 #ifdef MOZ_WEBM
@@ -475,38 +474,34 @@ void nsHTMLMediaElement::AbortExistingLo
   // Abort any already-running instance of the resource selection algorithm.
   mLoadWaitStatus = NOT_WAITING;
 
   // Set a new load ID. This will cause events which were enqueued
   // with a different load ID to silently be cancelled.
   mCurrentLoadID++;
 
   bool fireTimeUpdate = false;
-
   if (mDecoder) {
-    RemoveMediaElementFromURITable();
     fireTimeUpdate = mDecoder->GetCurrentTime() != 0.0;
     mDecoder->Shutdown();
     mDecoder = nsnull;
   }
-  mLoadingSrc = nsnull;
 
   if (mNetworkState == nsIDOMHTMLMediaElement::NETWORK_LOADING ||
       mNetworkState == nsIDOMHTMLMediaElement::NETWORK_IDLE)
   {
     DispatchEvent(NS_LITERAL_STRING("abort"));
   }
 
   mError = nsnull;
   mLoadedFirstFrame = false;
   mAutoplaying = true;
   mIsLoadingFromSourceChildren = false;
   mSuspendedAfterFirstFrame = false;
   mAllowSuspendAfterFirstFrame = true;
-  mLoadIsSuspended = false;
   mSourcePointer = nsnull;
 
   // TODO: The playback rate must be set to the default playback rate.
 
   if (mNetworkState != nsIDOMHTMLMediaElement::NETWORK_EMPTY) {
     mNetworkState = nsIDOMHTMLMediaElement::NETWORK_EMPTY;
     ChangeReadyState(nsIDOMHTMLMediaElement::HAVE_NOTHING);
     mPaused = true;
@@ -648,22 +643,22 @@ void nsHTMLMediaElement::SelectResource(
     if (NS_SUCCEEDED(rv)) {
       LOG(PR_LOG_DEBUG, ("%p Trying load from src=%s", this, NS_ConvertUTF16toUTF8(src).get()));
       NS_ASSERTION(!mIsLoadingFromSourceChildren,
         "Should think we're not loading from source children by default");
       mLoadingSrc = uri;
       if (mPreloadAction == nsHTMLMediaElement::PRELOAD_NONE) {
         // preload:none media, suspend the load here before we make any
         // network requests.
-        SuspendLoad();
+        SuspendLoad(uri);
         mIsRunningSelectResource = false;
         return;
       }
 
-      rv = LoadResource();
+      rv = LoadResource(uri);
       if (NS_SUCCEEDED(rv)) {
         mIsRunningSelectResource = false;
         return;
       }
     }
     NoSupportedMediaSourceError();
   } else {
     // Otherwise, the source elements will be used.
@@ -750,54 +745,55 @@ void nsHTMLMediaElement::LoadFromSourceC
 
     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
       // network requests.
-      SuspendLoad();
+      SuspendLoad(uri);
       return;
     }
 
-    if (NS_SUCCEEDED(LoadResource())) {
+    if (NS_SUCCEEDED(LoadResource(uri))) {
       return;
     }
 
     // If we fail to load, loop back and try loading the next resource.
     DispatchAsyncSourceError(child);
   }
   NS_NOTREACHED("Execution should not reach here!");
 }
 
-void nsHTMLMediaElement::SuspendLoad()
+void nsHTMLMediaElement::SuspendLoad(nsIURI* aURI)
 {
   mLoadIsSuspended = true;
   mNetworkState = nsIDOMHTMLMediaElement::NETWORK_IDLE;
   DispatchAsyncEvent(NS_LITERAL_STRING("suspend"));
   ChangeDelayLoadStatus(false);
 }
 
 void nsHTMLMediaElement::ResumeLoad(PreloadAction aAction)
 {
   NS_ASSERTION(mLoadIsSuspended, "Can only resume preload if halted for one");
+  nsCOMPtr<nsIURI> uri = mLoadingSrc;
   mLoadIsSuspended = false;
   mPreloadAction = aAction;
   ChangeDelayLoadStatus(true);
   mNetworkState = nsIDOMHTMLMediaElement::NETWORK_LOADING;
   if (!mIsLoadingFromSourceChildren) {
     // We were loading from the element's src attribute.
-    if (NS_FAILED(LoadResource())) {
+    if (NS_FAILED(LoadResource(uri))) {
       NoSupportedMediaSourceError();
     }
   } else {
     // We were loading from a child <source> element. Try to resume the
     // load of that child, and if that fails, try the next child.
-    if (NS_FAILED(LoadResource())) {
+    if (NS_FAILED(LoadResource(uri))) {
       LoadFromSourceChildren();
     }
   }
 }
 
 static bool IsAutoplayEnabled()
 {
   return Preferences::GetBool("media.autoplay.enabled");
@@ -872,44 +868,36 @@ void nsHTMLMediaElement::UpdatePreloadAc
       // value "none". The preload value has changed to preload:metadata, so
       // resume the load. We'll pause the load again after we've read the
       // metadata.
       ResumeLoad(PRELOAD_METADATA);
     }
   }
 }
 
-nsresult nsHTMLMediaElement::LoadResource()
+nsresult nsHTMLMediaElement::LoadResource(nsIURI* aURI)
 {
   NS_ASSERTION(mDelayingLoadEvent,
                "Should delay load event (if in document) during load");
 
   // If a previous call to mozSetup() was made, kill that media stream
   // in order to use this new src instead.
   if (mAudioStream) {
     mAudioStream->Shutdown();
     mAudioStream = nsnull;
   }
 
   if (mChannel) {
     mChannel->Cancel(NS_BINDING_ABORTED);
     mChannel = nsnull;
   }
 
-  nsHTMLMediaElement* other = LookupMediaElementURITable(mLoadingSrc);
-  if (other) {
-    // Clone it.
-    nsresult rv = InitializeDecoderAsClone(other->mDecoder);
-    if (NS_SUCCEEDED(rv))
-      return rv;
-  }
-
   PRInt16 shouldLoad = nsIContentPolicy::ACCEPT;
   nsresult rv = NS_CheckContentLoadPolicy(nsIContentPolicy::TYPE_MEDIA,
-                                          mLoadingSrc,
+                                          aURI,
                                           NodePrincipal(),
                                           static_cast<nsGenericElement*>(this),
                                           EmptyCString(), // mime type
                                           nsnull, // extra
                                           &shouldLoad,
                                           nsContentUtils::GetContentPolicy(),
                                           nsContentUtils::GetSecurityManager());
   NS_ENSURE_SUCCESS(rv, rv);
@@ -927,17 +915,17 @@ nsresult nsHTMLMediaElement::LoadResourc
   NS_ENSURE_SUCCESS(rv,rv);
   if (csp) {
     channelPolicy = do_CreateInstance("@mozilla.org/nschannelpolicy;1");
     channelPolicy->SetContentSecurityPolicy(csp);
     channelPolicy->SetLoadType(nsIContentPolicy::TYPE_MEDIA);
   }
   nsCOMPtr<nsIChannel> channel;
   rv = NS_NewChannel(getter_AddRefs(channel),
-                     mLoadingSrc,
+                     aURI,
                      nsnull,
                      loadGroup,
                      nsnull,
                      nsICachingChannel::LOAD_BYPASS_LOCAL_CACHE_IF_BUSY,
                      channelPolicy);
   NS_ENSURE_SUCCESS(rv,rv);
 
   // The listener holds a strong reference to us.  This creates a
@@ -956,17 +944,17 @@ nsresult nsHTMLMediaElement::LoadResourc
       new nsCORSListenerProxy(loadListener,
                               NodePrincipal(),
                               channel,
                               false,
                               &rv);
   } else {
     rv = nsContentUtils::GetSecurityManager()->
            CheckLoadURIWithPrincipal(NodePrincipal(),
-                                     mLoadingSrc,
+                                     aURI,
                                      nsIScriptSecurityManager::STANDARD);
     listener = loadListener;
   }
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsCOMPtr<nsIHttpChannel> hc = do_QueryInterface(channel);
   if (hc) {
     // Use a byte range request from the start of the resource.
@@ -998,21 +986,19 @@ nsresult nsHTMLMediaElement::LoadWithCha
 {
   NS_ENSURE_ARG_POINTER(aChannel);
   NS_ENSURE_ARG_POINTER(aListener);
 
   *aListener = nsnull;
 
   AbortExistingLoads();
 
-  nsresult rv = aChannel->GetOriginalURI(getter_AddRefs(mLoadingSrc));
-  NS_ENSURE_SUCCESS(rv, rv);
-
   ChangeDelayLoadStatus(true);
-  rv = InitializeDecoderForChannel(aChannel, aListener);
+
+  nsresult rv = InitializeDecoderForChannel(aChannel, aListener);
   if (NS_FAILED(rv)) {
     ChangeDelayLoadStatus(false);
     return rv;
   }
 
   DispatchAsyncEvent(NS_LITERAL_STRING("loadstart"));
 
   return NS_OK;
@@ -1026,17 +1012,16 @@ NS_IMETHODIMP nsHTMLMediaElement::MozLoa
 
   nsCOMPtr<nsIContent> content = do_QueryInterface(aOther);
   nsHTMLMediaElement* other = static_cast<nsHTMLMediaElement*>(content.get());
   if (!other || !other->mDecoder)
     return NS_OK;
 
   ChangeDelayLoadStatus(true);
 
-  mLoadingSrc = other->mLoadingSrc;
   nsresult rv = InitializeDecoderAsClone(other->mDecoder);
   if (NS_FAILED(rv)) {
     ChangeDelayLoadStatus(false);
     return rv;
   }
 
   DispatchAsyncEvent(NS_LITERAL_STRING("loadstart"));
 
@@ -1251,86 +1236,16 @@ NS_IMETHODIMP nsHTMLMediaElement::SetMut
     mAudioStream->SetVolume(mMuted ? 0.0 : mVolume);
   }
 
   DispatchAsyncEvent(NS_LITERAL_STRING("volumechange"));
 
   return NS_OK;
 }
 
-class MediaElementSetForURI : public nsURIHashKey {
-public:
-  MediaElementSetForURI(const nsIURI* aKey) : nsURIHashKey(aKey) {}
-  MediaElementSetForURI(const MediaElementSetForURI& toCopy)
-    : nsURIHashKey(toCopy), mElements(toCopy.mElements) {}
-  nsTArray<nsHTMLMediaElement*> mElements;
-};
-
-typedef nsTHashtable<MediaElementSetForURI> MediaElementURITable;
-// Elements in this table must have non-null mDecoder and mLoadingSrc, and those
-// can't change while the element is in the table. The table is keyed by
-// the element's mLoadingSrc. Each entry has a list of all elements with the
-// same mLoadingSrc.
-static MediaElementURITable* gElementTable;
-
-void
-nsHTMLMediaElement::AddMediaElementToURITable()
-{
-  NS_ASSERTION(mDecoder && mDecoder->GetStream(), "Call this only with decoder Load called");
-  if (!gElementTable) {
-    gElementTable = new MediaElementURITable();
-    gElementTable->Init();
-  }
-  MediaElementSetForURI* entry = gElementTable->PutEntry(mLoadingSrc);
-  entry->mElements.AppendElement(this);
-}
-
-void
-nsHTMLMediaElement::RemoveMediaElementFromURITable()
-{
-  NS_ASSERTION(mDecoder, "Don't call this without decoder!");
-  NS_ASSERTION(mLoadingSrc, "Can't have decoder without source!");
-  if (!gElementTable)
-    return;
-  MediaElementSetForURI* entry = gElementTable->GetEntry(mLoadingSrc);
-  if (!entry)
-    return;
-  entry->mElements.RemoveElement(this);
-  if (entry->mElements.IsEmpty()) {
-    gElementTable->RemoveEntry(mLoadingSrc);
-    if (gElementTable->Count() == 0) {
-      delete gElementTable;
-      gElementTable = nsnull;
-    }
-  }
-}
-
-nsHTMLMediaElement*
-nsHTMLMediaElement::LookupMediaElementURITable(nsIURI* aURI)
-{
-  if (!gElementTable)
-    return nsnull;
-  MediaElementSetForURI* entry = gElementTable->GetEntry(aURI);
-  if (!entry)
-    return nsnull;
-  for (PRUint32 i = 0; i < entry->mElements.Length(); ++i) {
-    nsHTMLMediaElement* elem = entry->mElements[i];
-    bool equal;
-    // Look for elements that have the same principal.
-    // XXX when we implement crossorigin for video, we'll also need to check
-    // for the same crossorigin mode here. Ditto for anything else that could
-    // cause us to send different headers.
-    if (NS_SUCCEEDED(elem->NodePrincipal()->Equals(NodePrincipal(), &equal)) && equal) {
-      NS_ASSERTION(elem->mDecoder && elem->mDecoder->GetStream(), "Decoder gone");
-      return elem;
-    }
-  }
-  return nsnull;
-}
-
 nsHTMLMediaElement::nsHTMLMediaElement(already_AddRefed<nsINodeInfo> aNodeInfo)
   : nsGenericHTMLElement(aNodeInfo),
     mCurrentLoadID(0),
     mNetworkState(nsIDOMHTMLMediaElement::NETWORK_EMPTY),
     mReadyState(nsIDOMHTMLMediaElement::HAVE_NOTHING),
     mLoadWaitStatus(NOT_WAITING),
     mVolume(1.0),
     mChannels(0),
@@ -1377,24 +1292,26 @@ nsHTMLMediaElement::nsHTMLMediaElement(a
 
 nsHTMLMediaElement::~nsHTMLMediaElement()
 {
   NS_ASSERTION(!mHasSelfReference,
                "How can we be destroyed if we're still holding a self reference?");
 
   UnregisterFreezableElement();
   if (mDecoder) {
-    RemoveMediaElementFromURITable();
     mDecoder->Shutdown();
+    mDecoder = nsnull;
   }
   if (mChannel) {
     mChannel->Cancel(NS_BINDING_ABORTED);
+    mChannel = nsnull;
   }
   if (mAudioStream) {
     mAudioStream->Shutdown();
+    mAudioStream = nsnull;
   }
 }
 
 void nsHTMLMediaElement::StopSuspendingAfterFirstFrame()
 {
   mAllowSuspendAfterFirstFrame = false;
   if (!mSuspendedAfterFirstFrame)
     return;
@@ -1425,23 +1342,19 @@ void nsHTMLMediaElement::SetPlayedOrSeek
 NS_IMETHODIMP nsHTMLMediaElement::Play()
 {
   StopSuspendingAfterFirstFrame();
   SetPlayedOrSeeked(true);
 
   if (mNetworkState == nsIDOMHTMLMediaElement::NETWORK_EMPTY) {
     nsresult rv = Load();
     NS_ENSURE_SUCCESS(rv, rv);
-  }
-  if (mLoadIsSuspended) {
+  } else if (mLoadIsSuspended) {
     ResumeLoad(PRELOAD_ENOUGH);
-  }
-  // Even if we just did Load() or ResumeLoad(), we could already have a decoder
-  // here if we managed to clone an existing decoder.
-  if (mDecoder) {
+  } else if (mDecoder) {
     if (mDecoder->IsEnded()) {
       SetCurrentTime(0);
     }
     if (!mPausedForInactiveDocument) {
       nsresult rv = mDecoder->Play();
       NS_ENSURE_SUCCESS(rv, rv);
     }
   }
@@ -1893,19 +1806,17 @@ nsHTMLMediaElement::CreateDecoder(const 
     }
   }
 #endif
   return nsnull;
 }
 
 nsresult nsHTMLMediaElement::InitializeDecoderAsClone(nsMediaDecoder* aOriginal)
 {
-  NS_ASSERTION(mLoadingSrc, "mLoadingSrc must already be set");
-
-  nsMediaStream* originalStream = aOriginal->GetStream();
+  nsMediaStream* originalStream = aOriginal->GetCurrentStream();
   if (!originalStream)
     return NS_ERROR_FAILURE;
   nsRefPtr<nsMediaDecoder> decoder = aOriginal->Clone();
   if (!decoder)
     return NS_ERROR_FAILURE;
 
   LOG(PR_LOG_DEBUG, ("%p Cloned decoder %p from %p", this, decoder.get(), aOriginal));
 
@@ -1932,18 +1843,16 @@ nsresult nsHTMLMediaElement::InitializeD
   }
 
   return FinishDecoderSetup(decoder);
 }
 
 nsresult nsHTMLMediaElement::InitializeDecoderForChannel(nsIChannel *aChannel,
                                                          nsIStreamListener **aListener)
 {
-  NS_ASSERTION(mLoadingSrc, "mLoadingSrc must already be set");
-
   nsCAutoString mimeType;
   aChannel->GetContentType(mimeType);
 
   nsRefPtr<nsMediaDecoder> decoder = CreateDecoder(mimeType);
   if (!decoder) {
     return NS_ERROR_FAILURE;
   }
 
@@ -1964,20 +1873,20 @@ nsresult nsHTMLMediaElement::InitializeD
   // which owns the channel.
   mChannel = nsnull;
 
   return FinishDecoderSetup(decoder);
 }
 
 nsresult nsHTMLMediaElement::FinishDecoderSetup(nsMediaDecoder* aDecoder)
 {
-  NS_ASSERTION(mLoadingSrc, "mLoadingSrc set up");
-
   mDecoder = aDecoder;
-  AddMediaElementToURITable();
+
+  // Decoder has assumed ownership responsibility for remembering the URI.
+  mLoadingSrc = nsnull;
 
   // Force a same-origin check before allowing events for this media resource.
   mMediaSecurityVerified = false;
 
   // The new stream has not been suspended by us.
   mPausedForInactiveDocument = false;
   // But we may want to suspend it now.
   // This will also do an AddRemoveSelfReference.
@@ -2105,21 +2014,19 @@ void nsHTMLMediaElement::ResourceLoaded(
 void nsHTMLMediaElement::NetworkError()
 {
   Error(nsIDOMMediaError::MEDIA_ERR_NETWORK);
 }
 
 void nsHTMLMediaElement::DecodeError()
 {
   if (mDecoder) {
-    RemoveMediaElementFromURITable();
     mDecoder->Shutdown();
     mDecoder = nsnull;
   }
-  mLoadingSrc = nsnull;
   if (mIsLoadingFromSourceChildren) {
     mError = nsnull;
     if (mSourceLoadCandidate) {
       DispatchAsyncSourceError(mSourceLoadCandidate);
       QueueLoadFromSourceTask();
     } else {
       NS_WARNING("Should know the source we were loading from!");
     }
@@ -2757,20 +2664,23 @@ void nsHTMLMediaElement::FireTimeUpdate(
     mFragmentEnd = -1.0;
     mFragmentStart = -1.0;
     mDecoder->SetEndTime(mFragmentEnd);
   }
 }
 
 void nsHTMLMediaElement::GetCurrentSpec(nsCString& aString)
 {
-  if (mLoadingSrc) {
+  if (mDecoder) {
+    nsMediaStream* stream = mDecoder->GetCurrentStream();
+    if (stream) {
+      stream->URI()->GetSpec(aString);
+    }
+  } else if (mLoadingSrc) {
     mLoadingSrc->GetSpec(aString);
-  } else {
-    aString.Truncate();
   }
 }
 
 /* attribute double initialTime; */
 NS_IMETHODIMP nsHTMLMediaElement::GetInitialTime(double *aTime)
 {
   // If there is no start fragment then the initalTime is zero.
   // Clamp to duration if it is greater than duration.
--- a/content/media/nsBuiltinDecoder.cpp
+++ b/content/media/nsBuiltinDecoder.cpp
@@ -372,17 +372,17 @@ nsresult nsBuiltinDecoder::PlaybackRateC
 }
 
 double nsBuiltinDecoder::GetCurrentTime()
 {
   NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
   return mCurrentTime;
 }
 
-nsMediaStream* nsBuiltinDecoder::GetStream()
+nsMediaStream* nsBuiltinDecoder::GetCurrentStream()
 {
   return mStream;
 }
 
 already_AddRefed<nsIPrincipal> nsBuiltinDecoder::GetCurrentPrincipal()
 {
   NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
   return mStream ? mStream->GetCurrentPrincipal() : nsnull;
--- a/content/media/nsBuiltinDecoder.h
+++ b/content/media/nsBuiltinDecoder.h
@@ -400,17 +400,17 @@ class nsBuiltinDecoder : public nsMediaD
 
   virtual void Pause();
   virtual void SetVolume(double aVolume);
   virtual double GetDuration();
 
   virtual void SetInfinite(bool aInfinite);
   virtual bool IsInfinite();
 
-  virtual nsMediaStream* GetStream();
+  virtual nsMediaStream* GetCurrentStream();
   virtual already_AddRefed<nsIPrincipal> GetCurrentPrincipal();
 
   virtual void NotifySuspendedStatusChanged();
   virtual void NotifyBytesDownloaded();
   virtual void NotifyDownloadEnded(nsresult aStatus);
   // Called by the decode thread to keep track of the number of bytes read
   // from the resource.
   void NotifyBytesConsumed(PRInt64 aBytes);
--- a/content/media/nsBuiltinDecoderStateMachine.cpp
+++ b/content/media/nsBuiltinDecoderStateMachine.cpp
@@ -1589,17 +1589,17 @@ private:
   nsRefPtr<nsBuiltinDecoder> mDecoder;
   nsCOMPtr<nsBuiltinDecoderStateMachine> mStateMachine;
 };
 
 nsresult nsBuiltinDecoderStateMachine::RunStateMachine()
 {
   mDecoder->GetReentrantMonitor().AssertCurrentThreadIn();
 
-  nsMediaStream* stream = mDecoder->GetStream();
+  nsMediaStream* stream = mDecoder->GetCurrentStream();
   NS_ENSURE_TRUE(stream, NS_ERROR_NULL_POINTER);
 
   switch (mState) {
     case DECODER_STATE_SHUTDOWN: {
       if (IsPlaying()) {
         StopPlayback();
       }
       StopAudioThread();
@@ -1670,17 +1670,17 @@ nsresult nsBuiltinDecoderStateMachine::R
 
       TimeStamp now = TimeStamp::Now();
       NS_ASSERTION(!mBufferingStart.IsNull(), "Must know buffering start time.");
 
       // We will remain in the buffering state if we've not decoded enough
       // data to begin playback, or if we've not downloaded a reasonable
       // amount of data inside our buffering time.
       TimeDuration elapsed = now - mBufferingStart;
-      bool isLiveStream = mDecoder->GetStream()->GetLength() == -1;
+      bool isLiveStream = mDecoder->GetCurrentStream()->GetLength() == -1;
       if ((isLiveStream || !mDecoder->CanPlayThrough()) &&
             elapsed < TimeDuration::FromSeconds(mBufferingWait) &&
             (mQuickBuffering ? HasLowDecodedData(QUICK_BUFFERING_LOW_DATA_USECS)
                             : (GetUndecodedData() < mBufferingWait * USECS_PER_S / 1000)) &&
             !stream->IsDataCachedToEndOfStream(mDecoder->mDecoderPosition) &&
             !stream->IsSuspended())
       {
         LOG(PR_LOG_DEBUG,
@@ -1868,17 +1868,17 @@ void nsBuiltinDecoderStateMachine::Advan
         ? (DurationToUsecs(TimeStamp::Now() - mPlayStartTime) + mPlayDuration)
         : mPlayDuration;
       remainingTime = frame->mTime - mStartTime - now;
     }
   }
 
   // Check to see if we don't have enough data to play up to the next frame.
   // If we don't, switch to buffering mode.
-  nsMediaStream* stream = mDecoder->GetStream();
+  nsMediaStream* stream = mDecoder->GetCurrentStream();
   if (mState == DECODER_STATE_DECODING &&
       mDecoder->GetState() == nsBuiltinDecoder::PLAY_STATE_PLAYING &&
       HasLowDecodedData(remainingTime + EXHAUSTED_DATA_MARGIN_USECS) &&
       !stream->IsDataCachedToEndOfStream(mDecoder->mDecoderPosition) &&
       !stream->IsSuspended() &&
       (JustExitedQuickBuffering() || HasLowUndecodedData()))
   {
     if (currentFrame) {
@@ -2046,17 +2046,17 @@ void nsBuiltinDecoderStateMachine::Start
   nsMediaDecoder::Statistics stats = mDecoder->GetStatistics();
   LOG(PR_LOG_DEBUG, ("%p Playback rate: %.1lfKB/s%s download rate: %.1lfKB/s%s",
     mDecoder.get(),
     stats.mPlaybackRate/1024, stats.mPlaybackRateReliable ? "" : " (unreliable)",
     stats.mDownloadRate/1024, stats.mDownloadRateReliable ? "" : " (unreliable)"));
 }
 
 nsresult nsBuiltinDecoderStateMachine::GetBuffered(nsTimeRanges* aBuffered) {
-  nsMediaStream* stream = mDecoder->GetStream();
+  nsMediaStream* stream = mDecoder->GetCurrentStream();
   NS_ENSURE_TRUE(stream, NS_ERROR_FAILURE);
   stream->Pin();
   nsresult res = mReader->GetBuffered(aBuffered, mStartTime);
   stream->Unpin();
   return res;
 }
 
 bool nsBuiltinDecoderStateMachine::IsPausedAndDecoderWaiting() {
--- a/content/media/nsMediaDecoder.cpp
+++ b/content/media/nsMediaDecoder.cpp
@@ -256,27 +256,27 @@ void nsMediaDecoder::SetVideoData(const 
 double nsMediaDecoder::GetFrameDelay()
 {
   MutexAutoLock lock(mVideoUpdateLock);
   return mPaintDelay.ToSeconds();
 }
 
 void nsMediaDecoder::PinForSeek()
 {
-  nsMediaStream* stream = GetStream();
+  nsMediaStream* stream = GetCurrentStream();
   if (!stream || mPinnedForSeek) {
     return;
   }
   mPinnedForSeek = true;
   stream->Pin();
 }
 
 void nsMediaDecoder::UnpinForSeek()
 {
-  nsMediaStream* stream = GetStream();
+  nsMediaStream* stream = GetCurrentStream();
   if (!stream || !mPinnedForSeek) {
     return;
   }
   mPinnedForSeek = false;
   stream->Unpin();
 }
 
 bool nsMediaDecoder::CanPlayThrough()
--- a/content/media/nsMediaDecoder.h
+++ b/content/media/nsMediaDecoder.h
@@ -86,18 +86,18 @@ public:
   virtual nsMediaDecoder* Clone() = 0;
 
   // Perform any initialization required for the decoder.
   // Return true on successful initialisation, false
   // on failure.
   virtual bool Init(nsHTMLMediaElement* aElement);
 
   // Get the current nsMediaStream being used. Its URI will be returned
-  // by currentSrc. Returns what was passed to Load(), if Load() has been called.
-  virtual nsMediaStream* GetStream() = 0;
+  // by currentSrc.
+  virtual nsMediaStream* GetCurrentStream() = 0;
 
   // Return the principal of the current URI being played or downloaded.
   virtual already_AddRefed<nsIPrincipal> GetCurrentPrincipal() = 0;
 
   // Return the time position in the video stream being
   // played measured in seconds.
   virtual double GetCurrentTime() = 0;
 
--- a/content/media/nsMediaStream.h
+++ b/content/media/nsMediaStream.h
@@ -164,17 +164,17 @@ class nsMediaStream
 public:
   virtual ~nsMediaStream()
   {
     MOZ_COUNT_DTOR(nsMediaStream);
   }
 
   // The following can be called on the main thread only:
   // Get the URI
-  nsIURI* URI() const { return mURI; }
+  nsIURI* URI() { return mURI; }
   // Close the stream, stop any listeners, channels, etc.
   // Cancels any currently blocking Read request and forces that request to
   // return an error.
   virtual nsresult Close() = 0;
   // Suspend any downloads that are in progress.
   // If aCloseImmediately is set, resources should be released immediately
   // since we don't expect to resume again any time soon. Otherwise we
   // may resume again soon so resources should be held for a little
--- a/content/media/ogg/nsOggReader.cpp
+++ b/content/media/ogg/nsOggReader.cpp
@@ -310,17 +310,17 @@ nsresult nsOggReader::ReadMetadata(nsVid
         LOG(PR_LOG_DEBUG, ("Got duration from Skeleton index %lld", duration));
       }
     }
   }
 
   {
     ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
 
-    nsMediaStream* stream = mDecoder->GetStream();
+    nsMediaStream* stream = mDecoder->GetCurrentStream();
     if (mDecoder->GetStateMachine()->GetDuration() == -1 &&
         mDecoder->GetStateMachine()->GetState() != nsDecoderStateMachine::DECODER_STATE_SHUTDOWN &&
         stream->GetLength() >= 0 &&
         mDecoder->GetStateMachine()->IsSeekable())
     {
       // We didn't get a duration from the index or a Content-Duration header.
       // Seek to the end of file to find the end time.
       PRInt64 length = stream->GetLength();
@@ -547,17 +547,17 @@ PRInt64 nsOggReader::ReadOggPage(ogg_pag
     // with the given size. This buffer is stored
     // in the ogg synchronisation structure.
     char* buffer = ogg_sync_buffer(&mOggState, 4096);
     NS_ASSERTION(buffer, "ogg_sync_buffer failed");
 
     // Read from the stream into the buffer
     PRUint32 bytesRead = 0;
 
-    nsresult rv = mDecoder->GetStream()->Read(buffer, 4096, &bytesRead);
+    nsresult rv = mDecoder->GetCurrentStream()->Read(buffer, 4096, &bytesRead);
     if (NS_FAILED(rv) || (bytesRead == 0 && ret == 0)) {
       // End of file.
       return -1;
     }
 
     mDecoder->NotifyBytesConsumed(bytesRead);
     // Update the synchronisation layer with the number
     // of bytes written to the buffer
@@ -611,17 +611,17 @@ GetChecksum(ogg_page* page)
                (p[2] << 16) +
                (p[3] << 24);
   return c;
 }
 
 PRInt64 nsOggReader::RangeStartTime(PRInt64 aOffset)
 {
   NS_ASSERTION(mDecoder->OnDecodeThread(), "Should be on decode thread.");
-  nsMediaStream* stream = mDecoder->GetStream();
+  nsMediaStream* stream = mDecoder->GetCurrentStream();
   NS_ENSURE_TRUE(stream != nsnull, nsnull);
   nsresult res = stream->Seek(nsISeekableStream::NS_SEEK_SET, aOffset);
   NS_ENSURE_SUCCESS(res, nsnull);
   PRInt64 startTime = 0;
   nsBuiltinDecoderReader::FindStartTime(startTime);
   return startTime;
 }
 
@@ -635,30 +635,30 @@ struct nsAutoOggSyncState {
   ogg_sync_state mState;
 };
 
 PRInt64 nsOggReader::RangeEndTime(PRInt64 aEndOffset)
 {
   NS_ASSERTION(mDecoder->OnStateMachineThread() || mDecoder->OnDecodeThread(),
                "Should be on state machine or decode thread.");
 
-  nsMediaStream* stream = mDecoder->GetStream();
+  nsMediaStream* stream = mDecoder->GetCurrentStream();
   NS_ENSURE_TRUE(stream != nsnull, -1);
   PRInt64 position = stream->Tell();
   PRInt64 endTime = RangeEndTime(0, aEndOffset, false);
   nsresult res = stream->Seek(nsISeekableStream::NS_SEEK_SET, position);
   NS_ENSURE_SUCCESS(res, -1);
   return endTime;
 }
 
 PRInt64 nsOggReader::RangeEndTime(PRInt64 aStartOffset,
                                   PRInt64 aEndOffset,
                                   bool aCachedDataOnly)
 {
-  nsMediaStream* stream = mDecoder->GetStream();
+  nsMediaStream* stream = mDecoder->GetCurrentStream();
   nsAutoOggSyncState sync;
 
   // We need to find the last page which ends before aEndOffset that
   // has a granulepos that we can convert to a timestamp. We do this by
   // backing off from aEndOffset until we encounter a page on which we can
   // interpret the granulepos. If while backing off we encounter a page which
   // we've previously encountered before, we'll either backoff again if we
   // haven't found an end time yet, or return the last end time found.
@@ -765,17 +765,17 @@ PRInt64 nsOggReader::RangeEndTime(PRInt6
 
   return endTime;
 }
 
 nsresult nsOggReader::GetSeekRanges(nsTArray<SeekRange>& aRanges)
 {
   NS_ASSERTION(mDecoder->OnDecodeThread(), "Should be on decode thread.");
   nsTArray<nsByteRange> cached;
-  nsresult res = mDecoder->GetStream()->GetCachedRanges(cached);
+  nsresult res = mDecoder->GetCurrentStream()->GetCachedRanges(cached);
   NS_ENSURE_SUCCESS(res, res);
 
   for (PRUint32 index = 0; index < cached.Length(); index++) {
     nsByteRange& range = cached[index];
     PRInt64 startTime = -1;
     PRInt64 endTime = -1;
     if (NS_FAILED(ResetDecode())) {
       return NS_ERROR_FAILURE;
@@ -804,17 +804,17 @@ nsOggReader::SeekRange
 nsOggReader::SelectSeekRange(const nsTArray<SeekRange>& ranges,
                              PRInt64 aTarget,
                              PRInt64 aStartTime,
                              PRInt64 aEndTime,
                              bool aExact)
 {
   NS_ASSERTION(mDecoder->OnDecodeThread(), "Should be on decode thread.");
   PRInt64 so = 0;
-  PRInt64 eo = mDecoder->GetStream()->GetLength();
+  PRInt64 eo = mDecoder->GetCurrentStream()->GetLength();
   PRInt64 st = aStartTime;
   PRInt64 et = aEndTime;
   for (PRUint32 i = 0; i < ranges.Length(); i++) {
     const SeekRange &r = ranges[i];
     if (r.mTimeStart < aTarget) {
       so = r.mOffsetStart;
       st = r.mTimeStart;
     }
@@ -832,26 +832,26 @@ nsOggReader::SelectSeekRange(const nsTAr
     return SeekRange();
   }
   return SeekRange(so, eo, st, et);
 }
 
 nsOggReader::IndexedSeekResult nsOggReader::RollbackIndexedSeek(PRInt64 aOffset)
 {
   mSkeletonState->Deactivate();
-  nsMediaStream* stream = mDecoder->GetStream();
+  nsMediaStream* stream = mDecoder->GetCurrentStream();
   NS_ENSURE_TRUE(stream != nsnull, SEEK_FATAL_ERROR);
   nsresult res = stream->Seek(nsISeekableStream::NS_SEEK_SET, aOffset);
   NS_ENSURE_SUCCESS(res, SEEK_FATAL_ERROR);
   return SEEK_INDEX_FAIL;
 }
  
 nsOggReader::IndexedSeekResult nsOggReader::SeekToKeyframeUsingIndex(PRInt64 aTarget)
 {
-  nsMediaStream* stream = mDecoder->GetStream();
+  nsMediaStream* stream = mDecoder->GetCurrentStream();
   NS_ENSURE_TRUE(stream != nsnull, SEEK_FATAL_ERROR);
   if (!HasSkeleton() || !mSkeletonState->HasIndex()) {
     return SEEK_INDEX_FAIL;
   }
   // We have an index from the Skeleton track, try to use it to seek.
   nsAutoTArray<PRUint32, 2> tracks;
   if (HasVideo()) {
     tracks.AppendElement(mTheoraState->mSerial);
@@ -1009,17 +1009,17 @@ nsresult nsOggReader::SeekInUnbuffered(P
 nsresult nsOggReader::Seek(PRInt64 aTarget,
                            PRInt64 aStartTime,
                            PRInt64 aEndTime,
                            PRInt64 aCurrentTime)
 {
   NS_ASSERTION(mDecoder->OnDecodeThread(), "Should be on decode thread.");
   LOG(PR_LOG_DEBUG, ("%p About to seek to %lld", mDecoder, aTarget));
   nsresult res;
-  nsMediaStream* stream = mDecoder->GetStream();
+  nsMediaStream* stream = mDecoder->GetCurrentStream();
   NS_ENSURE_TRUE(stream != nsnull, NS_ERROR_FAILURE);
 
   if (aTarget == aStartTime) {
     // We've seeked to the media start. Just seek to the offset of the first
     // content page.
     res = stream->Seek(nsISeekableStream::NS_SEEK_SET, 0);
     NS_ENSURE_SUCCESS(res,res);
 
@@ -1134,17 +1134,17 @@ PageSync(nsMediaStream* aStream,
 }
 
 nsresult nsOggReader::SeekBisection(PRInt64 aTarget,
                                     const SeekRange& aRange,
                                     PRUint32 aFuzz)
 {
   NS_ASSERTION(mDecoder->OnDecodeThread(), "Should be on decode thread.");
   nsresult res;
-  nsMediaStream* stream = mDecoder->GetStream();
+  nsMediaStream* stream = mDecoder->GetCurrentStream();
 
   if (aTarget == aRange.mTimeStart) {
     if (NS_FAILED(ResetDecode())) {
       return NS_ERROR_FAILURE;
     }
     res = stream->Seek(nsISeekableStream::NS_SEEK_SET, 0);
     NS_ENSURE_SUCCESS(res,res);
     mPageOffset = 0;
@@ -1402,17 +1402,17 @@ nsresult nsOggReader::GetBuffered(nsTime
   // 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->GetStream();
+  nsMediaStream* stream = mDecoder->GetCurrentStream();
   nsTArray<nsByteRange> ranges;
   nsresult res = stream->GetCachedRanges(ranges);
   NS_ENSURE_SUCCESS(res, res);
 
   // 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
--- a/content/media/test/Makefile.in
+++ b/content/media/test/Makefile.in
@@ -68,17 +68,16 @@ include $(topsrcdir)/config/rules.mk
 
 _TEST_FILES = \
 		allowed.sjs \
 		can_play_type_ogg.js \
 		can_play_type_wave.js \
 		can_play_type_webm.js \
 		cancellable_request.sjs \
 		dynamic_redirect.sjs \
-		dynamic_resource.sjs \
 		file_access_controls.html \
 		fragment_play.js \
 		fragment_noplay.js \
 		manifest.js \
 		reactivate_helper.html \
 		redirect.sjs \
 		referer.sjs \
 		seek1.js \
@@ -116,17 +115,16 @@ include $(topsrcdir)/config/rules.mk
 		test_decode_error.html \
 		test_decoder_disable.html \
 		test_delay_load.html \
 		test_error_on_404.html \
 		test_error_in_video_document.html \
 		test_info_leak.html \
 		test_load.html \
 		test_load_candidates.html \
-		test_load_same_resource.html \
 		test_load_source.html \
 		test_loop.html \
 		test_media_selection.html \
 		test_mozLoadFrom.html \
 		test_networkState.html \
 		test_new_audio.html \
 		test_paused.html \
 		test_paused_after_ended.html \
deleted file mode 100644
--- a/content/media/test/dynamic_resource.sjs
+++ /dev/null
@@ -1,34 +0,0 @@
-// Return resource1 file content for the first request with a given key.
-// All subsequent requests return resource2. Both must be video/ogg.
-function handleRequest(request, response)
-{
-  var key = (request.queryString.match(/^key=([^&]*)&/))[1];
-  var resource1 = (request.queryString.match(/res1=([^&]*)/))[1];
-  var resource2 = (request.queryString.match(/res2=([^&]*)/))[1];
-
-  var resource = getState(key) == "2" ? resource2 : resource1;
-  setState(key, "2");
-
-  var file = Components.classes["@mozilla.org/file/directory_service;1"].
-                        getService(Components.interfaces.nsIProperties).
-                        get("CurWorkD", Components.interfaces.nsILocalFile);
-  var fis  = Components.classes['@mozilla.org/network/file-input-stream;1'].
-                        createInstance(Components.interfaces.nsIFileInputStream);
-  var bis  = Components.classes["@mozilla.org/binaryinputstream;1"].
-                        createInstance(Components.interfaces.nsIBinaryInputStream);
-  var paths = "tests/content/media/test/" + resource;
-  var split = paths.split("/");
-  for(var i = 0; i < split.length; ++i) {
-    file.append(split[i]);
-  }
-  fis.init(file, -1, -1, false);
-  dump("file=" + file + "\n");
-  bis.setInputStream(fis);
-  var bytes = bis.readBytes(bis.available());
-  response.setStatusLine(request.httpVersion, 206, "Partial Content");
-  response.setHeader("Content-Range", "bytes 0-" + (bytes.length - 1) + "/" + bytes.length);
-  response.setHeader("Content-Length", ""+bytes.length, false);
-  response.setHeader("Content-Type", "video/ogg", false);
-  response.write(bytes, bytes.length);
-  bis.close();
-}
--- a/content/media/test/manifest.js
+++ b/content/media/test/manifest.js
@@ -20,23 +20,19 @@ var gProgressTests = [
   { name:"seek.ogv", type:"video/ogg", duration:3.966, size:285310 },
   { name:"320x240.ogv", type:"video/ogg", width:320, height:240, duration:0.233, size:28942 },
   { name:"seek.webm", type:"video/webm", duration:3.966, size:215529 },
   { name:"bogus.duh", type:"bogus/duh" }
 ];
 
 // Used by test_mozLoadFrom.  Need one test file per decoder backend, plus
 // anything for testing clone-specific bugs.
-var cloneKey = Math.floor(Math.random()*100000000);
 var gCloneTests = gSmallTests.concat([
   // Actual duration is ~200ms, we have Content-Duration lie about it.
   { name:"bug520908.ogv", type:"video/ogg", duration:9000 },
-  // short-video is more like 1s, so if you load this twice you'll get an unexpected duration
-  { name:"dynamic_resource.sjs?key=" + cloneKey + "&res1=320x240.ogv&res2=short-video.ogv",
-    type:"video/ogg", duration:0.233 },
 ]);
 
 // Used by test_play_twice.  Need one test file per decoder backend, plus
 // anything for testing bugs that occur when replying a played file.
 var gReplayTests = gSmallTests.concat([
   { name:"bug533822.ogg", type:"audio/ogg" },
 ]);
 
deleted file mode 100644
--- a/content/media/test/test_load_same_resource.html
+++ /dev/null
@@ -1,70 +0,0 @@
-<!DOCTYPE HTML>
-<html>
-<head>
-  <title>Test loading of the same resource in multiple elements</title>
-  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
-  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
-  <script type="text/javascript" src="manifest.js"></script>
-</head>
-<body>
-
-<pre id="test">
-<script class="testbody" type="text/javascript">
-
-var manager = new MediaTestManager;
-
-function cloneLoaded(event) {
-  ok(true, "Clone loaded OK");
-  var e = event.target;
-
-  if (e._expectedDuration) {
-    ok(Math.abs(e.duration - e._expectedDuration) < 0.1,
-       "Clone " + e.currentSrc + " duration: " + e.duration + " expected: " + e._expectedDuration);
-  }
-
-  manager.finished(e.token);
-}
-
-function tryClone(event) {
-  var e = event.target;
-  var clone = e.cloneNode(false);
-  clone.token = e.token;
-  
-  if (e._expectedDuration) {
-    ok(Math.abs(e.duration - e._expectedDuration) < 0.1,
-       e.currentSrc + " duration: " + e.duration + " expected: " + e._expectedDuration);
-    clone._expectedDuration = e._expectedDuration;
-  }
-
-  clone.addEventListener("loadeddata", cloneLoaded, false);
-}
-
-// This test checks that loading the same URI twice in different elements at the same time
-// uses the same resource without doing another network fetch. One of the gCloneTests
-// uses dynamic_resource.sjs to return one resource on the first fetch and a different resource
-// on the second fetch. These resources have different lengths, so if the cloned element
-// does a network fetch it will get a resource with the wrong length and we get a test
-// failure.
-
-function initTest(test, token) {
-  var elemType = /^audio/.test(test.type) ? "audio" : "video";
-  var e = document.createElement(elemType);
-  if (e.canPlayType(test.type)) {
-    e.src = test.name;
-    if (test.duration) {
-      e._expectedDuration = test.duration;
-    }
-    ok(true, "Trying to load " + test.name);
-    e.addEventListener("loadeddata", tryClone, false);
-    e.load();
-    e.token = token;
-    manager.started(token);
-  }
-}
-
-manager.runTests(gCloneTests, initTest);
-
-</script>
-</pre>
-</body>
-</html>
--- a/content/media/test/test_preload_actions.html
+++ b/content/media/test/test_preload_actions.html
@@ -25,17 +25,16 @@ https://bugzilla.mozilla.org/show_bug.cg
 
 var manager = new MediaTestManager;
 
 manager.onFinished = function() {
   is(gotLoadEvent, true, "Should not have delayed the load event indefinitely");
 };
 
 var test = getPlayableVideo(gSeekTests);
-var baseName = test.name;
 var gTest = test;
 var bogusSrc = "bogus.duh";
 var bogusType = "video/bogus";
 var gotLoadEvent = false;
 var finished = false;
 
 addLoadEvent(function() {gotLoadEvent=true;});
 
@@ -140,19 +139,18 @@ var tests = [
       document.body.appendChild(v);
     },
   },
   {
     // 4. Add preload:none video to document. Call play(), should load then play through.
     suspend:
     function(e) {
       var v = e.target;
-      if (v._gotSuspend) {
+      if (v._gotSuspend) 
         return; // We can receive multiple suspend events, like the one after download completes.
-      }
       v._gotSuspend = true;
       is(v._gotLoadStart, true, "(4) Must get loadstart.");
       is(v._gotLoadedMetaData, false, "(4) Must not get loadedmetadata.");
       is(v.readyState, v.HAVE_NOTHING, "(4) ReadyState must be HAVE_NOTHING");
       is(v.networkState, v.NETWORK_IDLE, "(4) NetworkState must be NETWORK_IDLE");
       v.play(); // Should load and play through.
     },
     
@@ -554,30 +552,21 @@ var tests = [
       v.preload = "none";
       v.src = test.name; // Schedules async section to continue load algorithm.
       document.body.appendChild(v);
       v.play(); // Should cause preload:none to be overridden.
     },  
     }
 ];
 
-var iterationCount = 0;
 function startTest(test, token) {
-  if (test == tests[0]) {
-    ++iterationCount;
-  }
-  if (iterationCount == 2) {
-    // Do this series of tests on logically different resources
-    test.name = baseName + "?" + Math.floor(Math.random()*100000);
-  }
   var v = document.createElement("video");
   v.token = token;
   test.setup(v);
   manager.started(token);
 }
 
-var twiceTests = tests.concat(tests);
-manager.runTests(twiceTests, startTest);
+manager.runTests(tests, startTest);
 
 </script>
 </pre>
 </body>
 </html>
--- a/content/media/wave/nsWaveReader.cpp
+++ b/content/media/wave/nsWaveReader.cpp
@@ -256,52 +256,52 @@ nsresult nsWaveReader::Seek(PRInt64 aTar
   }
   double d = BytesToTime(GetDataLength());
   NS_ASSERTION(d < PR_INT64_MAX / USECS_PER_S, "Duration overflow"); 
   PRInt64 duration = static_cast<PRInt64>(d * USECS_PER_S);
   double seekTime = NS_MIN(aTarget, duration) / static_cast<double>(USECS_PER_S);
   PRInt64 position = RoundDownToFrame(static_cast<PRInt64>(TimeToBytes(seekTime)));
   NS_ASSERTION(PR_INT64_MAX - mWavePCMOffset > position, "Integer overflow during wave seek");
   position += mWavePCMOffset;
-  return mDecoder->GetStream()->Seek(nsISeekableStream::NS_SEEK_SET, position);
+  return mDecoder->GetCurrentStream()->Seek(nsISeekableStream::NS_SEEK_SET, position);
 }
 
 static double RoundToUsecs(double aSeconds) {
   return floor(aSeconds * USECS_PER_S) / USECS_PER_S;
 }
 
 nsresult nsWaveReader::GetBuffered(nsTimeRanges* aBuffered, PRInt64 aStartTime)
 {
-  PRInt64 startOffset = mDecoder->GetStream()->GetNextCachedData(mWavePCMOffset);
+  PRInt64 startOffset = mDecoder->GetCurrentStream()->GetNextCachedData(mWavePCMOffset);
   while (startOffset >= 0) {
-    PRInt64 endOffset = mDecoder->GetStream()->GetCachedDataEnd(startOffset);
+    PRInt64 endOffset = mDecoder->GetCurrentStream()->GetCachedDataEnd(startOffset);
     // Bytes [startOffset..endOffset] are cached.
     NS_ASSERTION(startOffset >= mWavePCMOffset, "Integer underflow in GetBuffered");
     NS_ASSERTION(endOffset >= mWavePCMOffset, "Integer underflow in GetBuffered");
 
     // We need to round the buffered ranges' times to microseconds so that they
     // have the same precision as the currentTime and duration attribute on 
     // the media element.
     aBuffered->Add(RoundToUsecs(BytesToTime(startOffset - mWavePCMOffset)),
                    RoundToUsecs(BytesToTime(endOffset - mWavePCMOffset)));
-    startOffset = mDecoder->GetStream()->GetNextCachedData(endOffset);
+    startOffset = mDecoder->GetCurrentStream()->GetNextCachedData(endOffset);
   }
   return NS_OK;
 }
 
 bool
 nsWaveReader::ReadAll(char* aBuf, PRInt64 aSize, PRInt64* aBytesRead)
 {
   PRUint32 got = 0;
   if (aBytesRead) {
     *aBytesRead = 0;
   }
   do {
     PRUint32 read = 0;
-    if (NS_FAILED(mDecoder->GetStream()->Read(aBuf + got, PRUint32(aSize - got), &read))) {
+    if (NS_FAILED(mDecoder->GetCurrentStream()->Read(aBuf + got, PRUint32(aSize - got), &read))) {
       NS_WARNING("Stream read failed");
       return false;
     }
     if (read == 0) {
       return false;
     }
     mDecoder->NotifyBytesConsumed(read);
     got += read;
@@ -313,17 +313,17 @@ nsWaveReader::ReadAll(char* aBuf, PRInt6
 }
 
 bool
 nsWaveReader::LoadRIFFChunk()
 {
   char riffHeader[RIFF_INITIAL_SIZE];
   const char* p = riffHeader;
 
-  NS_ABORT_IF_FALSE(mDecoder->GetStream()->Tell() == 0,
+  NS_ABORT_IF_FALSE(mDecoder->GetCurrentStream()->Tell() == 0,
                     "LoadRIFFChunk called when stream in invalid state");
 
   if (!ReadAll(riffHeader, sizeof(riffHeader))) {
     return false;
   }
 
   PR_STATIC_ASSERT(sizeof(PRUint32) * 2 <= RIFF_INITIAL_SIZE);
   if (ReadUint32BE(&p) != RIFF_CHUNK_MAGIC) {
@@ -385,17 +385,17 @@ nsWaveReader::ScanForwardUntil(PRUint32 
 bool
 nsWaveReader::LoadFormatChunk()
 {
   PRUint32 fmtSize, rate, channels, frameSize, sampleFormat;
   char waveFormat[WAVE_FORMAT_CHUNK_SIZE];
   const char* p = waveFormat;
 
   // RIFF chunks are always word (two byte) aligned.
-  NS_ABORT_IF_FALSE(mDecoder->GetStream()->Tell() % 2 == 0,
+  NS_ABORT_IF_FALSE(mDecoder->GetCurrentStream()->Tell() % 2 == 0,
                     "LoadFormatChunk called with unaligned stream");
 
   // The "format" chunk may not directly follow the "riff" chunk, so skip
   // over any intermediate chunks.
   if (!ScanForwardUntil(FRMT_CHUNK_MAGIC, &fmtSize)) {
     return false;
   }
 
@@ -450,17 +450,17 @@ nsWaveReader::LoadFormatChunk()
       nsAutoArrayPtr<char> chunkExtension(new char[extra]);
       if (!ReadAll(chunkExtension.get(), extra)) {
         return false;
       }
     }
   }
 
   // RIFF chunks are always word (two byte) aligned.
-  NS_ABORT_IF_FALSE(mDecoder->GetStream()->Tell() % 2 == 0,
+  NS_ABORT_IF_FALSE(mDecoder->GetCurrentStream()->Tell() % 2 == 0,
                     "LoadFormatChunk left stream unaligned");
 
   // Make sure metadata is fairly sane.  The rate check is fairly arbitrary,
   // but the channels check is intentionally limited to mono or stereo
   // because that's what the audio backend currently supports.
   if (rate < 100 || rate > 96000 ||
       channels < 1 || channels > MAX_CHANNELS ||
       (frameSize != 1 && frameSize != 2 && frameSize != 4) ||
@@ -480,27 +480,27 @@ nsWaveReader::LoadFormatChunk()
   }
   return true;
 }
 
 bool
 nsWaveReader::FindDataOffset()
 {
   // RIFF chunks are always word (two byte) aligned.
-  NS_ABORT_IF_FALSE(mDecoder->GetStream()->Tell() % 2 == 0,
+  NS_ABORT_IF_FALSE(mDecoder->GetCurrentStream()->Tell() % 2 == 0,
                     "FindDataOffset called with unaligned stream");
 
   // The "data" chunk may not directly follow the "format" chunk, so skip
   // over any intermediate chunks.
   PRUint32 length;
   if (!ScanForwardUntil(DATA_CHUNK_MAGIC, &length)) {
     return false;
   }
 
-  PRInt64 offset = mDecoder->GetStream()->Tell();
+  PRInt64 offset = mDecoder->GetCurrentStream()->Tell();
   if (offset <= 0 || offset > PR_UINT32_MAX) {
     NS_WARNING("PCM data offset out of range");
     return false;
   }
 
   ReentrantMonitorAutoEnter monitor(mDecoder->GetReentrantMonitor());
   mWaveLength = length;
   mWavePCMOffset = PRUint32(offset);
@@ -530,21 +530,21 @@ nsWaveReader::RoundDownToFrame(PRInt64 a
 
 PRInt64
 nsWaveReader::GetDataLength()
 {
   PRInt64 length = mWaveLength;
   // If the decoder has a valid content length, and it's shorter than the
   // expected length of the PCM data, calculate the playback duration from
   // the content length rather than the expected PCM data length.
-  PRInt64 streamLength = mDecoder->GetStream()->GetLength();
+  PRInt64 streamLength = mDecoder->GetCurrentStream()->GetLength();
   if (streamLength >= 0) {
     PRInt64 dataLength = NS_MAX<PRInt64>(0, streamLength - mWavePCMOffset);
     length = NS_MIN(dataLength, length);
   }
   return length;
 }
 
 PRInt64
 nsWaveReader::GetPosition()
 {
-  return mDecoder->GetStream()->Tell();
+  return mDecoder->GetCurrentStream()->Tell();
 }
--- a/content/media/webm/nsWebMReader.cpp
+++ b/content/media/webm/nsWebMReader.cpp
@@ -81,17 +81,17 @@ public:
 
 // Functions for reading and seeking using nsMediaStream required for
 // nestegg_io. The 'user data' passed to these functions is the
 // decoder from which the media stream is obtained.
 static int webm_read(void *aBuffer, size_t aLength, void *aUserData)
 {
   NS_ASSERTION(aUserData, "aUserData must point to a valid nsBuiltinDecoder");
   nsBuiltinDecoder* decoder = reinterpret_cast<nsBuiltinDecoder*>(aUserData);
-  nsMediaStream* stream = decoder->GetStream();
+  nsMediaStream* stream = decoder->GetCurrentStream();
   NS_ASSERTION(stream, "Decoder has no media stream");
 
   nsresult rv = NS_OK;
   bool eof = false;
 
   char *p = static_cast<char *>(aBuffer);
   while (NS_SUCCEEDED(rv) && aLength > 0) {
     PRUint32 bytes = 0;
@@ -107,27 +107,27 @@ static int webm_read(void *aBuffer, size
 
   return NS_FAILED(rv) ? -1 : eof ? 0 : 1;
 }
 
 static int webm_seek(int64_t aOffset, int aWhence, void *aUserData)
 {
   NS_ASSERTION(aUserData, "aUserData must point to a valid nsBuiltinDecoder");
   nsBuiltinDecoder* decoder = reinterpret_cast<nsBuiltinDecoder*>(aUserData);
-  nsMediaStream* stream = decoder->GetStream();
+  nsMediaStream* stream = decoder->GetCurrentStream();
   NS_ASSERTION(stream, "Decoder has no media stream");
   nsresult rv = stream->Seek(aWhence, aOffset);
   return NS_SUCCEEDED(rv) ? 0 : -1;
 }
 
 static int64_t webm_tell(void *aUserData)
 {
   NS_ASSERTION(aUserData, "aUserData must point to a valid nsBuiltinDecoder");
   nsBuiltinDecoder* decoder = reinterpret_cast<nsBuiltinDecoder*>(aUserData);
-  nsMediaStream* stream = decoder->GetStream();
+  nsMediaStream* stream = decoder->GetCurrentStream();
   NS_ASSERTION(stream, "Decoder has no media stream");
   return stream->Tell();
 }
 
 nsWebMReader::nsWebMReader(nsBuiltinDecoder* aDecoder)
   : nsBuiltinDecoderReader(aDecoder),
   mContext(nsnull),
   mPacketCount(0),
@@ -554,17 +554,17 @@ nsReturnRef<NesteggPacketHolder> nsWebMR
     // Keep reading packets until we find a packet
     // for the track we want.
     do {
       nestegg_packet* packet;
       int r = nestegg_read_packet(mContext, &packet);
       if (r <= 0) {
         return nsReturnRef<NesteggPacketHolder>();
       }
-      PRInt64 offset = mDecoder->GetStream()->Tell();
+      PRInt64 offset = mDecoder->GetCurrentStream()->Tell();
       holder.own(new NesteggPacketHolder(packet, offset));
 
       unsigned int track = 0;
       r = nestegg_packet_track(packet, &track);
       if (r == -1) {
         return nsReturnRef<NesteggPacketHolder>();
       }
 
@@ -764,31 +764,31 @@ nsresult nsWebMReader::Seek(PRInt64 aTar
   if (r != 0) {
     return NS_ERROR_FAILURE;
   }
   return DecodeToTarget(aTarget);
 }
 
 nsresult nsWebMReader::GetBuffered(nsTimeRanges* aBuffered, PRInt64 aStartTime)
 {
-  nsMediaStream* stream = mDecoder->GetStream();
+  nsMediaStream* stream = mDecoder->GetCurrentStream();
 
   uint64_t timecodeScale;
   if (!mContext || nestegg_tstamp_scale(mContext, &timecodeScale) == -1) {
     return NS_OK;
   }
 
   // Special case completely cached files.  This also handles local files.
   if (stream->IsDataCachedToEndOfStream(0)) {
     uint64_t duration = 0;
     if (nestegg_duration(mContext, &duration) == 0) {
       aBuffered->Add(0, duration / NS_PER_S);
     }
   } else {
-    nsMediaStream* stream = mDecoder->GetStream();
+    nsMediaStream* stream = mDecoder->GetCurrentStream();
     nsTArray<nsByteRange> ranges;
     nsresult res = stream->GetCachedRanges(ranges);
     NS_ENSURE_SUCCESS(res, res);
 
     PRInt64 startTimeOffsetNS = aStartTime * NS_PER_USEC;
     for (PRUint32 index = 0; index < ranges.Length(); index++) {
       mBufferedState->CalculateBufferedForRange(aBuffered,
                                                 ranges[index].mStart,