diff --git a/browser/components/sessionstore/src/nsSessionStore.js b/browser/components/sessionstore/src/nsSessionStore.js index af6d513326e..3c4c4e08c84 100644 --- a/browser/components/sessionstore/src/nsSessionStore.js +++ b/browser/components/sessionstore/src/nsSessionStore.js @@ -4026,7 +4026,7 @@ SessionStoreService.prototype = { // Initialize the file output stream. var ostream = Cc["@mozilla.org/network/safe-file-output-stream;1"]. createInstance(Ci.nsIFileOutputStream); - ostream.init(aFile, 0x02 | 0x08 | 0x20, 0600, 0); + ostream.init(aFile, 0x02 | 0x08 | 0x20, 0600, ostream.DEFER_OPEN); // Obtain a converter to convert our data to a UTF-8 encoded input stream. var converter = Cc["@mozilla.org/intl/scriptableunicodeconverter"]. diff --git a/netwerk/base/public/nsIFileStreams.idl b/netwerk/base/public/nsIFileStreams.idl index c2ee44a723f..95956b7e4b0 100644 --- a/netwerk/base/public/nsIFileStreams.idl +++ b/netwerk/base/public/nsIFileStreams.idl @@ -81,6 +81,28 @@ interface nsIFileInputStream : nsIInputStream * (The file will only be reopened if it is closed for some reason.) */ const long REOPEN_ON_REWIND = 1<<3; + + /** + * If this is set, the file will be opened (i.e., a call to + * PR_Open done) only when we do an actual operation on the stream, + * or more specifically, when one of the following is called: + * - Seek + * - Tell + * - Available + * - Read + * - ReadLine + * + * DEFER_OPEN is useful if we use the stream on a background + * thread, so that the opening and possible |stat|ing of the file + * happens there as well. + * + * @note Using this flag results in the file not being opened + * during the call to Init. This means that any errors that might + * happen when this flag is not set would happen during the + * first read. Also, the file is not locked when Init is called, + * so it might be deleted before we try to read from it. + */ + const long DEFER_OPEN = 1<<4; }; /** @@ -102,6 +124,22 @@ interface nsIFileOutputStream : nsIOutputStream */ void init(in nsIFile file, in long ioFlags, in long perm, in long behaviorFlags); + + /** + * See the same constant in nsIFileInputStream. The deferred open will + * be performed when one of the following is called: + * - Seek + * - Tell + * - Write + * - Flush + * + * @note Using this flag results in the file not being opened + * during the call to Init. This means that any errors that might + * 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; }; /** diff --git a/netwerk/base/src/nsFileStreams.cpp b/netwerk/base/src/nsFileStreams.cpp index 4b9deb2acae..a386c76a8bd 100644 --- a/netwerk/base/src/nsFileStreams.cpp +++ b/netwerk/base/src/nsFileStreams.cpp @@ -72,41 +72,27 @@ nsFileStream::nsFileStream() : mFD(nsnull) - , mCloseFD(PR_TRUE) + , mBehaviorFlags(0) + , mDeferredOpen(false) { } nsFileStream::~nsFileStream() { - if (mCloseFD) - Close(); + Close(); } NS_IMPL_THREADSAFE_ISUPPORTS1(nsFileStream, nsISeekableStream) -nsresult -nsFileStream::InitWithFileDescriptor(PRFileDesc* fd, nsISupports* parent) -{ - NS_ENSURE_TRUE(mFD == nsnull, NS_ERROR_ALREADY_INITIALIZED); - // - // this file stream is dependent on its parent to keep the - // file descriptor valid. an owning reference to the parent - // prevents the file descriptor from going away prematurely. - // - mFD = fd; - mCloseFD = PR_FALSE; - mParent = parent; - return NS_OK; -} - nsresult nsFileStream::Close() { + CleanUpOpen(); + nsresult rv = NS_OK; if (mFD) { - if (mCloseFD) - if (PR_Close(mFD) == PR_FAILURE) - rv = NS_BASE_STREAM_OSERROR; + if (PR_Close(mFD) == PR_FAILURE) + rv = NS_BASE_STREAM_OSERROR; mFD = nsnull; } return rv; @@ -115,6 +101,9 @@ nsFileStream::Close() NS_IMETHODIMP nsFileStream::Seek(PRInt32 whence, PRInt64 offset) { + nsresult rv = DoPendingOpen(); + NS_ENSURE_SUCCESS(rv, rv); + if (mFD == nsnull) return NS_BASE_STREAM_CLOSED; @@ -128,6 +117,9 @@ nsFileStream::Seek(PRInt32 whence, PRInt64 offset) NS_IMETHODIMP nsFileStream::Tell(PRInt64 *result) { + nsresult rv = DoPendingOpen(); + NS_ENSURE_SUCCESS(rv, rv); + if (mFD == nsnull) return NS_BASE_STREAM_CLOSED; @@ -174,6 +166,62 @@ nsFileStream::SetEOF() return NS_OK; } +nsresult +nsFileStream::MaybeOpen(nsILocalFile* aFile, PRInt32 aIoFlags, PRInt32 aPerm, + bool aDeferred) +{ + mOpenParams.ioFlags = aIoFlags; + mOpenParams.perm = aPerm; + + if (aDeferred) { + // Clone the file, as it may change between now and the deferred open + nsCOMPtr file; + nsresult rv = aFile->Clone(getter_AddRefs(file)); + NS_ENSURE_SUCCESS(rv, rv); + + mOpenParams.localFile = do_QueryInterface(file); + NS_ENSURE_TRUE(mOpenParams.localFile, NS_ERROR_UNEXPECTED); + + mDeferredOpen = true; + return NS_OK; + } + + mOpenParams.localFile = aFile; + + return DoOpen(); +} + +void +nsFileStream::CleanUpOpen() +{ + mOpenParams.localFile = nsnull; + mDeferredOpen = false; +} + +nsresult +nsFileStream::DoOpen() +{ + NS_PRECONDITION(mOpenParams.localFile, "Must have a file to open"); + + PRFileDesc* fd; + nsresult rv = mOpenParams.localFile->OpenNSPRFileDesc(mOpenParams.ioFlags, mOpenParams.perm, &fd); + CleanUpOpen(); + NS_ENSURE_SUCCESS(rv, rv); + mFD = fd; + + return NS_OK; +} + +nsresult +nsFileStream::DoPendingOpen() +{ + if (!mDeferredOpen) { + return NS_OK; + } + + return DoOpen(); +} + //////////////////////////////////////////////////////////////////////////////// // nsFileInputStream @@ -232,12 +280,10 @@ nsFileInputStream::Open(nsIFile* aFile, PRInt32 aIOFlags, PRInt32 aPerm) if (aPerm == -1) aPerm = 0; - PRFileDesc* fd; - rv = localFile->OpenNSPRFileDesc(aIOFlags, aPerm, &fd); + rv = MaybeOpen(localFile, aIOFlags, aPerm, + mBehaviorFlags & nsIFileInputStream::DEFER_OPEN); if (NS_FAILED(rv)) return rv; - mFD = fd; - 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 @@ -259,7 +305,7 @@ 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); + NS_ENSURE_TRUE(!mDeferredOpen, NS_ERROR_ALREADY_INITIALIZED); mBehaviorFlags = aBehaviorFlags; @@ -291,6 +337,9 @@ nsFileInputStream::Close() NS_IMETHODIMP nsFileInputStream::Available(PRUint32* aResult) { + nsresult rv = DoPendingOpen(); + NS_ENSURE_SUCCESS(rv, rv); + if (!mFD) { return NS_BASE_STREAM_CLOSED; } @@ -310,6 +359,9 @@ nsFileInputStream::Available(PRUint32* aResult) NS_IMETHODIMP nsFileInputStream::Read(char* aBuf, PRUint32 aCount, PRUint32* aResult) { + nsresult rv = DoPendingOpen(); + NS_ENSURE_SUCCESS(rv, rv); + if (!mFD) { *aResult = 0; return NS_OK; @@ -333,6 +385,9 @@ nsFileInputStream::Read(char* aBuf, PRUint32 aCount, PRUint32* aResult) NS_IMETHODIMP nsFileInputStream::ReadLine(nsACString& aLine, PRBool* aResult) { + nsresult rv = DoPendingOpen(); + NS_ENSURE_SUCCESS(rv, rv); + if (!mLineBuffer) { nsresult rv = NS_InitLineBuffer(&mLineBuffer); if (NS_FAILED(rv)) return rv; @@ -365,6 +420,9 @@ nsFileInputStream::IsNonBlocking(PRBool *aNonBlocking) NS_IMETHODIMP nsFileInputStream::Seek(PRInt32 aWhence, PRInt64 aOffset) { + nsresult rv = DoPendingOpen(); + NS_ENSURE_SUCCESS(rv, rv); + PR_FREEIF(mLineBuffer); // this invalidates the line buffer if (!mFD) { if (mBehaviorFlags & REOPEN_ON_REWIND) { @@ -564,6 +622,9 @@ nsFileOutputStream::Init(nsIFile* file, PRInt32 ioFlags, PRInt32 perm, PRInt32 behaviorFlags) { NS_ENSURE_TRUE(mFD == nsnull, NS_ERROR_ALREADY_INITIALIZED); + NS_ENSURE_TRUE(!mDeferredOpen, NS_ERROR_ALREADY_INITIALIZED); + + mBehaviorFlags = behaviorFlags; nsresult rv; nsCOMPtr localFile = do_QueryInterface(file, &rv); @@ -573,12 +634,8 @@ nsFileOutputStream::Init(nsIFile* file, PRInt32 ioFlags, PRInt32 perm, if (perm <= 0) perm = 0664; - PRFileDesc* fd; - rv = localFile->OpenNSPRFileDesc(ioFlags, perm, &fd); - if (NS_FAILED(rv)) return rv; - - mFD = fd; - return NS_OK; + return MaybeOpen(localFile, ioFlags, perm, + mBehaviorFlags & nsIFileOutputStream::DEFER_OPEN); } NS_IMETHODIMP @@ -590,6 +647,9 @@ nsFileOutputStream::Close() NS_IMETHODIMP nsFileOutputStream::Write(const char *buf, PRUint32 count, PRUint32 *result) { + nsresult rv = DoPendingOpen(); + NS_ENSURE_SUCCESS(rv, rv); + if (mFD == nsnull) return NS_BASE_STREAM_CLOSED; @@ -604,6 +664,9 @@ nsFileOutputStream::Write(const char *buf, PRUint32 count, PRUint32 *result) NS_IMETHODIMP nsFileOutputStream::Flush(void) { + nsresult rv = DoPendingOpen(); + NS_ENSURE_SUCCESS(rv, rv); + if (mFD == nsnull) return NS_BASE_STREAM_CLOSED; @@ -653,7 +716,16 @@ NS_IMETHODIMP nsSafeFileOutputStream::Init(nsIFile* file, PRInt32 ioFlags, PRInt32 perm, PRInt32 behaviorFlags) { - NS_ENSURE_ARG(file); + return nsFileOutputStream::Init(file, ioFlags, perm, behaviorFlags); +} + +nsresult +nsSafeFileOutputStream::DoOpen() +{ + // Make sure mOpenParams.localFile will be empty if we bail somewhere in + // this function + nsCOMPtr file; + file.swap(mOpenParams.localFile); nsresult rv = file->Exists(&mTargetFileExists); if (NS_FAILED(rv)) { @@ -680,16 +752,21 @@ nsSafeFileOutputStream::Init(nsIFile* file, PRInt32 ioFlags, PRInt32 perm, PRUint32 origPerm; if (NS_FAILED(file->GetPermissions(&origPerm))) { NS_ERROR("Can't get permissions of target file"); - origPerm = perm; + origPerm = mOpenParams.perm; } // XXX What if |perm| is more restrictive then |origPerm|? // This leaves the user supplied permissions as they were. rv = tempResult->CreateUnique(nsIFile::NORMAL_FILE_TYPE, origPerm); } if (NS_SUCCEEDED(rv)) { + // nsFileOutputStream::DoOpen will work on the temporary file, so we + // prepare it and place it in mOpenParams.localFile. + nsCOMPtr localFile = do_QueryInterface(tempResult, &rv); + NS_ENSURE_SUCCESS(rv, rv); + mOpenParams.localFile = localFile; mTempFile = tempResult; mTargetFile = file; - rv = nsFileOutputStream::Init(mTempFile, ioFlags, perm, behaviorFlags); + rv = nsFileOutputStream::DoOpen(); } return rv; } diff --git a/netwerk/base/src/nsFileStreams.h b/netwerk/base/src/nsFileStreams.h index ef799f9b9bc..3206eae7fe4 100644 --- a/netwerk/base/src/nsFileStreams.h +++ b/netwerk/base/src/nsFileStreams.h @@ -64,13 +64,57 @@ public: virtual ~nsFileStream(); nsresult Close(); - nsresult InitWithFileDescriptor(PRFileDesc* fd, nsISupports* parent); protected: - PRFileDesc* mFD; - nsCOMPtr mParent; // strong reference to parent nsFileIO, - // which ensures mFD remains valid. - PRBool mCloseFD; + PRFileDesc* mFD; + + /** + * Flags describing our behavior. See the IDL file for possible values. + */ + PRInt32 mBehaviorFlags; + + /** + * Whether we have a pending open (see DEFER_OPEN in the IDL file). + */ + bool mDeferredOpen; + + struct OpenParams { + nsCOMPtr localFile; + PRInt32 ioFlags; + PRInt32 perm; + }; + + /** + * Data we need to do an open. + */ + OpenParams mOpenParams; + + /** + * Prepares the data we need to open the file, and either does the open now + * by calling DoOpen(), or leaves it to be opened later by a call to + * DoPendingOpen(). + */ + nsresult MaybeOpen(nsILocalFile* aFile, PRInt32 aIoFlags, PRInt32 aPerm, + bool aDeferred); + + /** + * Cleans up data prepared in MaybeOpen. + */ + void CleanUpOpen(); + + /** + * Open the file. This is called either from MaybeOpen (during Init) + * or from DoPendingOpen (if DEFER_OPEN is used when initializing this + * stream). The default behavior of DoOpen is to open the file and save the + * file descriptor. + */ + virtual nsresult DoOpen(); + + /** + * If there is a pending open, do it now. It's important for this to be + * inline since we do it in almost every stream API call. + */ + inline nsresult DoPendingOpen(); }; //////////////////////////////////////////////////////////////////////////////// @@ -93,7 +137,6 @@ public: nsFileInputStream() : nsFileStream() { mLineBuffer = nsnull; - mBehaviorFlags = 0; } virtual ~nsFileInputStream() { @@ -118,10 +161,6 @@ protected: * The permissions passed to Init() for the file open. */ PRInt32 mPerm; - /** - * Flags describing our behavior. See the IDL file for possible values. - */ - PRInt32 mBehaviorFlags; protected: /** @@ -194,6 +233,8 @@ public: virtual ~nsSafeFileOutputStream() { nsSafeFileOutputStream::Close(); } + virtual nsresult DoOpen(); + NS_IMETHODIMP Close(); NS_IMETHODIMP Write(const char *buf, PRUint32 count, PRUint32 *result); NS_IMETHODIMP Init(nsIFile* file, PRInt32 ioFlags, PRInt32 perm, PRInt32 behaviorFlags); diff --git a/netwerk/test/unit/test_NetUtil.js b/netwerk/test/unit/test_NetUtil.js index e807a41cb98..83468a33029 100644 --- a/netwerk/test/unit/test_NetUtil.js +++ b/netwerk/test/unit/test_NetUtil.js @@ -49,6 +49,9 @@ Components.utils.import("resource://gre/modules/NetUtil.jsm"); // files. do_get_profile(); +const OUTPUT_STREAM_CONTRACT_ID = "@mozilla.org/network/file-output-stream;1"; +const SAFE_OUTPUT_STREAM_CONTRACT_ID = "@mozilla.org/network/safe-file-output-stream;1"; + //////////////////////////////////////////////////////////////////////////////// //// Helper Methods @@ -75,10 +78,15 @@ function getFileContents(aFile) return string.value; } -//////////////////////////////////////////////////////////////////////////////// -//// Tests - -function test_async_write_file() +/** + * Tests asynchronously writing a file using NetUtil.asyncCopy. + * + * @param aContractId + * The contract ID to use for the output stream + * @param aDeferOpen + * Whether to use DEFER_OPEN in the output stream. + */ +function async_write_file(aContractId, aDeferOpen) { do_test_pending(); @@ -90,9 +98,8 @@ function test_async_write_file() file.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, 0666); // Then, we need an output stream to our output file. - let ostream = Cc["@mozilla.org/network/file-output-stream;1"]. - createInstance(Ci.nsIFileOutputStream); - ostream.init(file, -1, -1, 0); + let ostream = Cc[aContractId].createInstance(Ci.nsIFileOutputStream); + ostream.init(file, -1, -1, aDeferOpen ? Ci.nsIFileOutputStream.DEFER_OPEN : 0); // Finally, we need an input stream to take data from. const TEST_DATA = "this is a test string"; @@ -113,39 +120,23 @@ function test_async_write_file() }); } -function test_async_write_file_nsISafeOutputStream() -{ - do_test_pending(); +//////////////////////////////////////////////////////////////////////////////// +//// Tests - // First, we need an output file to write to. - let file = Cc["@mozilla.org/file/directory_service;1"]. - getService(Ci.nsIProperties). - get("ProfD", Ci.nsIFile); - file.append("NetUtil-async-test-file.tmp"); - file.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, 0666); +function test_async_write_file() { + async_write_file(OUTPUT_STREAM_CONTRACT_ID); +} - // Then, we need an output stream to our output file. - let ostream = Cc["@mozilla.org/network/safe-file-output-stream;1"]. - createInstance(Ci.nsIFileOutputStream); - ostream.init(file, -1, -1, 0); +function test_async_write_file_deferred() { + async_write_file(OUTPUT_STREAM_CONTRACT_ID, true); +} - // Finally, we need an input stream to take data from. - const TEST_DATA = "this is a test string"; - let istream = Cc["@mozilla.org/io/string-input-stream;1"]. - createInstance(Ci.nsIStringInputStream); - istream.setData(TEST_DATA, TEST_DATA.length); +function test_async_write_file_safe() { + async_write_file(SAFE_OUTPUT_STREAM_CONTRACT_ID); +} - NetUtil.asyncCopy(istream, ostream, function(aResult) { - // Make sure the copy was successful! - do_check_true(Components.isSuccessCode(aResult)); - - // Check the file contents. - do_check_eq(TEST_DATA, getFileContents(file)); - - // Finish the test. - do_test_finished(); - run_next_test(); - }); +function test_async_write_file_safe_deferred() { + async_write_file(SAFE_OUTPUT_STREAM_CONTRACT_ID, true); } function test_newURI_no_spec_throws() @@ -529,7 +520,9 @@ function test_readInputStreamToString_too_many_bytes() let tests = [ test_async_write_file, - test_async_write_file_nsISafeOutputStream, + test_async_write_file_deferred, + test_async_write_file_safe, + test_async_write_file_safe_deferred, test_newURI_no_spec_throws, test_newURI, test_newURI_takes_nsIFile, diff --git a/netwerk/test/unit/test_filestreams.js b/netwerk/test/unit/test_filestreams.js new file mode 100644 index 00000000000..bb3ad1b9787 --- /dev/null +++ b/netwerk/test/unit/test_filestreams.js @@ -0,0 +1,231 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +let Cc = Components.classes; +let Ci = Components.interfaces; + +// We need the profile directory so the test harness will clean up our test +// files. +do_get_profile(); + +const OUTPUT_STREAM_CONTRACT_ID = "@mozilla.org/network/file-output-stream;1"; +const SAFE_OUTPUT_STREAM_CONTRACT_ID = "@mozilla.org/network/safe-file-output-stream;1"; + +//////////////////////////////////////////////////////////////////////////////// +//// Helper Methods + +/** + * Generates a leafName for a file that does not exist, but does *not* + * create the file. Similar to createUnique except for the fact that createUnique + * does create the file. + * + * @param aFile + * The file to modify in order for it to have a unique leafname. + */ +function ensure_unique(aFile) +{ + ensure_unique.fileIndex = ensure_unique.fileIndex || 0; + + var leafName = aFile.leafName; + while (aFile.clone().exists()) { + aFile.leafName = leafName + "_" + (ensure_unique.fileIndex++); + } +} + +/** + * Tests for files being accessed at the right time. Streams that use + * DEFER_OPEN should only open or create the file when an operation is + * done, and not during Init(). + * + * Note that for writing, we check for actual writing in test_NetUtil (async) + * and in sync_operations in this file (sync), whereas in this function we + * just check that the file is *not* created during init. + * + * @param aContractId + * The contract ID to use for the output stream + * @param aDeferOpen + * Whether to check with DEFER_OPEN or not + * @param aTrickDeferredOpen + * Whether we try to 'trick' deferred opens by changing the file object before + * the actual open. The stream should have a clone, so changes to the file + * object after Init and before Open should not affect it. + */ +function check_access(aContractId, aDeferOpen, aTrickDeferredOpen) +{ + const LEAF_NAME = "filestreams-test-file.tmp"; + const TRICKY_LEAF_NAME = "BetYouDidNotExpectThat.tmp"; + let file = Cc["@mozilla.org/file/directory_service;1"]. + getService(Ci.nsIProperties). + get("ProfD", Ci.nsIFile); + file.append(LEAF_NAME); + + // Writing + + ensure_unique(file); + let ostream = Cc[aContractId].createInstance(Ci.nsIFileOutputStream); + ostream.init(file, -1, -1, aDeferOpen ? Ci.nsIFileOutputStream.DEFER_OPEN : 0); + do_check_eq(aDeferOpen, !file.clone().exists()); // If defer, should not exist and vice versa + if (aDeferOpen) { + // File should appear when we do write to it. + if (aTrickDeferredOpen) { + // See |@param aDeferOpen| in the JavaDoc comment for this function + file.leafName = TRICKY_LEAF_NAME; + } + ostream.write("data", 4); + if (aTrickDeferredOpen) { + file.leafName = LEAF_NAME; + } + // We did a write, so the file should now exist + do_check_true(file.clone().exists()); + } + ostream.close(); + + // Reading + + ensure_unique(file); + let istream = Cc["@mozilla.org/network/file-input-stream;1"]. + createInstance(Ci.nsIFileInputStream); + var initOk, getOk; + try { + istream.init(file, -1, 0, aDeferOpen ? Ci.nsIFileInputStream.DEFER_OPEN : 0); + initOk = true; + } + catch(e) { + initOk = false; + } + try { + let fstream = Cc["@mozilla.org/network/file-input-stream;1"]. + createInstance(Ci.nsIFileInputStream); + fstream.init(aFile, -1, 0, 0); + getOk = true; + } + catch(e) { + getOk = false; + } + + // If the open is deferred, then Init should succeed even though the file we + // intend to read does not exist, and then trying to read from it should + // fail. The other case is where the open is not deferred, and there we should + // get an error when we Init (and also when we try to read). + do_check_true( (aDeferOpen && initOk && !getOk) || + (!aDeferOpen && !initOk && !getOk) ); + istream.close(); +} + +/** + * We test async operations in test_NetUtil.js, and here test for simple sync + * operations on input streams. + * + * @param aDeferOpen + * Whether to use DEFER_OPEN in the streams. + */ +function sync_operations(aDeferOpen) +{ + const TEST_DATA = "this is a test string"; + const LEAF_NAME = "filestreams-test-file.tmp"; + + let file = Cc["@mozilla.org/file/directory_service;1"]. + getService(Ci.nsIProperties). + get("ProfD", Ci.nsIFile); + file.append(LEAF_NAME); + file.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, 0666); + + let ostream = Cc[OUTPUT_STREAM_CONTRACT_ID]. + createInstance(Ci.nsIFileOutputStream); + ostream.init(file, -1, -1, aDeferOpen ? Ci.nsIFileOutputStream.DEFER_OPEN : 0); + + ostream.write(TEST_DATA, TEST_DATA.length); + ostream.close(); + + let fstream = Cc["@mozilla.org/network/file-input-stream;1"]. + createInstance(Ci.nsIFileInputStream); + fstream.init(file, -1, 0, aDeferOpen ? Ci.nsIFileInputStream.DEFER_OPEN : 0); + + let cstream = Cc["@mozilla.org/intl/converter-input-stream;1"]. + createInstance(Ci.nsIConverterInputStream); + cstream.init(fstream, "UTF-8", 0, 0); + + let string = {}; + cstream.readString(-1, string); + cstream.close(); + fstream.close(); + + do_check_eq(string.value, TEST_DATA); +} + +//////////////////////////////////////////////////////////////////////////////// +//// Tests + +function test_access() +{ + check_access(OUTPUT_STREAM_CONTRACT_ID, false, false); +} + +function test_access_trick() +{ + check_access(OUTPUT_STREAM_CONTRACT_ID, false, true); +} + +function test_access_defer() +{ + check_access(OUTPUT_STREAM_CONTRACT_ID, true, false); +} + +function test_access_defer_trick() +{ + check_access(OUTPUT_STREAM_CONTRACT_ID, true, true); +} + +function test_access_safe() +{ + check_access(SAFE_OUTPUT_STREAM_CONTRACT_ID, false, false); +} + +function test_access_safe_trick() +{ + check_access(SAFE_OUTPUT_STREAM_CONTRACT_ID, false, true); +} + +function test_access_safe_defer() +{ + check_access(SAFE_OUTPUT_STREAM_CONTRACT_ID, true, false); +} + +function test_access_safe_defer_trick() +{ + check_access(SAFE_OUTPUT_STREAM_CONTRACT_ID, true, true); +} + +function test_sync_operations() +{ + sync_operations(); +} + +function test_sync_operations_deferred() +{ + sync_operations(true); +} + +//////////////////////////////////////////////////////////////////////////////// +//// Test Runner + +let tests = [ + test_access, + test_access_trick, + test_access_defer, + test_access_defer_trick, + test_access_safe, + test_access_safe_trick, + test_access_safe_defer, + test_access_safe_defer_trick, + test_sync_operations, + test_sync_operations_deferred, +]; + +function run_test() +{ + tests.forEach(function(test) { + test(); + }); +} + diff --git a/toolkit/components/search/nsSearchService.js b/toolkit/components/search/nsSearchService.js index e08e68d99eb..36e438457b1 100644 --- a/toolkit/components/search/nsSearchService.js +++ b/toolkit/components/search/nsSearchService.js @@ -2560,7 +2560,7 @@ SearchService.prototype = { try { LOG("_buildCache: Writing to cache file."); - ostream.init(cacheFile, (MODE_WRONLY | MODE_CREATE | MODE_TRUNCATE), PERMS_FILE, 0); + ostream.init(cacheFile, (MODE_WRONLY | MODE_CREATE | MODE_TRUNCATE), PERMS_FILE, ostream.DEFER_OPEN); converter.charset = "UTF-8"; let data = converter.convertToInputStream(JSON.stringify(cache));