Bug 982779 - Ensure that child->parent IPC blobs are never mysterious, r=baku.

This commit is contained in:
Ben Turner 2014-04-29 13:02:41 -07:00
Родитель 727cf86888
Коммит e7d0d6522b
7 изменённых файлов: 124 добавлений и 146 удалений

Просмотреть файл

@ -34,6 +34,7 @@
#include "nsWrapperCache.h" #include "nsWrapperCache.h"
#include "nsCycleCollectionParticipant.h" #include "nsCycleCollectionParticipant.h"
class nsDOMMultipartFile;
class nsIFile; class nsIFile;
class nsIInputStream; class nsIInputStream;
class nsIClassInfo; class nsIClassInfo;
@ -42,6 +43,8 @@ class nsDOMFileBase : public nsIDOMFile,
public nsIXHRSendable, public nsIXHRSendable,
public nsIMutable public nsIMutable
{ {
friend class nsDOMMultipartFile;
public: public:
typedef mozilla::dom::indexedDB::FileInfo FileInfo; typedef mozilla::dom::indexedDB::FileInfo FileInfo;
@ -361,8 +364,9 @@ public:
nsDOMMemoryFile(void *aMemoryBuffer, nsDOMMemoryFile(void *aMemoryBuffer,
uint64_t aLength, uint64_t aLength,
const nsAString& aName, const nsAString& aName,
const nsAString& aContentType) const nsAString& aContentType,
: nsDOMFile(aName, aContentType, aLength, UINT64_MAX), uint64_t aLastModifiedDate)
: nsDOMFile(aName, aContentType, aLength, aLastModifiedDate),
mDataOwner(new DataOwner(aMemoryBuffer, aLength)) mDataOwner(new DataOwner(aMemoryBuffer, aLength))
{ {
NS_ASSERTION(mDataOwner && mDataOwner->mData, "must have data"); NS_ASSERTION(mDataOwner && mDataOwner->mData, "must have data");

Просмотреть файл

@ -27,26 +27,6 @@ NS_IMPL_ISUPPORTS_INHERITED(nsDOMMultipartFile, nsDOMFile,
NS_IMETHODIMP NS_IMETHODIMP
nsDOMMultipartFile::GetSize(uint64_t* aLength) nsDOMMultipartFile::GetSize(uint64_t* aLength)
{ {
if (mLength == UINT64_MAX) {
CheckedUint64 length = 0;
uint32_t i;
uint32_t len = mBlobs.Length();
for (i = 0; i < len; i++) {
nsIDOMBlob* blob = mBlobs.ElementAt(i).get();
uint64_t l = 0;
nsresult rv = blob->GetSize(&l);
NS_ENSURE_SUCCESS(rv, rv);
length += l;
}
NS_ENSURE_TRUE(length.isValid(), NS_ERROR_FAILURE);
mLength = length.value();
}
*aLength = mLength; *aLength = mLength;
return NS_OK; return NS_OK;
} }
@ -214,6 +194,8 @@ nsDOMMultipartFile::InitBlob(JSContext* aCx,
return ParseBlobArrayArgument(aCx, aArgv[0], nativeEOL, aUnwrapFunc); return ParseBlobArrayArgument(aCx, aArgv[0], nativeEOL, aUnwrapFunc);
} }
SetLengthAndModifiedDate();
return NS_OK; return NS_OK;
} }
@ -279,9 +261,48 @@ nsDOMMultipartFile::ParseBlobArrayArgument(JSContext* aCx, JS::Value& aValue,
} }
mBlobs = blobSet.GetBlobs(); mBlobs = blobSet.GetBlobs();
SetLengthAndModifiedDate();
return NS_OK; return NS_OK;
} }
void
nsDOMMultipartFile::SetLengthAndModifiedDate()
{
MOZ_ASSERT(mLength == UINT64_MAX);
MOZ_ASSERT(mLastModificationDate == UINT64_MAX);
uint64_t totalLength = 0;
for (uint32_t index = 0, count = mBlobs.Length(); index < count; index++) {
nsCOMPtr<nsIDOMBlob>& blob = mBlobs[index];
#ifdef DEBUG
{
// XXX This is only safe so long as all blob implementations in our tree
// inherit nsDOMFileBase.
const auto* blobBase = static_cast<nsDOMFileBase*>(blob.get());
MOZ_ASSERT(!blobBase->IsSizeUnknown());
MOZ_ASSERT(!blobBase->IsDateUnknown());
}
#endif
uint64_t subBlobLength;
MOZ_ALWAYS_TRUE(NS_SUCCEEDED(blob->GetSize(&subBlobLength)));
MOZ_ASSERT(UINT64_MAX - subBlobLength >= totalLength);
totalLength += subBlobLength;
}
mLength = totalLength;
if (mIsFile) {
mLastModificationDate = PR_Now();
}
}
NS_IMETHODIMP NS_IMETHODIMP
nsDOMMultipartFile::GetMozFullPathInternal(nsAString &aFilename) nsDOMMultipartFile::GetMozFullPathInternal(nsAString &aFilename)
{ {
@ -374,7 +395,18 @@ nsDOMMultipartFile::InitChromeFile(JSContext* aCx,
file->GetLeafName(mName); file->GetLeafName(mName);
} }
blob = new nsDOMFileFile(file); nsRefPtr<nsDOMFileFile> domFile = new nsDOMFileFile(file);
// Pre-cache size.
uint64_t unused;
rv = domFile->GetSize(&unused);
NS_ENSURE_SUCCESS(rv, rv);
// Pre-cache modified date.
rv = domFile->GetMozLastModifiedDate(&unused);
NS_ENSURE_SUCCESS(rv, rv);
blob = domFile.forget();
} }
// XXXkhuey this is terrible // XXXkhuey this is terrible
@ -386,6 +418,8 @@ nsDOMMultipartFile::InitChromeFile(JSContext* aCx,
blobSet.AppendBlob(blob); blobSet.AppendBlob(blob);
mBlobs = blobSet.GetBlobs(); mBlobs = blobSet.GetBlobs();
SetLengthAndModifiedDate();
return NS_OK; return NS_OK;
} }

Просмотреть файл

@ -32,6 +32,7 @@ public:
mBlobs(aBlobs), mBlobs(aBlobs),
mIsFromNsiFile(false) mIsFromNsiFile(false)
{ {
SetLengthAndModifiedDate();
} }
// Create as a blob // Create as a blob
@ -41,6 +42,7 @@ public:
mBlobs(aBlobs), mBlobs(aBlobs),
mIsFromNsiFile(false) mIsFromNsiFile(false)
{ {
SetLengthAndModifiedDate();
} }
// Create as a file to be later initialized // Create as a file to be later initialized
@ -109,6 +111,8 @@ protected:
nsresult ParseBlobArrayArgument(JSContext* aCx, JS::Value& aValue, nsresult ParseBlobArrayArgument(JSContext* aCx, JS::Value& aValue,
bool aNativeEOL, UnwrapFuncPtr aUnwrapFunc); bool aNativeEOL, UnwrapFuncPtr aUnwrapFunc);
void SetLengthAndModifiedDate();
nsTArray<nsCOMPtr<nsIDOMBlob> > mBlobs; nsTArray<nsCOMPtr<nsIDOMBlob> > mBlobs;
bool mIsFromNsiFile; bool mIsFromNsiFile;
}; };
@ -170,7 +174,7 @@ protected:
// and put it on the stack // and put it on the stack
nsCOMPtr<nsIDOMBlob> blob = nsCOMPtr<nsIDOMBlob> blob =
new nsDOMMemoryFile(mData, mDataLen, EmptyString(), EmptyString()); new nsDOMMemoryFile(mData, mDataLen, EmptyString());
mBlobs.AppendElement(blob); mBlobs.AppendElement(blob);
mData = nullptr; // The nsDOMMemoryFile takes ownership of the buffer mData = nullptr; // The nsDOMMemoryFile takes ownership of the buffer
mDataLen = 0; mDataLen = 0;

Просмотреть файл

@ -591,7 +591,7 @@ HTMLCanvasElement::MozGetAsFileImpl(const nsAString& aName,
// The DOMFile takes ownership of the buffer // The DOMFile takes ownership of the buffer
nsRefPtr<nsDOMMemoryFile> file = nsRefPtr<nsDOMMemoryFile> file =
new nsDOMMemoryFile(imgData, (uint32_t)imgSize, aName, type); new nsDOMMemoryFile(imgData, (uint32_t)imgSize, aName, type, PR_Now());
file.forget(aResult); file.forget(aResult);
return NS_OK; return NS_OK;

Просмотреть файл

@ -1821,10 +1821,15 @@ BlobParent::Create(ContentParent* aManager,
const ChildBlobConstructorParams& blobParams = aParams.blobParams(); const ChildBlobConstructorParams& blobParams = aParams.blobParams();
MOZ_ASSERT(blobParams.type() !=
ChildBlobConstructorParams::TMysteryBlobConstructorParams);
switch (blobParams.type()) { switch (blobParams.type()) {
case ChildBlobConstructorParams::TMysteryBlobConstructorParams:
return nullptr;
case ChildBlobConstructorParams::TNormalBlobConstructorParams: case ChildBlobConstructorParams::TNormalBlobConstructorParams:
case ChildBlobConstructorParams::TFileBlobConstructorParams: case ChildBlobConstructorParams::TFileBlobConstructorParams:
case ChildBlobConstructorParams::TMysteryBlobConstructorParams:
return new BlobParent(aManager, aParams); return new BlobParent(aManager, aParams);
case ChildBlobConstructorParams::TSlicedBlobConstructorParams: { case ChildBlobConstructorParams::TSlicedBlobConstructorParams: {

Просмотреть файл

@ -876,43 +876,6 @@ ContentChild::GetOrCreateActorForBlob(nsIDOMBlob* aBlob)
return actor; return actor;
} }
// XXX This is only safe so long as all blob implementations in our tree
// inherit nsDOMFileBase. If that ever changes then this will need to grow
// a real interface or something.
const nsDOMFileBase* blob = static_cast<nsDOMFileBase*>(aBlob);
// We often pass blobs that are multipart but that only contain one sub-blob
// (WebActivities does this a bunch). Unwrap to reduce the number of actors
// that we have to maintain.
const nsTArray<nsCOMPtr<nsIDOMBlob> >* subBlobs = blob->GetSubBlobs();
if (subBlobs && subBlobs->Length() == 1) {
const nsCOMPtr<nsIDOMBlob>& subBlob = subBlobs->ElementAt(0);
MOZ_ASSERT(subBlob);
// We can only take this shortcut if the multipart and the sub-blob are both
// Blob objects or both File objects.
nsCOMPtr<nsIDOMFile> multipartBlobAsFile = do_QueryInterface(aBlob);
nsCOMPtr<nsIDOMFile> subBlobAsFile = do_QueryInterface(subBlob);
if (!multipartBlobAsFile == !subBlobAsFile) {
// The wrapping was unnecessary, see if we can simply pass an existing
// remote blob.
if (nsCOMPtr<nsIRemoteBlob> remoteBlob = do_QueryInterface(subBlob)) {
BlobChild* actor =
static_cast<BlobChild*>(
static_cast<PBlobChild*>(remoteBlob->GetPBlob()));
MOZ_ASSERT(actor);
return actor;
}
// No need to add a reference here since the original blob must have a
// strong reference in the caller and it must also have a strong reference
// to this sub-blob.
aBlob = subBlob;
blob = static_cast<nsDOMFileBase*>(aBlob);
subBlobs = blob->GetSubBlobs();
}
}
// All blobs shared between processes must be immutable. // All blobs shared between processes must be immutable.
nsCOMPtr<nsIMutable> mutableBlob = do_QueryInterface(aBlob); nsCOMPtr<nsIMutable> mutableBlob = do_QueryInterface(aBlob);
if (!mutableBlob || NS_FAILED(mutableBlob->SetMutable(false))) { if (!mutableBlob || NS_FAILED(mutableBlob->SetMutable(false))) {
@ -920,56 +883,59 @@ ContentChild::GetOrCreateActorForBlob(nsIDOMBlob* aBlob)
return nullptr; return nullptr;
} }
#ifdef DEBUG
{
// XXX This is only safe so long as all blob implementations in our tree
// inherit nsDOMFileBase. If that ever changes then this will need to
// grow a real interface or something.
const auto* blob = static_cast<nsDOMFileBase*>(aBlob);
MOZ_ASSERT(!blob->IsSizeUnknown());
MOZ_ASSERT(!blob->IsDateUnknown());
}
#endif
ParentBlobConstructorParams params; ParentBlobConstructorParams params;
if (blob->IsSizeUnknown() || blob->IsDateUnknown()) { nsString contentType;
// We don't want to call GetSize or GetLastModifiedDate nsresult rv = aBlob->GetType(contentType);
// yet since that may stat a file on the main thread NS_ENSURE_SUCCESS(rv, nullptr);
// here. Instead we'll learn the size lazily from the
// other process. uint64_t length;
params.blobParams() = MysteryBlobConstructorParams(); rv = aBlob->GetSize(&length);
params.optionalInputStreamParams() = void_t(); NS_ENSURE_SUCCESS(rv, nullptr);
}
else { nsCOMPtr<nsIInputStream> stream;
nsString contentType; rv = aBlob->GetInternalStream(getter_AddRefs(stream));
nsresult rv = aBlob->GetType(contentType); NS_ENSURE_SUCCESS(rv, nullptr);
InputStreamParams inputStreamParams;
nsTArray<mozilla::ipc::FileDescriptor> fds;
SerializeInputStream(stream, inputStreamParams, fds);
MOZ_ASSERT(fds.IsEmpty());
params.optionalInputStreamParams() = inputStreamParams;
nsCOMPtr<nsIDOMFile> file = do_QueryInterface(aBlob);
if (file) {
FileBlobConstructorParams fileParams;
rv = file->GetName(fileParams.name());
NS_ENSURE_SUCCESS(rv, nullptr); NS_ENSURE_SUCCESS(rv, nullptr);
uint64_t length; rv = file->GetMozLastModifiedDate(&fileParams.modDate());
rv = aBlob->GetSize(&length);
NS_ENSURE_SUCCESS(rv, nullptr); NS_ENSURE_SUCCESS(rv, nullptr);
nsCOMPtr<nsIInputStream> stream; fileParams.contentType() = contentType;
rv = aBlob->GetInternalStream(getter_AddRefs(stream)); fileParams.length() = length;
NS_ENSURE_SUCCESS(rv, nullptr);
InputStreamParams inputStreamParams; params.blobParams() = fileParams;
nsTArray<mozilla::ipc::FileDescriptor> fds; } else {
SerializeInputStream(stream, inputStreamParams, fds); NormalBlobConstructorParams blobParams;
MOZ_ASSERT(fds.IsEmpty()); blobParams.contentType() = contentType;
blobParams.length() = length;
params.optionalInputStreamParams() = inputStreamParams; params.blobParams() = blobParams;
nsCOMPtr<nsIDOMFile> file = do_QueryInterface(aBlob);
if (file) {
FileBlobConstructorParams fileParams;
rv = file->GetName(fileParams.name());
NS_ENSURE_SUCCESS(rv, nullptr);
rv = file->GetMozLastModifiedDate(&fileParams.modDate());
NS_ENSURE_SUCCESS(rv, nullptr);
fileParams.contentType() = contentType;
fileParams.length() = length;
params.blobParams() = fileParams;
} else {
NormalBlobConstructorParams blobParams;
blobParams.contentType() = contentType;
blobParams.length() = length;
params.blobParams() = blobParams;
}
} }
BlobChild* actor = BlobChild::Create(this, aBlob); BlobChild* actor = BlobChild::Create(this, aBlob);

Просмотреть файл

@ -2374,46 +2374,6 @@ ContentParent::GetOrCreateActorForBlob(nsIDOMBlob* aBlob)
} }
} }
// XXX This is only safe so long as all blob implementations in our tree
// inherit nsDOMFileBase. If that ever changes then this will need to grow
// a real interface or something.
const nsDOMFileBase* blob = static_cast<nsDOMFileBase*>(aBlob);
// We often pass blobs that are multipart but that only contain one sub-blob
// (WebActivities does this a bunch). Unwrap to reduce the number of actors
// that we have to maintain.
const nsTArray<nsCOMPtr<nsIDOMBlob> >* subBlobs = blob->GetSubBlobs();
if (subBlobs && subBlobs->Length() == 1) {
const nsCOMPtr<nsIDOMBlob>& subBlob = subBlobs->ElementAt(0);
MOZ_ASSERT(subBlob);
// We can only take this shortcut if the multipart and the sub-blob are both
// Blob objects or both File objects.
nsCOMPtr<nsIDOMFile> multipartBlobAsFile = do_QueryInterface(aBlob);
nsCOMPtr<nsIDOMFile> subBlobAsFile = do_QueryInterface(subBlob);
if (!multipartBlobAsFile == !subBlobAsFile) {
// The wrapping might have been unnecessary, see if we can simply pass an
// existing remote blob for this ContentParent.
if (nsCOMPtr<nsIRemoteBlob> remoteSubBlob = do_QueryInterface(subBlob)) {
BlobParent* actor =
static_cast<BlobParent*>(
static_cast<PBlobParent*>(remoteSubBlob->GetPBlob()));
MOZ_ASSERT(actor);
if (static_cast<ContentParent*>(actor->Manager()) == this) {
return actor;
}
}
// No need to add a reference here since the original blob must have a
// strong reference in the caller and it must also have a strong reference
// to this sub-blob.
aBlob = subBlob;
blob = static_cast<nsDOMFileBase*>(aBlob);
subBlobs = blob->GetSubBlobs();
}
}
// All blobs shared between processes must be immutable. // All blobs shared between processes must be immutable.
nsCOMPtr<nsIMutable> mutableBlob = do_QueryInterface(aBlob); nsCOMPtr<nsIMutable> mutableBlob = do_QueryInterface(aBlob);
if (!mutableBlob || NS_FAILED(mutableBlob->SetMutable(false))) { if (!mutableBlob || NS_FAILED(mutableBlob->SetMutable(false))) {
@ -2421,6 +2381,11 @@ ContentParent::GetOrCreateActorForBlob(nsIDOMBlob* aBlob)
return nullptr; return nullptr;
} }
// XXX This is only safe so long as all blob implementations in our tree
// inherit nsDOMFileBase. If that ever changes then this will need to grow
// a real interface or something.
const auto* blob = static_cast<nsDOMFileBase*>(aBlob);
ChildBlobConstructorParams params; ChildBlobConstructorParams params;
if (blob->IsSizeUnknown() || blob->IsDateUnknown()) { if (blob->IsSizeUnknown() || blob->IsDateUnknown()) {