author | Iris Hsiao <ihsiao@mozilla.com> |
Wed, 22 Feb 2017 16:19:50 +0800 | |
changeset 344212 | 94e98e7e32276f5e55c452d93e09e8a82101b601 |
parent 344211 | 13a54cc4d8be58afabb3078c6ba5f4241dd396f4 |
child 344213 | e3b42bc5273c59b8dd1057f518ce32159b665ca1 |
push id | 31402 |
push user | cbook@mozilla.com |
push date | Wed, 22 Feb 2017 13:33:50 +0000 |
treeherder | mozilla-central@f5372cb6c3c7 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
bugs | 1339710 |
milestone | 54.0a1 |
backs out | 7c0ec55b44dc159a6eb0c9ff65b922952e6e4f11 cbb4e1c2aada22f98894e2fe94ef9a0e7bde4293 c685dca493f56fdb5c4c63405fb3b501b0365518 4761a15d45fdbe24993fdafa38f7bd457300dcf3 7561692c1bc7ae4e4f7d968412e65015c611050f |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/dom/file/FileBlobImpl.cpp +++ b/dom/file/FileBlobImpl.cpp @@ -226,31 +226,23 @@ const uint32_t sFileStreamFlags = nsIFileInputStream::CLOSE_ON_EOF | nsIFileInputStream::REOPEN_ON_REWIND | nsIFileInputStream::DEFER_OPEN | nsIFileInputStream::SHARE_DELETE; void FileBlobImpl::GetInternalStream(nsIInputStream** aStream, ErrorResult& aRv) { - nsCOMPtr<nsIInputStream> stream; - aRv = NS_NewLocalFileInputStream(getter_AddRefs(stream), mFile, -1, -1, - sFileStreamFlags); - if (NS_WARN_IF(aRv.Failed())) { + if (mWholeFile) { + aRv = NS_NewLocalFileInputStream(aStream, mFile, -1, -1, sFileStreamFlags); return; } - if (mWholeFile) { - stream.forget(aStream); - return; - } - - RefPtr<SlicedInputStream> slicedInputStream = - new SlicedInputStream(stream, mStart, mLength); - slicedInputStream.forget(aStream); + aRv = NS_NewPartialLocalFileInputStream(aStream, mFile, mStart, mLength, + -1, -1, sFileStreamFlags); } bool FileBlobImpl::IsDirectory() const { bool isDirectory = false; if (mFile) { mFile->IsDirectory(&isDirectory);
--- a/ipc/glue/InputStreamParams.ipdlh +++ b/ipc/glue/InputStreamParams.ipdlh @@ -24,16 +24,23 @@ struct StringInputStreamParams struct FileInputStreamParams { uint32_t fileDescriptorIndex; int32_t behaviorFlags; int32_t ioFlags; }; +struct PartialFileInputStreamParams +{ + FileInputStreamParams fileStreamParams; + uint64_t begin; + uint64_t length; +}; + struct TemporaryFileInputStreamParams { uint32_t fileDescriptorIndex; uint64_t startPos; uint64_t endPos; }; struct MultiplexInputStreamParams @@ -52,36 +59,27 @@ struct RemoteInputStreamParams // XXX This may only be used for same-process inter-thread communication! The // value should be reinterpret_cast'd to nsIInputStream. It carries a // reference. struct SameProcessInputStreamParams { intptr_t addRefedInputStream; }; -struct SlicedInputStreamParams -{ - InputStreamParams stream; - uint64_t start; - uint64_t length; - uint64_t curPos; - bool closed; -}; - union InputStreamParams { StringInputStreamParams; FileInputStreamParams; + PartialFileInputStreamParams; TemporaryFileInputStreamParams; BufferedInputStreamParams; MIMEInputStreamParams; MultiplexInputStreamParams; RemoteInputStreamParams; SameProcessInputStreamParams; - SlicedInputStreamParams; }; union OptionalInputStreamParams { void_t; InputStreamParams; };
--- a/ipc/glue/InputStreamUtils.cpp +++ b/ipc/glue/InputStreamUtils.cpp @@ -17,24 +17,24 @@ #include "nsID.h" #include "nsIXULRuntime.h" #include "nsMIMEInputStream.h" #include "nsMultiplexInputStream.h" #include "nsNetCID.h" #include "nsStringStream.h" #include "nsTemporaryFileInputStream.h" #include "nsXULAppAPI.h" -#include "SlicedInputStream.h" using namespace mozilla::dom; namespace { NS_DEFINE_CID(kStringInputStreamCID, NS_STRINGINPUTSTREAM_CID); NS_DEFINE_CID(kFileInputStreamCID, NS_LOCALFILEINPUTSTREAM_CID); +NS_DEFINE_CID(kPartialFileInputStreamCID, NS_PARTIALLOCALFILEINPUTSTREAM_CID); NS_DEFINE_CID(kBufferedInputStreamCID, NS_BUFFEREDINPUTSTREAM_CID); NS_DEFINE_CID(kMIMEInputStreamCID, NS_MIMEINPUTSTREAM_CID); NS_DEFINE_CID(kMultiplexInputStreamCID, NS_MULTIPLEXINPUTSTREAM_CID); } // namespace namespace mozilla { namespace ipc { @@ -85,16 +85,20 @@ DeserializeInputStream(const InputStream case InputStreamParams::TStringInputStreamParams: serializable = do_CreateInstance(kStringInputStreamCID); break; case InputStreamParams::TFileInputStreamParams: serializable = do_CreateInstance(kFileInputStreamCID); break; + case InputStreamParams::TPartialFileInputStreamParams: + serializable = do_CreateInstance(kPartialFileInputStreamCID); + break; + case InputStreamParams::TTemporaryFileInputStreamParams: serializable = new nsTemporaryFileInputStream(); break; case InputStreamParams::TBufferedInputStreamParams: serializable = do_CreateInstance(kBufferedInputStreamCID); break; @@ -139,20 +143,16 @@ DeserializeInputStream(const InputStream stream = dont_AddRef( reinterpret_cast<nsIInputStream*>(params.addRefedInputStream())); MOZ_ASSERT(stream); return stream.forget(); } - case InputStreamParams::TSlicedInputStreamParams: - serializable = new SlicedInputStream(); - break; - default: MOZ_ASSERT(false, "Unknown params!"); return nullptr; } MOZ_ASSERT(serializable); if (!serializable->Deserialize(aParams, aFileDescriptors)) {
--- a/netwerk/base/nsFileStreams.cpp +++ b/netwerk/base/nsFileStreams.cpp @@ -248,16 +248,19 @@ nsresult nsFileStreamBase::ReadSegments(nsWriteSegmentFun aWriter, void* aClosure, uint32_t aCount, uint32_t* aResult) { // ReadSegments is not implemented because it would be inefficient when // the writer does not consume all data. If you want to call ReadSegments, // wrap a BufferedInputStream around the file stream. That will call // Read(). + // If this is ever implemented you might need to modify + // nsPartialFileInputStream::ReadSegments + return NS_ERROR_NOT_IMPLEMENTED; } nsresult nsFileStreamBase::IsNonBlocking(bool *aNonBlocking) { *aNonBlocking = false; return NS_OK; @@ -680,16 +683,234 @@ nsFileInputStream::Deserialize(const Inp Maybe<uint64_t> nsFileInputStream::ExpectedSerializedLength() { return Nothing(); } //////////////////////////////////////////////////////////////////////////////// +// nsPartialFileInputStream + +NS_IMPL_ADDREF_INHERITED(nsPartialFileInputStream, nsFileStreamBase) +NS_IMPL_RELEASE_INHERITED(nsPartialFileInputStream, nsFileStreamBase) + +NS_IMPL_CLASSINFO(nsPartialFileInputStream, nullptr, nsIClassInfo::THREADSAFE, + NS_PARTIALLOCALFILEINPUTSTREAM_CID) + +// Don't forward to nsFileInputStream as we don't want to QI to +// nsIFileInputStream +NS_INTERFACE_MAP_BEGIN(nsPartialFileInputStream) + NS_INTERFACE_MAP_ENTRY(nsIInputStream) + NS_INTERFACE_MAP_ENTRY(nsIPartialFileInputStream) + NS_INTERFACE_MAP_ENTRY(nsILineInputStream) + NS_INTERFACE_MAP_ENTRY(nsIIPCSerializableInputStream) + NS_IMPL_QUERY_CLASSINFO(nsPartialFileInputStream) +NS_INTERFACE_MAP_END_INHERITING(nsFileStreamBase) + +NS_IMPL_CI_INTERFACE_GETTER(nsPartialFileInputStream, + nsIInputStream, + nsIPartialFileInputStream, + nsISeekableStream, + nsILineInputStream) + +nsresult +nsPartialFileInputStream::Create(nsISupports *aOuter, REFNSIID aIID, + void **aResult) +{ + NS_ENSURE_NO_AGGREGATION(aOuter); + + nsPartialFileInputStream* stream = new nsPartialFileInputStream(); + + NS_ADDREF(stream); + nsresult rv = stream->QueryInterface(aIID, aResult); + NS_RELEASE(stream); + return rv; +} + +NS_IMETHODIMP +nsPartialFileInputStream::Init(nsIFile* aFile, uint64_t aStart, + uint64_t aLength, int32_t aIOFlags, + int32_t aPerm, int32_t aBehaviorFlags) +{ + mStart = aStart; + mLength = aLength; + mPosition = 0; + + nsresult rv = nsFileInputStream::Init(aFile, aIOFlags, aPerm, + aBehaviorFlags); + + // aFile is a partial file, it must exist. + NS_ENSURE_SUCCESS(rv, rv); + + mDeferredSeek = true; + + return rv; +} + +NS_IMETHODIMP +nsPartialFileInputStream::Tell(int64_t *aResult) +{ + int64_t tell = 0; + + nsresult rv = DoPendingSeek(); + NS_ENSURE_SUCCESS(rv, rv); + + rv = nsFileInputStream::Tell(&tell); + NS_ENSURE_SUCCESS(rv, rv); + + + *aResult = tell - mStart; + return rv; +} + +NS_IMETHODIMP +nsPartialFileInputStream::Available(uint64_t* aResult) +{ + uint64_t available = 0; + + nsresult rv = DoPendingSeek(); + NS_ENSURE_SUCCESS(rv, rv); + + rv = nsFileInputStream::Available(&available); + NS_ENSURE_SUCCESS(rv, rv); + + *aResult = TruncateSize(available); + return rv; +} + +NS_IMETHODIMP +nsPartialFileInputStream::Read(char* aBuf, uint32_t aCount, uint32_t* aResult) +{ + nsresult rv = DoPendingSeek(); + NS_ENSURE_SUCCESS(rv, rv); + + uint32_t readsize = (uint32_t) TruncateSize(aCount); + if (readsize == 0 && mBehaviorFlags & CLOSE_ON_EOF) { + Close(); + *aResult = 0; + return NS_OK; + } + + rv = nsFileInputStream::Read(aBuf, readsize, aResult); + NS_ENSURE_SUCCESS(rv, rv); + + mPosition += readsize; + return rv; +} + +NS_IMETHODIMP +nsPartialFileInputStream::Seek(int32_t aWhence, int64_t aOffset) +{ + nsresult rv = DoPendingSeek(); + NS_ENSURE_SUCCESS(rv, rv); + + int64_t offset; + switch (aWhence) { + case NS_SEEK_SET: + offset = mStart + aOffset; + break; + case NS_SEEK_CUR: + offset = mStart + mPosition + aOffset; + break; + case NS_SEEK_END: + offset = mStart + mLength + aOffset; + break; + default: + return NS_ERROR_ILLEGAL_VALUE; + } + + if (offset < (int64_t)mStart || offset > (int64_t)(mStart + mLength)) { + return NS_ERROR_INVALID_ARG; + } + + rv = nsFileInputStream::Seek(NS_SEEK_SET, offset); + NS_ENSURE_SUCCESS(rv, rv); + + mPosition = offset - mStart; + return rv; +} + +void +nsPartialFileInputStream::Serialize(InputStreamParams& aParams, + FileDescriptorArray& aFileDescriptors) +{ + // Serialize the base class first. + InputStreamParams fileParams; + nsFileInputStream::Serialize(fileParams, aFileDescriptors); + + PartialFileInputStreamParams params; + + params.fileStreamParams() = fileParams.get_FileInputStreamParams(); + params.begin() = mStart; + params.length() = mLength; + + aParams = params; +} + +bool +nsPartialFileInputStream::Deserialize( + const InputStreamParams& aParams, + const FileDescriptorArray& aFileDescriptors) +{ + NS_ASSERTION(!mFD, "Already have a file descriptor?!"); + NS_ASSERTION(!mStart, "Already have a start?!"); + NS_ASSERTION(!mLength, "Already have a length?!"); + NS_ASSERTION(!mPosition, "Already have a position?!"); + + if (aParams.type() != InputStreamParams::TPartialFileInputStreamParams) { + NS_WARNING("Received unknown parameters from the other process!"); + return false; + } + + const PartialFileInputStreamParams& params = + aParams.get_PartialFileInputStreamParams(); + + // Deserialize the base class first. + InputStreamParams fileParams(params.fileStreamParams()); + if (!nsFileInputStream::Deserialize(fileParams, aFileDescriptors)) { + NS_WARNING("Base class deserialize failed!"); + return false; + } + + NS_ASSERTION(mFD, "Must have a file descriptor now!"); + + mStart = params.begin(); + mLength = params.length(); + mPosition = 0; + + if (!mStart) { + return true; + } + + // XXX This is so broken. Main thread IO alert. + return NS_SUCCEEDED(nsFileInputStream::Seek(NS_SEEK_SET, mStart)); +} + +Maybe<uint64_t> +nsPartialFileInputStream::ExpectedSerializedLength() +{ + return Some(mLength); +} + + +nsresult +nsPartialFileInputStream::DoPendingSeek() +{ + if (!mDeferredSeek) { + return NS_OK; + } + + mDeferredSeek = false; + + // This is the first time to open the file, don't clear mLinebuffer. + // mLineBuffer might be already initialized by ReadLine(). + return nsFileInputStream::SeekInternal(NS_SEEK_SET, mStart, false); +} +//////////////////////////////////////////////////////////////////////////////// // nsFileOutputStream NS_IMPL_ISUPPORTS_INHERITED(nsFileOutputStream, nsFileStreamBase, nsIOutputStream, nsIFileOutputStream) nsresult
--- a/netwerk/base/nsFileStreams.h +++ b/netwerk/base/nsFileStreams.h @@ -170,16 +170,57 @@ 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); }; //////////////////////////////////////////////////////////////////////////////// +class nsPartialFileInputStream : public nsFileInputStream, + public nsIPartialFileInputStream +{ +public: + using nsFileInputStream::Init; + using nsFileInputStream::Read; + NS_DECL_ISUPPORTS_INHERITED + NS_DECL_NSIPARTIALFILEINPUTSTREAM + NS_DECL_NSIIPCSERIALIZABLEINPUTSTREAM + + nsPartialFileInputStream() + : mStart(0), mLength(0), mPosition(0), mDeferredSeek(false) + { } + + NS_IMETHOD Tell(int64_t *aResult) override; + NS_IMETHOD Available(uint64_t *aResult) override; + NS_IMETHOD Read(char* aBuf, uint32_t aCount, uint32_t* aResult) override; + NS_IMETHOD Seek(int32_t aWhence, int64_t aOffset) override; + + static nsresult + Create(nsISupports *aOuter, REFNSIID aIID, void **aResult); + +protected: + ~nsPartialFileInputStream() + { } + + inline nsresult DoPendingSeek(); + +private: + uint64_t TruncateSize(uint64_t aSize) { + return std::min<uint64_t>(mLength - mPosition, aSize); + } + + uint64_t mStart; + uint64_t mLength; + uint64_t mPosition; + bool mDeferredSeek; +}; + +//////////////////////////////////////////////////////////////////////////////// + class nsFileOutputStream : public nsFileStreamBase, public nsIFileOutputStream { public: NS_DECL_ISUPPORTS_INHERITED NS_DECL_NSIFILEOUTPUTSTREAM NS_FORWARD_NSIOUTPUTSTREAM(nsFileStreamBase::)
--- a/netwerk/base/nsIFileStreams.idl +++ b/netwerk/base/nsIFileStreams.idl @@ -133,16 +133,47 @@ interface nsIFileOutputStream : nsIOutpu * happen when this flag is not set would happen during the * first write, and if the file is to be created, then it will not * appear on the disk until the first write. */ const long DEFER_OPEN = 1<<0; }; /** + * An input stream that allows you to read from a slice of a file. + */ +[scriptable, uuid(3ce03a2f-97f7-4375-b6bb-1788a60cad3b)] +interface nsIPartialFileInputStream : nsISupports +{ + /** + * Initialize with a file and new start/end positions. Both start and + * start+length must be smaller than the size of the file. Not doing so + * will lead to undefined behavior. + * You must initialize the stream, and only initialize it once, before it + * can be used. + * + * @param file file to read from + * @param start start offset of slice to read. Must be smaller + * than the size of the file. + * @param length length of slice to read. Must be small enough that + * start+length is smaller than the size of the file. + * @param ioFlags file open flags listed in prio.h (see + * PR_Open documentation) or -1 to open the + * file in default mode (PR_RDONLY). + * @param perm file mode bits listed in prio.h or -1 to + * use the default value (0) + * @param behaviorFlags flags specifying various behaviors of the class + * (see enumerations in nsIFileInputStream) + */ + void init(in nsIFile file, in unsigned long long start, + in unsigned long long length, + in long ioFlags, in long perm, in long behaviorFlags); +}; + +/** * A stream that allows you to read from a file or stream to a file. */ [scriptable, uuid(82cf605a-8393-4550-83ab-43cd5578e006)] interface nsIFileStream : nsISupports { /** * @param file file to read from or stream to * @param ioFlags file open flags listed in prio.h (see
--- a/netwerk/base/nsNetUtil.cpp +++ b/netwerk/base/nsNetUtil.cpp @@ -984,16 +984,36 @@ NS_ExtractCharsetFromContentType(const n return util->ExtractCharsetFromContentType(rawContentType, contentCharset, charsetStart, charsetEnd, hadCharset); } nsresult +NS_NewPartialLocalFileInputStream(nsIInputStream **result, + nsIFile *file, + uint64_t offset, + uint64_t length, + int32_t ioFlags /* = -1 */, + int32_t perm /* = -1 */, + int32_t behaviorFlags /* = 0 */) +{ + nsresult rv; + nsCOMPtr<nsIPartialFileInputStream> in = + do_CreateInstance(NS_PARTIALLOCALFILEINPUTSTREAM_CONTRACTID, &rv); + if (NS_SUCCEEDED(rv)) { + rv = in->Init(file, offset, length, ioFlags, perm, behaviorFlags); + if (NS_SUCCEEDED(rv)) + rv = CallQueryInterface(in, result); + } + return rv; +} + +nsresult NS_NewAtomicFileOutputStream(nsIOutputStream **result, nsIFile *file, int32_t ioFlags /* = -1 */, int32_t perm /* = -1 */, int32_t behaviorFlags /* = 0 */) { nsresult rv; nsCOMPtr<nsIFileOutputStream> out =
--- a/netwerk/base/nsNetUtil.h +++ b/netwerk/base/nsNetUtil.h @@ -474,16 +474,24 @@ nsresult NS_ExtractCharsetFromContentTyp int32_t *charsetEnd); nsresult NS_NewLocalFileInputStream(nsIInputStream **result, nsIFile *file, int32_t ioFlags = -1, int32_t perm = -1, int32_t behaviorFlags = 0); +nsresult NS_NewPartialLocalFileInputStream(nsIInputStream **result, + nsIFile *file, + uint64_t offset, + uint64_t length, + int32_t ioFlags = -1, + int32_t perm = -1, + int32_t behaviorFlags = 0); + nsresult NS_NewLocalFileOutputStream(nsIOutputStream **result, nsIFile *file, int32_t ioFlags = -1, int32_t perm = -1, int32_t behaviorFlags = 0); // returns a file output stream which can be QI'ed to nsISafeOutputStream. nsresult NS_NewAtomicFileOutputStream(nsIOutputStream **result,
--- a/netwerk/build/nsNetCID.h +++ b/netwerk/build/nsNetCID.h @@ -386,16 +386,26 @@ #define NS_LOCALFILEOUTPUTSTREAM_CID \ { /* c272fee0-c7e9-11d3-8cda-0060b0fc14a3 */ \ 0xc272fee0, \ 0xc7e9, \ 0x11d3, \ {0x8c, 0xda, 0x00, 0x60, 0xb0, 0xfc, 0x14, 0xa3} \ } +#define NS_PARTIALLOCALFILEINPUTSTREAM_CONTRACTID \ + "@mozilla.org/network/partial-file-input-stream;1" +#define NS_PARTIALLOCALFILEINPUTSTREAM_CID \ +{ /* 8738afd6-162a-418d-a99b-75b3a6b10a56 */ \ + 0x8738afd6, \ + 0x162a, \ + 0x418d, \ + {0xa9, 0x9b, 0x75, 0xb3, 0xa6, 0xb1, 0x0a, 0x56} \ +} + #define NS_BUFFEREDINPUTSTREAM_CONTRACTID \ "@mozilla.org/network/buffered-input-stream;1" #define NS_BUFFEREDINPUTSTREAM_CID \ { /* 9226888e-da08-11d3-8cda-0060b0fc14a3 */ \ 0x9226888e, \ 0xda08, \ 0x11d3, \ {0x8c, 0xda, 0x00, 0x60, 0xb0, 0xfc, 0x14, 0xa3} \
--- a/netwerk/build/nsNetModule.cpp +++ b/netwerk/build/nsNetModule.cpp @@ -751,16 +751,17 @@ NS_DEFINE_NAMED_CID(NS_BACKGROUNDFILESAV NS_DEFINE_NAMED_CID(NS_BACKGROUNDFILESAVERSTREAMLISTENER_CID); NS_DEFINE_NAMED_CID(NS_SYNCSTREAMLISTENER_CID); NS_DEFINE_NAMED_CID(NS_REQUESTOBSERVERPROXY_CID); NS_DEFINE_NAMED_CID(NS_SIMPLESTREAMLISTENER_CID); NS_DEFINE_NAMED_CID(NS_STREAMLISTENERTEE_CID); NS_DEFINE_NAMED_CID(NS_LOADGROUP_CID); NS_DEFINE_NAMED_CID(NS_LOCALFILEINPUTSTREAM_CID); NS_DEFINE_NAMED_CID(NS_LOCALFILEOUTPUTSTREAM_CID); +NS_DEFINE_NAMED_CID(NS_PARTIALLOCALFILEINPUTSTREAM_CID); NS_DEFINE_NAMED_CID(NS_ATOMICLOCALFILEOUTPUTSTREAM_CID); NS_DEFINE_NAMED_CID(NS_SAFELOCALFILEOUTPUTSTREAM_CID); NS_DEFINE_NAMED_CID(NS_LOCALFILESTREAM_CID); NS_DEFINE_NAMED_CID(NS_INCREMENTALDOWNLOAD_CID); NS_DEFINE_NAMED_CID(NS_STDURLPARSER_CID); NS_DEFINE_NAMED_CID(NS_NOAUTHURLPARSER_CID); NS_DEFINE_NAMED_CID(NS_AUTHURLPARSER_CID); NS_DEFINE_NAMED_CID(NS_STANDARDURL_CID); @@ -903,16 +904,17 @@ static const mozilla::Module::CIDEntry k mozilla::net::BackgroundFileSaverStreamListenerConstructor }, { &kNS_SYNCSTREAMLISTENER_CID, false, nullptr, nsSyncStreamListenerConstructor }, { &kNS_REQUESTOBSERVERPROXY_CID, false, nullptr, nsRequestObserverProxyConstructor }, { &kNS_SIMPLESTREAMLISTENER_CID, false, nullptr, nsSimpleStreamListenerConstructor }, { &kNS_STREAMLISTENERTEE_CID, false, nullptr, nsStreamListenerTeeConstructor }, { &kNS_LOADGROUP_CID, false, nullptr, nsLoadGroupConstructor }, { &kNS_LOCALFILEINPUTSTREAM_CID, false, nullptr, nsFileInputStream::Create }, { &kNS_LOCALFILEOUTPUTSTREAM_CID, false, nullptr, nsFileOutputStream::Create }, + { &kNS_PARTIALLOCALFILEINPUTSTREAM_CID, false, nullptr, nsPartialFileInputStream::Create }, { &kNS_ATOMICLOCALFILEOUTPUTSTREAM_CID, false, nullptr, nsAtomicFileOutputStreamConstructor }, { &kNS_SAFELOCALFILEOUTPUTSTREAM_CID, false, nullptr, nsSafeFileOutputStreamConstructor }, { &kNS_LOCALFILESTREAM_CID, false, nullptr, nsFileStreamConstructor }, { &kNS_INCREMENTALDOWNLOAD_CID, false, nullptr, net_NewIncrementalDownload }, { &kNS_STDURLPARSER_CID, false, nullptr, nsStdURLParserConstructor }, { &kNS_NOAUTHURLPARSER_CID, false, nullptr, nsNoAuthURLParserConstructor }, { &kNS_AUTHURLPARSER_CID, false, nullptr, nsAuthURLParserConstructor }, { &kNS_STANDARDURL_CID, false, nullptr, nsStandardURLConstructor }, @@ -1057,16 +1059,17 @@ static const mozilla::Module::ContractID { NS_BACKGROUNDFILESAVERSTREAMLISTENER_CONTRACTID, &kNS_BACKGROUNDFILESAVERSTREAMLISTENER_CID }, { NS_SYNCSTREAMLISTENER_CONTRACTID, &kNS_SYNCSTREAMLISTENER_CID }, { NS_REQUESTOBSERVERPROXY_CONTRACTID, &kNS_REQUESTOBSERVERPROXY_CID }, { NS_SIMPLESTREAMLISTENER_CONTRACTID, &kNS_SIMPLESTREAMLISTENER_CID }, { NS_STREAMLISTENERTEE_CONTRACTID, &kNS_STREAMLISTENERTEE_CID }, { NS_LOADGROUP_CONTRACTID, &kNS_LOADGROUP_CID }, { NS_LOCALFILEINPUTSTREAM_CONTRACTID, &kNS_LOCALFILEINPUTSTREAM_CID }, { NS_LOCALFILEOUTPUTSTREAM_CONTRACTID, &kNS_LOCALFILEOUTPUTSTREAM_CID }, + { NS_PARTIALLOCALFILEINPUTSTREAM_CONTRACTID, &kNS_PARTIALLOCALFILEINPUTSTREAM_CID }, { NS_ATOMICLOCALFILEOUTPUTSTREAM_CONTRACTID, &kNS_ATOMICLOCALFILEOUTPUTSTREAM_CID }, { NS_SAFELOCALFILEOUTPUTSTREAM_CONTRACTID, &kNS_SAFELOCALFILEOUTPUTSTREAM_CID }, { NS_LOCALFILESTREAM_CONTRACTID, &kNS_LOCALFILESTREAM_CID }, { NS_INCREMENTALDOWNLOAD_CONTRACTID, &kNS_INCREMENTALDOWNLOAD_CID }, { NS_STDURLPARSER_CONTRACTID, &kNS_STDURLPARSER_CID }, { NS_NOAUTHURLPARSER_CONTRACTID, &kNS_NOAUTHURLPARSER_CID }, { NS_AUTHURLPARSER_CONTRACTID, &kNS_AUTHURLPARSER_CID }, { NS_STANDARDURL_CONTRACTID, &kNS_STANDARDURL_CID },
new file mode 100644 --- /dev/null +++ b/netwerk/test/unit/test_file_partial_inputstream.js @@ -0,0 +1,515 @@ +/* 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/. */ + +// Test nsIPartialFileInputStream +// NOTE! These tests often use do_check_true(a == b) rather than +// do_check_eq(a, b) to avoid outputting characters which confuse +// the console + +"use strict"; + +var CC = Components.Constructor; +const BinaryInputStream = CC("@mozilla.org/binaryinputstream;1", + "nsIBinaryInputStream", + "setInputStream"); +const PR_RDONLY = 0x1; // see prio.h + +// We need the profile directory so the test harness will clean up our test +// files. +do_get_profile(); + +var binary_test_file_name = "data/image.png"; +var text_test_file_name = "test_file_partial_inputstream.js"; +// This is a global variable since if it's passed as an argument stack traces +// become unreadable. +var test_file_data; + +function run_test() +{ + // Binary tests + let binaryFile = do_get_file(binary_test_file_name); + let size = binaryFile.fileSize; + // Want to make sure we're working with a large enough file + dump("**** binary file size is: " + size + " ****\n"); + do_check_true(size > 65536); + + let binaryStream = new BinaryInputStream(new_file_input_stream(binaryFile)); + test_file_data = ""; + let avail = 0; + while ((avail = binaryStream.available()) > 0) { + test_file_data += binaryStream.readBytes(avail); + } + do_check_eq(test_file_data.length, size); + binaryStream.close(); + + test_binary_portion(0, 10); + test_binary_portion(0, 20000); + test_binary_portion(0, size); + test_binary_portion(20000, 10); + test_binary_portion(20000, 20000); + test_binary_portion(20000, size-20000); + test_binary_portion(size-10, 10); + test_binary_portion(size-20000, 20000); + test_binary_portion(0, 0); + test_binary_portion(20000, 0); + test_binary_portion(size-1, 1); + + + // Text-file tests + let textFile = do_get_file(binary_test_file_name); + size = textFile.fileSize; + // Want to make sure we're working with a large enough file + dump("**** text file size is: " + size + " ****\n"); + do_check_true(size > 7000); + + let textStream = new BinaryInputStream(new_file_input_stream(textFile)); + test_file_data = ""; + while ((avail = textStream.available()) > 0) + test_file_data += textStream.readBytes(avail); + do_check_eq(test_file_data.length, size); + textStream.close(); + + test_text_portion(0, 100); + test_text_portion(0, size); + test_text_portion(5000, 1000); + test_text_portion(size-10, 10); + test_text_portion(size-5000, 5000); + test_text_portion(10, 0); + test_text_portion(size-1, 1); + + // Test auto-closing files + // Test behavior when *not* autoclosing + let tempFile = create_temp_file("01234567890123456789"); + let tempInputStream = new_partial_file_input_stream(tempFile, 5, 10); + tempInputStream.QueryInterface(Ci.nsILineInputStream); + do_check_eq(read_line_stream(tempInputStream)[1], "5678901234"); + try { + // This fails on some platforms + tempFile.remove(false); + } + catch (ex) { + } + tempInputStream.QueryInterface(Ci.nsISeekableStream); + tempInputStream.seek(SET, 1); + do_check_eq(read_line_stream(tempInputStream)[1], "678901234"); + + // Test removing the file when autoclosing + tempFile = create_temp_file("01234567890123456789"); + tempInputStream = new_partial_file_input_stream(tempFile, 5, 10, + Ci.nsIFileInputStream.CLOSE_ON_EOF | + Ci.nsIFileInputStream.REOPEN_ON_REWIND); + tempInputStream.QueryInterface(Ci.nsILineInputStream); + do_check_eq(read_line_stream(tempInputStream)[1], "5678901234"); + tempFile.remove(false); + tempInputStream.QueryInterface(Ci.nsISeekableStream); + try { + // The seek should reopen the file, which should fail. + tempInputStream.seek(SET, 1); + do_check_true(false); + } + catch (ex) { + } + + // Test editing the file when autoclosing + tempFile = create_temp_file("01234567890123456789"); + tempInputStream = new_partial_file_input_stream(tempFile, 5, 10, + Ci.nsIFileInputStream.CLOSE_ON_EOF | + Ci.nsIFileInputStream.REOPEN_ON_REWIND); + tempInputStream.QueryInterface(Ci.nsILineInputStream); + do_check_eq(read_line_stream(tempInputStream)[1], "5678901234"); + let ostream = Cc["@mozilla.org/network/file-output-stream;1"]. + createInstance(Ci.nsIFileOutputStream); + ostream.init(tempFile, 0x02 | 0x08 | 0x20, // write, create, truncate + 0o666, 0); + let newData = "abcdefghijklmnopqrstuvwxyz"; + ostream.write(newData, newData.length); + ostream.close(); + tempInputStream.QueryInterface(Ci.nsISeekableStream); + tempInputStream.seek(SET, 1); + do_check_eq(read_line_stream(tempInputStream)[1], newData.substr(6,9)); + + // Test auto-delete and auto-close together + tempFile = create_temp_file("01234567890123456789"); + tempInputStream = new_partial_file_input_stream(tempFile, 5, 10, + Ci.nsIFileInputStream.CLOSE_ON_EOF | + Ci.nsIFileInputStream.DELETE_ON_CLOSE); + tempInputStream.QueryInterface(Ci.nsILineInputStream); + do_check_eq(read_line_stream(tempInputStream)[1], "5678901234"); + do_check_false(tempFile.exists()); +} + +function test_binary_portion(start, length) { + let subFile = create_temp_file(test_file_data.substr(start, length)); + + let streamTests = [ + test_4k_read, + test_max_read, + test_seek, + test_seek_then_read, + ]; + + for (var test of streamTests) { + let fileStream = new_file_input_stream(subFile); + let partialStream = new_partial_file_input_stream(do_get_file(binary_test_file_name), + start, length); + test(fileStream, partialStream, length); + fileStream.close(); + partialStream.close(); + } +} + +function test_4k_read(fileStreamA, fileStreamB) { + fileStreamA.QueryInterface(Ci.nsISeekableStream); + fileStreamB.QueryInterface(Ci.nsISeekableStream); + let streamA = new BinaryInputStream(fileStreamA); + let streamB = new BinaryInputStream(fileStreamB); + + while(1) { + do_check_eq(fileStreamA.tell(), fileStreamB.tell()); + + let availA = streamA.available(); + let availB = streamB.available(); + do_check_eq(availA, availB); + if (availA == 0) + return; + + let readSize = availA > 4096 ? 4096 : availA; + + do_check_true(streamA.readBytes(readSize) == + streamB.readBytes(readSize)); + } +} + +function test_max_read(fileStreamA, fileStreamB) { + fileStreamA.QueryInterface(Ci.nsISeekableStream); + fileStreamB.QueryInterface(Ci.nsISeekableStream); + let streamA = new BinaryInputStream(fileStreamA); + let streamB = new BinaryInputStream(fileStreamB); + + while(1) { + do_check_eq(fileStreamA.tell(), fileStreamB.tell()); + + let availA = streamA.available(); + let availB = streamB.available(); + do_check_eq(availA, availB); + if (availA == 0) + return; + + do_check_true(streamA.readBytes(availA) == + streamB.readBytes(availB)); + } +} + +const SET = Ci.nsISeekableStream.NS_SEEK_SET; +const CUR = Ci.nsISeekableStream.NS_SEEK_CUR; +const END = Ci.nsISeekableStream.NS_SEEK_END; +function test_seek(dummy, partialFileStream, size) { + // We can't test the "real" filestream here as our existing file streams + // are very broken and allows searching past the end of the file. + + partialFileStream.QueryInterface(Ci.nsISeekableStream); + + var tests = [ + [SET, 0], + [SET, 5], + [SET, 1000], + [SET, size-10], + [SET, size-5], + [SET, size-1], + [SET, size], + [SET, size+10], + [SET, 0], + [CUR, 5], + [CUR, -5], + [SET, 5000], + [CUR, -100], + [CUR, 200], + [CUR, -5000], + [CUR, 5000], + [CUR, size * 2], + [SET, 1], + [CUR, -1], + [CUR, -1], + [CUR, -1], + [CUR, -1], + [CUR, -1], + [SET, size-1], + [CUR, 1], + [CUR, 1], + [CUR, 1], + [CUR, 1], + [CUR, 1], + [END, 0], + [END, -1], + [END, -5], + [END, -1000], + [END, -size+10], + [END, -size+5], + [END, -size+1], + [END, -size], + [END, -size-10], + [END, 10], + [CUR, 10], + [CUR, 10], + [CUR, 100], + [CUR, 1000], + [END, -1000], + [CUR, 100], + [CUR, 900], + [CUR, 100], + [CUR, 100], + ]; + + let pos = 0; + for (var test of tests) { + let didThrow = false; + try { + partialFileStream.seek(test[0], test[1]); + } + catch (ex) { + didThrow = true; + } + + let newPos = test[0] == SET ? test[1] : + test[0] == CUR ? pos + test[1] : + size + test[1]; + if (newPos > size || newPos < 0) { + do_check_true(didThrow); + } + else { + do_check_false(didThrow); + pos = newPos; + } + + do_check_eq(partialFileStream.tell(), pos); + do_check_eq(partialFileStream.available(), size - pos); + } +} + +function test_seek_then_read(fileStreamA, fileStreamB, size) { + // For now we only test seeking inside the file since our existing file + // streams behave very strange when seeking to past the end of the file. + if (size < 20000) { + return; + } + + fileStreamA.QueryInterface(Ci.nsISeekableStream); + fileStreamB.QueryInterface(Ci.nsISeekableStream); + let streamA = new BinaryInputStream(fileStreamA); + let streamB = new BinaryInputStream(fileStreamB); + + let read = {}; + + var tests = [ + [SET, 0], + [read, 1000], + [read, 1000], + [SET, 5], + [read, 1000], + [read, 5000], + [CUR, 100], + [read, 1000], + [read, 5000], + [CUR, -100], + [read, 1000], + [CUR, -100], + [read, 5000], + [END, -10], + [read, 10], + [END, -100], + [read, 101], + [CUR, -100], + [read, 10], + [SET, 0], + [read, 20000], + [read, 1], + [read, 100], + ]; + + for (var test of tests) { + if (test[0] === read) { + + let didThrowA = false; + let didThrowB = false; + + let bytesA, bytesB; + try { + bytesA = streamA.readBytes(test[1]); + } + catch (ex) { + didThrowA = true; + } + try { + bytesB = streamB.readBytes(test[1]); + } + catch (ex) { + didThrowB = true; + } + + do_check_eq(didThrowA, didThrowB); + do_check_true(bytesA == bytesB); + } + else { + fileStreamA.seek(test[0], test[1]); + fileStreamB.seek(test[0], test[1]); + } + do_check_eq(fileStreamA.tell(), fileStreamB.tell()); + do_check_eq(fileStreamA.available(), fileStreamB.available()); + } +} + +function test_text_portion(start, length) { + let subFile = create_temp_file(test_file_data.substr(start, length)); + + let streamTests = [ + test_readline, + test_seek_then_readline, + ]; + + for (var test of streamTests) { + let fileStream = new_file_input_stream(subFile) + .QueryInterface(Ci.nsILineInputStream); + let partialStream = new_partial_file_input_stream(do_get_file(binary_test_file_name), + start, length) + .QueryInterface(Ci.nsILineInputStream); + test(fileStream, partialStream, length); + fileStream.close(); + partialStream.close(); + } +} + +function test_readline(fileStreamA, fileStreamB) +{ + let moreA = true, moreB; + while(moreA) { + let lineA, lineB; + [moreA, lineA] = read_line_stream(fileStreamA); + [moreB, lineB] = read_line_stream(fileStreamB); + do_check_eq(moreA, moreB); + do_check_true(lineA.value == lineB.value); + } +} + +function test_seek_then_readline(fileStreamA, fileStreamB, size) { + // For now we only test seeking inside the file since our existing file + // streams behave very strange when seeking to past the end of the file. + if (size < 100) { + return; + } + + fileStreamA.QueryInterface(Ci.nsISeekableStream); + fileStreamB.QueryInterface(Ci.nsISeekableStream); + + let read = {}; + + var tests = [ + [SET, 0], + [read, 5], + [read, 5], + [SET, 5], + [read, 5], + [read, 15], + [CUR, 100], + [read, 5], + [read, 15], + [CUR, -100], + [read, 5], + [CUR, -100], + [read, 25], + [END, -10], + [read, 1], + [END, -50], + [read, 30], + [read, 1], + [read, 1], + [CUR, -100], + [read, 1], + [SET, 0], + [read, 10000], + [read, 1], + [read, 1], + [SET, 0], + [read, 1], + ]; + + for (var test of tests) { + if (test[0] === read) { + + for (let i = 0; i < test[1]; ++i) { + let didThrowA = false; + let didThrowB = false; + + let lineA, lineB, moreA, moreB; + try { + [moreA, lineA] = read_line_stream(fileStreamA); + } + catch (ex) { + didThrowA = true; + } + try { + [moreB, lineB] = read_line_stream(fileStreamB); + } + catch (ex) { + didThrowB = true; + } + + do_check_eq(didThrowA, didThrowB); + do_check_eq(moreA, moreB); + do_check_true(lineA == lineB); + do_check_eq(fileStreamA.tell(), fileStreamB.tell()); + do_check_eq(fileStreamA.available(), fileStreamB.available()); + if (!moreA) + break; + } + } + else { + if (!(test[0] == CUR && (test[1] > fileStreamA.available() || + test[1] < -fileStreamA.tell()))) { + fileStreamA.seek(test[0], test[1]); + fileStreamB.seek(test[0], test[1]); + do_check_eq(fileStreamA.tell(), fileStreamB.tell()); + do_check_eq(fileStreamA.available(), fileStreamB.available()); + } + } + } +} + +function read_line_stream(stream) { + let line = {}; + let more = stream.readLine(line); + return [more, line.value]; +} + +function new_file_input_stream(file) { + var stream = + Cc["@mozilla.org/network/file-input-stream;1"] + .createInstance(Ci.nsIFileInputStream); + stream.init(file, PR_RDONLY, 0, 0); + return stream.QueryInterface(Ci.nsIInputStream); +} + +function new_partial_file_input_stream(file, start, length, flags) { + var stream = + Cc["@mozilla.org/network/partial-file-input-stream;1"] + .createInstance(Ci.nsIPartialFileInputStream); + stream.init(file, start, length, PR_RDONLY, 0, flags || 0); + return stream.QueryInterface(Ci.nsIInputStream); +} + +function create_temp_file(data) { + let file = Cc["@mozilla.org/file/directory_service;1"]. + getService(Ci.nsIProperties). + get("ProfD", Ci.nsIFile); + file.append("fileinputstream-test-file.tmp"); + file.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, 0o666); + + let ostream = Cc["@mozilla.org/network/file-output-stream;1"]. + createInstance(Ci.nsIFileOutputStream); + ostream.init(file, 0x02 | 0x08 | 0x20, // write, create, truncate + 0o666, 0); + do_check_eq(ostream.write(data, data.length), data.length); + ostream.close(); + + return file; +}
--- a/netwerk/test/unit/xpcshell.ini +++ b/netwerk/test/unit/xpcshell.ini @@ -203,16 +203,17 @@ skip-if = bits != 32 [test_fallback_no-cache-entry_canceled.js] [test_fallback_no-cache-entry_passing.js] [test_fallback_redirect-to-different-origin_canceled.js] [test_fallback_redirect-to-different-origin_passing.js] [test_fallback_request-error_canceled.js] [test_fallback_request-error_passing.js] [test_fallback_response-error_canceled.js] [test_fallback_response-error_passing.js] +[test_file_partial_inputstream.js] [test_file_protocol.js] [test_filestreams.js] [test_freshconnection.js] [test_gre_resources.js] [test_gzipped_206.js] [test_head.js] [test_header_Accept-Language.js] [test_header_Accept-Language_case.js]
--- a/xpcom/io/SlicedInputStream.cpp +++ b/xpcom/io/SlicedInputStream.cpp @@ -1,106 +1,46 @@ /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* 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/. */ #include "SlicedInputStream.h" -#include "mozilla/ipc/InputStreamUtils.h" #include "nsISeekableStream.h" #include "nsStreamUtils.h" -using namespace mozilla::ipc; - -NS_IMPL_ADDREF(SlicedInputStream); -NS_IMPL_RELEASE(SlicedInputStream); - -NS_INTERFACE_MAP_BEGIN(SlicedInputStream) - NS_INTERFACE_MAP_ENTRY(nsIInputStream) - NS_INTERFACE_MAP_ENTRY(nsIAsyncInputStream) - NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsICloneableInputStream, - mWeakCloneableInputStream || !mInputStream) - NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIIPCSerializableInputStream, - mWeakIPCSerializableInputStream || !mInputStream) - NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsISeekableStream, - mWeakSeekableInputStream || !mInputStream) - NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIInputStream) -NS_INTERFACE_MAP_END +NS_IMPL_ISUPPORTS(SlicedInputStream, nsIInputStream, + nsICloneableInputStream, nsIAsyncInputStream) SlicedInputStream::SlicedInputStream(nsIInputStream* aInputStream, uint64_t aStart, uint64_t aLength) - : mWeakCloneableInputStream(nullptr) - , mWeakIPCSerializableInputStream(nullptr) - , mWeakSeekableInputStream(nullptr) + : mInputStream(aInputStream) , mStart(aStart) , mLength(aLength) , mCurPos(0) , mClosed(false) { MOZ_ASSERT(aInputStream); - SetSourceStream(aInputStream); } -SlicedInputStream::SlicedInputStream() - : mWeakCloneableInputStream(nullptr) - , mWeakIPCSerializableInputStream(nullptr) - , mWeakSeekableInputStream(nullptr) - , mStart(0) - , mLength(0) - , mCurPos(0) - , mClosed(false) -{} - SlicedInputStream::~SlicedInputStream() {} -void -SlicedInputStream::SetSourceStream(nsIInputStream* aInputStream) -{ - MOZ_ASSERT(!mInputStream); - MOZ_ASSERT(aInputStream); - - mInputStream = aInputStream; - - nsCOMPtr<nsICloneableInputStream> cloneableStream = - do_QueryInterface(aInputStream); - if (cloneableStream && SameCOMIdentity(aInputStream, cloneableStream)) { - mWeakCloneableInputStream = cloneableStream; - } - - nsCOMPtr<nsIIPCSerializableInputStream> serializableStream = - do_QueryInterface(aInputStream); - if (serializableStream && - SameCOMIdentity(aInputStream, serializableStream)) { - mWeakIPCSerializableInputStream = serializableStream; - } - - nsCOMPtr<nsISeekableStream> seekableStream = - do_QueryInterface(aInputStream); - if (seekableStream && SameCOMIdentity(aInputStream, seekableStream)) { - mWeakSeekableInputStream = seekableStream; - } -} - NS_IMETHODIMP SlicedInputStream::Close() { - NS_ENSURE_STATE(mInputStream); - mClosed = true; return NS_OK; } // nsIInputStream interface NS_IMETHODIMP SlicedInputStream::Available(uint64_t* aLength) { - NS_ENSURE_STATE(mInputStream); - if (mClosed) { return NS_BASE_STREAM_CLOSED; } nsresult rv = mInputStream->Available(aLength); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } @@ -116,19 +56,33 @@ SlicedInputStream::Available(uint64_t* a } return NS_OK; } NS_IMETHODIMP SlicedInputStream::Read(char* aBuffer, uint32_t aCount, uint32_t* aReadCount) { + return ReadSegments(NS_CopySegmentToBuffer, aBuffer, aCount, aReadCount); +} + +NS_IMETHODIMP +SlicedInputStream::ReadSegments(nsWriteSegmentFun aWriter, void* aClosure, + uint32_t aCount, uint32_t *aResult) +{ + uint32_t result; + + if (!aResult) { + aResult = &result; + } + + *aResult = 0; + if (mClosed) { - *aReadCount = 0; - return NS_OK; + return NS_BASE_STREAM_CLOSED; } if (mCurPos < mStart) { nsCOMPtr<nsISeekableStream> seekableStream = do_QueryInterface(mInputStream); if (seekableStream) { nsresult rv = seekableStream->Seek(nsISeekableStream::NS_SEEK_SET, mStart); @@ -152,229 +106,104 @@ SlicedInputStream::Read(char* aBuffer, u } } // Let's reduce aCount in case it's too big. if (mCurPos + aCount > mStart + mLength) { aCount = mStart + mLength - mCurPos; } - nsresult rv = mInputStream->Read(aBuffer, aCount, aReadCount); - if (NS_WARN_IF(NS_FAILED(rv)) || *aReadCount == 0) { - return rv; + char buf[4096]; + while (mCurPos < mStart + mLength && *aResult < aCount) { + uint32_t bytesRead; + uint64_t bufCount = XPCOM_MIN(aCount - *aResult, (uint32_t)sizeof(buf)); + nsresult rv = mInputStream->Read(buf, bufCount, &bytesRead); + if (NS_WARN_IF(NS_FAILED(rv)) || bytesRead == 0) { + return rv; + } + + mCurPos += bytesRead; + + uint32_t bytesWritten = 0; + while (bytesWritten < bytesRead) { + uint32_t writerCount = 0; + rv = aWriter(this, aClosure, buf + bytesWritten, *aResult, + bytesRead - bytesWritten, &writerCount); + if (NS_FAILED(rv) || writerCount == 0) { + return NS_OK; + } + + MOZ_ASSERT(writerCount <= bytesRead - bytesWritten); + bytesWritten += writerCount; + *aResult += writerCount; + } } - mCurPos += *aReadCount; return NS_OK; } NS_IMETHODIMP -SlicedInputStream::ReadSegments(nsWriteSegmentFun aWriter, void* aClosure, - uint32_t aCount, uint32_t *aResult) -{ - return NS_ERROR_NOT_IMPLEMENTED; -} - -NS_IMETHODIMP SlicedInputStream::IsNonBlocking(bool* aNonBlocking) { - NS_ENSURE_STATE(mInputStream); return mInputStream->IsNonBlocking(aNonBlocking); } // nsICloneableInputStream interface NS_IMETHODIMP SlicedInputStream::GetCloneable(bool* aCloneable) { - NS_ENSURE_STATE(mInputStream); - NS_ENSURE_STATE(mWeakCloneableInputStream); - *aCloneable = true; return NS_OK; } NS_IMETHODIMP SlicedInputStream::Clone(nsIInputStream** aResult) { - NS_ENSURE_STATE(mInputStream); - NS_ENSURE_STATE(mWeakCloneableInputStream); + nsCOMPtr<nsIInputStream> clonedStream; + nsCOMPtr<nsIInputStream> replacementStream; - nsCOMPtr<nsIInputStream> clonedStream; - nsresult rv = mWeakCloneableInputStream->Clone(getter_AddRefs(clonedStream)); + nsresult rv = NS_CloneInputStream(mInputStream, getter_AddRefs(clonedStream), + getter_AddRefs(replacementStream)); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } + if (replacementStream) { + mInputStream = replacementStream.forget(); + } + nsCOMPtr<nsIInputStream> sis = new SlicedInputStream(clonedStream, mStart, mLength); sis.forget(aResult); return NS_OK; } // nsIAsyncInputStream interface NS_IMETHODIMP SlicedInputStream::CloseWithStatus(nsresult aStatus) { - NS_ENSURE_STATE(mInputStream); - nsCOMPtr<nsIAsyncInputStream> asyncStream = do_QueryInterface(mInputStream); if (!asyncStream) { return NS_ERROR_FAILURE; } return asyncStream->CloseWithStatus(aStatus); } NS_IMETHODIMP SlicedInputStream::AsyncWait(nsIInputStreamCallback* aCallback, uint32_t aFlags, uint32_t aRequestedCount, nsIEventTarget* aEventTarget) { - NS_ENSURE_STATE(mInputStream); - nsCOMPtr<nsIAsyncInputStream> asyncStream = do_QueryInterface(mInputStream); if (!asyncStream) { return NS_ERROR_FAILURE; } return asyncStream->AsyncWait(aCallback, aFlags, aRequestedCount, aEventTarget); } - -// nsIIPCSerializableInputStream - -void -SlicedInputStream::Serialize(mozilla::ipc::InputStreamParams& aParams, - FileDescriptorArray& aFileDescriptors) -{ - MOZ_ASSERT(mInputStream); - MOZ_ASSERT(mWeakIPCSerializableInputStream); - - SlicedInputStreamParams params; - SerializeInputStream(mInputStream, params.stream(), aFileDescriptors); - params.start() = mStart; - params.length() = mLength; - params.curPos() = mCurPos; - params.closed() = mClosed; - - aParams = params; -} - -bool -SlicedInputStream::Deserialize(const mozilla::ipc::InputStreamParams& aParams, - const FileDescriptorArray& aFileDescriptors) -{ - MOZ_ASSERT(!mInputStream); - MOZ_ASSERT(!mWeakIPCSerializableInputStream); - - if (aParams.type() != - InputStreamParams::TSlicedInputStreamParams) { - NS_ERROR("Received unknown parameters from the other process!"); - return false; - } - - const SlicedInputStreamParams& params = - aParams.get_SlicedInputStreamParams(); - - nsCOMPtr<nsIInputStream> stream = - DeserializeInputStream(params.stream(), aFileDescriptors); - if (!stream) { - NS_WARNING("Deserialize failed!"); - return false; - } - - SetSourceStream(stream); - - mStart = params.start(); - mLength = params.length(); - mCurPos = params.curPos(); - mClosed = params.closed(); - - return true; -} - -mozilla::Maybe<uint64_t> -SlicedInputStream::ExpectedSerializedLength() -{ - if (!mInputStream || !mWeakIPCSerializableInputStream) { - return mozilla::Nothing(); - } - - return mWeakIPCSerializableInputStream->ExpectedSerializedLength(); -} - -// nsISeekableStream - -NS_IMETHODIMP -SlicedInputStream::Seek(int32_t aWhence, int64_t aOffset) -{ - NS_ENSURE_STATE(mInputStream); - NS_ENSURE_STATE(mWeakSeekableInputStream); - - int64_t offset; - switch (aWhence) { - case NS_SEEK_SET: - offset = mStart + aOffset; - break; - case NS_SEEK_CUR: - offset = mStart + mCurPos + aOffset; - break; - case NS_SEEK_END: - offset = mStart + mLength + aOffset; - break; - default: - return NS_ERROR_ILLEGAL_VALUE; - } - - if (offset < (int64_t)mStart || offset > (int64_t)(mStart + mLength)) { - return NS_ERROR_INVALID_ARG; - } - - nsresult rv = mWeakSeekableInputStream->Seek(NS_SEEK_SET, offset); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - mCurPos = offset - mStart; - return NS_OK; -} - -NS_IMETHODIMP -SlicedInputStream::Tell(int64_t *aResult) -{ - NS_ENSURE_STATE(mInputStream); - NS_ENSURE_STATE(mWeakSeekableInputStream); - - int64_t tell = 0; - - nsresult rv = mWeakSeekableInputStream->Tell(&tell); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - if (tell < (int64_t)mStart) { - *aResult = 0; - return NS_OK; - } - - *aResult = tell - mStart; - if (*aResult > (int64_t)mLength) { - *aResult = mLength; - } - - return NS_OK; -} - -NS_IMETHODIMP -SlicedInputStream::SetEOF() -{ - NS_ENSURE_STATE(mInputStream); - NS_ENSURE_STATE(mWeakSeekableInputStream); - - mClosed = true; - return mWeakSeekableInputStream->SetEOF(); -}
--- a/xpcom/io/SlicedInputStream.h +++ b/xpcom/io/SlicedInputStream.h @@ -5,64 +5,46 @@ #ifndef SlicedInputStream_h #define SlicedInputStream_h #include "mozilla/Attributes.h" #include "nsCOMPtr.h" #include "nsIAsyncInputStream.h" #include "nsICloneableInputStream.h" -#include "nsIIPCSerializableInputStream.h" -#include "nsISeekableStream.h" // A wrapper for a slice of an underlying input stream. class SlicedInputStream final : public nsIAsyncInputStream , public nsICloneableInputStream - , public nsIIPCSerializableInputStream - , public nsISeekableStream { public: NS_DECL_THREADSAFE_ISUPPORTS NS_DECL_NSIINPUTSTREAM + NS_DECL_NSICLONEABLEINPUTSTREAM NS_DECL_NSIASYNCINPUTSTREAM - NS_DECL_NSICLONEABLEINPUTSTREAM - NS_DECL_NSIIPCSERIALIZABLEINPUTSTREAM - NS_DECL_NSISEEKABLESTREAM // Create an input stream whose data comes from a slice of aInputStream. The // slice begins at aStart bytes beyond aInputStream's current position, and // extends for a maximum of aLength bytes. If aInputStream contains fewer // than aStart bytes, reading from SlicedInputStream returns no data. If // aInputStream contains more than aStart bytes, but fewer than aStart + // aLength bytes, reading from SlicedInputStream returns as many bytes as can // be consumed from aInputStream after reading aLength bytes. // // aInputStream should not be read from after constructing a // SlicedInputStream wrapper around it. SlicedInputStream(nsIInputStream* aInputStream, uint64_t aStart, uint64_t aLength); - // This CTOR is meant to be used just for IPC. - SlicedInputStream(); - private: ~SlicedInputStream(); - void - SetSourceStream(nsIInputStream* aInputStream); - nsCOMPtr<nsIInputStream> mInputStream; - - // Raw pointers because these are just QI of mInputStream. - nsICloneableInputStream* mWeakCloneableInputStream; - nsIIPCSerializableInputStream* mWeakIPCSerializableInputStream; - nsISeekableStream* mWeakSeekableInputStream; - uint64_t mStart; uint64_t mLength; uint64_t mCurPos; bool mClosed; }; #endif // SlicedInputStream_h