Bug 730765 - Media cache shouldn't be used when loading from blob: urls. r=biesi

This commit is contained in:
Paul Adenot 2012-08-15 10:36:12 -07:00
parent 544845e37a
commit a952a58489
9 changed files with 359 additions and 56 deletions

View File

@ -42,6 +42,7 @@ nsReferencedElement.h \
nsTreeSanitizer.h \
nsXMLNameSpaceMap.h \
nsIXFormsUtilityService.h \
nsBlobProtocolHandler.h \
$(NULL)
EXPORTS_NAMESPACES = mozilla/dom mozilla

View File

@ -0,0 +1,51 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef nsBlobProtocolHandler_h
#define nsBlobProtocolHandler_h
#include "nsIProtocolHandler.h"
#include "nsIURI.h"
#include "nsCOMPtr.h"
#define BLOBURI_SCHEME "blob"
class nsIDOMBlob;
class nsIPrincipal;
class nsIInputStream;
inline bool IsBlobURI(nsIURI* aUri)
{
bool isBlob;
return NS_SUCCEEDED(aUri->SchemeIs(BLOBURI_SCHEME, &isBlob)) && isBlob;
}
extern nsresult
NS_GetStreamForBlobURI(nsIURI* aURI, nsIInputStream** aStream);
class nsBlobProtocolHandler : public nsIProtocolHandler
{
public:
NS_DECL_ISUPPORTS
// nsIProtocolHandler methods:
NS_DECL_NSIPROTOCOLHANDLER
// nsBlobProtocolHandler methods:
nsBlobProtocolHandler() {}
virtual ~nsBlobProtocolHandler() {}
// Methods for managing uri->file mapping
static void AddFileDataEntry(nsACString& aUri,
nsIDOMBlob* aFile,
nsIPrincipal* aPrincipal);
static void RemoveFileDataEntry(nsACString& aUri);
static nsIPrincipal* GetFileDataEntryPrincipal(nsACString& aUri);
};
#define NS_BLOBPROTOCOLHANDLER_CID \
{ 0xb43964aa, 0xa078, 0x44b2, \
{ 0xb0, 0x6b, 0xfd, 0x4d, 0x1b, 0x17, 0x2e, 0x66 } }
#endif /* nsBlobProtocolHandler_h */

View File

@ -163,8 +163,8 @@ nsBlobProtocolHandler::NewChannel(nsIURI* uri, nsIChannel* *result)
nsCOMPtr<nsIChannel> channel;
rv = NS_NewInputStreamChannel(getter_AddRefs(channel),
uri,
stream);
uri,
stream);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsISupports> owner = do_QueryInterface(info->mPrincipal);
@ -189,3 +189,23 @@ nsBlobProtocolHandler::AllowPort(PRInt32 port, const char *scheme,
*_retval = false;
return NS_OK;
}
nsresult
NS_GetStreamForBlobURI(nsIURI* aURI, nsIInputStream** aStream)
{
NS_ASSERTION(IsBlobURI(aURI), "Only call this with blob URIs");
*aStream = nullptr;
nsCString spec;
aURI->GetSpec(spec);
FileDataInfo* info =
GetFileDataInfo(spec);
if (!info) {
return NS_ERROR_DOM_BAD_URI;
}
return info->mFile->GetInternalStream(aStream);
}

View File

@ -6,11 +6,23 @@
#define nsBlobProtocolHandler_h
#include "nsIProtocolHandler.h"
#include "nsIURI.h"
#include "nsCOMPtr.h"
#define BLOBURI_SCHEME "blob"
class nsIDOMBlob;
class nsIPrincipal;
class nsIInputStream;
inline bool IsBlobURI(nsIURI* aUri)
{
bool isBlob;
return NS_SUCCEEDED(aUri->SchemeIs(BLOBURI_SCHEME, &isBlob)) && isBlob;
}
extern nsresult
NS_GetStreamForBlobURI(nsIURI* aURI, nsIInputStream** aStream);
class nsBlobProtocolHandler : public nsIProtocolHandler
{
@ -26,11 +38,10 @@ public:
// Methods for managing uri->file mapping
static void AddFileDataEntry(nsACString& aUri,
nsIDOMBlob* aFile,
nsIDOMBlob* aFile,
nsIPrincipal* aPrincipal);
static void RemoveFileDataEntry(nsACString& aUri);
static nsIPrincipal* GetFileDataEntryPrincipal(nsACString& aUri);
};
#define NS_BLOBPROTOCOLHANDLER_CID \

View File

@ -28,6 +28,7 @@
#include "nsIAsyncVerifyRedirectCallback.h"
#include "mozilla/Util.h" // for DebugOnly
#include "nsContentUtils.h"
#include "nsBlobProtocolHandler.h"
static const PRUint32 HTTP_OK_CODE = 200;
static const PRUint32 HTTP_PARTIAL_RESPONSE_CODE = 206;
@ -1038,14 +1039,15 @@ nsresult FileMediaResource::Open(nsIStreamListener** aStreamListener)
// implements nsISeekableStream, so we have to find the underlying
// file and reopen it
nsCOMPtr<nsIFileChannel> fc(do_QueryInterface(mChannel));
if (!fc)
return NS_ERROR_UNEXPECTED;
if (fc) {
nsCOMPtr<nsIFile> file;
rv = fc->GetFile(getter_AddRefs(file));
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIFile> file;
rv = fc->GetFile(getter_AddRefs(file));
NS_ENSURE_SUCCESS(rv, rv);
rv = NS_NewLocalFileInputStream(getter_AddRefs(mInput), file);
rv = NS_NewLocalFileInputStream(getter_AddRefs(mInput), file);
} else if (IsBlobURI(mURI)) {
rv = NS_GetStreamForBlobURI(mURI, getter_AddRefs(mInput));
}
} else {
// Ensure that we never load a local file from some page on a
// web server.
@ -1201,7 +1203,7 @@ MediaResource*
MediaResource::Create(nsMediaDecoder* aDecoder, nsIChannel* aChannel)
{
NS_ASSERTION(NS_IsMainThread(),
"MediaResource::Open called on non-main thread");
"MediaResource::Open called on non-main thread");
// If the channel was redirected, we want the post-redirect URI;
// but if the URI scheme was expanded, say from chrome: to jar:file:,
@ -1211,7 +1213,7 @@ MediaResource::Create(nsMediaDecoder* aDecoder, nsIChannel* aChannel)
NS_ENSURE_SUCCESS(rv, nullptr);
nsCOMPtr<nsIFileChannel> fc = do_QueryInterface(aChannel);
if (fc) {
if (fc || IsBlobURI(uri)) {
return new FileMediaResource(aDecoder, aChannel, uri);
}
return new ChannelMediaResource(aDecoder, aChannel, uri);

View File

@ -44,9 +44,11 @@ interface nsIFileInputStream : nsIInputStream
const long CLOSE_ON_EOF = 1<<2;
/**
* If this is set, the file will be reopened whenever Seek(0) occurs. If
* the file is already open and the seek occurs, it will happen naturally.
* (The file will only be reopened if it is closed for some reason.)
* If this is set, the file will be reopened whenever we reach the start of
* the file, either by doing a Seek(0, NS_SEEK_CUR), or by doing a relative
* seek that happen to reach the beginning of the file. If the file is
* already open and the seek occurs, it will happen naturally. (The file
* will only be reopened if it is closed for some reason.)
*/
const long REOPEN_ON_REWIND = 1<<3;

View File

@ -410,6 +410,14 @@ nsFileInputStream::Init(nsIFile* aFile, PRInt32 aIOFlags, PRInt32 aPerm,
NS_IMETHODIMP
nsFileInputStream::Close()
{
// Get the cache position at the time the file was close. This allows
// NS_SEEK_CUR on a closed file that has been opened with
// REOPEN_ON_REWIND.
if (mBehaviorFlags & REOPEN_ON_REWIND) {
// Get actual position. Not one modified by subclasses
nsFileStreamBase::Tell(&mCachedPosition);
}
// null out mLineBuffer in case Close() is called again after failing
PR_FREEIF(mLineBuffer);
nsresult rv = nsFileStreamBase::Close();
@ -461,9 +469,15 @@ nsFileInputStream::Seek(PRInt32 aWhence, PRInt64 aOffset)
PR_FREEIF(mLineBuffer); // this invalidates the line buffer
if (!mFD) {
if (mBehaviorFlags & REOPEN_ON_REWIND) {
nsresult rv = Reopen();
if (NS_FAILED(rv)) {
return rv;
rv = Open(mFile, mIOFlags, mPerm);
NS_ENSURE_SUCCESS(rv, rv);
// If the file was closed, and we do a relative seek, use the
// position we cached when we closed the file to seek to the right
// location.
if (aWhence == NS_SEEK_CUR) {
aWhence = NS_SEEK_SET;
aOffset += mCachedPosition;
}
} else {
return NS_BASE_STREAM_CLOSED;
@ -473,6 +487,23 @@ nsFileInputStream::Seek(PRInt32 aWhence, PRInt64 aOffset)
return nsFileStreamBase::Seek(aWhence, aOffset);
}
NS_IMETHODIMP
nsFileInputStream::Tell(PRInt64 *aResult)
{
return nsFileStreamBase::Tell(aResult);
}
NS_IMETHODIMP
nsFileInputStream::Available(PRUint64 *aResult)
{
//if (mFD == nullptr && mBehaviorFlags & REOPEN_ON_REWIND) {
//*aResult = 0;
//return NS_OK;
//}
return nsFileStreamBase::Available(aResult);
}
bool
nsFileInputStream::Read(const IPC::Message *aMsg, void **aIter)
{

View File

@ -114,10 +114,7 @@ public:
NS_DECL_NSIIPCSERIALIZABLEINPUTSTREAM
NS_IMETHOD Close();
NS_IMETHOD Available(PRUint64* _retval)
{
return nsFileStreamBase::Available(_retval);
}
NS_IMETHOD Available(PRUint64* _retval);
NS_IMETHOD Read(char* aBuf, PRUint32 aCount, PRUint32* _retval);
NS_IMETHOD ReadSegments(nsWriteSegmentFun aWriter, void *aClosure,
PRUint32 aCount, PRUint32* _retval)
@ -132,12 +129,12 @@ public:
// Overrided from nsFileStreamBase
NS_IMETHOD Seek(PRInt32 aWhence, PRInt64 aOffset);
NS_IMETHOD Tell(PRInt64 *aResult);
nsFileInputStream()
: mIOFlags(0), mPerm(0)
{
mLineBuffer = nullptr;
}
: mLineBuffer(nullptr), mCachedPosition(0)
{ }
virtual ~nsFileInputStream()
{
@ -163,16 +160,17 @@ protected:
*/
PRInt32 mPerm;
/**
* Cached position for Tell for automatically reopening streams.
*/
PRInt64 mCachedPosition;
protected:
/**
* Internal, called to open a file. Parameters are the same as their
* Init() analogues.
*/
nsresult Open(nsIFile* file, PRInt32 ioFlags, PRInt32 perm);
/**
* Reopen the file (for OPEN_ON_READ only!)
*/
nsresult Reopen() { return Open(mFile, mIOFlags, mPerm); }
};
////////////////////////////////////////////////////////////////////////////////

View File

@ -55,7 +55,7 @@ private:
const char* aFromRawSegment, PRUint32 aToOffset,
PRUint32 aCount, PRUint32 *aWriteCount);
nsCOMArray<nsIInputStream> mStreams;
nsTArray<nsCOMPtr<nsIInputStream> > mStreams;
PRUint32 mCurrentStream;
bool mStartedReadingCurrent;
nsresult mStatus;
@ -89,7 +89,7 @@ nsMultiplexInputStream::nsMultiplexInputStream()
NS_IMETHODIMP
nsMultiplexInputStream::GetCount(PRUint32 *aCount)
{
*aCount = mStreams.Count();
*aCount = mStreams.Length();
return NS_OK;
}
@ -97,14 +97,14 @@ nsMultiplexInputStream::GetCount(PRUint32 *aCount)
NS_IMETHODIMP
nsMultiplexInputStream::AppendStream(nsIInputStream *aStream)
{
return mStreams.AppendObject(aStream) ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
return mStreams.AppendElement(aStream) ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
}
/* void insertStream (in nsIInputStream stream, in unsigned long index); */
NS_IMETHODIMP
nsMultiplexInputStream::InsertStream(nsIInputStream *aStream, PRUint32 aIndex)
{
bool result = mStreams.InsertObjectAt(aStream, aIndex);
bool result = mStreams.InsertElementAt(aIndex, aStream);
NS_ENSURE_TRUE(result, NS_ERROR_OUT_OF_MEMORY);
if (mCurrentStream > aIndex ||
(mCurrentStream == aIndex && mStartedReadingCurrent))
@ -116,8 +116,7 @@ nsMultiplexInputStream::InsertStream(nsIInputStream *aStream, PRUint32 aIndex)
NS_IMETHODIMP
nsMultiplexInputStream::RemoveStream(PRUint32 aIndex)
{
bool result = mStreams.RemoveObjectAt(aIndex);
NS_ENSURE_TRUE(result, NS_ERROR_NOT_AVAILABLE);
mStreams.RemoveElementAt(aIndex);
if (mCurrentStream > aIndex)
--mCurrentStream;
else if (mCurrentStream == aIndex)
@ -130,7 +129,7 @@ nsMultiplexInputStream::RemoveStream(PRUint32 aIndex)
NS_IMETHODIMP
nsMultiplexInputStream::GetStream(PRUint32 aIndex, nsIInputStream **_retval)
{
*_retval = mStreams.SafeObjectAt(aIndex);
*_retval = mStreams.SafeElementAt(aIndex, nullptr);
NS_ENSURE_TRUE(*_retval, NS_ERROR_NOT_AVAILABLE);
NS_ADDREF(*_retval);
@ -145,7 +144,7 @@ nsMultiplexInputStream::Close()
nsresult rv = NS_OK;
PRUint32 len = mStreams.Count();
PRUint32 len = mStreams.Length();
for (PRUint32 i = 0; i < len; ++i) {
nsresult rv2 = mStreams[i]->Close();
// We still want to close all streams, but we should return an error
@ -165,7 +164,7 @@ nsMultiplexInputStream::Available(PRUint64 *_retval)
nsresult rv;
PRUint64 avail = 0;
PRUint32 len = mStreams.Count();
PRUint32 len = mStreams.Length();
for (PRUint32 i = mCurrentStream; i < len; i++) {
PRUint64 streamAvail;
rv = mStreams[i]->Available(&streamAvail);
@ -193,7 +192,7 @@ nsMultiplexInputStream::Read(char * aBuf, PRUint32 aCount, PRUint32 *_retval)
nsresult rv = NS_OK;
PRUint32 len = mStreams.Count();
PRUint32 len = mStreams.Length();
while (mCurrentStream < len && aCount) {
PRUint32 read;
rv = mStreams[mCurrentStream]->Read(aBuf, aCount, &read);
@ -247,7 +246,7 @@ nsMultiplexInputStream::ReadSegments(nsWriteSegmentFun aWriter, void *aClosure,
state.mClosure = aClosure;
state.mDone = false;
PRUint32 len = mStreams.Count();
PRUint32 len = mStreams.Length();
while (mCurrentStream < len && aCount) {
PRUint32 read;
rv = mStreams[mCurrentStream]->ReadSegments(ReadSegCb, &state, aCount, &read);
@ -305,7 +304,7 @@ nsMultiplexInputStream::ReadSegCb(nsIInputStream* aIn, void* aClosure,
NS_IMETHODIMP
nsMultiplexInputStream::IsNonBlocking(bool *aNonBlocking)
{
PRUint32 len = mStreams.Count();
PRUint32 len = mStreams.Length();
if (len == 0) {
// Claim to be non-blocking, since we won't block the caller.
// On the other hand we'll never return NS_BASE_STREAM_WOULD_BLOCK,
@ -335,20 +334,208 @@ nsMultiplexInputStream::Seek(PRInt32 aWhence, PRInt64 aOffset)
nsresult rv;
// rewinding to start is easy, and should be the most common case
if (aWhence == NS_SEEK_SET && aOffset == 0)
{
PRUint32 i, last;
last = mStartedReadingCurrent ? mCurrentStream+1 : mCurrentStream;
for (i = 0; i < last; ++i) {
nsCOMPtr<nsISeekableStream> stream = do_QueryInterface(mStreams[i]);
NS_ENSURE_TRUE(stream, NS_ERROR_NO_INTERFACE);
PRUint32 oldCurrentStream = mCurrentStream;
bool oldStartedReadingCurrent = mStartedReadingCurrent;
rv = stream->Seek(NS_SEEK_SET, 0);
NS_ENSURE_SUCCESS(rv, rv);
if (aWhence == NS_SEEK_SET) {
PRInt64 remaining = aOffset;
if (aOffset == 0) {
mCurrentStream = 0;
}
mCurrentStream = 0;
mStartedReadingCurrent = false;
for (PRUint32 i = 0; i < mStreams.Length(); ++i) {
nsCOMPtr<nsISeekableStream> stream =
do_QueryInterface(mStreams[i]);
if (!stream) {
return NS_ERROR_FAILURE;
}
// See if all remaining streams should be rewound
if (remaining == 0) {
if (i < oldCurrentStream ||
(i == oldCurrentStream && oldStartedReadingCurrent)) {
rv = stream->Seek(NS_SEEK_SET, 0);
NS_ENSURE_SUCCESS(rv, rv);
continue;
}
else {
break;
}
}
// Get position in current stream
PRInt64 streamPos;
if (i > oldCurrentStream ||
(i == oldCurrentStream && !oldStartedReadingCurrent)) {
streamPos = 0;
}
else {
rv = stream->Tell(&streamPos);
NS_ENSURE_SUCCESS(rv, rv);
}
// See if we need to seek current stream forward or backward
if (remaining < streamPos) {
rv = stream->Seek(NS_SEEK_SET, remaining);
NS_ENSURE_SUCCESS(rv, rv);
mCurrentStream = i;
mStartedReadingCurrent = remaining != 0;
remaining = 0;
}
else if (remaining > streamPos) {
if (i < oldCurrentStream) {
// We're already at end so no need to seek this stream
remaining -= streamPos;
}
else {
PRUint64 avail;
rv = mStreams[i]->Available(&avail);
NS_ENSURE_SUCCESS(rv, rv);
PRInt64 newPos = streamPos +
NS_MIN((PRInt64)avail, remaining);
rv = stream->Seek(NS_SEEK_SET, newPos);
NS_ENSURE_SUCCESS(rv, rv);
mCurrentStream = i;
mStartedReadingCurrent = true;
remaining -= newPos;
}
}
else {
NS_ASSERTION(remaining == streamPos, "Huh?");
remaining = 0;
}
}
return NS_OK;
}
if (aWhence == NS_SEEK_CUR && aOffset > 0) {
PRInt64 remaining = aOffset;
for (PRUint32 i = mCurrentStream; remaining && i < mStreams.Length(); ++i) {
nsCOMPtr<nsISeekableStream> stream =
do_QueryInterface(mStreams[i]);
PRUint64 avail;
rv = mStreams[i]->Available(&avail);
NS_ENSURE_SUCCESS(rv, rv);
PRInt64 seek = NS_MIN((PRInt64)avail, remaining);
rv = stream->Seek(NS_SEEK_CUR, seek);
NS_ENSURE_SUCCESS(rv, rv);
mCurrentStream = i;
mStartedReadingCurrent = true;
remaining -= seek;
}
return NS_OK;
}
if (aWhence == NS_SEEK_CUR && aOffset < 0) {
PRInt64 remaining = -aOffset;
for (PRUint32 i = mCurrentStream; remaining && i != (PRUint32)-1; --i) {
nsCOMPtr<nsISeekableStream> stream =
do_QueryInterface(mStreams[i]);
PRInt64 pos;
rv = stream->Tell(&pos);
NS_ENSURE_SUCCESS(rv, rv);
PRInt64 seek = NS_MIN(pos, remaining);
rv = stream->Seek(NS_SEEK_CUR, -seek);
NS_ENSURE_SUCCESS(rv, rv);
mCurrentStream = i;
mStartedReadingCurrent = seek != -pos;
remaining -= seek;
}
return NS_OK;
}
if (aWhence == NS_SEEK_CUR) {
NS_ASSERTION(aOffset == 0, "Should have handled all non-zero values");
return NS_OK;
}
if (aWhence == NS_SEEK_END) {
if (aOffset > 0) {
return NS_ERROR_INVALID_ARG;
}
PRInt64 remaining = aOffset;
for (PRUint32 i = mStreams.Length() - 1; i != (PRUint32)-1; --i) {
nsCOMPtr<nsISeekableStream> stream =
do_QueryInterface(mStreams[i]);
// See if all remaining streams should be seeked to end
if (remaining == 0) {
if (i >= oldCurrentStream) {
rv = stream->Seek(NS_SEEK_END, 0);
NS_ENSURE_SUCCESS(rv, rv);
}
else {
break;
}
}
// Get position in current stream
PRInt64 streamPos;
if (i < oldCurrentStream) {
streamPos = 0;
} else {
PRUint64 avail;
rv = mStreams[i]->Available(&avail);
NS_ENSURE_SUCCESS(rv, rv);
streamPos = avail;
}
// See if we have enough data in the current stream.
if (NS_ABS(remaining) < streamPos) {
rv = stream->Seek(NS_SEEK_END, remaining);
NS_ENSURE_SUCCESS(rv, rv);
mCurrentStream = i;
mStartedReadingCurrent = true;
remaining = 0;
} else if (NS_ABS(remaining) > streamPos) {
if (i > oldCurrentStream ||
(i == oldCurrentStream && !oldStartedReadingCurrent)) {
// We're already at start so no need to seek this stream
remaining += streamPos;
} else {
PRInt64 avail;
rv = stream->Tell(&avail);
NS_ENSURE_SUCCESS(rv, rv);
PRInt64 newPos = streamPos + NS_MIN(avail, NS_ABS(remaining));
rv = stream->Seek(NS_SEEK_END, -newPos);
NS_ENSURE_SUCCESS(rv, rv);
mCurrentStream = i;
mStartedReadingCurrent = true;
remaining += newPos;
}
}
else {
NS_ASSERTION(remaining == streamPos, "Huh?");
remaining = 0;
}
}
return NS_OK;
}
@ -442,11 +629,11 @@ nsMultiplexInputStream::Write(IPC::Message *aMsg)
{
using IPC::WriteParam;
PRUint32 count = mStreams.Count();
PRUint32 count = mStreams.Length();
WriteParam(aMsg, count);
for (PRUint32 i = 0; i < count; i++) {
IPC::InputStream inputStream(mStreams.ObjectAt(i));
IPC::InputStream inputStream(mStreams[i]);
WriteParam(aMsg, inputStream);
}