From a0a822ebdc7354a5015572dc03f2f2bee43b2f30 Mon Sep 17 00:00:00 2001 From: "jkeiser%netscape.com" Date: Thu, 18 Apr 2002 22:02:09 +0000 Subject: [PATCH] Close submitted files (input type=file) when done with them. (bug 126829) r=dougt@netscape.com, sr=darin@netscape.com git-svn-id: svn://10.0.0.236/trunk@119316 18797224-902f-48f8-a5cc-f745e15eee43 --- .../html/content/src/nsFormSubmission.cpp | 10 +- .../html/content/src/nsHTMLInputElement.cpp | 31 ++-- mozilla/docshell/base/nsDocShell.cpp | 3 +- .../src/nsWebBrowserPersist.cpp | 3 +- .../modules/plugin/base/src/nsIPluginHost.h | 2 +- .../netwerk/base/public/nsIFileStreams.idl | 125 ++++++++++------ mozilla/netwerk/base/src/nsFileStreams.cpp | 138 ++++++++++++------ mozilla/netwerk/base/src/nsFileStreams.h | 36 ++++- mozilla/xpcom/io/nsMultiplexInputStream.cpp | 38 ++--- 9 files changed, 262 insertions(+), 124 deletions(-) diff --git a/mozilla/content/html/content/src/nsFormSubmission.cpp b/mozilla/content/html/content/src/nsFormSubmission.cpp index 11f5a1c61da..4bfe635199c 100644 --- a/mozilla/content/html/content/src/nsFormSubmission.cpp +++ b/mozilla/content/html/content/src/nsFormSubmission.cpp @@ -524,16 +524,14 @@ nsFSMultipartFormData::AddNameFilePair(nsIDOMHTMLElement* aSource, + NS_LITERAL_CSTRING("Content-Type: ") + aContentType + NS_LITERAL_CSTRING(CRLF CRLF); - // - // We need to dump the data up to this point into the POST data stream here, - // since we're about to add the file input stream - // - AddPostDataStream(); - // // Add the file to the stream // if (aStream) { + // We need to dump the data up to this point into the POST data stream here, + // since we're about to add the file input stream + AddPostDataStream(); + mPostDataStream->AppendStream(aStream); } diff --git a/mozilla/content/html/content/src/nsHTMLInputElement.cpp b/mozilla/content/html/content/src/nsHTMLInputElement.cpp index 442e1e6d7e5..eec7fc80f62 100644 --- a/mozilla/content/html/content/src/nsHTMLInputElement.cpp +++ b/mozilla/content/html/content/src/nsHTMLInputElement.cpp @@ -2220,21 +2220,28 @@ nsHTMLInputElement::SubmitNamesValues(nsIFormSubmission* aFormSubmission, // // Get input stream // - nsCOMPtr rawStream; - rv = NS_NewLocalFileInputStream(getter_AddRefs(rawStream), file); - if (rawStream) { + nsCOMPtr fileStream; + rv = NS_NewLocalFileInputStream(getter_AddRefs(fileStream), + file, -1, -1, + nsIFileInputStream::CLOSE_ON_EOF | + nsIFileInputStream::REOPEN_ON_REWIND); + if (fileStream) { + // + // Create buffered stream (for efficiency) + // nsCOMPtr bufferedStream; rv = NS_NewBufferedInputStream(getter_AddRefs(bufferedStream), - rawStream, 8192); + fileStream, 8192); NS_ENSURE_SUCCESS(rv, rv); - - // - // Submit - // - aFormSubmission->AddNameFilePair(this, name, filename, - bufferedStream, contentType, - PR_FALSE); - return rv; + if (bufferedStream) { + // + // Submit + // + aFormSubmission->AddNameFilePair(this, name, filename, + bufferedStream, contentType, + PR_FALSE); + return rv; + } } } diff --git a/mozilla/docshell/base/nsDocShell.cpp b/mozilla/docshell/base/nsDocShell.cpp index 44cb077a920..5b9401fd1ae 100644 --- a/mozilla/docshell/base/nsDocShell.cpp +++ b/mozilla/docshell/base/nsDocShell.cpp @@ -4854,7 +4854,8 @@ nsDocShell::DoURILoad(nsIURI * aURI, nsCOMPtr postDataSeekable(do_QueryInterface(aPostData)); if (postDataSeekable) { - postDataSeekable->Seek(nsISeekableStream::NS_SEEK_SET, 0); + rv = postDataSeekable->Seek(nsISeekableStream::NS_SEEK_SET, 0); + NS_ENSURE_SUCCESS(rv, rv); } nsCOMPtr uploadChannel(do_QueryInterface(httpChannel)); diff --git a/mozilla/embedding/components/webbrowserpersist/src/nsWebBrowserPersist.cpp b/mozilla/embedding/components/webbrowserpersist/src/nsWebBrowserPersist.cpp index b43b6e5e67f..3ac055c69b9 100644 --- a/mozilla/embedding/components/webbrowserpersist/src/nsWebBrowserPersist.cpp +++ b/mozilla/embedding/components/webbrowserpersist/src/nsWebBrowserPersist.cpp @@ -1597,7 +1597,8 @@ nsWebBrowserPersist::MakeOutputStreamFromFile( do_CreateInstance(NS_LOCALFILEOUTPUTSTREAM_CONTRACTID, &rv); NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE); - rv = fileOutputStream->Init(aFile, -1, -1); // brade: get the right flags here! XXX + // XXX brade: get the right flags here! + rv = fileOutputStream->Init(aFile, -1, -1, 0); NS_ENSURE_SUCCESS(rv, rv); NS_ENSURE_SUCCESS(CallQueryInterface(fileOutputStream, aOutputStream), NS_ERROR_FAILURE); diff --git a/mozilla/modules/plugin/base/src/nsIPluginHost.h b/mozilla/modules/plugin/base/src/nsIPluginHost.h index 39430761543..b5e83d62a11 100644 --- a/mozilla/modules/plugin/base/src/nsIPluginHost.h +++ b/mozilla/modules/plugin/base/src/nsIPluginHost.h @@ -147,7 +147,7 @@ NS_NewPluginPostDataStream(nsIInputStream **result, file, PR_RDONLY, 0600, - PR_TRUE)) + nsIFileInputStream::DELETE_ON_CLOSE)) ) { // wrap the file stream with a buffered input stream diff --git a/mozilla/netwerk/base/public/nsIFileStreams.idl b/mozilla/netwerk/base/public/nsIFileStreams.idl index 9a3ed2e3082..456bdca911b 100644 --- a/mozilla/netwerk/base/public/nsIFileStreams.idl +++ b/mozilla/netwerk/base/public/nsIFileStreams.idl @@ -41,19 +41,49 @@ interface nsIFile; +/** + * An input stream that allows you to read from a file. + */ [scriptable, uuid(e3d56a20-c7ec-11d3-8cda-0060b0fc14a3)] interface nsIFileInputStream : nsIInputStream { /** - * @param file - file to read from (must QI to nsILocalFile) - * @param ioFlags - file open flags listed in prio.h - * @param perm - file mode bits listed in prio.h - * @param deleteOnClose - if true, the file is deleted from the filesystem - * after the file stream is closed. + * @param file file to read from (must QI to nsILocalFile) + * @param ioFlags file open flags listed in prio.h + * @param perm file mode bits listed in prio.h + * @param behaviorFlags flags specifying various behaviors of the class + * (see enumerations in the class) */ - void init(in nsIFile file, in long ioFlags, in long perm, in boolean deleteOnClose); + void init(in nsIFile file, in long ioFlags, in long perm, + in long behaviorFlags); + + /** + * If this is set, the file will be deleted by the time the stream is + * closed. It may be removed before the stream is closed if it is possible + * to delete it and still read from it. + * + * If OPEN_ON_READ is defined, and the file was recreated after the first + * delete, the file will be deleted again when it is closed again. + */ + const long DELETE_ON_CLOSE = 1<<1; + + /** + * If this is set, the file will close automatically when the end of the + * file is reached. + */ + const long CLOSE_ON_EOF = 1<<2; + + /** + * If this is set, the file will be reopened whenever Seek(0) occurs. If + * the file is already open and the seek occurs, it will happen naturally. + * (The file will only be reopened if it is closed for some reason.) + */ + const long REOPEN_ON_REWIND = 1<<3; }; +/** + * An output stream that lets you stream to a file. + */ [scriptable, uuid(e6f68040-c7ec-11d3-8cda-0060b0fc14a3)] interface nsIFileOutputStream : nsIOutputStream { @@ -61,10 +91,17 @@ interface nsIFileOutputStream : nsIOutputStream * @param file - file to write to (must QI to nsILocalFile) * @param ioFlags - file open flags listed in prio.h * @param perm - file mode bits listed in prio.h + * @param behaviorFlags flags specifying various behaviors of the class + * (currently none supported) */ - void init(in nsIFile file, in long ioFlags, in long perm); + void init(in nsIFile file, in long ioFlags, in long perm, + in long behaviorFlags); }; +/** + * An input stream that reads ahead and keeps a buffer coming from another input + * stream so that fewer accesses to the underlying stream are necessary. + */ [scriptable, uuid(616f5b48-da09-11d3-8cda-0060b0fc14a3)] interface nsIBufferedInputStream : nsIInputStream { @@ -76,6 +113,11 @@ interface nsIBufferedInputStream : nsIInputStream in unsigned long bufferSize); }; +/** + * An output stream that stores up data to write out to another output stream + * and does the entire write only when the buffer is full, so that fewer writes + * to the underlying output stream are necessary. + */ [scriptable, uuid(6476378a-da09-11d3-8cda-0060b0fc14a3)] interface nsIBufferedOutputStream : nsIOutputStream { @@ -149,10 +191,10 @@ interface nsIBufferedOutputStream : nsIOutputStream // This will QI the file argument to an nsILocalFile in the Init method. inline nsresult -NS_NewLocalFileChannel(nsIFileChannel **result, - nsIFile* file, - PRInt32 ioFlags = -1, - PRInt32 perm = -1) +NS_NewLocalFileChannel(nsIFileChannel** aResult, + nsIFile* aFile, + PRInt32 aIOFlags = -1, + PRInt32 aPerm = -1) { nsresult rv; nsCOMPtr channel; @@ -162,21 +204,21 @@ NS_NewLocalFileChannel(nsIFileChannel **result, NS_GET_IID(nsIFileChannel), getter_AddRefs(channel)); if (NS_FAILED(rv)) return rv; - rv = channel->Init(file, ioFlags, perm); + rv = channel->Init(aFile, aIOFlags, aPerm); if (NS_FAILED(rv)) return rv; - *result = channel; - NS_ADDREF(*result); + *aResult = channel; + NS_ADDREF(*aResult); return NS_OK; } // This will QI the file argument to an nsILocalFile in the Init method. inline nsresult -NS_NewLocalFileInputStream(nsIInputStream* *result, - nsIFile* file, - PRInt32 ioFlags = -1, - PRInt32 perm = -1, - PRBool deleteOnClose = PR_FALSE) +NS_NewLocalFileInputStream(nsIInputStream** aResult, + nsIFile* aFile, + PRInt32 aIOFlags = -1, + PRInt32 aPerm = -1, + PRInt32 aBehaviorFlags = 0) { nsresult rv; nsCOMPtr in; @@ -186,20 +228,21 @@ NS_NewLocalFileInputStream(nsIInputStream* *result, NS_GET_IID(nsIFileInputStream), getter_AddRefs(in)); if (NS_FAILED(rv)) return rv; - rv = in->Init(file, ioFlags, perm, deleteOnClose); + rv = in->Init(aFile, aIOFlags, aPerm, aBehaviorFlags); if (NS_FAILED(rv)) return rv; - *result = in; - NS_ADDREF(*result); + *aResult = in; + NS_ADDREF(*aResult); return NS_OK; } // This will QI the file argument to an nsILocalFile in the Init method. inline nsresult -NS_NewLocalFileOutputStream(nsIOutputStream* *result, - nsIFile* file, - PRInt32 ioFlags = -1, - PRInt32 perm = -1) +NS_NewLocalFileOutputStream(nsIOutputStream** aResult, + nsIFile* aFile, + PRInt32 aIOFlags = -1, + PRInt32 aPerm = -1, + PRInt32 aBehaviorFlags = 0) { nsresult rv; nsCOMPtr out; @@ -209,20 +252,20 @@ NS_NewLocalFileOutputStream(nsIOutputStream* *result, NS_GET_IID(nsIFileOutputStream), getter_AddRefs(out)); if (NS_FAILED(rv)) return rv; - rv = out->Init(file, ioFlags, perm); + rv = out->Init(aFile, aIOFlags, aPerm, aBehaviorFlags); if (NS_FAILED(rv)) return rv; - *result = out; - NS_ADDREF(*result); + *aResult = out; + NS_ADDREF(*aResult); return NS_OK; } //////////////////////////////////////////////////////////////////////////////// inline nsresult -NS_NewBufferedInputStream(nsIInputStream* *result, - nsIInputStream* str, - PRUint32 bufferSize) +NS_NewBufferedInputStream(nsIInputStream** aResult, + nsIInputStream* aStr, + PRUint32 aBufferSize) { nsresult rv; nsCOMPtr in; @@ -232,18 +275,18 @@ NS_NewBufferedInputStream(nsIInputStream* *result, NS_GET_IID(nsIBufferedInputStream), getter_AddRefs(in)); if (NS_FAILED(rv)) return rv; - rv = in->Init(str, bufferSize); + rv = in->Init(aStr, aBufferSize); if (NS_FAILED(rv)) return rv; - *result = in; - NS_ADDREF(*result); + *aResult = in; + NS_ADDREF(*aResult); return NS_OK; } inline nsresult -NS_NewBufferedOutputStream(nsIOutputStream* *result, - nsIOutputStream* str, - PRUint32 bufferSize) +NS_NewBufferedOutputStream(nsIOutputStream** aResult, + nsIOutputStream* aStr, + PRUint32 aBufferSize) { nsresult rv; nsCOMPtr out; @@ -253,11 +296,11 @@ NS_NewBufferedOutputStream(nsIOutputStream* *result, NS_GET_IID(nsIBufferedOutputStream), getter_AddRefs(out)); if (NS_FAILED(rv)) return rv; - rv = out->Init(str, bufferSize); + rv = out->Init(aStr, aBufferSize); if (NS_FAILED(rv)) return rv; - *result = out; - NS_ADDREF(*result); + *aResult = out; + NS_ADDREF(*aResult); return NS_OK; } diff --git a/mozilla/netwerk/base/src/nsFileStreams.cpp b/mozilla/netwerk/base/src/nsFileStreams.cpp index ec48f2611b2..4ae1bfda52e 100644 --- a/mozilla/netwerk/base/src/nsFileStreams.cpp +++ b/mozilla/netwerk/base/src/nsFileStreams.cpp @@ -533,38 +533,66 @@ nsFileInputStream::Create(nsISupports *aOuter, REFNSIID aIID, void **aResult) return rv; } -NS_IMETHODIMP -nsFileInputStream::Init(nsIFile* file, PRInt32 ioFlags, PRInt32 perm, PRBool deleteOnClose) +nsresult +nsFileInputStream::Open(nsIFile* aFile, PRInt32 aIOFlags, PRInt32 aPerm) { - NS_ENSURE_TRUE(mFD == nsnull, NS_ERROR_ALREADY_INITIALIZED); - nsresult rv = NS_OK; - nsCOMPtr localFile = do_QueryInterface(file, &rv); + + // If the previous file is open, close it + if (mFD) { + rv = Close(); + if (NS_FAILED(rv)) return rv; + } + + // Open the file + nsCOMPtr localFile = do_QueryInterface(aFile, &rv); if (NS_FAILED(rv)) return rv; - if (ioFlags == -1) - ioFlags = PR_RDONLY; - if (perm == -1) - perm = 0; + if (aIOFlags == -1) + aIOFlags = PR_RDONLY; + if (aPerm == -1) + aPerm = 0; PRFileDesc* fd; - rv = localFile->OpenNSPRFileDesc(ioFlags, perm, &fd); + rv = localFile->OpenNSPRFileDesc(aIOFlags, aPerm, &fd); if (NS_FAILED(rv)) return rv; mFD = fd; - if (deleteOnClose) { + if (mBehaviorFlags & DELETE_ON_CLOSE) { // POSIX compatible filesystems allow a file to be unlinked while a // file descriptor is still referencing the file. since we've already // opened the file descriptor, we'll try to remove the file. if that // fails, then we'll just remember the nsIFile and remove it after we // close the file descriptor. - nsresult rv = file->Remove(PR_FALSE); - if (NS_FAILED(rv)) - mFileToDelete = file; + rv = aFile->Remove(PR_FALSE); + if (NS_FAILED(rv) && !(mBehaviorFlags & REOPEN_ON_REWIND)) { + // If REOPEN_ON_REWIND is not happenin', we haven't saved the file yet + mFile = aFile; + } } + return NS_OK; } +NS_IMETHODIMP +nsFileInputStream::Init(nsIFile* aFile, PRInt32 aIOFlags, PRInt32 aPerm, + PRInt32 aBehaviorFlags) +{ + NS_ENSURE_TRUE(!mFD, NS_ERROR_ALREADY_INITIALIZED); + NS_ENSURE_TRUE(!mParent, NS_ERROR_ALREADY_INITIALIZED); + + mBehaviorFlags = aBehaviorFlags; + + // If the file will be reopened on rewind, save the info to open the file + if (mBehaviorFlags & REOPEN_ON_REWIND) { + mFile = aFile; + mIOFlags = aIOFlags; + mPerm = aPerm; + } + + return Open(aFile, aIOFlags, aPerm); +} + NS_IMETHODIMP nsFileInputStream::Close() { @@ -572,70 +600,74 @@ nsFileInputStream::Close() mLineBuffer = nsnull; // in case Close() is called again after failing nsresult rv = nsFileStream::Close(); if (NS_FAILED(rv)) return rv; - if (mFileToDelete) { - rv = mFileToDelete->Remove(PR_FALSE); + if (mFile && (mBehaviorFlags & DELETE_ON_CLOSE)) { + rv = mFile->Remove(PR_FALSE); NS_ASSERTION(NS_SUCCEEDED(rv), "failed to delete file"); + // If we don't need to save the file for reopening, free it up + if (!(mBehaviorFlags & REOPEN_ON_REWIND)) { + mFile = nsnull; + } } return rv; } NS_IMETHODIMP -nsFileInputStream::Available(PRUint32 *result) +nsFileInputStream::Available(PRUint32* aResult) { - if (mFD == nsnull) + if (!mFD) { return NS_BASE_STREAM_CLOSED; + } PRInt32 avail = PR_Available(mFD); if (avail == -1) { return NS_ErrorAccordingToNSPR(); } - *result = avail; + *aResult = avail; return NS_OK; } NS_IMETHODIMP -nsFileInputStream::Read(char * buf, PRUint32 count, PRUint32 *result) +nsFileInputStream::Read(char* aBuf, PRUint32 aCount, PRUint32* aResult) { - if (mFD == nsnull) + if (!mFD) { return NS_BASE_STREAM_CLOSED; + } - PRInt32 cnt = PR_Read(mFD, buf, count); - if (cnt == -1) { + PRInt32 bytesRead = PR_Read(mFD, aBuf, aCount); + if (bytesRead == -1) { return NS_ErrorAccordingToNSPR(); } - *result = cnt; + // Check if we're at the end of file and need to close + if (mBehaviorFlags & CLOSE_ON_EOF) { + if (bytesRead == 0) { + Close(); + } + } + + *aResult = bytesRead; return NS_OK; } NS_IMETHODIMP -nsFileInputStream::ReadLine(nsAString & aLine, PRBool *_retval) +nsFileInputStream::ReadLine(nsAString& aLine, PRBool* aResult) { if (!mLineBuffer) { nsresult rv = NS_InitLineBuffer(&mLineBuffer); if (NS_FAILED(rv)) return rv; } - return NS_ReadLine(this, mLineBuffer, aLine, _retval); + return NS_ReadLine(this, mLineBuffer, aLine, aResult); } NS_IMETHODIMP -nsFileInputStream::ReadSegments(nsWriteSegmentFun writer, void * closure, PRUint32 count, PRUint32 *_retval) +nsFileInputStream::ReadSegments(nsWriteSegmentFun aWriter, void* aClosure, + PRUint32 aCount, PRUint32* aResult) { - PRUint32 nBytes; - char *readBuf = (char *)nsMemory::Alloc(count); - if (!readBuf) - return NS_ERROR_OUT_OF_MEMORY; - - nsresult rv = Read(readBuf, count, &nBytes); - - *_retval = 0; - if (NS_SUCCEEDED(rv)) { - rv = writer(this, closure, readBuf, 0, nBytes, _retval); - NS_ASSERTION(NS_SUCCEEDED(rv) ? nBytes == *_retval : PR_TRUE, "Didn't write all Data."); - // XXX this assertion is invalid! - } - - nsMemory::Free(readBuf); - return rv; + // 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(). + NS_NOTREACHED("ReadSegments"); + return NS_ERROR_NOT_IMPLEMENTED; } NS_IMETHODIMP @@ -645,6 +677,23 @@ nsFileInputStream::IsNonBlocking(PRBool *aNonBlocking) return NS_OK; } +NS_IMETHODIMP +nsFileInputStream::Seek(PRInt32 aWhence, PRInt32 aOffset) +{ + if (!mFD) { + if (mBehaviorFlags & REOPEN_ON_REWIND) { + nsresult rv = Reopen(); + if (NS_FAILED(rv)) { + return rv; + } + } else { + return NS_BASE_STREAM_CLOSED; + } + } + + return nsFileStream::Seek(aWhence, aOffset); +} + //////////////////////////////////////////////////////////////////////////////// // nsFileOutputStream @@ -668,7 +717,8 @@ nsFileOutputStream::Create(nsISupports *aOuter, REFNSIID aIID, void **aResult) } NS_IMETHODIMP -nsFileOutputStream::Init(nsIFile* file, PRInt32 ioFlags, PRInt32 perm) +nsFileOutputStream::Init(nsIFile* file, PRInt32 ioFlags, PRInt32 perm, + PRInt32 behaviorFlags) { NS_ENSURE_TRUE(mFD == nsnull, NS_ERROR_ALREADY_INITIALIZED); diff --git a/mozilla/netwerk/base/src/nsFileStreams.h b/mozilla/netwerk/base/src/nsFileStreams.h index e3425e92c78..efe19ca8a7b 100644 --- a/mozilla/netwerk/base/src/nsFileStreams.h +++ b/mozilla/netwerk/base/src/nsFileStreams.h @@ -109,9 +109,13 @@ public: NS_DECL_NSIFILEINPUTSTREAM NS_DECL_NSILINEINPUTSTREAM + // Overrided from nsFileStream + NS_IMETHOD Seek(PRInt32 aWhence, PRInt32 aOffset); + nsFileInputStream() : nsFileStream() { mLineBuffer = nsnull; + mBehaviorFlags = 0; } virtual ~nsFileInputStream() { @@ -123,7 +127,37 @@ public: protected: nsLineBuffer *mLineBuffer; - nsCOMPtr mFileToDelete; + + /** + * The file being opened. Only stored when DELETE_ON_CLOSE or + * REOPEN_ON_REWIND are true. + */ + nsCOMPtr mFile; + /** + * The IO flags passed to Init() for the file open. + * Only set for REOPEN_ON_REWIND. + */ + PRInt32 mIOFlags; + /** + * The permissions passed to Init() for the file open. + * Only set for REOPEN_ON_REWIND. + */ + PRInt32 mPerm; + /** + * Flags describing our behavior. See the IDL file for possible values. + */ + PRInt32 mBehaviorFlags; + +protected: + /** + * Internal, called to open a file. Parameters are the same as their + * Init() analogues. + */ + nsresult Open(nsIFile* file, PRInt32 ioFlags, PRInt32 perm); + /** + * Reopen the file (for OPEN_ON_READ only!) + */ + nsresult Reopen() { return Open(mFile, mIOFlags, mPerm); } }; //////////////////////////////////////////////////////////////////////////////// diff --git a/mozilla/xpcom/io/nsMultiplexInputStream.cpp b/mozilla/xpcom/io/nsMultiplexInputStream.cpp index 6efbfc34b8a..9b95d8beb00 100644 --- a/mozilla/xpcom/io/nsMultiplexInputStream.cpp +++ b/mozilla/xpcom/io/nsMultiplexInputStream.cpp @@ -240,13 +240,8 @@ nsMultiplexInputStream::ReadSegments(nsWriteSegmentFun aWriter, void *aClosure, while (mCurrentStream < len) { nsCOMPtr stream(do_QueryElementAt(&mStreams, mCurrentStream)); - PRUint32 avail, read, count; - rv = stream->Available(&avail); - if (NS_FAILED(rv)) return rv; - - count = PR_MIN(avail, aCount); - - rv = stream->ReadSegments(ReadSegCb, &state, count, &read); + PRUint32 read; + rv = stream->ReadSegments(ReadSegCb, &state, aCount, &read); // If we got an NS_BASE_STREAM_WOULD_BLOCK error since the reader // didn't want any more data. This might not be an error for us if // data was read from a previous stream in this run @@ -254,23 +249,32 @@ nsMultiplexInputStream::ReadSegments(nsWriteSegmentFun aWriter, void *aClosure, !read && state.mOffset) break; - NS_ENSURE_SUCCESS(rv, rv); - if (read > count) { - NS_ERROR("Read more than requested"); - read = count; // truncate and hope for the best... + // If the return value is NS_BASE_STREAM_CLOSED, we are done with this + // stream. + if (rv == NS_BASE_STREAM_CLOSED) { + ++mCurrentStream; + mStartedReadingCurrent = PR_FALSE; + continue; } + + NS_ENSURE_SUCCESS(rv, rv); + NS_ASSERTION(aCount >= read, "Read more than requested"); mStartedReadingCurrent = PR_TRUE; state.mOffset += read; - count -= read; aCount -= read; if (state.mDone || // writer doesn't want anymore data - aCount == 0 || // already read total amount requested - count > 0) // the current stream still has data + aCount == 0) // the current stream still has data break; - ++mCurrentStream; - mStartedReadingCurrent = PR_FALSE; + // If the writer accepted all data but we didn't read any, this stream + // is finished. Go on to the next stream. These aren't the droids + // you're looking for. + if (read == 0) { + ++mCurrentStream; + mStartedReadingCurrent = PR_FALSE; + } } + *_retval = state.mOffset; return NS_OK; } @@ -290,7 +294,7 @@ nsMultiplexInputStream::ReadSegCb(nsIInputStream* aIn, void* aClosure, aCount, aWriteCount); if (rv == NS_BASE_STREAM_WOULD_BLOCK || - (NS_SUCCEEDED(rv) && *aWriteCount == 0 && aCount != 0)) + (NS_SUCCEEDED(rv) && *aWriteCount < aCount && aCount != 0)) state->mDone = PR_TRUE; return rv;