From 5755e41c5c3aef23fbbf71d102107c1f498421d3 Mon Sep 17 00:00:00 2001 From: Robert O'Callahan Date: Fri, 25 Nov 2011 15:06:22 +1300 Subject: [PATCH] Bug 703379. Keep a table of media elements indexed by URI and when loading, try to find an existing element with the same URI and clone its decoder. r=doublec --- .../html/content/public/nsHTMLMediaElement.h | 34 +++- .../html/content/src/nsHTMLMediaElement.cpp | 146 ++++++++++++++---- content/media/test/Makefile.in | 2 + content/media/test/allowed.sjs | 17 +- content/media/test/cancellable_request.sjs | 18 ++- content/media/test/dynamic_redirect.sjs | 18 ++- content/media/test/dynamic_resource.sjs | 48 ++++++ content/media/test/file_access_controls.html | 8 +- content/media/test/manifest.js | 4 + content/media/test/redirect.sjs | 33 ++-- content/media/test/referer.sjs | 17 +- content/media/test/test_decoder_disable.html | 10 +- .../media/test/test_load_same_resource.html | 70 +++++++++ content/media/test/test_preload_actions.html | 15 +- 14 files changed, 374 insertions(+), 66 deletions(-) create mode 100644 content/media/test/dynamic_resource.sjs create mode 100644 content/media/test/test_load_same_resource.html diff --git a/content/html/content/public/nsHTMLMediaElement.h b/content/html/content/public/nsHTMLMediaElement.h index 6976acbcae3..a836f46b2d0 100644 --- a/content/html/content/public/nsHTMLMediaElement.h +++ b/content/html/content/public/nsHTMLMediaElement.h @@ -370,21 +370,38 @@ protected: /** * 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 @@ -431,7 +448,7 @@ protected: /** * The resource-fetch algorithm step of the load algorithm. */ - nsresult LoadResource(nsIURI* aURI); + nsresult LoadResource(); /** * Selects the next child from which to load a resource. Called @@ -492,11 +509,11 @@ protected: }; /** - * Suspends the load of resource at aURI, so that it can be resumed later + * Suspends the load of mLoadingSrc, 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(nsIURI* aURI); + void SuspendLoad(); /** * Resumes a previously suspended load (suspended by SuspendLoad(uri)). @@ -535,6 +552,7 @@ protected: */ void ProcessMediaFragmentURI(); + // The current decoder. Load() has been called on this decoder. nsRefPtr mDecoder; // A reference to the ImageContainer which contains the current frame @@ -597,11 +615,11 @@ protected: // Current audio sample rate. PRUint32 mRate; - // 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. + // 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. nsCOMPtr mLoadingSrc; // Stores the current preload action for this element. Initially set to diff --git a/content/html/content/src/nsHTMLMediaElement.cpp b/content/html/content/src/nsHTMLMediaElement.cpp index 100bd950a5e..035f566b491 100644 --- a/content/html/content/src/nsHTMLMediaElement.cpp +++ b/content/html/content/src/nsHTMLMediaElement.cpp @@ -89,6 +89,7 @@ #include "nsIPrivateDOMEvent.h" #include "nsIDOMNotifyAudioAvailableEvent.h" #include "nsMediaFragmentURIParser.h" +#include "nsURIHashKey.h" #ifdef MOZ_OGG #include "nsOggDecoder.h" @@ -479,11 +480,14 @@ void nsHTMLMediaElement::AbortExistingLoads() 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) @@ -497,6 +501,7 @@ void nsHTMLMediaElement::AbortExistingLoads() mIsLoadingFromSourceChildren = false; mSuspendedAfterFirstFrame = false; mAllowSuspendAfterFirstFrame = true; + mLoadIsSuspended = false; mSourcePointer = nsnull; // TODO: The playback rate must be set to the default playback rate. @@ -648,12 +653,12 @@ void nsHTMLMediaElement::SelectResource() if (mPreloadAction == nsHTMLMediaElement::PRELOAD_NONE) { // preload:none media, suspend the load here before we make any // network requests. - SuspendLoad(uri); + SuspendLoad(); mIsRunningSelectResource = false; return; } - rv = LoadResource(uri); + rv = LoadResource(); if (NS_SUCCEEDED(rv)) { mIsRunningSelectResource = false; return; @@ -750,11 +755,11 @@ void nsHTMLMediaElement::LoadFromSourceChildren() if (mPreloadAction == nsHTMLMediaElement::PRELOAD_NONE) { // preload:none media, suspend the load here before we make any // network requests. - SuspendLoad(uri); + SuspendLoad(); return; } - if (NS_SUCCEEDED(LoadResource(uri))) { + if (NS_SUCCEEDED(LoadResource())) { return; } @@ -764,7 +769,7 @@ void nsHTMLMediaElement::LoadFromSourceChildren() NS_NOTREACHED("Execution should not reach here!"); } -void nsHTMLMediaElement::SuspendLoad(nsIURI* aURI) +void nsHTMLMediaElement::SuspendLoad() { mLoadIsSuspended = true; mNetworkState = nsIDOMHTMLMediaElement::NETWORK_IDLE; @@ -775,20 +780,19 @@ void nsHTMLMediaElement::SuspendLoad(nsIURI* aURI) void nsHTMLMediaElement::ResumeLoad(PreloadAction aAction) { NS_ASSERTION(mLoadIsSuspended, "Can only resume preload if halted for one"); - nsCOMPtr 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(uri))) { + if (NS_FAILED(LoadResource())) { NoSupportedMediaSourceError(); } } else { // We were loading from a child element. Try to resume the // load of that child, and if that fails, try the next child. - if (NS_FAILED(LoadResource(uri))) { + if (NS_FAILED(LoadResource())) { LoadFromSourceChildren(); } } @@ -873,7 +877,7 @@ void nsHTMLMediaElement::UpdatePreloadAction() } } -nsresult nsHTMLMediaElement::LoadResource(nsIURI* aURI) +nsresult nsHTMLMediaElement::LoadResource() { NS_ASSERTION(mDelayingLoadEvent, "Should delay load event (if in document) during load"); @@ -890,9 +894,17 @@ nsresult nsHTMLMediaElement::LoadResource(nsIURI* aURI) 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, - aURI, + mLoadingSrc, NodePrincipal(), static_cast(this), EmptyCString(), // mime type @@ -920,7 +932,7 @@ nsresult nsHTMLMediaElement::LoadResource(nsIURI* aURI) } nsCOMPtr channel; rv = NS_NewChannel(getter_AddRefs(channel), - aURI, + mLoadingSrc, nsnull, loadGroup, nsnull, @@ -949,7 +961,7 @@ nsresult nsHTMLMediaElement::LoadResource(nsIURI* aURI) } else { rv = nsContentUtils::GetSecurityManager()-> CheckLoadURIWithPrincipal(NodePrincipal(), - aURI, + mLoadingSrc, nsIScriptSecurityManager::STANDARD); listener = loadListener; } @@ -991,9 +1003,11 @@ nsresult nsHTMLMediaElement::LoadWithChannel(nsIChannel *aChannel, AbortExistingLoads(); - ChangeDelayLoadStatus(true); + nsresult rv = aChannel->GetOriginalURI(getter_AddRefs(mLoadingSrc)); + NS_ENSURE_SUCCESS(rv, rv); - nsresult rv = InitializeDecoderForChannel(aChannel, aListener); + ChangeDelayLoadStatus(true); + rv = InitializeDecoderForChannel(aChannel, aListener); if (NS_FAILED(rv)) { ChangeDelayLoadStatus(false); return rv; @@ -1017,6 +1031,7 @@ NS_IMETHODIMP nsHTMLMediaElement::MozLoadFrom(nsIDOMHTMLMediaElement* aOther) ChangeDelayLoadStatus(true); + mLoadingSrc = other->mLoadingSrc; nsresult rv = InitializeDecoderAsClone(other->mDecoder); if (NS_FAILED(rv)) { ChangeDelayLoadStatus(false); @@ -1241,6 +1256,76 @@ NS_IMETHODIMP nsHTMLMediaElement::SetMuted(bool aMuted) return NS_OK; } +class MediaElementSetForURI : public nsURIHashKey { +public: + MediaElementSetForURI(const nsIURI* aKey) : nsURIHashKey(aKey) {} + MediaElementSetForURI(const MediaElementSetForURI& toCopy) + : nsURIHashKey(toCopy), mElements(toCopy.mElements) {} + nsTArray mElements; +}; + +typedef nsTHashtable 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 aNodeInfo) : nsGenericHTMLElement(aNodeInfo), mCurrentLoadID(0), @@ -1297,16 +1382,14 @@ nsHTMLMediaElement::~nsHTMLMediaElement() UnregisterFreezableElement(); if (mDecoder) { + RemoveMediaElementFromURITable(); mDecoder->Shutdown(); - mDecoder = nsnull; } if (mChannel) { mChannel->Cancel(NS_BINDING_ABORTED); - mChannel = nsnull; } if (mAudioStream) { mAudioStream->Shutdown(); - mAudioStream = nsnull; } } @@ -1347,9 +1430,13 @@ NS_IMETHODIMP nsHTMLMediaElement::Play() if (mNetworkState == nsIDOMHTMLMediaElement::NETWORK_EMPTY) { nsresult rv = Load(); NS_ENSURE_SUCCESS(rv, rv); - } else if (mLoadIsSuspended) { + } + if (mLoadIsSuspended) { ResumeLoad(PRELOAD_ENOUGH); - } else if (mDecoder) { + } + // 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) { if (mDecoder->IsEnded()) { SetCurrentTime(0); } @@ -1811,6 +1898,8 @@ nsHTMLMediaElement::CreateDecoder(const nsACString& aType) nsresult nsHTMLMediaElement::InitializeDecoderAsClone(nsMediaDecoder* aOriginal) { + NS_ASSERTION(mLoadingSrc, "mLoadingSrc must already be set"); + nsMediaStream* originalStream = aOriginal->GetStream(); if (!originalStream) return NS_ERROR_FAILURE; @@ -1848,6 +1937,8 @@ nsresult nsHTMLMediaElement::InitializeDecoderAsClone(nsMediaDecoder* aOriginal) nsresult nsHTMLMediaElement::InitializeDecoderForChannel(nsIChannel *aChannel, nsIStreamListener **aListener) { + NS_ASSERTION(mLoadingSrc, "mLoadingSrc must already be set"); + nsCAutoString mimeType; aChannel->GetContentType(mimeType); @@ -1878,10 +1969,10 @@ nsresult nsHTMLMediaElement::InitializeDecoderForChannel(nsIChannel *aChannel, nsresult nsHTMLMediaElement::FinishDecoderSetup(nsMediaDecoder* aDecoder) { - mDecoder = aDecoder; + NS_ASSERTION(mLoadingSrc, "mLoadingSrc set up"); - // Decoder has assumed ownership responsibility for remembering the URI. - mLoadingSrc = nsnull; + mDecoder = aDecoder; + AddMediaElementToURITable(); // Force a same-origin check before allowing events for this media resource. mMediaSecurityVerified = false; @@ -2019,9 +2110,11 @@ void nsHTMLMediaElement::NetworkError() void nsHTMLMediaElement::DecodeError() { if (mDecoder) { + RemoveMediaElementFromURITable(); mDecoder->Shutdown(); mDecoder = nsnull; } + mLoadingSrc = nsnull; if (mIsLoadingFromSourceChildren) { mError = nsnull; if (mSourceLoadCandidate) { @@ -2669,13 +2762,10 @@ void nsHTMLMediaElement::FireTimeUpdate(bool aPeriodic) void nsHTMLMediaElement::GetCurrentSpec(nsCString& aString) { - if (mDecoder) { - nsMediaStream* stream = mDecoder->GetStream(); - if (stream) { - stream->URI()->GetSpec(aString); - } - } else if (mLoadingSrc) { + if (mLoadingSrc) { mLoadingSrc->GetSpec(aString); + } else { + aString.Truncate(); } } diff --git a/content/media/test/Makefile.in b/content/media/test/Makefile.in index 63b7308f588..a5d37de45be 100644 --- a/content/media/test/Makefile.in +++ b/content/media/test/Makefile.in @@ -73,6 +73,7 @@ _TEST_FILES = \ can_play_type_webm.js \ cancellable_request.sjs \ dynamic_redirect.sjs \ + dynamic_resource.sjs \ file_access_controls.html \ fragment_play.js \ fragment_noplay.js \ @@ -120,6 +121,7 @@ _TEST_FILES = \ 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 \ diff --git a/content/media/test/allowed.sjs b/content/media/test/allowed.sjs index a9e5798cbf2..daf6a4f7fd4 100644 --- a/content/media/test/allowed.sjs +++ b/content/media/test/allowed.sjs @@ -1,3 +1,17 @@ +function parseQuery(request, key) { + var params = request.queryString.split('&'); + for (var j = 0; j < params.length; ++j) { + var p = params[j]; + if (p == key) + return true; + if (p.indexOf(key + "=") == 0) + return p.substring(key.length + 1); + if (p.indexOf("=") < 0 && key == "") + return p; + } + return false; +} + var types = { ogg: "video/ogg", ogv: "video/ogg", @@ -10,7 +24,8 @@ var types = { // allow headers. function handleRequest(request, response) { - var resource = request.queryString; + var resource = parseQuery(request, ""); + var file = Components.classes["@mozilla.org/file/directory_service;1"]. getService(Components.interfaces.nsIProperties). get("CurWorkD", Components.interfaces.nsILocalFile); diff --git a/content/media/test/cancellable_request.sjs b/content/media/test/cancellable_request.sjs index 300b8c43ffa..aeea526e527 100644 --- a/content/media/test/cancellable_request.sjs +++ b/content/media/test/cancellable_request.sjs @@ -1,3 +1,17 @@ +function parseQuery(request, key) { + var params = request.queryString.split('&'); + for (var j = 0; j < params.length; ++j) { + var p = params[j]; + if (p == key) + return true; + if (p.indexOf(key + "=") == 0) + return p.substring(key.length + 1); + if (p.indexOf("=") < 0 && key == "") + return p; + } + return false; +} + function push32BE(array, input) { array.push(String.fromCharCode((input >> 24) & 0xff)); array.push(String.fromCharCode((input >> 16) & 0xff)); @@ -60,7 +74,7 @@ function poll(f) { function handleRequest(request, response) { - var cancel = request.queryString.match(/^cancelkey=(.*)$/); + var cancel = parseQuery(request, "cancelkey"); if (cancel) { setState(cancel[1], "cancelled"); response.setStatusLine(request.httpVersion, 200, "OK"); @@ -74,7 +88,7 @@ function handleRequest(request, response) } var bytes = buildWave(samples, 44100).join(""); - var key = request.queryString.match(/^key=(.*)$/); + var key = parseQuery(request, "key"); response.setHeader("Content-Type", "audio/x-wav"); response.setHeader("Content-Length", ""+bytes.length, false); diff --git a/content/media/test/dynamic_redirect.sjs b/content/media/test/dynamic_redirect.sjs index 2723246bddc..4b09e7ea25a 100644 --- a/content/media/test/dynamic_redirect.sjs +++ b/content/media/test/dynamic_redirect.sjs @@ -1,9 +1,23 @@ +function parseQuery(request, key) { + var params = request.queryString.split('&'); + for (var j = 0; j < params.length; ++j) { + var p = params[j]; + if (p == key) + return true; + if (p.indexOf(key + "=") == 0) + return p.substring(key.length + 1); + if (p.indexOf("=") < 0 && key == "") + return p; + } + return false; +} + // Return seek.ogv file content for the first request with a given key. // All subsequent requests return a redirect to a different-origin resource. function handleRequest(request, response) { - var key = (request.queryString.match(/^key=(.*)&/))[1]; - var resource = (request.queryString.match(/res=(.*)$/))[1]; + var key = parseQuery(request, "key"); + var resource = parseQuery(request, "res"); if (getState(key) == "redirect") { var origin = request.host == "mochi.test" ? "example.org" : "mochi.test:8888"; diff --git a/content/media/test/dynamic_resource.sjs b/content/media/test/dynamic_resource.sjs new file mode 100644 index 00000000000..07001375123 --- /dev/null +++ b/content/media/test/dynamic_resource.sjs @@ -0,0 +1,48 @@ +function parseQuery(request, key) { + var params = request.queryString.split('&'); + for (var j = 0; j < params.length; ++j) { + var p = params[j]; + if (p == key) + return true; + if (p.indexOf(key + "=") == 0) + return p.substring(key.length + 1); + if (p.indexOf("=") < 0 && key == "") + return p; + } + return false; +} + +// 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 = parseQuery(request, "key"); + var resource1 = parseQuery(request, "res1"); + var resource2 = parseQuery(request, "res2"); + + 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(); +} diff --git a/content/media/test/file_access_controls.html b/content/media/test/file_access_controls.html index d5ffd50cefe..d732e6c8e1f 100644 --- a/content/media/test/file_access_controls.html +++ b/content/media/test/file_access_controls.html @@ -140,7 +140,13 @@ function nextTest() { gVideo = createVideo(); gVideo.expectedResult = gTests[gTestNum].result; gVideo.testDescription = gTests[gTestNum].description; - gVideo.src = gTests[gTestNum].url; + // Uniquify the resource URL to ensure that the resources loaded by earlier or subsequent tests + // don't overlap with the resources we load here, which are loaded with non-default preferences set. + // We also want to make sure that an HTTP fetch actually happens for each testcase. + var url = gTests[gTestNum].url; + var random = Math.floor(Math.random()*1000000000); + url += (url.search(/\?/) < 0 ? "?" : "&") + "rand=" + random; + gVideo.src = url; //dump("Starting test " + gTestNum + " at " + gVideo.src + " expecting:" + gVideo.expectedResult + "\n"); if (!gTestedRemoved) { document.body.appendChild(gVideo); diff --git a/content/media/test/manifest.js b/content/media/test/manifest.js index 7405ce4c6d1..4106c9eb2a0 100644 --- a/content/media/test/manifest.js +++ b/content/media/test/manifest.js @@ -25,9 +25,13 @@ var gProgressTests = [ // 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 diff --git a/content/media/test/redirect.sjs b/content/media/test/redirect.sjs index 710846d9b9b..68ac8234b8a 100644 --- a/content/media/test/redirect.sjs +++ b/content/media/test/redirect.sjs @@ -1,25 +1,26 @@ +function parseQuery(request, key) { + var params = request.queryString.split('&'); + for (var j = 0; j < params.length; ++j) { + var p = params[j]; + if (p == key) + return true; + if (p.indexOf(key + "=") == 0) + return p.substring(key.length + 1); + if (p.indexOf("=") < 0 && key == "") + return p; + } + return false; +} + // Return file content for the first request with a given key. // All subsequent requests return a redirect to a different-origin resource. function handleRequest(request, response) { - var params = request.queryString.split('&'); - var domain = null; - var file = null; - var allowed = false; - - for (var i=0; i - + - - + + diff --git a/content/media/test/test_load_same_resource.html b/content/media/test/test_load_same_resource.html new file mode 100644 index 00000000000..c72306b9edc --- /dev/null +++ b/content/media/test/test_load_same_resource.html @@ -0,0 +1,70 @@ + + + + Test loading of the same resource in multiple elements + + + + + + +
+
+
+ + diff --git a/content/media/test/test_preload_actions.html b/content/media/test/test_preload_actions.html index 74de9c29b90..ee7aab18263 100644 --- a/content/media/test/test_preload_actions.html +++ b/content/media/test/test_preload_actions.html @@ -30,6 +30,7 @@ manager.onFinished = function() { }; var test = getPlayableVideo(gSeekTests); +var baseName = test.name; var gTest = test; var bogusSrc = "bogus.duh"; var bogusType = "video/bogus"; @@ -144,8 +145,9 @@ var tests = [ 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."); @@ -557,14 +559,23 @@ var tests = [ } ]; +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); } -manager.runTests(tests, startTest); +var twiceTests = tests.concat(tests); +manager.runTests(twiceTests, startTest);