diff --git a/dom/indexedDB/FileSnapshot.cpp b/dom/indexedDB/FileSnapshot.cpp index e303ae39628..519ffe564f8 100644 --- a/dom/indexedDB/FileSnapshot.cpp +++ b/dom/indexedDB/FileSnapshot.cpp @@ -86,7 +86,7 @@ FileImplSnapshot::AssertSanity() #endif // DEBUG -NS_IMPL_ISUPPORTS_INHERITED0(FileImplSnapshot, FileImpl) +NS_IMPL_ISUPPORTS_INHERITED(FileImplSnapshot, FileImpl, PIFileImplSnapshot) void FileImplSnapshot::Unlink() diff --git a/dom/indexedDB/FileSnapshot.h b/dom/indexedDB/FileSnapshot.h index 050dc3708d6..e79408f70cd 100644 --- a/dom/indexedDB/FileSnapshot.h +++ b/dom/indexedDB/FileSnapshot.h @@ -11,6 +11,18 @@ #include "mozilla/dom/File.h" #include "nsAutoPtr.h" #include "nsCOMPtr.h" +#include "nsISupports.h" + +#define FILEIMPLSNAPSHOT_IID \ + {0x0dfc11b1, 0x75d3, 0x473b, {0x8c, 0x67, 0xb7, 0x23, 0xf4, 0x67, 0xd6, 0x73}} + +class PIFileImplSnapshot : public nsISupports +{ +public: + NS_DECLARE_STATIC_IID_ACCESSOR(FILEIMPLSNAPSHOT_IID) +}; + +NS_DEFINE_STATIC_IID_ACCESSOR(PIFileImplSnapshot, FILEIMPLSNAPSHOT_IID) namespace mozilla { namespace dom { @@ -23,6 +35,7 @@ class IDBFileHandle; class FileImplSnapshot MOZ_FINAL : public FileImplBase + , public PIFileImplSnapshot { typedef mozilla::dom::MetadataParameters MetadataParameters; diff --git a/dom/indexedDB/test/mochitest.ini b/dom/indexedDB/test/mochitest.ini index c4b85293fe4..e049e024c9f 100644 --- a/dom/indexedDB/test/mochitest.ini +++ b/dom/indexedDB/test/mochitest.ini @@ -19,6 +19,7 @@ support-files = unit/test_advance.js unit/test_autoIncrement.js unit/test_autoIncrement_indexes.js + unit/test_blob_file_backed.js unit/test_blocked_order.js unit/test_clear.js unit/test_complex_keyPaths.js @@ -111,6 +112,9 @@ skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 [test_blob_archive.html] skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 +[test_blob_file_backed.html] +# This test can only run in the main process. +skip-if = buildapp == 'b2g' || buildapp == 'mulet' || e10s [test_blob_simple.html] skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116 [test_blob_worker_crash.html] diff --git a/dom/indexedDB/test/test_blob_file_backed.html b/dom/indexedDB/test/test_blob_file_backed.html new file mode 100644 index 00000000000..3c3f103d6e0 --- /dev/null +++ b/dom/indexedDB/test/test_blob_file_backed.html @@ -0,0 +1,18 @@ + + + + IndexedDB Test + + + + + + + + + + + diff --git a/dom/indexedDB/test/unit/test_blob_file_backed.js b/dom/indexedDB/test/unit/test_blob_file_backed.js new file mode 100644 index 00000000000..f3cc13cfa93 --- /dev/null +++ b/dom/indexedDB/test/unit/test_blob_file_backed.js @@ -0,0 +1,95 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ +let testGenerator = testSteps(); + +function createFileReader() { + return SpecialPowers.Cc["@mozilla.org/files/filereader;1"] + .createInstance(SpecialPowers.Ci.nsIDOMFileReader); +} + +function testSteps() +{ + const fileIOFlags = 0x02 | // PR_WRONLY + 0x08 | // PR_CREATEFILE + 0x20; // PR_TRUNCATE + const filePerms = 0664; + const fileData = "abcdefghijklmnopqrstuvwxyz"; + const fileType = "text/plain"; + + const databaseName = + ("window" in this) ? window.location.pathname : "Test"; + const objectStoreName = "foo"; + const objectStoreKey = "10"; + + info("Creating temp file"); + + let dirSvc = + SpecialPowers.Cc["@mozilla.org/file/directory_service;1"] + .getService(SpecialPowers.Ci.nsIProperties); + let testFile = dirSvc.get("ProfD", SpecialPowers.Ci.nsIFile); + testFile.createUnique(SpecialPowers.Ci.nsIFile.NORMAL_FILE_TYPE, filePerms); + + info("Writing temp file"); + + let outStream = + SpecialPowers.Cc["@mozilla.org/network/file-output-stream;1"] + .createInstance(SpecialPowers.Ci.nsIFileOutputStream); + outStream.init(testFile, fileIOFlags, filePerms, 0); + outStream.write(fileData, fileData.length); + outStream.close(); + + let file = SpecialPowers.createDOMFile(testFile.path, { type: fileType }); + ok(file instanceof File, "Got a File object"); + is(file.size, fileData.length, "Correct size"); + is(file.type, fileType, "Correct type"); + + let fileReader = createFileReader(); + fileReader.onload = grabEventAndContinueHandler; + fileReader.readAsText(file); + + let event = yield undefined; + + is(fileReader.result, fileData, "Correct data"); + + let request = indexedDB.open(databaseName, 1); + request.onerror = errorHandler; + request.onupgradeneeded = grabEventAndContinueHandler; + request.onsuccess = grabEventAndContinueHandler; + + event = yield undefined; + + let db = event.target.result; + let objectStore = db.createObjectStore(objectStoreName); + objectStore.put(file, objectStoreKey); + + event = yield undefined; + + db = event.target.result; + + file = null; + testFile.remove(false); + + objectStore = db.transaction(objectStoreName).objectStore(objectStoreName); + objectStore.get(objectStoreKey).onsuccess = grabEventAndContinueHandler; + + event = yield undefined; + + file = event.target.result; + + ok(file instanceof File, "Got a File object"); + is(file.size, fileData.length, "Correct size"); + is(file.type, fileType, "Correct type"); + + fileReader = createFileReader(); + fileReader.onload = grabEventAndContinueHandler; + fileReader.readAsText(file); + + event = yield undefined; + + is(fileReader.result, fileData, "Correct data"); + + finishTest(); + yield undefined; +} diff --git a/dom/indexedDB/test/unit/xpcshell-head-parent-process.js b/dom/indexedDB/test/unit/xpcshell-head-parent-process.js index 29f0d2d5d82..c359b0a8cda 100644 --- a/dom/indexedDB/test/unit/xpcshell-head-parent-process.js +++ b/dom/indexedDB/test/unit/xpcshell-head-parent-process.js @@ -46,7 +46,7 @@ if (!this.runTest) { enableExperimental(); } - Cu.importGlobalProperties(["indexedDB"]); + Cu.importGlobalProperties(["indexedDB", "Blob", "File"]); do_test_pending(); testGenerator.next(); @@ -332,5 +332,21 @@ var SpecialPowers = { var prefService = Cc["@mozilla.org/preferences-service;1"].getService(Ci.nsIPrefService); return prefService.getBranch(null); - } + }, + + get Cc() { + return Cc; + }, + + get Ci() { + return Ci; + }, + + get Cu() { + return Cu; + }, + + createDOMFile: function(file, options) { + return new File(file, options); + }, }; diff --git a/dom/indexedDB/test/unit/xpcshell-parent-process.ini b/dom/indexedDB/test/unit/xpcshell-parent-process.ini index 4f076c5a703..01e21eeb9e4 100644 --- a/dom/indexedDB/test/unit/xpcshell-parent-process.ini +++ b/dom/indexedDB/test/unit/xpcshell-parent-process.ini @@ -18,6 +18,7 @@ support-files = [include:xpcshell-shared.ini] +[test_blob_file_backed.js] [test_bug1056939.js] [test_globalObjects_ipc.js] [test_invalidate.js] diff --git a/dom/ipc/Blob.cpp b/dom/ipc/Blob.cpp index 82e8eaaf690..a7636a96fc4 100644 --- a/dom/ipc/Blob.cpp +++ b/dom/ipc/Blob.cpp @@ -21,6 +21,7 @@ #include "mozilla/dom/nsIContentChild.h" #include "mozilla/dom/PBlobStreamChild.h" #include "mozilla/dom/PBlobStreamParent.h" +#include "mozilla/dom/indexedDB/FileSnapshot.h" #include "mozilla/dom/indexedDB/IndexedDatabaseManager.h" #include "mozilla/ipc/InputStreamUtils.h" #include "mozilla/ipc/PBackgroundChild.h" @@ -837,13 +838,13 @@ struct MOZ_STACK_CLASS CreateBlobImplMetadata MOZ_FINAL uint64_t mLength; uint64_t mLastModifiedDate; bool mHasRecursed; - bool mIsSameProcessActor; + const bool mIsSameProcessActor; - CreateBlobImplMetadata() + CreateBlobImplMetadata(bool aIsSameProcessActor) : mLength(0) , mLastModifiedDate(0) , mHasRecursed(false) - , mIsSameProcessActor(false) + , mIsSameProcessActor(aIsSameProcessActor) { MOZ_COUNT_CTOR(CreateBlobImplMetadata); @@ -863,10 +864,30 @@ struct MOZ_STACK_CLASS CreateBlobImplMetadata MOZ_FINAL }; already_AddRefed -CreateBlobImplFromParams(const StringInputStreamParams& aParams, - const CreateBlobImplMetadata& aMetadata) +CreateBlobImpl(const nsID& aKnownBlobIDData, + const CreateBlobImplMetadata& aMetadata) { - static_assert(sizeof(aParams.data().Length()) <= sizeof(size_t), + MOZ_ASSERT(gProcessType == GeckoProcessType_Default); + MOZ_ASSERT(aMetadata.mHasRecursed); + + nsRefPtr blobImpl = BlobParent::GetBlobImplForID(aKnownBlobIDData); + if (NS_WARN_IF(!blobImpl)) { + ASSERT_UNLESS_FUZZING(); + return nullptr; + } + + DebugOnly isMutable; + MOZ_ASSERT(NS_SUCCEEDED(blobImpl->GetMutable(&isMutable))); + MOZ_ASSERT(!isMutable); + + return blobImpl.forget(); +} + +already_AddRefed +CreateBlobImpl(const nsTArray& aMemoryData, + const CreateBlobImplMetadata& aMetadata) +{ + static_assert(sizeof(aMemoryData.Length()) <= sizeof(size_t), "String length won't fit in size_t!"); static_assert(sizeof(size_t) <= sizeof(uint64_t), "size_t won't fit in uint64_t!"); @@ -875,19 +896,22 @@ CreateBlobImplFromParams(const StringInputStreamParams& aParams, nsRefPtr blobImpl; - if (auto length = static_cast(aParams.data().Length())) { + if (auto length = static_cast(aMemoryData.Length())) { + static MOZ_CONSTEXPR_VAR size_t elementSizeMultiplier = + sizeof(aMemoryData[0]) / sizeof(char); + if (!aMetadata.mHasRecursed && NS_WARN_IF(aMetadata.mLength != uint64_t(length))) { ASSERT_UNLESS_FUZZING(); return nullptr; } - void* buffer = moz_malloc(aParams.data().Length()); + void* buffer = moz_malloc(length * elementSizeMultiplier); if (NS_WARN_IF(!buffer)) { return nullptr; } - memcpy(buffer, aParams.data().get(), length); + memcpy(buffer, aMemoryData.Elements(), length * elementSizeMultiplier); if (!aMetadata.mHasRecursed && aMetadata.IsFile()) { blobImpl = @@ -915,36 +939,16 @@ CreateBlobImplFromParams(const StringInputStreamParams& aParams, } already_AddRefed -CreateBlobImplFromParams(const RemoteInputStreamParams& aParams, - const CreateBlobImplMetadata& aMetadata) -{ - MOZ_ASSERT(gProcessType == GeckoProcessType_Default); - MOZ_ASSERT(aMetadata.mHasRecursed); - - nsRefPtr blobImpl = BlobParent::GetBlobImplForID(aParams.id()); - if (NS_WARN_IF(!blobImpl)) { - ASSERT_UNLESS_FUZZING(); - return nullptr; - } - - DebugOnly isMutable; - MOZ_ASSERT(NS_SUCCEEDED(blobImpl->GetMutable(&isMutable))); - MOZ_ASSERT(!isMutable); - - return blobImpl.forget(); -} - -already_AddRefed -CreateBlobImplFromParams(const SameProcessInputStreamParams& aParams, - const CreateBlobImplMetadata& aMetadata) +CreateBlobImpl(intptr_t aAddRefedInputStream, + const CreateBlobImplMetadata& aMetadata) { MOZ_ASSERT(gProcessType == GeckoProcessType_Default); MOZ_ASSERT(aMetadata.mIsSameProcessActor); - MOZ_ASSERT(aParams.addRefedInputStream()); + MOZ_ASSERT(aAddRefedInputStream); nsCOMPtr inputStream = dont_AddRef( - reinterpret_cast(aParams.addRefedInputStream())); + reinterpret_cast(aAddRefedInputStream)); nsRefPtr blobImpl; if (!aMetadata.mHasRecursed && aMetadata.IsFile()) { @@ -969,73 +973,35 @@ CreateBlobImplFromParams(const SameProcessInputStreamParams& aParams, } already_AddRefed -CreateBlobImplFromParams(const MultiplexInputStreamParams& aParams, - CreateBlobImplMetadata& aMetadata); +CreateBlobImpl(const nsTArray& aBlobData, + CreateBlobImplMetadata& aMetadata); already_AddRefed -CreateBlobImplFromInputStreamParams(const InputStreamParams& aParams, - CreateBlobImplMetadata& aMetadata) +CreateBlobImplFromBlobData(const BlobData& aBlobData, + CreateBlobImplMetadata& aMetadata) { MOZ_ASSERT(gProcessType == GeckoProcessType_Default); nsRefPtr blobImpl; - switch (aParams.type()) { - case InputStreamParams::TStringInputStreamParams: { - const StringInputStreamParams& params = - aParams.get_StringInputStreamParams(); - blobImpl = CreateBlobImplFromParams(params, aMetadata); + switch (aBlobData.type()) { + case BlobData::TnsID: { + blobImpl = CreateBlobImpl(aBlobData.get_nsID(), aMetadata); break; } - case InputStreamParams::TFileInputStreamParams: { - ASSERT_UNLESS_FUZZING(); - return nullptr; - } - - case InputStreamParams::TPartialFileInputStreamParams: { - ASSERT_UNLESS_FUZZING(); - return nullptr; - } - - case InputStreamParams::TBufferedInputStreamParams: { - ASSERT_UNLESS_FUZZING(); - return nullptr; - } - - case InputStreamParams::TMIMEInputStreamParams: { - ASSERT_UNLESS_FUZZING(); - return nullptr; - } - - case InputStreamParams::TMultiplexInputStreamParams: { - const MultiplexInputStreamParams& params = - aParams.get_MultiplexInputStreamParams(); - blobImpl = CreateBlobImplFromParams(params, aMetadata); + case BlobData::TArrayOfuint8_t: { + blobImpl = CreateBlobImpl(aBlobData.get_ArrayOfuint8_t(), aMetadata); break; } - case InputStreamParams::TRemoteInputStreamParams: { - if (NS_WARN_IF(!aMetadata.mHasRecursed)) { - ASSERT_UNLESS_FUZZING(); - return nullptr; - } - - const RemoteInputStreamParams& params = - aParams.get_RemoteInputStreamParams(); - blobImpl = CreateBlobImplFromParams(params, aMetadata); + case BlobData::Tintptr_t: { + blobImpl = CreateBlobImpl(aBlobData.get_intptr_t(), aMetadata); break; } - case InputStreamParams::TSameProcessInputStreamParams: { - if (NS_WARN_IF(!aMetadata.mIsSameProcessActor)) { - ASSERT_UNLESS_FUZZING(); - return nullptr; - } - - const SameProcessInputStreamParams& params = - aParams.get_SameProcessInputStreamParams(); - blobImpl = CreateBlobImplFromParams(params, aMetadata); + case BlobData::TArrayOfBlobData: { + blobImpl = CreateBlobImpl(aBlobData.get_ArrayOfBlobData(), aMetadata); break; } @@ -1047,34 +1013,17 @@ CreateBlobImplFromInputStreamParams(const InputStreamParams& aParams, } already_AddRefed -CreateBlobImplFromParams(const MultiplexInputStreamParams& aParams, - CreateBlobImplMetadata& aMetadata) +CreateBlobImpl(const nsTArray& aBlobDatas, + CreateBlobImplMetadata& aMetadata) { MOZ_ASSERT(gProcessType == GeckoProcessType_Default); - if (NS_WARN_IF(aParams.currentStream())) { - ASSERT_UNLESS_FUZZING(); - return nullptr; - } - - if (NS_WARN_IF(NS_FAILED(aParams.status()))) { - ASSERT_UNLESS_FUZZING(); - return nullptr; - } - - if (NS_WARN_IF(aParams.startedReadingCurrent())) { - ASSERT_UNLESS_FUZZING(); - return nullptr; - } - - const nsTArray& streams = aParams.streams(); - // Special case for a multipart blob with only one part. - if (streams.Length() == 1) { - const InputStreamParams& params = streams[0]; + if (aBlobDatas.Length() == 1) { + const BlobData& blobData = aBlobDatas[0]; nsRefPtr blobImpl = - CreateBlobImplFromInputStreamParams(params, aMetadata); + CreateBlobImplFromBlobData(blobData, aMetadata); if (NS_WARN_IF(!blobImpl)) { return nullptr; } @@ -1087,7 +1036,7 @@ CreateBlobImplFromParams(const MultiplexInputStreamParams& aParams, } FallibleTArray> fallibleBlobImpls; - if (NS_WARN_IF(!fallibleBlobImpls.SetLength(streams.Length()))) { + if (NS_WARN_IF(!fallibleBlobImpls.SetLength(aBlobDatas.Length()))) { return nullptr; } @@ -1097,11 +1046,13 @@ CreateBlobImplFromParams(const MultiplexInputStreamParams& aParams, const bool hasRecursed = aMetadata.mHasRecursed; aMetadata.mHasRecursed = true; - for (uint32_t count = streams.Length(), index = 0; index < count; index++) { - const InputStreamParams& params = streams[index]; + for (uint32_t count = aBlobDatas.Length(), index = 0; + index < count; + index++) { + const BlobData& blobData = aBlobDatas[index]; nsRefPtr& blobImpl = blobImpls[index]; - blobImpl = CreateBlobImplFromParams(params, aMetadata); + blobImpl = CreateBlobImplFromBlobData(blobData, aMetadata); if (NS_WARN_IF(!blobImpl)) { return nullptr; } @@ -1126,18 +1077,17 @@ CreateBlobImplFromParams(const MultiplexInputStreamParams& aParams, } already_AddRefed -CreateBlobImplFromParams(const ParentBlobConstructorParams& aParams, - bool aIsSameProcessActor) +CreateBlobImpl(const ParentBlobConstructorParams& aParams, + const BlobData& aBlobData, + bool aIsSameProcessActor) { MOZ_ASSERT(gProcessType == GeckoProcessType_Default); MOZ_ASSERT(aParams.blobParams().type() == AnyBlobConstructorParams::TNormalBlobConstructorParams || aParams.blobParams().type() == AnyBlobConstructorParams::TFileBlobConstructorParams); - MOZ_ASSERT(aParams.optionalInputStreamParams().type() == - OptionalInputStreamParams::TInputStreamParams); - CreateBlobImplMetadata metadata; + CreateBlobImplMetadata metadata(aIsSameProcessActor); if (aParams.blobParams().type() == AnyBlobConstructorParams::TNormalBlobConstructorParams) { @@ -1171,16 +1121,71 @@ CreateBlobImplFromParams(const ParentBlobConstructorParams& aParams, metadata.mLastModifiedDate = params.modDate(); } - metadata.mIsSameProcessActor = aIsSameProcessActor; - - const InputStreamParams& inputStreamParams = - aParams.optionalInputStreamParams().get_InputStreamParams(); - nsRefPtr blobImpl = - CreateBlobImplFromInputStreamParams(inputStreamParams, metadata); + CreateBlobImplFromBlobData(aBlobData, metadata); return blobImpl.forget(); } +void +BlobDataFromBlobImpl(FileImpl* aBlobImpl, BlobData& aBlobData) +{ + MOZ_ASSERT(gProcessType != GeckoProcessType_Default); + MOZ_ASSERT(aBlobImpl); + + const nsTArray>* subBlobs = aBlobImpl->GetSubBlobImpls(); + + if (subBlobs) { + aBlobData = nsTArray(); + + nsTArray& subBlobDatas = aBlobData.get_ArrayOfBlobData(); + subBlobDatas.SetLength(subBlobs->Length()); + + for (uint32_t count = subBlobs->Length(), index = 0; + index < count; + index++) { + BlobDataFromBlobImpl(subBlobs->ElementAt(index), subBlobDatas[index]); + } + + return; + } + + nsCOMPtr remoteBlob = do_QueryInterface(aBlobImpl); + if (remoteBlob) { + BlobChild* actor = remoteBlob->GetBlobChild(); + MOZ_ASSERT(actor); + + aBlobData = actor->ParentID(); + return; + } + + MOZ_ASSERT(aBlobImpl->IsMemoryFile()); + + nsCOMPtr inputStream; + MOZ_ALWAYS_TRUE(NS_SUCCEEDED( + aBlobImpl->GetInternalStream(getter_AddRefs(inputStream)))); + + DebugOnly isNonBlocking; + MOZ_ASSERT(NS_SUCCEEDED(inputStream->IsNonBlocking(&isNonBlocking))); + MOZ_ASSERT(isNonBlocking); + + uint64_t available; + MOZ_ALWAYS_TRUE(NS_SUCCEEDED(inputStream->Available(&available))); + + MOZ_ASSERT(available <= uint64_t(UINT32_MAX)); + + aBlobData = nsTArray(); + + nsTArray& blobData = aBlobData.get_ArrayOfuint8_t(); + + blobData.SetLength(size_t(available)); + + uint32_t readCount; + MOZ_ALWAYS_TRUE(NS_SUCCEEDED( + inputStream->Read(reinterpret_cast(blobData.Elements()), + uint32_t(available), + &readCount))); +} + } // anonymous namespace StaticAutoPtr BlobParent::sIDTable; @@ -1548,6 +1553,9 @@ protected: BlobChild* mActor; nsCOMPtr mActorTarget; + + nsRefPtr mSameProcessFileImpl; + const bool mIsSlice; public: @@ -1563,6 +1571,20 @@ public: const nsAString& aContentType, uint64_t aLength); + // For same-process blobs. + RemoteBlobImpl(BlobChild* aActor, + FileImpl* aSameProcessBlobImpl, + const nsAString& aName, + const nsAString& aContentType, + uint64_t aLength, + uint64_t aModDate); + + // For same-process blobs. + RemoteBlobImpl(BlobChild* aActor, + FileImpl* aSameProcessBlobImpl, + const nsAString& aContentType, + uint64_t aLength); + // For mystery blobs. explicit RemoteBlobImpl(BlobChild* aActor); @@ -1853,6 +1875,38 @@ RemoteBlobImpl::RemoteBlobImpl(BlobChild* aActor, CommonInit(aActor); } +BlobChild:: +RemoteBlobImpl::RemoteBlobImpl(BlobChild* aActor, + FileImpl* aSameProcessBlobImpl, + const nsAString& aName, + const nsAString& aContentType, + uint64_t aLength, + uint64_t aModDate) + : FileImplBase(aName, aContentType, aLength, aModDate) + , mSameProcessFileImpl(aSameProcessBlobImpl) + , mIsSlice(false) +{ + MOZ_ASSERT(aSameProcessBlobImpl); + MOZ_ASSERT(gProcessType == GeckoProcessType_Default); + + CommonInit(aActor); +} + +BlobChild:: +RemoteBlobImpl::RemoteBlobImpl(BlobChild* aActor, + FileImpl* aSameProcessBlobImpl, + const nsAString& aContentType, + uint64_t aLength) + : FileImplBase(aContentType, aLength) + , mSameProcessFileImpl(aSameProcessBlobImpl) + , mIsSlice(false) +{ + MOZ_ASSERT(aSameProcessBlobImpl); + MOZ_ASSERT(gProcessType == GeckoProcessType_Default); + + CommonInit(aActor); +} + BlobChild:: RemoteBlobImpl::RemoteBlobImpl(BlobChild* aActor) : FileImplBase(EmptyString(), EmptyString(), UINT64_MAX, UINT64_MAX) @@ -1954,6 +2008,13 @@ RemoteBlobImpl::GetMozFullPathInternal(nsAString& aFilePath, MOZ_CRASH("Not implemented!"); } + if (mSameProcessFileImpl) { + MOZ_ASSERT(gProcessType == GeckoProcessType_Default); + + mSameProcessFileImpl->GetMozFullPathInternal(aFilePath, aRv); + return; + } + if (!mActor) { aRv.Throw(NS_ERROR_UNEXPECTED); return; @@ -1976,9 +2037,18 @@ RemoteBlobImpl::CreateSlice(uint64_t aStart, ErrorResult& aRv) { // May be called on any thread. - nsRefPtr slice = - new RemoteBlobSliceImpl(this, aStart, aLength, aContentType); - return slice.forget(); + if (mSameProcessFileImpl) { + MOZ_ASSERT(gProcessType == GeckoProcessType_Default); + + return mSameProcessFileImpl->CreateSlice(aStart, + aLength, + aContentType, + aRv); + } + + nsRefPtr slice = + new RemoteBlobSliceImpl(this, aStart, aLength, aContentType); + return slice.forget(); } nsresult @@ -1986,6 +2056,22 @@ BlobChild:: RemoteBlobImpl::GetInternalStream(nsIInputStream** aStream) { // May be called on any thread. + if (mSameProcessFileImpl) { + MOZ_ASSERT(gProcessType == GeckoProcessType_Default); + + nsCOMPtr realStream; + nsresult rv = + mSameProcessFileImpl->GetInternalStream(getter_AddRefs(realStream)); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + nsRefPtr tether = + new BlobInputStreamTether(realStream, mSameProcessFileImpl); + tether.forget(aStream); + return NS_OK; + } + nsRefPtr helper = new CreateStreamHelper(this); return helper->GetStream(aStream); } @@ -1998,6 +2084,12 @@ RemoteBlobImpl::GetFileId() MOZ_CRASH("Not implemented!"); } + if (mSameProcessFileImpl) { + MOZ_ASSERT(gProcessType == GeckoProcessType_Default); + + return mSameProcessFileImpl->GetFileId(); + } + int64_t fileId; if (mActor && mActor->SendGetFileId(&fileId)) { return fileId; @@ -2209,8 +2301,7 @@ RemoteBlobSliceImpl::GetBlobChild() id /* id */, mStart /* begin */, mStart + mLength /* end */, - mContentType /* contentType */), - void_t() /* optionalInputStream */); + mContentType /* contentType */)); if (nsIContentChild* contentManager = baseActor->GetContentManager()) { mActor = SendSliceConstructor(contentManager, this, params); @@ -2648,6 +2739,8 @@ BlobChild::CommonInit(const ChildBlobConstructorParams& aParams) AnyBlobConstructorParams::Type paramsType = blobParams.type(); MOZ_ASSERT(paramsType != AnyBlobConstructorParams::T__None && + paramsType != + AnyBlobConstructorParams::TSlicedBlobConstructorParams && paramsType != AnyBlobConstructorParams::TKnownBlobConstructorParams); @@ -2673,6 +2766,44 @@ BlobChild::CommonInit(const ChildBlobConstructorParams& aParams) break; } + case AnyBlobConstructorParams::TSameProcessBlobConstructorParams: { + MOZ_ASSERT(gProcessType == GeckoProcessType_Default); + + const SameProcessBlobConstructorParams& params = + blobParams.get_SameProcessBlobConstructorParams(); + MOZ_ASSERT(params.addRefedFileImpl()); + + nsRefPtr blobImpl = + dont_AddRef(reinterpret_cast(params.addRefedFileImpl())); + + ErrorResult rv; + uint64_t size = blobImpl->GetSize(rv); + MOZ_ASSERT(!rv.Failed()); + + nsString contentType; + blobImpl->GetType(contentType); + + if (blobImpl->IsFile()) { + nsString name; + blobImpl->GetName(name); + + uint64_t lastModifiedDate = blobImpl->GetLastModified(rv); + MOZ_ASSERT(!rv.Failed()); + + remoteBlob = + new RemoteBlobImpl(this, + blobImpl, + name, + contentType, + size, + lastModifiedDate); + } else { + remoteBlob = new RemoteBlobImpl(this, blobImpl, contentType, size); + } + + break; + } + case AnyBlobConstructorParams::TMysteryBlobConstructorParams: { remoteBlob = new RemoteBlobImpl(this); break; @@ -2798,7 +2929,7 @@ BlobChild::GetOrCreateFromImpl(ChildManagerType* aManager, } } - // All blobs shared between processes must be immutable. + // All blobs shared between threads or processes must be immutable. if (NS_WARN_IF(NS_FAILED(aBlobImpl->SetMutable(false)))) { return nullptr; } @@ -2808,40 +2939,55 @@ BlobChild::GetOrCreateFromImpl(ChildManagerType* aManager, AnyBlobConstructorParams blobParams; - nsString contentType; - aBlobImpl->GetType(contentType); + nsCOMPtr snapshotInputStream; - ErrorResult rv; - uint64_t length = aBlobImpl->GetSize(rv); - MOZ_ASSERT(!rv.Failed()); - - nsCOMPtr stream; - MOZ_ALWAYS_TRUE(NS_SUCCEEDED( - aBlobImpl->GetInternalStream(getter_AddRefs(stream)))); - - if (aBlobImpl->IsFile()) { - nsString name; - aBlobImpl->GetName(name); - - uint64_t modDate = aBlobImpl->GetLastModified(rv); - MOZ_ASSERT(!rv.Failed()); - - blobParams = FileBlobConstructorParams(name, contentType, length, modDate); - } else { - blobParams = NormalBlobConstructorParams(contentType, length); + if (gProcessType == GeckoProcessType_Default) { + nsCOMPtr snapshot = do_QueryInterface(aBlobImpl); + if (snapshot) { + MOZ_ALWAYS_TRUE(NS_SUCCEEDED( + aBlobImpl->GetInternalStream(getter_AddRefs(snapshotInputStream)))); + } } - InputStreamParams inputStreamParams; + if (gProcessType == GeckoProcessType_Default && !snapshotInputStream) { + nsRefPtr sameProcessImpl = aBlobImpl; + auto addRefedFileImpl = + reinterpret_cast(sameProcessImpl.forget().take()); - nsTArray fds; - SerializeInputStream(stream, inputStreamParams, fds); + blobParams = SameProcessBlobConstructorParams(addRefedFileImpl); + } else { + BlobData blobData; + if (snapshotInputStream) { + blobData = + reinterpret_cast(snapshotInputStream.forget().take()); + } else { + BlobDataFromBlobImpl(aBlobImpl, blobData); + } - MOZ_ASSERT(inputStreamParams.type() != InputStreamParams::T__None); - MOZ_ASSERT(fds.IsEmpty()); + nsString contentType; + aBlobImpl->GetType(contentType); + + ErrorResult rv; + uint64_t length = aBlobImpl->GetSize(rv); + MOZ_ASSERT(!rv.Failed()); + + if (aBlobImpl->IsFile()) { + nsString name; + aBlobImpl->GetName(name); + + uint64_t modDate = aBlobImpl->GetLastModified(rv); + MOZ_ASSERT(!rv.Failed()); + + blobParams = + FileBlobConstructorParams(name, contentType, length, modDate, blobData); + } else { + blobParams = NormalBlobConstructorParams(contentType, length, blobData); + } + } BlobChild* actor = new BlobChild(aManager, aBlobImpl); - ParentBlobConstructorParams params(blobParams, inputStreamParams); + ParentBlobConstructorParams params(blobParams); if (NS_WARN_IF(!aManager->SendPBlobConstructor(actor, params))) { BlobChild::Destroy(actor); @@ -2865,6 +3011,7 @@ BlobChild::CreateFromParams(ChildManagerType* aManager, switch (blobParams.type()) { case AnyBlobConstructorParams::TNormalBlobConstructorParams: case AnyBlobConstructorParams::TFileBlobConstructorParams: + case AnyBlobConstructorParams::TSameProcessBlobConstructorParams: case AnyBlobConstructorParams::TMysteryBlobConstructorParams: { return new BlobChild(aManager, aParams); } @@ -2896,8 +3043,6 @@ BlobChild::SendSliceConstructor(ChildManagerType* aManager, MOZ_ASSERT(aRemoteBlobSliceImpl); MOZ_ASSERT(aParams.blobParams().type() == AnyBlobConstructorParams::TSlicedBlobConstructorParams); - MOZ_ASSERT(aParams.optionalInputStreamParams().type() == - OptionalInputStreamParams::Tvoid_t); const nsID& id = aParams.blobParams().get_SlicedBlobConstructorParams().id(); @@ -2933,8 +3078,7 @@ BlobChild::MaybeGetActorFromRemoteBlob(nsIRemoteBlob* aRemoteBlob, actor = new BlobChild(aManager, actor); ParentBlobConstructorParams params( - KnownBlobConstructorParams(actor->ParentID()) /* blobParams */, - void_t() /* optionalInputStream */); + KnownBlobConstructorParams(actor->ParentID())); aManager->SendPBlobConstructor(actor, params); @@ -2963,8 +3107,7 @@ BlobChild::MaybeGetActorFromRemoteBlob(nsIRemoteBlob* aRemoteBlob, actor = new BlobChild(aManager, actor); ParentBlobConstructorParams params( - KnownBlobConstructorParams(actor->ParentID()) /* blobParams */, - void_t() /* optionalInputStream */); + KnownBlobConstructorParams(actor->ParentID())); aManager->SendPBlobConstructor(actor, params); @@ -3019,8 +3162,11 @@ BlobChild::SetMysteryBlobInfo(const nsString& aName, mBlobImpl->SetLazyData(aName, aContentType, aLength, aLastModifiedDate); - FileBlobConstructorParams params(aName, aContentType, aLength, - aLastModifiedDate); + FileBlobConstructorParams params(aName, + aContentType, + aLength, + aLastModifiedDate, + void_t() /* optionalBlobData */); return SendResolveMystery(params); } @@ -3036,7 +3182,9 @@ BlobChild::SetMysteryBlobInfo(const nsString& aContentType, uint64_t aLength) mBlobImpl->SetLazyData(voidString, aContentType, aLength, UINT64_MAX); - NormalBlobConstructorParams params(aContentType, aLength); + NormalBlobConstructorParams params(aContentType, + aLength, + void_t() /* optionalBlobData */); return SendResolveMystery(params); } @@ -3326,37 +3474,56 @@ BlobParent::GetOrCreateFromImpl(ParentManagerType* aManager, } } - // All blobs shared between processes must be immutable. + // All blobs shared between threads or processes must be immutable. if (NS_WARN_IF(NS_FAILED(aBlobImpl->SetMutable(false)))) { return nullptr; } + const bool isSameProcessActor = ActorManagerIsSameProcess(aManager); + AnyBlobConstructorParams blobParams; - if (aBlobImpl->IsSizeUnknown() || aBlobImpl->IsDateUnknown()) { - // We don't want to call GetSize or GetLastModifiedDate yet since that may - // stat a file on the this thread. Instead we'll learn the size lazily from - // the other side. - blobParams = MysteryBlobConstructorParams(); + bool isSnapshot; + + if (isSameProcessActor) { + nsCOMPtr snapshot = do_QueryInterface(aBlobImpl); + isSnapshot = !!snapshot; } else { - nsString contentType; - aBlobImpl->GetType(contentType); + isSnapshot = false; + } - ErrorResult rv; - uint64_t length = aBlobImpl->GetSize(rv); - MOZ_ASSERT(!rv.Failed()); + if (isSameProcessActor && !isSnapshot) { + nsRefPtr sameProcessImpl = aBlobImpl; + auto addRefedFileImpl = + reinterpret_cast(sameProcessImpl.forget().take()); - if (aBlobImpl->IsFile()) { - nsString name; - aBlobImpl->GetName(name); + blobParams = SameProcessBlobConstructorParams(addRefedFileImpl); + } else { + if (aBlobImpl->IsSizeUnknown() || aBlobImpl->IsDateUnknown()) { + // We don't want to call GetSize or GetLastModifiedDate yet since that may + // stat a file on the this thread. Instead we'll learn the size lazily + // from the other side. + blobParams = MysteryBlobConstructorParams(); + } else { + nsString contentType; + aBlobImpl->GetType(contentType); - uint64_t modDate = aBlobImpl->GetLastModified(rv); + ErrorResult rv; + uint64_t length = aBlobImpl->GetSize(rv); MOZ_ASSERT(!rv.Failed()); - blobParams = - FileBlobConstructorParams(name, contentType, length, modDate); - } else { - blobParams = NormalBlobConstructorParams(contentType, length); + if (aBlobImpl->IsFile()) { + nsString name; + aBlobImpl->GetName(name); + + uint64_t modDate = aBlobImpl->GetLastModified(rv); + MOZ_ASSERT(!rv.Failed()); + + blobParams = + FileBlobConstructorParams(name, contentType, length, modDate, void_t()); + } else { + blobParams = NormalBlobConstructorParams(contentType, length, void_t()); + } } } @@ -3397,14 +3564,21 @@ BlobParent::CreateFromParams(ParentManagerType* aManager, case AnyBlobConstructorParams::TNormalBlobConstructorParams: case AnyBlobConstructorParams::TFileBlobConstructorParams: { - if (aParams.optionalInputStreamParams().type() != - OptionalInputStreamParams::TInputStreamParams) { + const OptionalBlobData& optionalBlobData = + blobParams.type() == + AnyBlobConstructorParams::TNormalBlobConstructorParams ? + blobParams.get_NormalBlobConstructorParams().optionalBlobData() : + blobParams.get_FileBlobConstructorParams().optionalBlobData(); + + if (NS_WARN_IF(optionalBlobData.type() != OptionalBlobData::TBlobData)) { ASSERT_UNLESS_FUZZING(); return nullptr; } nsRefPtr blobImpl = - CreateBlobImplFromParams(aParams, ActorManagerIsSameProcess(aManager)); + CreateBlobImpl(aParams, + optionalBlobData.get_BlobData(), + ActorManagerIsSameProcess(aManager)); if (NS_WARN_IF(!blobImpl)) { ASSERT_UNLESS_FUZZING(); return nullptr; @@ -3424,12 +3598,6 @@ BlobParent::CreateFromParams(ParentManagerType* aManager, } case AnyBlobConstructorParams::TSlicedBlobConstructorParams: { - if (aParams.optionalInputStreamParams().type() != - OptionalInputStreamParams::Tvoid_t) { - ASSERT_UNLESS_FUZZING(); - return nullptr; - } - const SlicedBlobConstructorParams& params = blobParams.get_SlicedBlobConstructorParams(); @@ -3472,12 +3640,6 @@ BlobParent::CreateFromParams(ParentManagerType* aManager, } case AnyBlobConstructorParams::TKnownBlobConstructorParams: { - if (aParams.optionalInputStreamParams().type() != - OptionalInputStreamParams::Tvoid_t) { - ASSERT_UNLESS_FUZZING(); - return nullptr; - } - const KnownBlobConstructorParams& params = blobParams.get_KnownBlobConstructorParams(); @@ -3494,6 +3656,29 @@ BlobParent::CreateFromParams(ParentManagerType* aManager, return new BlobParent(aManager, blobImpl, idTableEntry); } + case AnyBlobConstructorParams::TSameProcessBlobConstructorParams: { + if (NS_WARN_IF(!ActorManagerIsSameProcess(aManager))) { + ASSERT_UNLESS_FUZZING(); + return nullptr; + } + + const SameProcessBlobConstructorParams& params = + blobParams.get_SameProcessBlobConstructorParams(); + + nsRefPtr blobImpl = + dont_AddRef(reinterpret_cast(params.addRefedFileImpl())); + MOZ_ASSERT(blobImpl); + + nsID id; + MOZ_ALWAYS_TRUE(NS_SUCCEEDED(gUUIDGenerator->GenerateUUIDInPlace(&id))); + + nsRefPtr idTableEntry = + IDTableEntry::Create(id, ActorManagerProcessID(aManager), blobImpl); + MOZ_ASSERT(idTableEntry); + + return new BlobParent(aManager, blobImpl, idTableEntry); + } + default: MOZ_CRASH("Unknown params!"); } diff --git a/dom/ipc/DOMTypes.ipdlh b/dom/ipc/DOMTypes.ipdlh index 0dc59456238..d1a2c73c323 100644 --- a/dom/ipc/DOMTypes.ipdlh +++ b/dom/ipc/DOMTypes.ipdlh @@ -5,7 +5,6 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ include protocol PBlob; -include InputStreamParams; using struct mozilla::void_t from "ipc/IPCMessageUtils.h"; @@ -13,6 +12,9 @@ using struct mozilla::void_t using struct mozilla::SerializedStructuredCloneBuffer from "ipc/IPCMessageUtils.h"; +using struct nsID + from "nsID.h"; + namespace mozilla { namespace dom { @@ -28,10 +30,35 @@ struct ClonedMessageData PBlob[] blobs; }; +union BlobData +{ + // For remote blobs. + nsID; + + // For memory-backed blobs. + uint8_t[]; + + // For file snapshots, this is an nsIInputStream. + intptr_t; + + // For multiplex blobs. + BlobData[]; +}; + +union OptionalBlobData +{ + BlobData; + void_t; +}; + struct NormalBlobConstructorParams { nsString contentType; uint64_t length; + + // This must be of type BlobData in a child->parent message, and will always + // be of type void_t in a parent->child message. + OptionalBlobData optionalBlobData; }; struct FileBlobConstructorParams @@ -40,6 +67,10 @@ struct FileBlobConstructorParams nsString contentType; uint64_t length; uint64_t modDate; + + // This must be of type BlobData in a child->parent message, and will always + // be of type void_t in a parent->child message. + OptionalBlobData optionalBlobData; }; struct SlicedBlobConstructorParams @@ -61,11 +92,20 @@ struct KnownBlobConstructorParams nsID id; }; +// This may only be used for same-process inter-thread communication! +struct SameProcessBlobConstructorParams +{ + // This member should be reinterpret_cast'd to mozilla::dom::FileImpl. It + // carries a reference. + intptr_t addRefedFileImpl; +}; + union AnyBlobConstructorParams { // These types may be sent to/from parent and child. NormalBlobConstructorParams; FileBlobConstructorParams; + SameProcessBlobConstructorParams; // This type may only be sent from parent to child. MysteryBlobConstructorParams; @@ -87,14 +127,6 @@ struct ParentBlobConstructorParams { // May not be MysteryBlobConstructorParams. AnyBlobConstructorParams blobParams; - - // This must be of type void_t for: - // - SlicedBlobConstructorParams - // - KnownBlobConstructorParams - // This must be of type InputStreamParams for: - // - NormalBlobConstructorParams - // - FileBlobConstructorParams - OptionalInputStreamParams optionalInputStreamParams; }; union BlobConstructorParams diff --git a/ipc/glue/InputStreamParams.ipdlh b/ipc/glue/InputStreamParams.ipdlh index 936d0228bcd..752960d4b8a 100644 --- a/ipc/glue/InputStreamParams.ipdlh +++ b/ipc/glue/InputStreamParams.ipdlh @@ -2,13 +2,13 @@ * 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/. */ - -using struct mozilla::void_t from "ipc/IPCMessageUtils.h"; include protocol PBlob; include protocol PFileDescriptorSet; -using struct nsID - from "nsID.h"; +include DOMTypes; + +using struct mozilla::void_t + from "ipc/IPCMessageUtils.h"; namespace mozilla { namespace ipc { diff --git a/testing/specialpowers/content/specialpowersAPI.js b/testing/specialpowers/content/specialpowersAPI.js index 133a1786a73..4e34fe2bd8a 100644 --- a/testing/specialpowers/content/specialpowersAPI.js +++ b/testing/specialpowers/content/specialpowersAPI.js @@ -18,6 +18,8 @@ Cu.import("resource://gre/modules/Services.jsm"); Cu.import("resource://gre/modules/PrivateBrowsingUtils.jsm"); Cu.import("resource://gre/modules/XPCOMUtils.jsm"); +Cu.importGlobalProperties(["File"]); + // Allow stuff from this scope to be accessed from non-privileged scopes. This // would crash if used outside of automation. Cu.forcePermissiveCOWs(); @@ -1877,6 +1879,10 @@ SpecialPowersAPI.prototype = { let msg = { op: op, uri: uri, appId: appId, inBrowser: inBrowser, id: id }; this._sendAsyncMessage(messageTopic, msg); }, + + createDOMFile: function(path, options) { + return new File(path, options); + }, }; this.SpecialPowersAPI = SpecialPowersAPI;