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

This commit is contained in:
Paul Adenot 2012-09-04 14:53:52 -07:00
parent b89a18ff9f
commit 6ad35d0369
9 changed files with 355 additions and 58 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);
@ -195,3 +195,23 @@ nsBlobProtocolHandler::AllowPort(int32_t 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 uint32_t HTTP_OK_CODE = 200;
static const uint32_t HTTP_PARTIAL_RESPONSE_CODE = 206;
@ -1105,14 +1106,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.
@ -1262,7 +1264,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:,
@ -1272,7 +1274,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

@ -409,6 +409,14 @@ nsFileInputStream::Init(nsIFile* aFile, int32_t aIOFlags, int32_t 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();
@ -460,9 +468,15 @@ nsFileInputStream::Seek(int32_t aWhence, int64_t 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;
@ -472,6 +486,18 @@ nsFileInputStream::Seek(int32_t aWhence, int64_t aOffset)
return nsFileStreamBase::Seek(aWhence, aOffset);
}
NS_IMETHODIMP
nsFileInputStream::Tell(int64_t *aResult)
{
return nsFileStreamBase::Tell(aResult);
}
NS_IMETHODIMP
nsFileInputStream::Available(uint64_t *aResult)
{
return nsFileStreamBase::Available(aResult);
}
void
nsFileInputStream::Serialize(InputStreamParams& aParams)
{

View File

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

View File

@ -50,7 +50,7 @@ private:
const char* aFromRawSegment, uint32_t aToOffset,
uint32_t aCount, uint32_t *aWriteCount);
nsCOMArray<nsIInputStream> mStreams;
nsTArray<nsCOMPtr<nsIInputStream> > mStreams;
uint32_t mCurrentStream;
bool mStartedReadingCurrent;
nsresult mStatus;
@ -83,7 +83,7 @@ nsMultiplexInputStream::nsMultiplexInputStream()
NS_IMETHODIMP
nsMultiplexInputStream::GetCount(uint32_t *aCount)
{
*aCount = mStreams.Count();
*aCount = mStreams.Length();
return NS_OK;
}
@ -91,14 +91,14 @@ nsMultiplexInputStream::GetCount(uint32_t *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, uint32_t 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))
@ -110,8 +110,7 @@ nsMultiplexInputStream::InsertStream(nsIInputStream *aStream, uint32_t aIndex)
NS_IMETHODIMP
nsMultiplexInputStream::RemoveStream(uint32_t 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)
@ -124,7 +123,7 @@ nsMultiplexInputStream::RemoveStream(uint32_t aIndex)
NS_IMETHODIMP
nsMultiplexInputStream::GetStream(uint32_t aIndex, nsIInputStream **_retval)
{
*_retval = mStreams.SafeObjectAt(aIndex);
*_retval = mStreams.SafeElementAt(aIndex, nullptr);
NS_ENSURE_TRUE(*_retval, NS_ERROR_NOT_AVAILABLE);
NS_ADDREF(*_retval);
@ -139,7 +138,7 @@ nsMultiplexInputStream::Close()
nsresult rv = NS_OK;
uint32_t len = mStreams.Count();
uint32_t len = mStreams.Length();
for (uint32_t i = 0; i < len; ++i) {
nsresult rv2 = mStreams[i]->Close();
// We still want to close all streams, but we should return an error
@ -159,7 +158,7 @@ nsMultiplexInputStream::Available(uint64_t *_retval)
nsresult rv;
uint64_t avail = 0;
uint32_t len = mStreams.Count();
uint32_t len = mStreams.Length();
for (uint32_t i = mCurrentStream; i < len; i++) {
uint64_t streamAvail;
rv = mStreams[i]->Available(&streamAvail);
@ -187,7 +186,7 @@ nsMultiplexInputStream::Read(char * aBuf, uint32_t aCount, uint32_t *_retval)
nsresult rv = NS_OK;
uint32_t len = mStreams.Count();
uint32_t len = mStreams.Length();
while (mCurrentStream < len && aCount) {
uint32_t read;
rv = mStreams[mCurrentStream]->Read(aBuf, aCount, &read);
@ -241,7 +240,7 @@ nsMultiplexInputStream::ReadSegments(nsWriteSegmentFun aWriter, void *aClosure,
state.mClosure = aClosure;
state.mDone = false;
uint32_t len = mStreams.Count();
uint32_t len = mStreams.Length();
while (mCurrentStream < len && aCount) {
uint32_t read;
rv = mStreams[mCurrentStream]->ReadSegments(ReadSegCb, &state, aCount, &read);
@ -299,7 +298,7 @@ nsMultiplexInputStream::ReadSegCb(nsIInputStream* aIn, void* aClosure,
NS_IMETHODIMP
nsMultiplexInputStream::IsNonBlocking(bool *aNonBlocking)
{
uint32_t len = mStreams.Count();
uint32_t 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,
@ -329,20 +328,208 @@ nsMultiplexInputStream::Seek(int32_t aWhence, int64_t aOffset)
nsresult rv;
// rewinding to start is easy, and should be the most common case
if (aWhence == NS_SEEK_SET && aOffset == 0)
{
uint32_t 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);
uint32_t oldCurrentStream = mCurrentStream;
bool oldStartedReadingCurrent = mStartedReadingCurrent;
rv = stream->Seek(NS_SEEK_SET, 0);
NS_ENSURE_SUCCESS(rv, rv);
if (aWhence == NS_SEEK_SET) {
int64_t remaining = aOffset;
if (aOffset == 0) {
mCurrentStream = 0;
}
mCurrentStream = 0;
mStartedReadingCurrent = false;
for (uint32_t 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
int64_t 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 {
uint64_t avail;
rv = mStreams[i]->Available(&avail);
NS_ENSURE_SUCCESS(rv, rv);
int64_t newPos = streamPos +
NS_MIN((int64_t)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) {
int64_t remaining = aOffset;
for (uint32_t i = mCurrentStream; remaining && i < mStreams.Length(); ++i) {
nsCOMPtr<nsISeekableStream> stream =
do_QueryInterface(mStreams[i]);
uint64_t avail;
rv = mStreams[i]->Available(&avail);
NS_ENSURE_SUCCESS(rv, rv);
int64_t seek = NS_MIN((int64_t)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) {
int64_t remaining = -aOffset;
for (uint32_t i = mCurrentStream; remaining && i != (uint32_t)-1; --i) {
nsCOMPtr<nsISeekableStream> stream =
do_QueryInterface(mStreams[i]);
int64_t pos;
rv = stream->Tell(&pos);
NS_ENSURE_SUCCESS(rv, rv);
int64_t 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;
}
int64_t remaining = aOffset;
for (uint32_t i = mStreams.Length() - 1; i != (uint32_t)-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
int64_t streamPos;
if (i < oldCurrentStream) {
streamPos = 0;
} else {
uint64_t 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 {
int64_t avail;
rv = stream->Tell(&avail);
NS_ENSURE_SUCCESS(rv, rv);
int64_t 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;
}
@ -408,7 +595,7 @@ nsMultiplexInputStream::Serialize(InputStreamParams& aParams)
{
MultiplexInputStreamParams params;
uint32_t streamCount = mStreams.Count();
uint32_t streamCount = mStreams.Length();
if (streamCount) {
InfallibleTArray<InputStreamParams>& streams = params.streams();
@ -416,7 +603,7 @@ nsMultiplexInputStream::Serialize(InputStreamParams& aParams)
streams.SetCapacity(streamCount);
for (uint32_t index = 0; index < streamCount; index++) {
nsCOMPtr<nsIIPCSerializableInputStream> serializable =
do_QueryInterface(mStreams.ObjectAt(index));
do_QueryInterface(mStreams[index]);
NS_ASSERTION(serializable, "Child stream isn't serializable!");
if (serializable) {