зеркало из https://github.com/mozilla/pjs.git
Bug 634666 - DEFER_OPEN option for file streams. r=bz,sdwilsh sr=bz a=blocking-fennec
This commit is contained in:
Родитель
c672f6531e
Коммит
2944a770b8
|
@ -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"].
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -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<nsIFile> 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<nsILocalFile> 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<nsILocalFile> 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<nsILocalFile> 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;
|
||||
}
|
||||
|
|
|
@ -64,13 +64,57 @@ public:
|
|||
virtual ~nsFileStream();
|
||||
|
||||
nsresult Close();
|
||||
nsresult InitWithFileDescriptor(PRFileDesc* fd, nsISupports* parent);
|
||||
|
||||
protected:
|
||||
PRFileDesc* mFD;
|
||||
nsCOMPtr<nsISupports> 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<nsILocalFile> 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);
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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();
|
||||
});
|
||||
}
|
||||
|
|
@ -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));
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче