Bug 634666 - DEFER_OPEN option for file streams. r=bz,sdwilsh sr=bz a=blocking-fennec

This commit is contained in:
Alon Zakai 2011-03-04 16:36:56 -08:00
Родитель c672f6531e
Коммит 2944a770b8
7 изменённых файлов: 464 добавлений и 84 удалений

Просмотреть файл

@ -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));