mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-12-02 01:48:05 +00:00
Bug 489415. Handle media streams with data from different principals in the media cache. r=bzbarsky,doublec
--HG-- extra : rebase_source : 2551f25e1828939dbb1619c31d3cedc2217a6623
This commit is contained in:
parent
ce9f80f759
commit
4c03f490b2
@ -172,11 +172,15 @@ void nsHTMLMediaElement::QueueLoadFromSourceTask()
|
||||
NS_DispatchToMainThread(event);
|
||||
}
|
||||
|
||||
class nsHTMLMediaElement::MediaLoadListener : public nsIStreamListener
|
||||
class nsHTMLMediaElement::MediaLoadListener : public nsIStreamListener,
|
||||
public nsIChannelEventSink,
|
||||
public nsIInterfaceRequestor
|
||||
{
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSIREQUESTOBSERVER
|
||||
NS_DECL_NSISTREAMLISTENER
|
||||
NS_DECL_NSICHANNELEVENTSINK
|
||||
NS_DECL_NSIINTERFACEREQUESTOR
|
||||
|
||||
public:
|
||||
MediaLoadListener(nsHTMLMediaElement* aElement)
|
||||
@ -190,7 +194,9 @@ private:
|
||||
nsCOMPtr<nsIStreamListener> mNextListener;
|
||||
};
|
||||
|
||||
NS_IMPL_ISUPPORTS2(nsHTMLMediaElement::MediaLoadListener, nsIRequestObserver, nsIStreamListener)
|
||||
NS_IMPL_ISUPPORTS4(nsHTMLMediaElement::MediaLoadListener, nsIRequestObserver,
|
||||
nsIStreamListener, nsIChannelEventSink,
|
||||
nsIInterfaceRequestor)
|
||||
|
||||
NS_IMETHODIMP nsHTMLMediaElement::MediaLoadListener::OnStartRequest(nsIRequest* aRequest, nsISupports* aContext)
|
||||
{
|
||||
@ -252,6 +258,21 @@ NS_IMETHODIMP nsHTMLMediaElement::MediaLoadListener::OnDataAvailable(nsIRequest*
|
||||
return mNextListener->OnDataAvailable(aRequest, aContext, aStream, aOffset, aCount);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP nsHTMLMediaElement::MediaLoadListener::OnChannelRedirect(nsIChannel* aOldChannel,
|
||||
nsIChannel* aNewChannel,
|
||||
PRUint32 aFlags)
|
||||
{
|
||||
nsCOMPtr<nsIChannelEventSink> sink = do_QueryInterface(mNextListener);
|
||||
if (sink)
|
||||
return sink->OnChannelRedirect(aOldChannel, aNewChannel, aFlags);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP nsHTMLMediaElement::MediaLoadListener::GetInterface(const nsIID & aIID, void **aResult)
|
||||
{
|
||||
return QueryInterface(aIID, aResult);
|
||||
}
|
||||
|
||||
NS_IMPL_ADDREF_INHERITED(nsHTMLMediaElement, nsGenericHTMLElement)
|
||||
NS_IMPL_RELEASE_INHERITED(nsHTMLMediaElement, nsGenericHTMLElement)
|
||||
|
||||
@ -513,8 +534,10 @@ nsresult nsHTMLMediaElement::LoadResource(nsIURI* aURI)
|
||||
// The listener holds a strong reference to us. This creates a reference
|
||||
// cycle which is manually broken in the listener's OnStartRequest method
|
||||
// after it is finished with the element.
|
||||
nsCOMPtr<nsIStreamListener> loadListener = new MediaLoadListener(this);
|
||||
nsRefPtr<MediaLoadListener> loadListener = new MediaLoadListener(this);
|
||||
if (!loadListener) return NS_ERROR_OUT_OF_MEMORY;
|
||||
|
||||
mChannel->SetNotificationCallbacks(loadListener);
|
||||
|
||||
nsCOMPtr<nsIStreamListener> listener;
|
||||
if (ShouldCheckAllowOrigin()) {
|
||||
|
@ -41,6 +41,8 @@
|
||||
|
||||
#include "nsTArray.h"
|
||||
#include "nsAutoLock.h"
|
||||
#include "nsIPrincipal.h"
|
||||
#include "nsCOMPtr.h"
|
||||
|
||||
/**
|
||||
* Media applications want fast, "on demand" random access to media data,
|
||||
@ -186,6 +188,13 @@
|
||||
* we must not acquire any nsMediaDecoder locks or nsMediaStream locks
|
||||
* while holding the nsMediaCache lock. But it's OK to hold those locks
|
||||
* and then get the nsMediaCache lock.
|
||||
*
|
||||
* nsMediaCache associates a principal with each stream. CacheClientSeek
|
||||
* can trigger new HTTP requests; due to redirects to other domains,
|
||||
* each HTTP load can return data with a different principal. This
|
||||
* principal must be passed to NotifyDataReceived, and nsMediaCache
|
||||
* will detect when different principals are associated with data in the
|
||||
* same stream, and replace them with a null principal.
|
||||
*/
|
||||
class nsMediaCache;
|
||||
// defined in nsMediaStream.h
|
||||
@ -215,7 +224,8 @@ public:
|
||||
mStreamOffset(0), mStreamLength(-1), mPlaybackBytesPerSecond(10000),
|
||||
mPinCount(0), mCurrentMode(MODE_PLAYBACK), mClosed(PR_FALSE),
|
||||
mIsSeekable(PR_FALSE), mCacheSuspended(PR_FALSE),
|
||||
mMetadataInPartialBlockBuffer(PR_FALSE) {}
|
||||
mMetadataInPartialBlockBuffer(PR_FALSE),
|
||||
mUsingNullPrincipal(PR_FALSE) {}
|
||||
~nsMediaCacheStream();
|
||||
|
||||
// Set up this stream with the cache. Can fail on OOM. Must be called
|
||||
@ -236,6 +246,8 @@ public:
|
||||
void Close();
|
||||
// This returns true when the stream has been closed
|
||||
PRBool IsClosed() const { return mClosed; }
|
||||
// Get the principal for this stream.
|
||||
nsIPrincipal* GetCurrentPrincipal() { return mPrincipal; }
|
||||
|
||||
// These callbacks are called on the main thread by the client
|
||||
// when data has been received via the channel.
|
||||
@ -263,7 +275,9 @@ public:
|
||||
// the starting offset is known via NotifyDataStarted or because
|
||||
// the cache requested the offset in
|
||||
// nsMediaChannelStream::CacheClientSeek, or because it defaulted to 0.
|
||||
void NotifyDataReceived(PRInt64 aSize, const char* aData);
|
||||
// We pass in the principal that was used to load this data.
|
||||
void NotifyDataReceived(PRInt64 aSize, const char* aData,
|
||||
nsIPrincipal* aPrincipal);
|
||||
// Notifies the cache that the channel has closed with the given status.
|
||||
void NotifyDataEnded(nsresult aStatus);
|
||||
|
||||
@ -363,9 +377,12 @@ private:
|
||||
// This is used to NotifyAll to wake up threads that might be
|
||||
// blocked on reading from this stream.
|
||||
void CloseInternal(nsAutoMonitor* aMonitor);
|
||||
// Update mPrincipal given that data has been received from aPrincipal
|
||||
void UpdatePrincipal(nsIPrincipal* aPrincipal);
|
||||
|
||||
// This field is main-thread-only.
|
||||
nsMediaChannelStream* mClient;
|
||||
// These fields are main-thread-only.
|
||||
nsMediaChannelStream* mClient;
|
||||
nsCOMPtr<nsIPrincipal> mPrincipal;
|
||||
|
||||
// All other fields are all protected by the cache's monitor and
|
||||
// can be accessed by by any thread.
|
||||
@ -401,6 +418,9 @@ private:
|
||||
PRPackedBool mCacheSuspended;
|
||||
// true if some data in mPartialBlockBuffer has been read as metadata
|
||||
PRPackedBool mMetadataInPartialBlockBuffer;
|
||||
// true if mPrincipal is a null principal because we saw data from
|
||||
// multiple origins
|
||||
PRPackedBool mUsingNullPrincipal;
|
||||
|
||||
// Data received for the block containing mChannelOffset. Data needs
|
||||
// to wait here so we can write back a complete block. The first
|
||||
|
@ -44,6 +44,8 @@
|
||||
#include "nsIPrincipal.h"
|
||||
#include "nsIURI.h"
|
||||
#include "nsIStreamListener.h"
|
||||
#include "nsIChannelEventSink.h"
|
||||
#include "nsIInterfaceRequestor.h"
|
||||
#include "prlock.h"
|
||||
#include "nsMediaCache.h"
|
||||
#include "nsTimeStamp.h"
|
||||
@ -149,8 +151,6 @@ public:
|
||||
}
|
||||
|
||||
// The following can be called on the main thread only:
|
||||
// Get the current principal for the channel
|
||||
already_AddRefed<nsIPrincipal> GetCurrentPrincipal();
|
||||
// Get the decoder
|
||||
nsMediaDecoder* Decoder() { return mDecoder; }
|
||||
// Close the stream, stop any listeners, channels, etc.
|
||||
@ -161,6 +161,8 @@ public:
|
||||
virtual void Suspend() = 0;
|
||||
// Resume any downloads that have been suspended.
|
||||
virtual void Resume() = 0;
|
||||
// Get the current principal for the channel
|
||||
virtual already_AddRefed<nsIPrincipal> GetCurrentPrincipal() = 0;
|
||||
|
||||
// These methods are called off the main thread.
|
||||
// The mode is initially MODE_PLAYBACK.
|
||||
@ -317,6 +319,7 @@ public:
|
||||
virtual nsresult Close();
|
||||
virtual void Suspend();
|
||||
virtual void Resume();
|
||||
virtual already_AddRefed<nsIPrincipal> GetCurrentPrincipal();
|
||||
// Return PR_TRUE if the stream has been closed.
|
||||
PRBool IsClosed() const { return mCacheStream.IsClosed(); }
|
||||
|
||||
@ -337,13 +340,18 @@ public:
|
||||
virtual PRBool IsSuspendedByCache();
|
||||
|
||||
protected:
|
||||
class Listener : public nsIStreamListener {
|
||||
class Listener : public nsIStreamListener,
|
||||
public nsIInterfaceRequestor,
|
||||
public nsIChannelEventSink
|
||||
{
|
||||
public:
|
||||
Listener(nsMediaChannelStream* aStream) : mStream(aStream) {}
|
||||
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSIREQUESTOBSERVER
|
||||
NS_DECL_NSISTREAMLISTENER
|
||||
NS_DECL_NSICHANNELEVENTSINK
|
||||
NS_DECL_NSIINTERFACEREQUESTOR
|
||||
|
||||
void Revoke() { mStream = nsnull; }
|
||||
|
||||
@ -358,10 +366,12 @@ protected:
|
||||
nsresult OnDataAvailable(nsIRequest* aRequest,
|
||||
nsIInputStream* aStream,
|
||||
PRUint32 aCount);
|
||||
nsresult OnChannelRedirect(nsIChannel* aOld, nsIChannel* aNew, PRUint32 aFlags);
|
||||
|
||||
// Opens the channel, using an HTTP byte range request to start at aOffset
|
||||
// if possible. Main thread only.
|
||||
nsresult OpenChannel(nsIStreamListener** aStreamListener, PRInt64 aOffset);
|
||||
void SetupChannelHeaders();
|
||||
// Closes the channel. Main thread only.
|
||||
void CloseChannel();
|
||||
|
||||
@ -373,9 +383,9 @@ protected:
|
||||
PRUint32 *aWriteCount);
|
||||
|
||||
// Main thread access only
|
||||
PRInt64 mLastSeekOffset;
|
||||
nsRefPtr<Listener> mListener;
|
||||
PRUint32 mSuspendCount;
|
||||
PRPackedBool mSeeking;
|
||||
|
||||
// Any thread access
|
||||
nsMediaCacheStream mCacheStream;
|
||||
|
@ -1390,10 +1390,47 @@ nsMediaCacheStream::NotifyDataStarted(PRInt64 aOffset)
|
||||
}
|
||||
|
||||
void
|
||||
nsMediaCacheStream::NotifyDataReceived(PRInt64 aSize, const char* aData)
|
||||
nsMediaCacheStream::UpdatePrincipal(nsIPrincipal* aPrincipal)
|
||||
{
|
||||
if (!mPrincipal) {
|
||||
NS_ASSERTION(!mUsingNullPrincipal, "Are we using a null principal or not?");
|
||||
if (mUsingNullPrincipal) {
|
||||
// Don't let mPrincipal be set to anything
|
||||
return;
|
||||
}
|
||||
mPrincipal = aPrincipal;
|
||||
return;
|
||||
}
|
||||
|
||||
if (mPrincipal == aPrincipal) {
|
||||
// Common case
|
||||
NS_ASSERTION(!mUsingNullPrincipal, "We can't receive data from a null principal");
|
||||
return;
|
||||
}
|
||||
if (mUsingNullPrincipal) {
|
||||
// We've already fallen back to a null principal, so nothing more
|
||||
// to do.
|
||||
return;
|
||||
}
|
||||
|
||||
PRBool equal;
|
||||
nsresult rv = mPrincipal->Equals(aPrincipal, &equal);
|
||||
if (NS_SUCCEEDED(rv) && equal)
|
||||
return;
|
||||
|
||||
// Principals are not equal, so set mPrincipal to a null principal.
|
||||
mPrincipal = do_CreateInstance("@mozilla.org/nullprincipal;1");
|
||||
mUsingNullPrincipal = PR_TRUE;
|
||||
}
|
||||
|
||||
void
|
||||
nsMediaCacheStream::NotifyDataReceived(PRInt64 aSize, const char* aData,
|
||||
nsIPrincipal* aPrincipal)
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
|
||||
|
||||
UpdatePrincipal(aPrincipal);
|
||||
|
||||
nsAutoMonitor mon(gMediaCache->Monitor());
|
||||
PRInt64 size = aSize;
|
||||
const char* data = aData;
|
||||
|
@ -65,7 +65,7 @@ using mozilla::TimeStamp;
|
||||
nsMediaChannelStream::nsMediaChannelStream(nsMediaDecoder* aDecoder,
|
||||
nsIChannel* aChannel, nsIURI* aURI)
|
||||
: nsMediaStream(aDecoder, aChannel, aURI),
|
||||
mSuspendCount(0), mSeeking(PR_FALSE),
|
||||
mLastSeekOffset(0), mSuspendCount(0),
|
||||
mCacheStream(this),
|
||||
mLock(nsAutoLock::NewLock("media.channel.stream")),
|
||||
mCacheSuspendCount(0)
|
||||
@ -89,7 +89,9 @@ nsMediaChannelStream::~nsMediaChannelStream()
|
||||
// disconnect the old listener from the nsMediaChannelStream and hook up
|
||||
// a new listener, so notifications from the old channel are discarded
|
||||
// and don't confuse us.
|
||||
NS_IMPL_ISUPPORTS2(nsMediaChannelStream::Listener, nsIRequestObserver, nsIStreamListener)
|
||||
NS_IMPL_ISUPPORTS4(nsMediaChannelStream::Listener,
|
||||
nsIRequestObserver, nsIStreamListener, nsIChannelEventSink,
|
||||
nsIInterfaceRequestor)
|
||||
|
||||
nsresult
|
||||
nsMediaChannelStream::Listener::OnStartRequest(nsIRequest* aRequest,
|
||||
@ -122,6 +124,22 @@ nsMediaChannelStream::Listener::OnDataAvailable(nsIRequest* aRequest,
|
||||
return mStream->OnDataAvailable(aRequest, aStream, aCount);
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsMediaChannelStream::Listener::OnChannelRedirect(nsIChannel* aOldChannel,
|
||||
nsIChannel* aNewChannel,
|
||||
PRUint32 aFlags)
|
||||
{
|
||||
if (!mStream)
|
||||
return NS_OK;
|
||||
return mStream->OnChannelRedirect(aOldChannel, aNewChannel, aFlags);
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsMediaChannelStream::Listener::GetInterface(const nsIID & aIID, void **aResult)
|
||||
{
|
||||
return QueryInterface(aIID, aResult);
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsMediaChannelStream::OnStartRequest(nsIRequest* aRequest)
|
||||
{
|
||||
@ -148,7 +166,7 @@ nsMediaChannelStream::OnStartRequest(nsIRequest* aRequest)
|
||||
ranges);
|
||||
PRBool acceptsRanges = ranges.EqualsLiteral("bytes");
|
||||
|
||||
if (!mSeeking) {
|
||||
if (mLastSeekOffset == 0) {
|
||||
// Look for duration headers from known Ogg content systems. In the case
|
||||
// of multiple options for obtaining the duration the order of precedence is;
|
||||
// 1) The Media resource metadata if possible (done by the decoder itself).
|
||||
@ -172,12 +190,12 @@ nsMediaChannelStream::OnStartRequest(nsIRequest* aRequest)
|
||||
|
||||
PRUint32 responseStatus = 0;
|
||||
hc->GetResponseStatus(&responseStatus);
|
||||
if (mSeeking && responseStatus == HTTP_OK_CODE) {
|
||||
if (mLastSeekOffset > 0 && responseStatus == HTTP_OK_CODE) {
|
||||
// If we get an OK response but we were seeking, we have to assume
|
||||
// that seeking doesn't work. We also need to tell the cache that
|
||||
// it's getting data for the start of the stream.
|
||||
mCacheStream.NotifyDataStarted(0);
|
||||
} else if (!mSeeking &&
|
||||
} else if (mLastSeekOffset == 0 &&
|
||||
(responseStatus == HTTP_OK_CODE ||
|
||||
responseStatus == HTTP_PARTIAL_RESPONSE_CODE)) {
|
||||
// We weren't seeking and got a valid response status,
|
||||
@ -243,6 +261,20 @@ nsMediaChannelStream::OnStopRequest(nsIRequest* aRequest, nsresult aStatus)
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsMediaChannelStream::OnChannelRedirect(nsIChannel* aOld, nsIChannel* aNew,
|
||||
PRUint32 aFlags)
|
||||
{
|
||||
mChannel = aNew;
|
||||
SetupChannelHeaders();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
struct CopySegmentClosure {
|
||||
nsCOMPtr<nsIPrincipal> mPrincipal;
|
||||
nsMediaChannelStream* mStream;
|
||||
};
|
||||
|
||||
NS_METHOD
|
||||
nsMediaChannelStream::CopySegmentToCache(nsIInputStream *aInStream,
|
||||
void *aClosure,
|
||||
@ -251,8 +283,9 @@ nsMediaChannelStream::CopySegmentToCache(nsIInputStream *aInStream,
|
||||
PRUint32 aCount,
|
||||
PRUint32 *aWriteCount)
|
||||
{
|
||||
nsMediaChannelStream* stream = static_cast<nsMediaChannelStream*>(aClosure);
|
||||
stream->mCacheStream.NotifyDataReceived(aCount, aFromSegment);
|
||||
CopySegmentClosure* closure = static_cast<CopySegmentClosure*>(aClosure);
|
||||
closure->mStream->mCacheStream.NotifyDataReceived(aCount, aFromSegment,
|
||||
closure->mPrincipal);
|
||||
*aWriteCount = aCount;
|
||||
return NS_OK;
|
||||
}
|
||||
@ -269,10 +302,17 @@ nsMediaChannelStream::OnDataAvailable(nsIRequest* aRequest,
|
||||
mChannelStatistics.AddBytes(aCount);
|
||||
}
|
||||
|
||||
CopySegmentClosure closure;
|
||||
nsIScriptSecurityManager* secMan = nsContentUtils::GetSecurityManager();
|
||||
if (secMan && mChannel) {
|
||||
secMan->GetChannelPrincipal(mChannel, getter_AddRefs(closure.mPrincipal));
|
||||
}
|
||||
closure.mStream = this;
|
||||
|
||||
PRUint32 count = aCount;
|
||||
while (count > 0) {
|
||||
PRUint32 read;
|
||||
nsresult rv = aStream->ReadSegments(CopySegmentToCache, this, count,
|
||||
nsresult rv = aStream->ReadSegments(CopySegmentToCache, &closure, count,
|
||||
&read);
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
@ -310,7 +350,7 @@ nsresult nsMediaChannelStream::OpenChannel(nsIStreamListener** aStreamListener,
|
||||
*aStreamListener = nsnull;
|
||||
}
|
||||
|
||||
mSeeking = aOffset != 0;
|
||||
mLastSeekOffset = aOffset;
|
||||
|
||||
mListener = new Listener(this);
|
||||
NS_ENSURE_TRUE(mListener, NS_ERROR_OUT_OF_MEMORY);
|
||||
@ -319,6 +359,8 @@ nsresult nsMediaChannelStream::OpenChannel(nsIStreamListener** aStreamListener,
|
||||
*aStreamListener = mListener;
|
||||
NS_ADDREF(*aStreamListener);
|
||||
} else {
|
||||
mChannel->SetNotificationCallbacks(mListener.get());
|
||||
|
||||
nsCOMPtr<nsIStreamListener> listener = mListener.get();
|
||||
|
||||
// Ensure that if we're loading cross domain, that the server is sending
|
||||
@ -342,18 +384,7 @@ nsresult nsMediaChannelStream::OpenChannel(nsIStreamListener** aStreamListener,
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
// Use a byte range request from the start of the resource.
|
||||
// This enables us to detect if the stream supports byte range
|
||||
// requests, and therefore seeking, early.
|
||||
nsCOMPtr<nsIHttpChannel> hc = do_QueryInterface(mChannel);
|
||||
if (hc) {
|
||||
nsCAutoString rangeString("bytes=");
|
||||
rangeString.AppendInt(aOffset);
|
||||
rangeString.Append("-");
|
||||
hc->SetRequestHeader(NS_LITERAL_CSTRING("Range"), rangeString, PR_FALSE);
|
||||
} else {
|
||||
NS_ASSERTION(aOffset == 0, "Don't know how to seek on this channel type");
|
||||
}
|
||||
SetupChannelHeaders();
|
||||
|
||||
nsresult rv = mChannel->AsyncOpen(listener, nsnull);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
@ -362,6 +393,23 @@ nsresult nsMediaChannelStream::OpenChannel(nsIStreamListener** aStreamListener,
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void nsMediaChannelStream::SetupChannelHeaders()
|
||||
{
|
||||
// Always use a byte range request even if we're reading from the start
|
||||
// of the resource.
|
||||
// This enables us to detect if the stream supports byte range
|
||||
// requests, and therefore seeking, early.
|
||||
nsCOMPtr<nsIHttpChannel> hc = do_QueryInterface(mChannel);
|
||||
if (hc) {
|
||||
nsCAutoString rangeString("bytes=");
|
||||
rangeString.AppendInt(mLastSeekOffset);
|
||||
rangeString.Append("-");
|
||||
hc->SetRequestHeader(NS_LITERAL_CSTRING("Range"), rangeString, PR_FALSE);
|
||||
} else {
|
||||
NS_ASSERTION(mLastSeekOffset == 0, "Don't know how to seek on this channel type");
|
||||
}
|
||||
}
|
||||
|
||||
nsresult nsMediaChannelStream::Close()
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
|
||||
@ -371,6 +419,14 @@ nsresult nsMediaChannelStream::Close()
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
already_AddRefed<nsIPrincipal> nsMediaChannelStream::GetCurrentPrincipal()
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
|
||||
|
||||
nsCOMPtr<nsIPrincipal> principal = mCacheStream.GetCurrentPrincipal();
|
||||
return principal.forget();
|
||||
}
|
||||
|
||||
void nsMediaChannelStream::CloseChannel()
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
|
||||
@ -616,6 +672,7 @@ public:
|
||||
virtual nsresult Close();
|
||||
virtual void Suspend() {}
|
||||
virtual void Resume() {}
|
||||
virtual already_AddRefed<nsIPrincipal> GetCurrentPrincipal();
|
||||
|
||||
// These methods are called off the main thread.
|
||||
|
||||
@ -757,6 +814,18 @@ nsresult nsMediaFileStream::Close()
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
already_AddRefed<nsIPrincipal> nsMediaFileStream::GetCurrentPrincipal()
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
|
||||
|
||||
nsCOMPtr<nsIPrincipal> principal;
|
||||
nsIScriptSecurityManager* secMan = nsContentUtils::GetSecurityManager();
|
||||
if (!secMan || !mChannel)
|
||||
return nsnull;
|
||||
secMan->GetChannelPrincipal(mChannel, getter_AddRefs(principal));
|
||||
return principal.forget();
|
||||
}
|
||||
|
||||
nsresult nsMediaFileStream::Read(char* aBuffer, PRUint32 aCount, PRUint32* aBytes)
|
||||
{
|
||||
nsAutoLock lock(mLock);
|
||||
@ -831,18 +900,6 @@ nsMediaStream::Open(nsMediaDecoder* aDecoder, nsIURI* aURI,
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
already_AddRefed<nsIPrincipal> nsMediaStream::GetCurrentPrincipal()
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
|
||||
|
||||
nsCOMPtr<nsIPrincipal> principal;
|
||||
nsIScriptSecurityManager* secMan = nsContentUtils::GetSecurityManager();
|
||||
if (!secMan || !mChannel)
|
||||
return nsnull;
|
||||
secMan->GetChannelPrincipal(mChannel, getter_AddRefs(principal));
|
||||
return principal.forget();
|
||||
}
|
||||
|
||||
void nsMediaStream::MoveLoadsToBackground() {
|
||||
NS_ASSERTION(!mLoadInBackground, "Why are you calling this more than once?");
|
||||
mLoadInBackground = PR_TRUE;
|
||||
|
@ -61,6 +61,7 @@ _TEST_FILES = \
|
||||
|
||||
ifdef MOZ_OGG
|
||||
_TEST_FILES += \
|
||||
dynamic_redirect.sjs \
|
||||
test_access_control.html \
|
||||
file_access_controls.html \
|
||||
test_bug448534.html \
|
||||
@ -82,6 +83,7 @@ _TEST_FILES += \
|
||||
test_info_leak.html \
|
||||
test_onloadedmetadata.html \
|
||||
test_load_candidates.html \
|
||||
test_mixed_principals.html \
|
||||
test_play.html \
|
||||
test_progress1.html \
|
||||
test_progress3.html \
|
||||
|
39
content/media/video/test/dynamic_redirect.sjs
Normal file
39
content/media/video/test/dynamic_redirect.sjs
Normal file
@ -0,0 +1,39 @@
|
||||
// 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=(.*)$/);
|
||||
|
||||
if (getState(key[1]) == "redirect") {
|
||||
var origin = request.host == "localhost" ? "example.org" : "localhost:8888";
|
||||
response.setStatusLine(request.httpVersion, 303, "See Other");
|
||||
response.setHeader("Location", "http://" + origin + "/tests/content/media/video/test/seek.ogv");
|
||||
response.setHeader("Content-Type", "text/html");
|
||||
return;
|
||||
}
|
||||
|
||||
setState(key[1], "redirect");
|
||||
|
||||
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/video/test/seek.ogv";
|
||||
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();
|
||||
}
|
68
content/media/video/test/test_mixed_principals.html
Normal file
68
content/media/video/test/test_mixed_principals.html
Normal file
@ -0,0 +1,68 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=489415
|
||||
-->
|
||||
<head>
|
||||
<title>Test for Bug 489415</title>
|
||||
<script type="application/javascript" src="/MochiKit/MochiKit.js"></script>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
</head>
|
||||
<body>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=489415">Mozilla Bug 489415</a>
|
||||
<p id="display"></p>
|
||||
|
||||
<video id="v1" autoplay onended="loaded('v1')"></video>
|
||||
<video id="v2" autoplay onended="loaded('v2')"></video>
|
||||
|
||||
<pre id="test">
|
||||
<script type="text/javascript">
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
var v1 = document.getElementById("v1");
|
||||
var v2 = document.getElementById("v2");
|
||||
|
||||
var count = 0;
|
||||
|
||||
function loaded(id) {
|
||||
var c = document.createElement("canvas");
|
||||
var ctx = c.getContext("2d");
|
||||
var v = document.getElementById(id);
|
||||
ctx.drawImage(v, 0, 0);
|
||||
try {
|
||||
c.toDataURL();
|
||||
ok(false, "Failed to throw exception in toDataURL for " + id);
|
||||
} catch (ex) {
|
||||
ok(true, "Threw exception in toDataURL for " + id);
|
||||
}
|
||||
if (++count == 2) {
|
||||
SimpleTest.finish();
|
||||
}
|
||||
}
|
||||
|
||||
// Generate a random key. The first load with that key will return
|
||||
// data, the second and subsequent loads with that key will return a redirect
|
||||
// to a different origin ('localhost:8888' will be redirected to 'example.org',
|
||||
// and 'example.org' will be redirected to 'localhost:8888'). We rely on the
|
||||
// fact that Ogg will do a seek to the end of the resource, triggering a new
|
||||
// load with the same key which will return a same-origin resource.
|
||||
// Loading data from two different origins should be detected by the media
|
||||
// cache and result in a null principal so that the canvas usage above fails.
|
||||
var key = Math.floor(Math.random()*100000000);
|
||||
|
||||
// In v1, try loading from same-origin first and then getting redirected to
|
||||
// another origin.
|
||||
v1.src = "http://localhost:8888/tests/content/media/video/test/dynamic_redirect.sjs?key=v1_" + key;
|
||||
v1.load();
|
||||
|
||||
// In v2, try loading cross-origin first and then getting redirected to
|
||||
// our origin.
|
||||
v2.src = "http://example.org/tests/content/media/video/test/dynamic_redirect.sjs?key=v2_" + key;
|
||||
v2.load();
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
|
||||
</body>
|
||||
</html>
|
Loading…
Reference in New Issue
Block a user