Bug 787804 - Rewrite quota handling (eliminate test_quota.c). r=bent,asuth,vladan

This commit is contained in:
Jan Varga 2012-12-17 20:25:10 +01:00
parent 5eddaa4414
commit 812af47d24
50 changed files with 1245 additions and 3393 deletions

View File

@ -9,8 +9,8 @@ Makefile.in there) that we use to build.
To move to a new version:
Copy the sqlite3.h and sqlite3.c files from the amalgamation of sqlite. Also,
copy test_quota.h and test_quota.c from the full source package.
Copy the sqlite3.h and sqlite3.c files from the amalgamation of sqlite.
(We no longer use test_quota.h and test_quota.c)
Be sure to update SQLITE_VERSION accordingly in $(topsrcdir)/configure.in as
well as the version number at the top of this file.

View File

@ -132,6 +132,7 @@ EXPORTS
sqlite3_transfer_bindings
sqlite3_unlock_notify
sqlite3_update_hook
sqlite3_uri_parameter
sqlite3_user_data
sqlite3_value_blob
sqlite3_value_bytes

File diff suppressed because it is too large Load Diff

View File

@ -1,274 +0,0 @@
/*
** 2011 December 1
**
** The author disclaims copyright to this source code. In place of
** a legal notice, here is a blessing:
**
** May you do good and not evil.
** May you find forgiveness for yourself and forgive others.
** May you share freely, never taking more than you give.
**
*************************************************************************
**
** This file contains the interface definition for the quota a VFS shim.
**
** This particular shim enforces a quota system on files. One or more
** database files are in a "quota group" that is defined by a GLOB
** pattern. A quota is set for the combined size of all files in the
** the group. A quota of zero means "no limit". If the total size
** of all files in the quota group is greater than the limit, then
** write requests that attempt to enlarge a file fail with SQLITE_FULL.
**
** However, before returning SQLITE_FULL, the write requests invoke
** a callback function that is configurable for each quota group.
** This callback has the opportunity to enlarge the quota. If the
** callback does enlarge the quota such that the total size of all
** files within the group is less than the new quota, then the write
** continues as if nothing had happened.
*/
#ifndef _QUOTA_H_
#include "sqlite3.h"
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#if SQLITE_OS_UNIX
# include <unistd.h>
#endif
#if SQLITE_OS_WIN
# include <windows.h>
#endif
/* Make this callable from C++ */
#ifdef __cplusplus
extern "C" {
#endif
/*
** Initialize the quota VFS shim. Use the VFS named zOrigVfsName
** as the VFS that does the actual work. Use the default if
** zOrigVfsName==NULL.
**
** The quota VFS shim is named "quota". It will become the default
** VFS if makeDefault is non-zero.
**
** THIS ROUTINE IS NOT THREADSAFE. Call this routine exactly once
** during start-up.
*/
int sqlite3_quota_initialize(const char *zOrigVfsName, int makeDefault);
/*
** Shutdown the quota system.
**
** All SQLite database connections must be closed before calling this
** routine.
**
** THIS ROUTINE IS NOT THREADSAFE. Call this routine exactly once while
** shutting down in order to free all remaining quota groups.
*/
int sqlite3_quota_shutdown(void);
/*
** Create or destroy a quota group.
**
** The quota group is defined by the zPattern. When calling this routine
** with a zPattern for a quota group that already exists, this routine
** merely updates the iLimit, xCallback, and pArg values for that quota
** group. If zPattern is new, then a new quota group is created.
**
** The zPattern is always compared against the full pathname of the file.
** Even if APIs are called with relative pathnames, SQLite converts the
** name to a full pathname before comparing it against zPattern. zPattern
** is a glob pattern with the following matching rules:
**
** '*' Matches any sequence of zero or more characters.
**
** '?' Matches exactly one character.
**
** [...] Matches one character from the enclosed list of
** characters. "]" can be part of the list if it is
** the first character. Within the list "X-Y" matches
** characters X or Y or any character in between the
** two. Ex: "[0-9]" matches any digit.
**
** [^...] Matches one character not in the enclosed list.
**
** / Matches either / or \. This allows glob patterns
** containing / to work on both unix and windows.
**
** Note that, unlike unix shell globbing, the directory separator "/"
** can match a wildcard. So, for example, the pattern "/abc/xyz/" "*"
** matches any files anywhere in the directory hierarchy beneath
** /abc/xyz.
**
** The glob algorithm works on bytes. Multi-byte UTF8 characters are
** matched as if each byte were a separate character.
**
** If the iLimit for a quota group is set to zero, then the quota group
** is disabled and will be deleted when the last database connection using
** the quota group is closed.
**
** Calling this routine on a zPattern that does not exist and with a
** zero iLimit is a no-op.
**
** A quota group must exist with a non-zero iLimit prior to opening
** database connections if those connections are to participate in the
** quota group. Creating a quota group does not affect database connections
** that are already open.
**
** The patterns that define the various quota groups should be distinct.
** If the same filename matches more than one quota group pattern, then
** the behavior of this package is undefined.
*/
int sqlite3_quota_set(
const char *zPattern, /* The filename pattern */
sqlite3_int64 iLimit, /* New quota to set for this quota group */
void (*xCallback)( /* Callback invoked when going over quota */
const char *zFilename, /* Name of file whose size increases */
sqlite3_int64 *piLimit, /* IN/OUT: The current limit */
sqlite3_int64 iSize, /* Total size of all files in the group */
void *pArg /* Client data */
),
void *pArg, /* client data passed thru to callback */
void (*xDestroy)(void*) /* Optional destructor for pArg */
);
/*
** Bring the named file under quota management, assuming its name matches
** the glob pattern of some quota group. Or if it is already under
** management, update its size. If zFilename does not match the glob
** pattern of any quota group, this routine is a no-op.
*/
int sqlite3_quota_file(const char *zFilename);
/*
** The following object serves the same role as FILE in the standard C
** library. It represents an open connection to a file on disk for I/O.
**
** A single quota_FILE should not be used by two or more threads at the
** same time. Multiple threads can be using different quota_FILE objects
** simultaneously, but not the same quota_FILE object.
*/
typedef struct quota_FILE quota_FILE;
/*
** Create a new quota_FILE object used to read and/or write to the
** file zFilename. The zMode parameter is as with standard library zMode.
*/
quota_FILE *sqlite3_quota_fopen(const char *zFilename, const char *zMode);
/*
** Perform I/O against a quota_FILE object. When doing writes, the
** quota mechanism may result in a short write, in order to prevent
** the sum of sizes of all files from going over quota.
*/
size_t sqlite3_quota_fread(void*, size_t, size_t, quota_FILE*);
size_t sqlite3_quota_fwrite(const void*, size_t, size_t, quota_FILE*);
/*
** Flush all written content held in memory buffers out to disk.
** This is the equivalent of fflush() in the standard library.
**
** If the hardSync parameter is true (non-zero) then this routine
** also forces OS buffers to disk - the equivalent of fsync().
**
** This routine return zero on success and non-zero if something goes
** wrong.
*/
int sqlite3_quota_fflush(quota_FILE*, int hardSync);
/*
** Close a quota_FILE object and free all associated resources. The
** file remains under quota management.
*/
int sqlite3_quota_fclose(quota_FILE*);
/*
** Move the read/write pointer for a quota_FILE object. Or tell the
** current location of the read/write pointer.
*/
int sqlite3_quota_fseek(quota_FILE*, long, int);
void sqlite3_quota_rewind(quota_FILE*);
long sqlite3_quota_ftell(quota_FILE*);
/*
** Test the error indicator for the given file.
**
** Return non-zero if the error indicator is set.
*/
int sqlite3_quota_ferror(quota_FILE*);
/*
** Truncate a file previously opened by sqlite3_quota_fopen(). Return
** zero on success and non-zero on any kind of failure.
**
** The newSize argument must be less than or equal to the current file size.
** Any attempt to "truncate" a file to a larger size results in
** undefined behavior.
*/
int sqlite3_quota_ftruncate(quota_FILE*, sqlite3_int64 newSize);
/*
** Return the last modification time of the opened file, in seconds
** since 1970.
*/
int sqlite3_quota_file_mtime(quota_FILE*, time_t *pTime);
/*
** Return the size of the file as it is known to the quota system.
**
** This size might be different from the true size of the file on
** disk if some outside process has modified the file without using the
** quota mechanism, or if calls to sqlite3_quota_fwrite() have occurred
** which have increased the file size, but those writes have not yet been
** forced to disk using sqlite3_quota_fflush().
**
** Return -1 if the file is not participating in quota management.
*/
sqlite3_int64 sqlite3_quota_file_size(quota_FILE*);
/*
** Return the true size of the file.
**
** The true size should be the same as the size of the file as known
** to the quota system, however the sizes might be different if the
** file has been extended or truncated via some outside process or if
** pending writes have not yet been flushed to disk.
**
** Return -1 if the file does not exist or if the size of the file
** cannot be determined for some reason.
*/
sqlite3_int64 sqlite3_quota_file_truesize(quota_FILE*);
/*
** Determine the amount of data in bytes available for reading
** in the given file.
**
** Return -1 if the amount cannot be determined for some reason.
*/
long sqlite3_quota_file_available(quota_FILE*);
/*
** Delete a file from the disk, if that file is under quota management.
** Adjust quotas accordingly.
**
** If zFilename is the name of a directory that matches one of the
** quota glob patterns, then all files under quota management that
** are contained within that directory are deleted.
**
** A standard SQLite result code is returned (SQLITE_OK, SQLITE_NOMEM, etc.)
** When deleting a directory of files, if the deletion of any one
** file fails (for example due to an I/O error), then this routine
** returns immediately, with the error code, and does not try to
** delete any of the other files in the specified directory.
**
** All files are removed from quota management and deleted from disk.
** However, no attempt is made to remove empty directories.
**
** This routine is a no-op for files that are not under quota management.
*/
int sqlite3_quota_remove(const char *zFilename);
#ifdef __cplusplus
} /* end of the 'extern "C"' block */
#endif
#endif /* _QUOTA_H_ */

View File

@ -58,6 +58,7 @@ PARALLEL_DIRS += \
media \
messages \
power \
quota \
settings \
sms \
mms \

View File

@ -8,6 +8,7 @@ DOM_SRCDIRS = \
dom/encoding \
dom/file \
dom/power \
dom/quota \
dom/media \
dom/network/src \
dom/settings \

View File

@ -8,7 +8,6 @@
#include "nsIFileStorage.h"
#include "nsISeekableStream.h"
#include "nsIStandardFileStream.h"
#include "mozilla/Attributes.h"
#include "FileHelper.h"
@ -246,16 +245,6 @@ FileOutputStreamWrapper::Close()
nsresult rv = NS_OK;
if (!mFirstTime) {
// We must flush buffers of the stream on the same thread on which we wrote
// some data.
nsCOMPtr<nsIStandardFileStream> sstream = do_QueryInterface(mFileStream);
if (sstream) {
rv = sstream->FlushBuffers();
if (NS_FAILED(rv)) {
NS_WARNING("Failed to flush buffers of the stream!");
}
}
NS_ASSERTION(PR_GetCurrentThread() == mWriteThread,
"Unsetting thread locals on wrong thread!");
mFileHelper->mFileStorage->UnsetThreadLocals();

View File

@ -953,10 +953,10 @@ FinishHelper::Run()
}
for (uint32_t index = 0; index < mParallelStreams.Length(); index++) {
nsCOMPtr<nsIOutputStream> ostream =
nsCOMPtr<nsIInputStream> stream =
do_QueryInterface(mParallelStreams[index]);
if (NS_FAILED(ostream->Close())) {
if (NS_FAILED(stream->Close())) {
NS_WARNING("Failed to close stream!");
}
@ -964,9 +964,9 @@ FinishHelper::Run()
}
if (mStream) {
nsCOMPtr<nsIOutputStream> ostream = do_QueryInterface(mStream);
nsCOMPtr<nsIInputStream> stream = do_QueryInterface(mStream);
if (NS_FAILED(ostream->Close())) {
if (NS_FAILED(stream->Close())) {
NS_WARNING("Failed to close stream!");
}

View File

@ -10,14 +10,17 @@
#include "nsISupports.h"
#define NS_FILESTORAGE_IID \
{0xbba9c2ff, 0x85c9, 0x47c1, \
{ 0xaf, 0xce, 0x0a, 0x7e, 0x6f, 0x21, 0x50, 0x95 } }
{0xa0801944, 0x2f1c, 0x4203, \
{ 0x9c, 0xaa, 0xaa, 0x47, 0xe0, 0x0c, 0x67, 0x92 } }
class nsIFileStorage : public nsISupports
{
public:
NS_DECLARE_STATIC_IID_ACCESSOR(NS_FILESTORAGE_IID)
virtual const nsACString&
StorageOrigin() = 0;
virtual nsISupports*
StorageId() = 0;
@ -36,20 +39,23 @@ public:
NS_DEFINE_STATIC_IID_ACCESSOR(nsIFileStorage, NS_FILESTORAGE_IID)
#define NS_DECL_NSIFILESTORAGE \
virtual nsISupports* \
StorageId(); \
\
virtual bool \
IsStorageInvalidated(); \
\
virtual bool \
IsStorageShuttingDown(); \
\
virtual void \
SetThreadLocals(); \
\
virtual void \
UnsetThreadLocals();
#define NS_DECL_NSIFILESTORAGE \
virtual const nsACString& \
StorageOrigin() MOZ_OVERRIDE; \
\
virtual nsISupports* \
StorageId() MOZ_OVERRIDE; \
\
virtual bool \
IsStorageInvalidated() MOZ_OVERRIDE; \
\
virtual bool \
IsStorageShuttingDown() MOZ_OVERRIDE; \
\
virtual void \
SetThreadLocals() MOZ_OVERRIDE; \
\
virtual void \
UnsetThreadLocals() MOZ_OVERRIDE;
#endif // nsIFileStorage_h__

View File

@ -7,8 +7,8 @@
#include "FileManager.h"
#include "mozIStorageConnection.h"
#include "mozIStorageServiceQuotaManagement.h"
#include "mozIStorageStatement.h"
#include "nsIInputStream.h"
#include "nsISimpleEnumerator.h"
#include "mozStorageCID.h"
@ -18,6 +18,8 @@
#include "IndexedDatabaseManager.h"
#include "OpenDatabaseHelper.h"
#include "IndexedDatabaseInlines.h"
#define JOURNAL_DIRECTORY_NAME "journals"
USING_INDEXEDDB_NAMESPACE
@ -262,13 +264,11 @@ FileManager::GetFileForId(nsIFile* aDirectory, int64_t aId)
// static
nsresult
FileManager::InitDirectory(mozIStorageServiceQuotaManagement* aService,
nsIFile* aDirectory,
FileManager::InitDirectory(nsIFile* aDirectory,
nsIFile* aDatabaseFile,
FactoryPrivilege aPrivilege)
const nsACString& aOrigin)
{
NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
NS_ASSERTION(aService, "Null service!");
NS_ASSERTION(aDirectory, "Null directory!");
NS_ASSERTION(aDatabaseFile, "Null database file!");
@ -310,8 +310,8 @@ FileManager::InitDirectory(mozIStorageServiceQuotaManagement* aService,
if (hasElements) {
nsCOMPtr<mozIStorageConnection> connection;
rv = OpenDatabaseHelper::CreateDatabaseConnection(
NullString(), aDatabaseFile, aDirectory, getter_AddRefs(connection));
rv = OpenDatabaseHelper::CreateDatabaseConnection(aDatabaseFile,
aDirectory, NullString(), aOrigin, getter_AddRefs(connection));
NS_ENSURE_SUCCESS(rv, rv);
mozStorageTransaction transaction(connection, false);
@ -377,12 +377,17 @@ FileManager::InitDirectory(mozIStorageServiceQuotaManagement* aService,
}
}
if (aPrivilege == Chrome) {
return NS_OK;
}
return NS_OK;
}
// static
nsresult
FileManager::GetUsage(nsIFile* aDirectory, uint64_t* aUsage)
{
uint64_t usage = 0;
nsCOMPtr<nsISimpleEnumerator> entries;
rv = aDirectory->GetDirectoryEntries(getter_AddRefs(entries));
nsresult rv = aDirectory->GetDirectoryEntries(getter_AddRefs(entries));
NS_ENSURE_SUCCESS(rv, rv);
bool hasMore;
@ -402,9 +407,13 @@ FileManager::InitDirectory(mozIStorageServiceQuotaManagement* aService,
continue;
}
rv = aService->UpdateQuotaInformationForFile(file);
int64_t fileSize;
rv = file->GetFileSize(&fileSize);
NS_ENSURE_SUCCESS(rv, rv);
IncrementUsage(&usage, uint64_t(fileSize));
}
*aUsage = usage;
return NS_OK;
}

View File

@ -24,10 +24,10 @@ class FileManager
friend class FileInfo;
public:
FileManager(const nsACString& aOrigin,
FileManager(const nsACString& aOrigin, FactoryPrivilege aPrivilege,
const nsAString& aDatabaseName)
: mOrigin(aOrigin), mDatabaseName(aDatabaseName), mLastFileId(0),
mInvalidated(false)
: mOrigin(aOrigin), mPrivilege(aPrivilege), mDatabaseName(aDatabaseName),
mLastFileId(0), mInvalidated(false)
{ }
~FileManager()
@ -40,6 +40,11 @@ public:
return mOrigin;
}
const FactoryPrivilege& Privilege() const
{
return mPrivilege;
}
const nsAString& DatabaseName() const
{
return mDatabaseName;
@ -68,12 +73,15 @@ public:
static already_AddRefed<nsIFile> GetFileForId(nsIFile* aDirectory,
int64_t aId);
static nsresult InitDirectory(mozIStorageServiceQuotaManagement* aService,
nsIFile* aDirectory, nsIFile* aDatabaseFile,
FactoryPrivilege aPrivilege);
static nsresult InitDirectory(nsIFile* aDirectory,
nsIFile* aDatabaseFile,
const nsACString& aOrigin);
static nsresult GetUsage(nsIFile* aDirectory, uint64_t* aUsage);
private:
nsCString mOrigin;
FactoryPrivilege mPrivilege;
nsString mDatabaseName;
nsString mDirectoryPath;

View File

@ -1,321 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "FileStream.h"
#include "nsIFile.h"
#include "nsThreadUtils.h"
#include "test_quota.h"
USING_INDEXEDDB_NAMESPACE
NS_IMPL_THREADSAFE_ADDREF(FileStream)
NS_IMPL_THREADSAFE_RELEASE(FileStream)
NS_INTERFACE_MAP_BEGIN(FileStream)
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIStandardFileStream)
NS_INTERFACE_MAP_ENTRY(nsISeekableStream)
NS_INTERFACE_MAP_ENTRY(nsIInputStream)
NS_INTERFACE_MAP_ENTRY(nsIOutputStream)
NS_INTERFACE_MAP_ENTRY(nsIStandardFileStream)
NS_INTERFACE_MAP_ENTRY(nsIFileMetadata)
NS_INTERFACE_MAP_END
NS_IMETHODIMP
FileStream::Seek(int32_t aWhence, int64_t aOffset)
{
// TODO: Add support for 64 bit file sizes, bug 752431
NS_ENSURE_TRUE(aOffset <= INT32_MAX, NS_ERROR_INVALID_ARG);
nsresult rv = DoPendingOpen();
NS_ENSURE_SUCCESS(rv, rv);
if (!mQuotaFile) {
return NS_BASE_STREAM_CLOSED;
}
int whence;
switch (aWhence) {
case nsISeekableStream::NS_SEEK_SET:
whence = SEEK_SET;
break;
case nsISeekableStream::NS_SEEK_CUR:
whence = SEEK_CUR;
break;
case nsISeekableStream::NS_SEEK_END:
whence = SEEK_END;
break;
default:
return NS_ERROR_INVALID_ARG;
}
NS_ASSERTION(!NS_IsMainThread(), "Performing sync IO on the main thread!");
int rc = sqlite3_quota_fseek(mQuotaFile, aOffset, whence);
NS_ENSURE_TRUE(rc == 0, NS_BASE_STREAM_OSERROR);
return NS_OK;
}
NS_IMETHODIMP
FileStream::Tell(int64_t* aResult)
{
nsresult rv = DoPendingOpen();
NS_ENSURE_SUCCESS(rv, rv);
if (!mQuotaFile) {
return NS_BASE_STREAM_CLOSED;
}
NS_ASSERTION(!NS_IsMainThread(), "Performing sync IO on the main thread!");
long rc = sqlite3_quota_ftell(mQuotaFile);
NS_ENSURE_TRUE(rc >= 0, NS_BASE_STREAM_OSERROR);
*aResult = rc;
return NS_OK;
}
NS_IMETHODIMP
FileStream::SetEOF()
{
int64_t pos;
nsresult rv = Tell(&pos);
NS_ENSURE_SUCCESS(rv, rv);
NS_ASSERTION(!NS_IsMainThread(), "Performing sync IO on the main thread!");
int rc = sqlite3_quota_ftruncate(mQuotaFile, pos);
NS_ENSURE_TRUE(rc == 0, NS_BASE_STREAM_OSERROR);
return NS_OK;
}
NS_IMETHODIMP
FileStream::Close()
{
CleanUpOpen();
if (mQuotaFile) {
NS_ASSERTION(!NS_IsMainThread(), "Performing sync IO on the main thread!");
int rc = sqlite3_quota_fclose(mQuotaFile);
mQuotaFile = nullptr;
NS_ENSURE_TRUE(rc == 0, NS_BASE_STREAM_OSERROR);
}
return NS_OK;
}
NS_IMETHODIMP
FileStream::Available(uint64_t* aResult)
{
nsresult rv = DoPendingOpen();
NS_ENSURE_SUCCESS(rv, rv);
if (!mQuotaFile) {
return NS_BASE_STREAM_CLOSED;
}
NS_ASSERTION(!NS_IsMainThread(), "Performing sync IO on the main thread!");
long rc = sqlite3_quota_file_available(mQuotaFile);
NS_ENSURE_TRUE(rc >= 0, NS_BASE_STREAM_OSERROR);
*aResult = rc;
return NS_OK;
}
NS_IMETHODIMP
FileStream::Read(char* aBuf, uint32_t aCount, uint32_t* aResult)
{
nsresult rv = DoPendingOpen();
NS_ENSURE_SUCCESS(rv, rv);
if (!mQuotaFile) {
return NS_BASE_STREAM_CLOSED;
}
NS_ASSERTION(!NS_IsMainThread(), "Performing sync IO on the main thread!");
size_t bytesRead = sqlite3_quota_fread(aBuf, 1, aCount, mQuotaFile);
if (bytesRead < aCount && sqlite3_quota_ferror(mQuotaFile)) {
return NS_BASE_STREAM_OSERROR;
}
*aResult = bytesRead;
return NS_OK;
}
NS_IMETHODIMP
FileStream::ReadSegments(nsWriteSegmentFun aWriter, void* aClosure,
uint32_t aCount, uint32_t* aResult)
{
NS_NOTREACHED("Don't call me!");
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
FileStream::IsNonBlocking(bool *aNonBlocking)
{
*aNonBlocking = false;
return NS_OK;
}
NS_IMETHODIMP
FileStream::Write(const char* aBuf, uint32_t aCount, uint32_t *aResult)
{
nsresult rv = DoPendingOpen();
NS_ENSURE_SUCCESS(rv, rv);
if (!mQuotaFile) {
return NS_BASE_STREAM_CLOSED;
}
NS_ASSERTION(!NS_IsMainThread(), "Performing sync IO on the main thread!");
size_t bytesWritten = sqlite3_quota_fwrite(aBuf, 1, aCount, mQuotaFile);
if (bytesWritten < aCount) {
return NS_BASE_STREAM_OSERROR;
}
*aResult = bytesWritten;
return NS_OK;
}
NS_IMETHODIMP
FileStream::Flush()
{
nsresult rv = DoPendingOpen();
NS_ENSURE_SUCCESS(rv, rv);
if (!mQuotaFile) {
return NS_BASE_STREAM_CLOSED;
}
NS_ASSERTION(!NS_IsMainThread(), "Performing sync IO on the main thread!");
int rc = sqlite3_quota_fflush(mQuotaFile, 1);
NS_ENSURE_TRUE(rc == 0, NS_BASE_STREAM_OSERROR);
return NS_OK;
}
NS_IMETHODIMP
FileStream::WriteFrom(nsIInputStream *inStr, uint32_t count, uint32_t *_retval)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
FileStream::WriteSegments(nsReadSegmentFun reader, void * closure, uint32_t count, uint32_t *_retval)
{
NS_NOTREACHED("Don't call me!");
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
FileStream::Init(nsIFile* aFile, const nsAString& aMode, int32_t aFlags)
{
NS_ASSERTION(!mQuotaFile && !mDeferredOpen, "Already initialized!");
nsresult rv = aFile->GetPath(mFilePath);
NS_ENSURE_SUCCESS(rv, rv);
mMode = aMode;
mFlags = aFlags;
if (mFlags & nsIStandardFileStream::FLAGS_DEFER_OPEN) {
mDeferredOpen = true;
return NS_OK;
}
return DoOpen();
}
NS_IMETHODIMP
FileStream::GetSize(int64_t* _retval)
{
nsresult rv = DoPendingOpen();
NS_ENSURE_SUCCESS(rv, rv);
if (!mQuotaFile) {
return NS_BASE_STREAM_CLOSED;
}
NS_ASSERTION(!NS_IsMainThread(), "Performing sync IO on the main thread!");
// TODO: Use sqlite3_quota_file_size() here, bug 760783
int64_t rc = sqlite3_quota_file_truesize(mQuotaFile);
NS_ASSERTION(rc >= 0, "The file is not under quota management!");
*_retval = rc;
return NS_OK;
}
NS_IMETHODIMP
FileStream::GetLastModified(int64_t* _retval)
{
nsresult rv = DoPendingOpen();
NS_ENSURE_SUCCESS(rv, rv);
if (!mQuotaFile) {
return NS_BASE_STREAM_CLOSED;
}
NS_ASSERTION(!NS_IsMainThread(), "Performing sync IO on the main thread!");
time_t mtime;
int rc = sqlite3_quota_file_mtime(mQuotaFile, &mtime);
NS_ENSURE_TRUE(rc == 0, NS_BASE_STREAM_OSERROR);
*_retval = mtime * PR_MSEC_PER_SEC;
return NS_OK;
}
NS_IMETHODIMP
FileStream::FlushBuffers()
{
nsresult rv = DoPendingOpen();
NS_ENSURE_SUCCESS(rv, rv);
if (!mQuotaFile) {
return NS_BASE_STREAM_CLOSED;
}
NS_ASSERTION(!NS_IsMainThread(), "Performing sync IO on the main thread!");
int rc = sqlite3_quota_fflush(mQuotaFile, 0);
NS_ENSURE_TRUE(rc == 0, NS_BASE_STREAM_OSERROR);
return NS_OK;
}
nsresult
FileStream::DoOpen()
{
NS_ASSERTION(!mFilePath.IsEmpty(), "Must have a file path");
NS_ASSERTION(!NS_IsMainThread(), "Performing sync IO on the main thread!");
quota_FILE* quotaFile =
sqlite3_quota_fopen(NS_ConvertUTF16toUTF8(mFilePath).get(),
NS_ConvertUTF16toUTF8(mMode).get());
CleanUpOpen();
if (!quotaFile) {
return NS_BASE_STREAM_OSERROR;
}
mQuotaFile = quotaFile;
return NS_OK;
}

View File

@ -1,140 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_dom_indexeddb_filestream_h__
#define mozilla_dom_indexeddb_filestream_h__
#include "IndexedDatabase.h"
#include "nsIFileStreams.h"
#include "nsIInputStream.h"
#include "nsIOutputStream.h"
#include "nsISeekableStream.h"
#include "nsIStandardFileStream.h"
class nsIFile;
struct quota_FILE;
BEGIN_INDEXEDDB_NAMESPACE
class FileStream : public nsISeekableStream,
public nsIInputStream,
public nsIOutputStream,
public nsIStandardFileStream,
public nsIFileMetadata
{
public:
FileStream()
: mFlags(0),
mDeferredOpen(false),
mQuotaFile(nullptr)
{ }
virtual ~FileStream()
{
Close();
}
NS_DECL_ISUPPORTS
NS_DECL_NSISEEKABLESTREAM
NS_DECL_NSISTANDARDFILESTREAM
NS_DECL_NSIFILEMETADATA
// nsIInputStream
NS_IMETHOD
Close();
NS_IMETHOD
Available(uint64_t* _retval);
NS_IMETHOD
Read(char* aBuf, uint32_t aCount, uint32_t* _retval);
NS_IMETHOD
ReadSegments(nsWriteSegmentFun aWriter, void* aClosure, uint32_t aCount,
uint32_t* _retval);
NS_IMETHOD
IsNonBlocking(bool* _retval);
// nsIOutputStream
// Close() already declared
NS_IMETHOD
Flush();
NS_IMETHOD
Write(const char* aBuf, uint32_t aCount, uint32_t* _retval);
NS_IMETHOD
WriteFrom(nsIInputStream* aFromStream, uint32_t aCount, uint32_t* _retval);
NS_IMETHOD
WriteSegments(nsReadSegmentFun aReader, void* aClosure, uint32_t aCount,
uint32_t* _retval);
// IsNonBlocking() already declared
protected:
/**
* Cleans up data prepared in Init.
*/
void
CleanUpOpen()
{
mFilePath.Truncate();
mDeferredOpen = false;
}
/**
* Open the file. This is called either from Init
* or from DoPendingOpen (if FLAGS_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
* inlined since we do it in almost every stream API call.
*/
nsresult
DoPendingOpen()
{
if (!mDeferredOpen) {
return NS_OK;
}
return DoOpen();
}
/**
* Data we need to do an open.
*/
nsString mFilePath;
nsString mMode;
/**
* Flags describing our behavior. See the IDL file for possible values.
*/
int32_t mFlags;
/**
* Whether we have a pending open (see FLAGS_DEFER_OPEN in the IDL file).
*/
bool mDeferredOpen;
/**
* File descriptor for opened file.
*/
quota_FILE* mQuotaFile;
};
END_INDEXEDDB_NAMESPACE
#endif // mozilla_dom_indexeddb_filestream_h__

View File

@ -779,6 +779,12 @@ IDBDatabase::Close()
return NS_OK;
}
const nsACString&
IDBDatabase::StorageOrigin()
{
return Origin();
}
nsISupports*
IDBDatabase::StorageId()
{

View File

@ -252,9 +252,27 @@ IDBFactory::Create(ContentParent* aContentParent,
return NS_OK;
}
// static
already_AddRefed<nsIFileURL>
IDBFactory::GetDatabaseFileURL(nsIFile* aDatabaseFile, const nsACString& aOrigin)
{
nsCOMPtr<nsIURI> uri;
nsresult rv = NS_NewFileURI(getter_AddRefs(uri), aDatabaseFile);
NS_ENSURE_SUCCESS(rv, nullptr);
nsCOMPtr<nsIFileURL> fileUrl = do_QueryInterface(uri);
NS_ASSERTION(fileUrl, "This should always succeed!");
rv = fileUrl->SetQuery(NS_LITERAL_CSTRING("origin=") + aOrigin);
NS_ENSURE_SUCCESS(rv, nullptr);
return fileUrl.forget();
}
// static
already_AddRefed<mozIStorageConnection>
IDBFactory::GetConnection(const nsAString& aDatabaseFilePath)
IDBFactory::GetConnection(const nsAString& aDatabaseFilePath,
const nsACString& aOrigin)
{
NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
NS_ASSERTION(StringEndsWith(aDatabaseFilePath, NS_LITERAL_STRING(".sqlite")),
@ -271,13 +289,15 @@ IDBFactory::GetConnection(const nsAString& aDatabaseFilePath)
NS_ENSURE_SUCCESS(rv, nullptr);
NS_ENSURE_TRUE(exists, nullptr);
nsCOMPtr<mozIStorageServiceQuotaManagement> ss =
nsCOMPtr<nsIFileURL> dbFileUrl = GetDatabaseFileURL(dbFile, aOrigin);
NS_ENSURE_TRUE(dbFileUrl, nullptr);
nsCOMPtr<mozIStorageService> ss =
do_GetService(MOZ_STORAGE_SERVICE_CONTRACTID);
NS_ENSURE_TRUE(ss, nullptr);
nsCOMPtr<mozIStorageConnection> connection;
rv = ss->OpenDatabaseWithVFS(dbFile, NS_LITERAL_CSTRING("quota"),
getter_AddRefs(connection));
rv = ss->OpenDatabaseWithFileURL(dbFileUrl, getter_AddRefs(connection));
NS_ENSURE_SUCCESS(rv, nullptr);
// Turn on foreign key constraints and recursive triggers.

View File

@ -15,6 +15,8 @@
#include "nsCycleCollectionParticipant.h"
class nsIAtom;
class nsIFile;
class nsIFileURL;
class nsPIDOMWindow;
namespace mozilla {
@ -75,8 +77,12 @@ public:
static nsresult Create(ContentParent* aContentParent,
IDBFactory** aFactory);
static already_AddRefed<nsIFileURL>
GetDatabaseFileURL(nsIFile* aDatabaseFile, const nsACString& aOrigin);
static already_AddRefed<mozIStorageConnection>
GetConnection(const nsAString& aDatabaseFilePath);
GetConnection(const nsAString& aDatabaseFilePath,
const nsACString& aOrigin);
static nsresult
LoadDatabaseInformation(mozIStorageConnection* aConnection,

View File

@ -6,15 +6,14 @@
#include "IDBFileHandle.h"
#include "nsIStandardFileStream.h"
#include "mozilla/dom/file/File.h"
#include "mozilla/dom/quota/FileStreams.h"
#include "nsDOMClassInfoID.h"
#include "FileStream.h"
#include "IDBDatabase.h"
USING_INDEXEDDB_NAMESPACE
USING_QUOTA_NAMESPACE
namespace {
@ -68,22 +67,22 @@ IDBFileHandle::Create(IDBDatabase* aDatabase,
already_AddRefed<nsISupports>
IDBFileHandle::CreateStream(nsIFile* aFile, bool aReadOnly)
{
nsRefPtr<FileStream> stream = new FileStream();
const nsACString& origin = mFileStorage->StorageOrigin();
nsCOMPtr<nsISupports> result;
nsString streamMode;
if (aReadOnly) {
streamMode.AssignLiteral("rb");
nsRefPtr<FileInputStream> stream = FileInputStream::Create(
origin, aFile, -1, -1, nsIFileInputStream::DEFER_OPEN);
result = NS_ISUPPORTS_CAST(nsIFileInputStream*, stream);
}
else {
streamMode.AssignLiteral("r+b");
nsRefPtr<FileStream> stream = FileStream::Create(
origin, aFile, -1, -1, nsIFileStream::DEFER_OPEN);
result = NS_ISUPPORTS_CAST(nsIFileStream*, stream);
}
NS_ENSURE_TRUE(result, nullptr);
nsresult rv = stream->Init(aFile, streamMode,
nsIStandardFileStream::FLAGS_DEFER_OPEN);
NS_ENSURE_SUCCESS(rv, nullptr);
nsCOMPtr<nsISupports> result =
NS_ISUPPORTS_CAST(nsIStandardFileStream*, stream);
return result.forget();
}

View File

@ -17,6 +17,7 @@
#include "mozilla/dom/ContentParent.h"
#include "mozilla/dom/StructuredCloneTags.h"
#include "mozilla/dom/ipc/Blob.h"
#include "mozilla/dom/quota/FileStreams.h"
#include "mozilla/storage.h"
#include "nsContentUtils.h"
#include "nsDOMClassInfo.h"
@ -27,10 +28,8 @@
#include "nsServiceManagerUtils.h"
#include "nsThreadUtils.h"
#include "snappy/snappy.h"
#include "test_quota.h"
#include "AsyncConnectionHelper.h"
#include "FileStream.h"
#include "IDBCursor.h"
#include "IDBEvents.h"
#include "IDBFileHandle.h"
@ -51,6 +50,7 @@
USING_INDEXEDDB_NAMESPACE
using namespace mozilla::dom;
using namespace mozilla::dom::indexedDB::ipc;
using mozilla::dom::quota::FileOutputStream;
namespace {
@ -2734,9 +2734,9 @@ AddHelper::DoDatabaseWork(mozIStorageConnection* aConnection)
nativeFile = fileManager->GetFileForId(directory, id);
NS_ENSURE_TRUE(nativeFile, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
nsRefPtr<FileStream> outputStream = new FileStream();
rv = outputStream->Init(nativeFile, NS_LITERAL_STRING("wb"), 0);
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
nsRefPtr<FileOutputStream> outputStream = FileOutputStream::Create(
mObjectStore->Transaction()->Database()->Origin(), nativeFile);
NS_ENSURE_TRUE(outputStream, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
rv = CopyData(inputStream, outputStream);
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);

View File

@ -352,7 +352,8 @@ IDBTransaction::GetOrCreateConnection(mozIStorageConnection** aResult)
if (!mConnection) {
nsCOMPtr<mozIStorageConnection> connection =
IDBFactory::GetConnection(mDatabase->FilePath());
IDBFactory::GetConnection(mDatabase->FilePath(),
mDatabase->Origin());
NS_ENSURE_TRUE(connection, NS_ERROR_FAILURE);
nsresult rv;

View File

@ -79,4 +79,17 @@ AppendConditionClause(const nsACString& aColumnName,
aResult += NS_LITERAL_CSTRING(" :") + aArgName;
}
inline void
IncrementUsage(uint64_t* aUsage, uint64_t aDelta)
{
// Watch for overflow!
if ((UINT64_MAX - *aUsage) < aDelta) {
NS_WARNING("Usage exceeds the maximum!");
*aUsage = UINT64_MAX;
}
else {
*aUsage += aDelta;
}
}
END_INDEXEDDB_NAMESPACE

View File

@ -22,6 +22,7 @@
#include "nsITimer.h"
#include "mozilla/dom/file/FileService.h"
#include "mozilla/dom/quota/QuotaManager.h"
#include "mozilla/dom/TabContext.h"
#include "mozilla/LazyIdleThread.h"
#include "mozilla/Preferences.h"
@ -36,7 +37,6 @@
#include "nsThreadUtils.h"
#include "nsXPCOM.h"
#include "nsXPCOMPrivate.h"
#include "test_quota.h"
#include "xpcpublic.h"
#include "AsyncConnectionHelper.h"
@ -48,6 +48,8 @@
#include "OpenDatabaseHelper.h"
#include "TransactionThreadPool.h"
#include "IndexedDatabaseInlines.h"
// The amount of time, in milliseconds, that our IO thread will stay alive
// after the last event it processes.
#define DEFAULT_THREAD_TIMEOUT_MS 30000
@ -70,6 +72,7 @@ using namespace mozilla::services;
using namespace mozilla::dom;
using mozilla::Preferences;
using mozilla::dom::file::FileService;
using mozilla::dom::quota::QuotaManager;
static NS_DEFINE_CID(kDOMSOF_CID, NS_DOM_SCRIPT_OBJECT_FACTORY_CID);
@ -103,29 +106,6 @@ GetDatabaseBaseFilename(const nsAString& aFilename,
return true;
}
class QuotaCallback MOZ_FINAL : public mozIStorageQuotaCallback
{
public:
NS_DECL_ISUPPORTS
NS_IMETHOD
QuotaExceeded(const nsACString& aFilename,
int64_t aCurrentSizeLimit,
int64_t aCurrentTotalSize,
nsISupports* aUserData,
int64_t* _retval)
{
if (IndexedDatabaseManager::QuotaIsLifted()) {
*_retval = 0;
return NS_OK;
}
return NS_ERROR_FAILURE;
}
};
NS_IMPL_THREADSAFE_ISUPPORTS1(QuotaCallback, mozIStorageQuotaCallback)
// Adds all databases in the hash to the given array.
template <class T>
PLDHashOperator
@ -440,8 +420,8 @@ IndexedDatabaseManager::GetOrCreate()
NS_LITERAL_CSTRING("IndexedDB I/O"),
LazyIdleThread::ManualShutdown);
// We need one quota callback object to hand to SQLite.
instance->mQuotaCallbackSingleton = new QuotaCallback();
// Make sure that the quota manager is up.
NS_ENSURE_TRUE(QuotaManager::GetOrCreate(), nullptr);
// Make a timer here to avoid potential failures later. We don't actually
// initialize the timer until shutdown.
@ -996,37 +976,15 @@ IndexedDatabaseManager::EnsureOriginIsInitialized(const nsACString& aOrigin,
return NS_OK;
}
// First figure out the filename pattern we'll use.
nsCOMPtr<nsIFile> patternFile;
rv = directory->Clone(getter_AddRefs(patternFile));
NS_ENSURE_SUCCESS(rv, rv);
rv = patternFile->Append(NS_LITERAL_STRING("*"));
NS_ENSURE_SUCCESS(rv, rv);
nsString pattern;
rv = patternFile->GetPath(pattern);
NS_ENSURE_SUCCESS(rv, rv);
// Now tell SQLite to start tracking this pattern for content.
nsCOMPtr<mozIStorageServiceQuotaManagement> ss =
do_GetService(MOZ_STORAGE_SERVICE_CONTRACTID);
NS_ENSURE_TRUE(ss, NS_ERROR_FAILURE);
if (aPrivilege != Chrome) {
rv = ss->SetQuotaForFilenamePattern(NS_ConvertUTF16toUTF8(pattern),
GetIndexedDBQuotaMB() * 1024 * 1024,
mQuotaCallbackSingleton, nullptr);
NS_ENSURE_SUCCESS(rv, rv);
}
// We need to see if there are any files in the directory already. If they
// are database files then we need to cleanup stored files (if it's needed)
// and also tell SQLite about all of them.
// and also initialize the quota.
nsAutoTArray<nsString, 20> subdirsToProcess;
nsAutoTArray<nsCOMPtr<nsIFile> , 20> unknownFiles;
uint64_t usage = 0;
nsTHashtable<nsStringHashKey> validSubdirs;
validSubdirs.Init(20);
@ -1068,20 +1026,28 @@ IndexedDatabaseManager::EnsureOriginIsInitialized(const nsACString& aOrigin,
continue;
}
nsCOMPtr<nsIFile> fileManagerDirectory;
rv = directory->Clone(getter_AddRefs(fileManagerDirectory));
nsCOMPtr<nsIFile> fmDirectory;
rv = directory->Clone(getter_AddRefs(fmDirectory));
NS_ENSURE_SUCCESS(rv, rv);
rv = fileManagerDirectory->Append(dbBaseFilename);
rv = fmDirectory->Append(dbBaseFilename);
NS_ENSURE_SUCCESS(rv, rv);
rv = FileManager::InitDirectory(ss, fileManagerDirectory, file,
aPrivilege);
rv = FileManager::InitDirectory(fmDirectory, file, aOrigin);
NS_ENSURE_SUCCESS(rv, rv);
if (aPrivilege != Chrome) {
rv = ss->UpdateQuotaInformationForFile(file);
uint64_t fileUsage;
rv = FileManager::GetUsage(fmDirectory, &fileUsage);
NS_ENSURE_SUCCESS(rv, rv);
IncrementUsage(&usage, fileUsage);
int64_t fileSize;
rv = file->GetFileSize(&fileSize);
NS_ENSURE_SUCCESS(rv, rv);
IncrementUsage(&usage, uint64_t(fileSize));
}
validSubdirs.PutEntry(dbBaseFilename);
@ -1117,12 +1083,39 @@ IndexedDatabaseManager::EnsureOriginIsInitialized(const nsACString& aOrigin,
}
}
if (aPrivilege != Chrome) {
QuotaManager* quotaManager = QuotaManager::Get();
NS_ASSERTION(quotaManager, "Shouldn't be null!");
quotaManager->InitQuotaForOrigin(aOrigin, GetIndexedDBQuotaMB(), usage);
}
mInitializedOrigins.AppendElement(aOrigin);
NS_ADDREF(*aDirectory = directory);
return NS_OK;
}
void
IndexedDatabaseManager::UninitializeOriginsByPattern(
const nsACString& aPattern)
{
#ifdef DEBUG
{
bool correctThread;
NS_ASSERTION(NS_SUCCEEDED(mIOThread->IsOnCurrentThread(&correctThread)) &&
correctThread,
"Running on the wrong thread!");
}
#endif
for (int32_t i = mInitializedOrigins.Length() - 1; i >= 0; i--) {
if (PatternMatchesOrigin(aPattern, mInitializedOrigins[i])) {
mInitializedOrigins.RemoveElementAt(i);
}
}
}
bool
IndexedDatabaseManager::QuotaIsLiftedInternal()
{
@ -1250,16 +1243,14 @@ IndexedDatabaseManager::GetFileManager(const nsACString& aOrigin,
}
void
IndexedDatabaseManager::AddFileManager(const nsACString& aOrigin,
const nsAString& aDatabaseName,
FileManager* aFileManager)
IndexedDatabaseManager::AddFileManager(FileManager* aFileManager)
{
NS_ASSERTION(aFileManager, "Null file manager!");
nsTArray<nsRefPtr<FileManager> >* array;
if (!mFileManagers.Get(aOrigin, &array)) {
if (!mFileManagers.Get(aFileManager->Origin(), &array)) {
array = new nsTArray<nsRefPtr<FileManager> >();
mFileManagers.Put(aOrigin, array);
mFileManagers.Put(aFileManager->Origin(), array);
}
array->AppendElement(aFileManager);
@ -1783,6 +1774,13 @@ OriginClearRunnable::DeleteFiles(IndexedDatabaseManager* aManager)
// correctly...
NS_ERROR("Failed to remove directory!");
}
QuotaManager* quotaManager = QuotaManager::Get();
NS_ASSERTION(quotaManager, "Shouldn't be null!");
quotaManager->RemoveQuotaForPattern(mOriginOrPattern);
aManager->UninitializeOriginsByPattern(mOriginOrPattern);
}
}
@ -1880,19 +1878,6 @@ IndexedDatabaseManager::AsyncUsageRunnable::Cancel()
}
}
inline void
IncrementUsage(uint64_t* aUsage, uint64_t aDelta)
{
// Watch for overflow!
if ((INT64_MAX - *aUsage) <= aDelta) {
NS_WARNING("Database sizes exceed max we can report!");
*aUsage = INT64_MAX;
}
else {
*aUsage += aDelta;
}
}
nsresult
IndexedDatabaseManager::AsyncUsageRunnable::TakeShortcut()
{
@ -2295,25 +2280,22 @@ IndexedDatabaseManager::AsyncDeleteFileRunnable::Run()
nsCOMPtr<nsIFile> file = mFileManager->GetFileForId(directory, mFileId);
NS_ENSURE_TRUE(file, NS_ERROR_FAILURE);
nsString filePath;
nsresult rv = file->GetPath(filePath);
NS_ENSURE_SUCCESS(rv, rv);
nsresult rv;
int64_t fileSize;
int rc = sqlite3_quota_remove(NS_ConvertUTF16toUTF8(filePath).get());
if (rc != SQLITE_OK) {
NS_WARNING("Failed to delete stored file!");
return NS_ERROR_FAILURE;
if (mFileManager->Privilege() != Chrome) {
rv = file->GetFileSize(&fileSize);
NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
}
// sqlite3_quota_remove won't actually remove anything if we're not tracking
// the quota here. Manually remove the file if it exists.
bool exists;
rv = file->Exists(&exists);
NS_ENSURE_SUCCESS(rv, rv);
rv = file->Remove(false);
NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
if (exists) {
rv = file->Remove(false);
NS_ENSURE_SUCCESS(rv, rv);
if (mFileManager->Privilege() != Chrome) {
QuotaManager* quotaManager = QuotaManager::Get();
NS_ASSERTION(quotaManager, "Shouldn't be null!");
quotaManager->DecreaseUsageForOrigin(mFileManager->Origin(), fileSize);
}
directory = mFileManager->GetJournalDirectory();

View File

@ -23,7 +23,6 @@
#define INDEXEDDB_MANAGER_CONTRACTID "@mozilla.org/dom/indexeddb/manager;1"
class mozIStorageQuotaCallback;
class nsIAtom;
class nsIFile;
class nsITimer;
@ -134,6 +133,8 @@ public:
FactoryPrivilege aPrivilege,
nsIFile** aDirectory);
void UninitializeOriginsByPattern(const nsACString& aPattern);
// Determine if the quota is lifted for the Window the current thread is
// using.
static inline bool
@ -172,9 +173,7 @@ public:
const nsAString& aDatabaseName);
void
AddFileManager(const nsACString& aOrigin,
const nsAString& aDatabaseName,
FileManager* aFileManager);
AddFileManager(FileManager* aFileManager);
void InvalidateFileManagersForPattern(const nsACString& aPattern);
@ -502,10 +501,6 @@ private:
// A timer that gets activated at shutdown to ensure we close all databases.
nsCOMPtr<nsITimer> mShutdownTimer;
// A single threadsafe instance of our quota callback. Created on the main
// thread during GetOrCreate().
nsCOMPtr<mozIStorageQuotaCallback> mQuotaCallbackSingleton;
// A list of all successfully initialized origins. This list isn't protected
// by any mutex but it is only ever touched on the IO thread.
nsTArray<nsCString> mInitializedOrigins;

View File

@ -25,7 +25,6 @@ CPPSRCS = \
DatabaseInfo.cpp \
FileInfo.cpp \
FileManager.cpp \
FileStream.cpp \
IDBCursor.cpp \
IDBDatabase.cpp \
IDBEvents.cpp \
@ -93,7 +92,6 @@ XPIDLSRCS = \
nsIIDBVersionChangeEvent.idl \
nsIIDBOpenDBRequest.idl \
nsIIndexedDatabaseManager.idl \
nsIStandardFileStream.idl \
$(NULL)
DIRS += ipc

View File

@ -8,11 +8,12 @@
#include "nsIFile.h"
#include "mozilla/dom/quota/QuotaManager.h"
#include "mozilla/storage.h"
#include "nsEscape.h"
#include "nsNetUtil.h"
#include "nsThreadUtils.h"
#include "snappy/snappy.h"
#include "test_quota.h"
#include "nsIBFCacheEntry.h"
#include "IDBEvents.h"
@ -21,6 +22,7 @@
using namespace mozilla;
USING_INDEXEDDB_NAMESPACE
USING_QUOTA_NAMESPACE
namespace {
@ -1632,15 +1634,15 @@ OpenDatabaseHelper::DoDatabaseWork()
rv = dbFile->GetPath(mDatabaseFilePath);
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
nsCOMPtr<nsIFile> fileManagerDirectory;
rv = dbDirectory->Clone(getter_AddRefs(fileManagerDirectory));
nsCOMPtr<nsIFile> fmDirectory;
rv = dbDirectory->Clone(getter_AddRefs(fmDirectory));
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
rv = fileManagerDirectory->Append(filename);
rv = fmDirectory->Append(filename);
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
nsCOMPtr<mozIStorageConnection> connection;
rv = CreateDatabaseConnection(mName, dbFile, fileManagerDirectory,
rv = CreateDatabaseConnection(dbFile, fmDirectory, mName, mASCIIOrigin,
getter_AddRefs(connection));
if (NS_FAILED(rv) &&
NS_ERROR_GET_MODULE(rv) != NS_ERROR_MODULE_DOM_INDEXEDDB) {
@ -1691,12 +1693,12 @@ OpenDatabaseHelper::DoDatabaseWork()
nsRefPtr<FileManager> fileManager = mgr->GetFileManager(mASCIIOrigin, mName);
if (!fileManager) {
fileManager = new FileManager(mASCIIOrigin, mName);
fileManager = new FileManager(mASCIIOrigin, mPrivilege, mName);
rv = fileManager->Init(fileManagerDirectory, connection);
rv = fileManager->Init(fmDirectory, connection);
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
mgr->AddFileManager(mASCIIOrigin, mName, fileManager);
mgr->AddFileManager(fileManager);
}
mFileManager = fileManager.forget();
@ -1707,23 +1709,26 @@ OpenDatabaseHelper::DoDatabaseWork()
// static
nsresult
OpenDatabaseHelper::CreateDatabaseConnection(
const nsAString& aName,
nsIFile* aDBFile,
nsIFile* aFileManagerDirectory,
nsIFile* aFMDirectory,
const nsAString& aName,
const nsACString& aOrigin,
mozIStorageConnection** aConnection)
{
NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
NS_NAMED_LITERAL_CSTRING(quotaVFSName, "quota");
nsCOMPtr<nsIFileURL> dbFileUrl =
IDBFactory::GetDatabaseFileURL(aDBFile, aOrigin);
NS_ENSURE_TRUE(dbFileUrl, NS_ERROR_FAILURE);
nsCOMPtr<mozIStorageServiceQuotaManagement> ss =
nsCOMPtr<mozIStorageService> ss =
do_GetService(MOZ_STORAGE_SERVICE_CONTRACTID);
NS_ENSURE_TRUE(ss, NS_ERROR_FAILURE);
nsCOMPtr<mozIStorageConnection> connection;
nsresult rv = ss->OpenDatabaseWithVFS(aDBFile, quotaVFSName,
getter_AddRefs(connection));
nsresult rv =
ss->OpenDatabaseWithFileURL(dbFileUrl, getter_AddRefs(connection));
if (rv == NS_ERROR_FILE_CORRUPTED) {
// If we're just opening the database during origin initialization, then
// we don't want to erase any files. The failure here will fail origin
@ -1737,21 +1742,20 @@ OpenDatabaseHelper::CreateDatabaseConnection(
NS_ENSURE_SUCCESS(rv, rv);
bool exists;
rv = aFileManagerDirectory->Exists(&exists);
rv = aFMDirectory->Exists(&exists);
NS_ENSURE_SUCCESS(rv, rv);
if (exists) {
bool isDirectory;
rv = aFileManagerDirectory->IsDirectory(&isDirectory);
rv = aFMDirectory->IsDirectory(&isDirectory);
NS_ENSURE_SUCCESS(rv, rv);
NS_ENSURE_TRUE(isDirectory, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
rv = aFileManagerDirectory->Remove(true);
rv = aFMDirectory->Remove(true);
NS_ENSURE_SUCCESS(rv, rv);
}
rv = ss->OpenDatabaseWithVFS(aDBFile, quotaVFSName,
getter_AddRefs(connection));
rv = ss->OpenDatabaseWithFileURL(dbFileUrl, getter_AddRefs(connection));
}
NS_ENSURE_SUCCESS(rv, rv);
@ -2347,6 +2351,8 @@ DeleteDatabaseHelper::DoDatabaseWork(mozIStorageConnection* aConnection)
{
NS_ASSERTION(!aConnection, "How did we get a connection here?");
const FactoryPrivilege& privilege = mOpenHelper->Privilege();
IndexedDatabaseManager* mgr = IndexedDatabaseManager::Get();
NS_ASSERTION(mgr, "This should never fail!");
@ -2372,59 +2378,57 @@ DeleteDatabaseHelper::DoDatabaseWork(mozIStorageConnection* aConnection)
rv = dbFile->Exists(&exists);
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
int rc;
if (exists) {
nsString dbFilePath;
rv = dbFile->GetPath(dbFilePath);
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
int64_t fileSize;
rc = sqlite3_quota_remove(NS_ConvertUTF16toUTF8(dbFilePath).get());
if (rc != SQLITE_OK) {
NS_WARNING("Failed to delete db file!");
return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
if (privilege != Chrome) {
rv = dbFile->GetFileSize(&fileSize);
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
}
// sqlite3_quota_remove won't actually remove anything if we're not tracking
// the quota here. Manually remove the file if it exists.
rv = dbFile->Exists(&exists);
rv = dbFile->Remove(false);
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
if (exists) {
rv = dbFile->Remove(false);
NS_ENSURE_SUCCESS(rv, rv);
if (privilege != Chrome) {
QuotaManager* quotaManager = QuotaManager::Get();
NS_ASSERTION(quotaManager, "Shouldn't be null!");
quotaManager->DecreaseUsageForOrigin(mASCIIOrigin, fileSize);
}
}
nsCOMPtr<nsIFile> fileManagerDirectory;
rv = directory->Clone(getter_AddRefs(fileManagerDirectory));
NS_ENSURE_SUCCESS(rv, rv);
rv = fileManagerDirectory->Append(filename);
nsCOMPtr<nsIFile> fmDirectory;
rv = directory->Clone(getter_AddRefs(fmDirectory));
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
rv = fileManagerDirectory->Exists(&exists);
rv = fmDirectory->Append(filename);
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
rv = fmDirectory->Exists(&exists);
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
if (exists) {
bool isDirectory;
rv = fileManagerDirectory->IsDirectory(&isDirectory);
rv = fmDirectory->IsDirectory(&isDirectory);
NS_ENSURE_SUCCESS(rv, rv);
NS_ENSURE_TRUE(isDirectory, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
nsString fileManagerDirectoryPath;
rv = fileManagerDirectory->GetPath(fileManagerDirectoryPath);
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
uint64_t usage = 0;
rc = sqlite3_quota_remove(
NS_ConvertUTF16toUTF8(fileManagerDirectoryPath).get());
if (rc != SQLITE_OK) {
NS_WARNING("Failed to delete file directory!");
return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
if (privilege != Chrome) {
rv = FileManager::GetUsage(fmDirectory, &usage);
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
}
rv = fileManagerDirectory->Remove(true);
NS_ENSURE_SUCCESS(rv, rv);
rv = fmDirectory->Remove(true);
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
if (privilege != Chrome) {
QuotaManager* quotaManager = QuotaManager::Get();
NS_ASSERTION(quotaManager, "Shouldn't be null!");
quotaManager->DecreaseUsageForOrigin(mASCIIOrigin, usage);
}
}
return NS_OK;

View File

@ -77,10 +77,16 @@ public:
return mDatabase;
}
const FactoryPrivilege& Privilege() const
{
return mPrivilege;
}
static
nsresult CreateDatabaseConnection(const nsAString& aName,
nsIFile* aDBFile,
nsIFile* aFileManagerDirectory,
nsresult CreateDatabaseConnection(nsIFile* aDBFile,
nsIFile* aFMDirectory,
const nsAString& aName,
const nsACString& aOrigin,
mozIStorageConnection** aConnection);
protected:

View File

@ -1,60 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "nsISupports.idl"
interface nsIFile;
/**
* A stream that allows you to read from a file or stream to a file
* using standard file APIs.
*/
[scriptable, uuid(ebbbb779-92a3-4b2a-b7cf-6efbe904c453)]
interface nsIStandardFileStream : nsISupports
{
/**
* If this is set, the file will be opened (i.e., a call to
* fopen done) only when we do an actual operation on the stream,
* or more specifically, when one of the following is called:
* - Seek
* - Tell
* - SetEOF
* - Available
* - Read
* - Write
* - Flush
* - GetSize
* - GetLastModified
* - Sync
*
* FLAGS_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 FLAGS_DEFER_OPEN = 1 << 0;
/**
* @param file file to read from or stream to
* @param mode file open mode (see fopen documentation)
* @param flags flags specifying various behaviors of the class
* (see enumerations in the class)
*/
void init(in nsIFile file,
in AString mode,
in long flags);
/**
* Flush all written content held in memory buffers out to disk.
* This is the equivalent of fflush()
*/
void flushBuffers();
};

View File

@ -54,11 +54,13 @@ MOCHITEST_FILES = \
test_file_os_delete.html \
test_file_put_get_object.html \
test_file_put_get_values.html \
test_file_quota.html \
test_file_replace.html \
test_file_resurrection_delete.html \
test_file_resurrection_transaction_abort.html \
test_file_sharing.html \
test_file_transaction_abort.html \
test_filehandle_quota.html \
test_filehandle_serialization.html \
test_filehandle_store_snapshot.html \
test_getAll.html \

View File

@ -3,6 +3,8 @@
* http://creativecommons.org/publicdomain/zero/1.0/
*/
const DEFAULT_QUOTA = 50 * 1024 * 1024;
var bufferCache = [];
var utils = SpecialPowers.getDOMWindowUtils(window);
@ -184,25 +186,6 @@ function getUsage(usageHandler)
idbManager.getUsageForURI(uri, callback);
}
function getUsageSync()
{
let usage;
getUsage(function(aUsage, aFileUsage) {
usage = aUsage;
});
let comp = SpecialPowers.wrap(Components);
let thread = comp.classes["@mozilla.org/thread-manager;1"]
.getService(comp.interfaces.nsIThreadManager)
.currentThread;
while (!usage) {
thread.processNextEvent(true);
}
return usage;
}
function scheduleGC()
{
SpecialPowers.exactGC(window, continueToNextStep);

View File

@ -13,14 +13,12 @@
function testSteps()
{
const READ_WRITE = IDBTransaction.READ_WRITE;
const DEFAULT_QUOTA_MB = 50;
const name = window.location.pathname;
const objectStoreName = "Blobs";
const testData = { key: 0, value: {} };
const fileData = { key: 1, file: null };
const fileData = { key: 1, file: getNullFile("random.bin", DEFAULT_QUOTA) };
let request = indexedDB.open(name, 1);
request.onerror = errorHandler;
@ -32,21 +30,17 @@
let db = event.target.result;
let objectStore = db.createObjectStore(objectStoreName, { });
objectStore.add(testData.value, testData.key);
let size = (DEFAULT_QUOTA_MB + 1) * 1024 * 1024 - getUsageSync();
fileData.file = getNullFile("random.bin", size);
db.createObjectStore(objectStoreName, { });
event = yield;
is(event.type, "success", "Got correct event type");
trans = db.transaction([objectStoreName], READ_WRITE);
objectStore = trans.objectStore(objectStoreName);
let objectStore = trans.objectStore(objectStoreName);
request = objectStore.add(fileData.file, fileData.key);
request.addEventListener("error", new ExpectError("UnknownError"));
request.addEventListener("error", new ExpectError("UnknownError", true));
request.onsuccess = unexpectedSuccessHandler;
event = yield;

View File

@ -13,7 +13,6 @@
function testSteps()
{
const READ_WRITE = IDBTransaction.READ_WRITE;
const DEFAULT_QUOTA_MB = 50;
const name = window.location.pathname;
@ -39,10 +38,10 @@
let lockedFile = fileHandle.open("readwrite");
let blob = getNullBlob((50 + 1) * 1024 * 1024 - getUsageSync());
let blob = getNullBlob(DEFAULT_QUOTA);
request = lockedFile.write(blob);
request.addEventListener("error", new ExpectError("UnknownError"));
request.addEventListener("error", new ExpectError("UnknownError", true));
request.onsuccess = unexpectedSuccessHandler;
event = yield;

123
dom/quota/FileStreams.cpp Normal file
View File

@ -0,0 +1,123 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "FileStreams.h"
USING_QUOTA_NAMESPACE
template <class FileStreamBase>
NS_IMETHODIMP
FileQuotaStream<FileStreamBase>::SetEOF()
{
nsresult rv = FileStreamBase::SetEOF();
NS_ENSURE_SUCCESS(rv, rv);
if (mQuotaObject) {
int64_t offset;
nsresult rv = FileStreamBase::Tell(&offset);
NS_ENSURE_SUCCESS(rv, rv);
mQuotaObject->UpdateSize(offset);
}
return NS_OK;
}
template <class FileStreamBase>
NS_IMETHODIMP
FileQuotaStream<FileStreamBase>::Close()
{
nsresult rv = FileStreamBase::Close();
NS_ENSURE_SUCCESS(rv, rv);
mQuotaObject = nullptr;
return NS_OK;
}
template <class FileStreamBase>
nsresult
FileQuotaStream<FileStreamBase>::DoOpen()
{
QuotaManager* quotaManager = QuotaManager::Get();
NS_ASSERTION(quotaManager, "Shouldn't be null!");
NS_ASSERTION(!mQuotaObject, "Creating quota object more than once?");
mQuotaObject = quotaManager->GetQuotaObject(mOrigin,
FileStreamBase::mOpenParams.localFile);
nsresult rv = FileStreamBase::DoOpen();
NS_ENSURE_SUCCESS(rv, rv);
if (mQuotaObject && (FileStreamBase::mOpenParams.ioFlags & PR_TRUNCATE)) {
mQuotaObject->UpdateSize(0);
}
return NS_OK;
}
template <class FileStreamBase>
NS_IMETHODIMP
FileQuotaStreamWithWrite<FileStreamBase>::Write(const char* aBuf,
uint32_t aCount,
uint32_t* _retval)
{
nsresult rv;
if (FileQuotaStreamWithWrite::mQuotaObject) {
int64_t offset;
rv = FileStreamBase::Tell(&offset);
NS_ENSURE_SUCCESS(rv, rv);
if (!FileQuotaStreamWithWrite::
mQuotaObject->MaybeAllocateMoreSpace(offset, aCount)) {
return NS_ERROR_FAILURE;
}
}
rv = FileStreamBase::Write(aBuf, aCount, _retval);
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
}
NS_IMPL_ISUPPORTS_INHERITED0(FileInputStream, nsFileInputStream)
already_AddRefed<FileInputStream>
FileInputStream::Create(const nsACString& aOrigin, nsIFile* aFile,
int32_t aIOFlags, int32_t aPerm,
int32_t aBehaviorFlags)
{
nsRefPtr<FileInputStream> stream = new FileInputStream(aOrigin);
nsresult rv = stream->Init(aFile, aIOFlags, aPerm, aBehaviorFlags);
NS_ENSURE_SUCCESS(rv, nullptr);
return stream.forget();
}
NS_IMPL_ISUPPORTS_INHERITED0(FileOutputStream, nsFileOutputStream)
already_AddRefed<FileOutputStream>
FileOutputStream::Create(const nsACString& aOrigin, nsIFile* aFile,
int32_t aIOFlags, int32_t aPerm,
int32_t aBehaviorFlags)
{
nsRefPtr<FileOutputStream> stream = new FileOutputStream(aOrigin);
nsresult rv = stream->Init(aFile, aIOFlags, aPerm, aBehaviorFlags);
NS_ENSURE_SUCCESS(rv, nullptr);
return stream.forget();
}
NS_IMPL_ISUPPORTS_INHERITED0(FileStream, nsFileStream)
already_AddRefed<FileStream>
FileStream::Create(const nsACString& aOrigin, nsIFile* aFile, int32_t aIOFlags,
int32_t aPerm, int32_t aBehaviorFlags)
{
nsRefPtr<FileStream> stream = new FileStream(aOrigin);
nsresult rv = stream->Init(aFile, aIOFlags, aPerm, aBehaviorFlags);
NS_ENSURE_SUCCESS(rv, nullptr);
return stream.forget();
}

115
dom/quota/FileStreams.h Normal file
View File

@ -0,0 +1,115 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_dom_quota_filestreams_h__
#define mozilla_dom_quota_filestreams_h__
#include "QuotaCommon.h"
#include "nsFileStreams.h"
#include "QuotaManager.h"
BEGIN_QUOTA_NAMESPACE
template <class FileStreamBase>
class FileQuotaStream : public FileStreamBase
{
public:
// nsFileStreamBase override
NS_IMETHOD
SetEOF() MOZ_OVERRIDE;
NS_IMETHOD
Close() MOZ_OVERRIDE;
protected:
FileQuotaStream(const nsACString& aOrigin)
: mOrigin(aOrigin)
{ }
// nsFileStreamBase override
virtual nsresult
DoOpen() MOZ_OVERRIDE;
nsCString mOrigin;
nsRefPtr<QuotaObject> mQuotaObject;
};
template <class FileStreamBase>
class FileQuotaStreamWithWrite : public FileQuotaStream<FileStreamBase>
{
public:
// nsFileStreamBase override
NS_IMETHOD
Write(const char* aBuf, uint32_t aCount, uint32_t* _retval) MOZ_OVERRIDE;
protected:
FileQuotaStreamWithWrite(const nsACString& aOrigin)
: FileQuotaStream<FileStreamBase>(aOrigin)
{ }
};
class FileInputStream : public FileQuotaStream<nsFileInputStream>
{
public:
NS_DECL_ISUPPORTS_INHERITED
static already_AddRefed<FileInputStream>
Create(const nsACString& aOrigin, nsIFile* aFile, int32_t aIOFlags = -1,
int32_t aPerm = -1, int32_t aBehaviorFlags = 0);
private:
FileInputStream(const nsACString& aOrigin)
: FileQuotaStream<nsFileInputStream>(aOrigin)
{ }
virtual ~FileInputStream() {
Close();
}
};
class FileOutputStream : public FileQuotaStreamWithWrite<nsFileOutputStream>
{
public:
NS_DECL_ISUPPORTS_INHERITED
static already_AddRefed<FileOutputStream>
Create(const nsACString& aOrigin, nsIFile* aFile, int32_t aIOFlags = -1,
int32_t aPerm = -1, int32_t aBehaviorFlags = 0);
private:
FileOutputStream(const nsACString& aOrigin)
: FileQuotaStreamWithWrite<nsFileOutputStream>(aOrigin)
{ }
virtual ~FileOutputStream() {
Close();
}
};
class FileStream : public FileQuotaStreamWithWrite<nsFileStream>
{
public:
NS_DECL_ISUPPORTS_INHERITED
static already_AddRefed<FileStream>
Create(const nsACString& aOrigin, nsIFile* aFile, int32_t aIOFlags = -1,
int32_t aPerm = -1, int32_t aBehaviorFlags = 0);
private:
FileStream(const nsACString& aOrigin)
: FileQuotaStreamWithWrite<nsFileStream>(aOrigin)
{ }
virtual ~FileStream() {
Close();
}
};
END_QUOTA_NAMESPACE
#endif /* mozilla_dom_quota_filestreams_h__ */

33
dom/quota/Makefile.in Normal file
View File

@ -0,0 +1,33 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
# You can obtain one at http://mozilla.org/MPL/2.0/.
DEPTH = ../..
topsrcdir = @top_srcdir@
srcdir = @srcdir@
VPATH = @srcdir@
include $(DEPTH)/config/autoconf.mk
MODULE = dom
LIBRARY_NAME = domquota_s
XPIDL_MODULE = dom_quota
LIBXUL_LIBRARY = 1
FORCE_STATIC_LIB = 1
include $(topsrcdir)/dom/dom-config.mk
EXPORTS_NAMESPACES = mozilla/dom/quota
CPPSRCS = \
FileStreams.cpp \
QuotaManager.cpp \
$(NULL)
EXPORTS_mozilla/dom/quota = \
FileStreams.h \
QuotaCommon.h \
QuotaManager.h \
$(NULL)
include $(topsrcdir)/config/rules.mk

23
dom/quota/QuotaCommon.h Normal file
View File

@ -0,0 +1,23 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_dom_quota_quotacommon_h__
#define mozilla_dom_quota_quotacommon_h__
#include "nsAutoPtr.h"
#include "nsCOMPtr.h"
#include "nsDebug.h"
#include "nsStringGlue.h"
#include "nsTArray.h"
#define BEGIN_QUOTA_NAMESPACE \
namespace mozilla { namespace dom { namespace quota {
#define END_QUOTA_NAMESPACE \
} /* namespace quota */ } /* namespace dom */ } /* namespace mozilla */
#define USING_QUOTA_NAMESPACE \
using namespace mozilla::dom::quota;
#endif // mozilla_dom_quota_quotacommon_h__

294
dom/quota/QuotaManager.cpp Normal file
View File

@ -0,0 +1,294 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "QuotaManager.h"
#include "nsIFile.h"
#include "mozilla/ClearOnShutdown.h"
#include "nsComponentManagerUtils.h"
#include "mozilla/dom/indexedDB/IndexedDatabaseManager.h"
USING_QUOTA_NAMESPACE
namespace {
nsAutoPtr<QuotaManager> gInstance;
PLDHashOperator
RemoveQuotaForPatternCallback(const nsACString& aKey,
nsRefPtr<OriginInfo>& aValue,
void* aUserArg)
{
NS_ASSERTION(!aKey.IsEmpty(), "Empty key!");
NS_ASSERTION(aValue, "Null pointer!");
NS_ASSERTION(aUserArg, "Null pointer!");
const nsACString* pattern =
static_cast<const nsACString*>(aUserArg);
if (StringBeginsWith(aKey, *pattern)) {
return PL_DHASH_REMOVE;
}
return PL_DHASH_NEXT;
}
} // anonymous namespace
void
QuotaObject::AddRef()
{
QuotaManager* quotaManager = QuotaManager::Get();
if (!quotaManager) {
NS_ERROR("Null quota manager, this shouldn't happen, possible leak!");
NS_AtomicIncrementRefcnt(mRefCnt);
return;
}
MutexAutoLock lock(quotaManager->mQuotaMutex);
++mRefCnt;
}
void
QuotaObject::Release()
{
QuotaManager* quotaManager = QuotaManager::Get();
if (!quotaManager) {
NS_ERROR("Null quota manager, this shouldn't happen, possible leak!");
nsrefcnt count = NS_AtomicDecrementRefcnt(mRefCnt);
if (count == 0) {
mRefCnt = 1;
delete this;
}
return;
}
{
MutexAutoLock lock(quotaManager->mQuotaMutex);
--mRefCnt;
if (mRefCnt > 0) {
return;
}
if (mOriginInfo) {
mOriginInfo->mQuotaObjects.Remove(mPath);
}
}
delete this;
}
void
QuotaObject::UpdateSize(int64_t aSize)
{
QuotaManager* quotaManager = QuotaManager::Get();
NS_ASSERTION(quotaManager, "Shouldn't be null!");
MutexAutoLock lock(quotaManager->mQuotaMutex);
if (mOriginInfo) {
mOriginInfo->mUsage -= mSize;
mSize = aSize;
mOriginInfo->mUsage += mSize;
}
}
bool
QuotaObject::MaybeAllocateMoreSpace(int64_t aOffset, int32_t aCount)
{
int64_t end = aOffset + aCount;
QuotaManager* quotaManager = QuotaManager::Get();
NS_ASSERTION(quotaManager, "Shouldn't be null!");
MutexAutoLock lock(quotaManager->mQuotaMutex);
if (mSize >= end || !mOriginInfo) {
return true;
}
int64_t newUsage = mOriginInfo->mUsage - mSize + end;
if (newUsage > mOriginInfo->mLimit) {
if (!indexedDB::IndexedDatabaseManager::QuotaIsLifted()) {
return false;
}
nsCString origin = mOriginInfo->mOrigin;
mOriginInfo->LockedClearOriginInfos();
NS_ASSERTION(!mOriginInfo,
"Should have cleared in LockedClearOriginInfos!");
quotaManager->mOriginInfos.Remove(origin);
mSize = end;
return true;
}
mOriginInfo->mUsage = newUsage;
mSize = end;
return true;
}
#ifdef DEBUG
void
OriginInfo::LockedClearOriginInfos()
{
QuotaManager* quotaManager = QuotaManager::Get();
NS_ASSERTION(quotaManager, "Shouldn't be null!");
quotaManager->mQuotaMutex.AssertCurrentThreadOwns();
mQuotaObjects.EnumerateRead(ClearOriginInfoCallback, nullptr);
}
#endif
// static
PLDHashOperator
OriginInfo::ClearOriginInfoCallback(const nsAString& aKey,
QuotaObject* aValue,
void* aUserArg)
{
NS_ASSERTION(!aKey.IsEmpty(), "Empty key!");
NS_ASSERTION(aValue, "Null pointer!");
aValue->mOriginInfo = nullptr;
return PL_DHASH_NEXT;
}
// static
QuotaManager*
QuotaManager::GetOrCreate()
{
if (!gInstance) {
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
gInstance = new QuotaManager();
ClearOnShutdown(&gInstance);
}
return gInstance;
}
// static
QuotaManager*
QuotaManager::Get()
{
// Does not return an owning reference.
return gInstance;
}
void
QuotaManager::InitQuotaForOrigin(const nsACString& aOrigin,
int64_t aLimit,
int64_t aUsage)
{
OriginInfo* info = new OriginInfo(aOrigin, aLimit * 1024 * 1024, aUsage);
MutexAutoLock lock(mQuotaMutex);
NS_ASSERTION(!mOriginInfos.GetWeak(aOrigin), "Replacing an existing entry!");
mOriginInfos.Put(aOrigin, info);
}
void
QuotaManager::DecreaseUsageForOrigin(const nsACString& aOrigin,
int64_t aSize)
{
MutexAutoLock lock(mQuotaMutex);
nsRefPtr<OriginInfo> originInfo;
mOriginInfos.Get(aOrigin, getter_AddRefs(originInfo));
if (originInfo) {
originInfo->mUsage -= aSize;
}
}
void
QuotaManager::RemoveQuotaForPattern(const nsACString& aPattern)
{
NS_ASSERTION(!aPattern.IsEmpty(), "Empty pattern!");
MutexAutoLock lock(mQuotaMutex);
mOriginInfos.Enumerate(RemoveQuotaForPatternCallback,
const_cast<nsACString*>(&aPattern));
}
already_AddRefed<QuotaObject>
QuotaManager::GetQuotaObject(const nsACString& aOrigin,
nsIFile* aFile)
{
NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
nsString path;
nsresult rv = aFile->GetPath(path);
NS_ENSURE_SUCCESS(rv, nullptr);
int64_t fileSize;
bool exists;
rv = aFile->Exists(&exists);
NS_ENSURE_SUCCESS(rv, nullptr);
if (exists) {
rv = aFile->GetFileSize(&fileSize);
NS_ENSURE_SUCCESS(rv, nullptr);
}
else {
fileSize = 0;
}
QuotaObject* info = nullptr;
{
MutexAutoLock lock(mQuotaMutex);
nsRefPtr<OriginInfo> originInfo;
mOriginInfos.Get(aOrigin, getter_AddRefs(originInfo));
if (!originInfo) {
return nullptr;
}
originInfo->mQuotaObjects.Get(path, &info);
if (!info) {
info = new QuotaObject(originInfo, path, fileSize);
originInfo->mQuotaObjects.Put(path, info);
}
}
nsRefPtr<QuotaObject> result = info;
return result.forget();
}
already_AddRefed<QuotaObject>
QuotaManager::GetQuotaObject(const nsACString& aOrigin,
const nsAString& aPath)
{
nsresult rv;
nsCOMPtr<nsIFile> file = do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv);
NS_ENSURE_SUCCESS(rv, nullptr);
rv = file->InitWithPath(aPath);
NS_ENSURE_SUCCESS(rv, nullptr);
return GetQuotaObject(aOrigin, file);
}

147
dom/quota/QuotaManager.h Normal file
View File

@ -0,0 +1,147 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_dom_quota_quotamanager_h__
#define mozilla_dom_quota_quotamanager_h__
#include "QuotaCommon.h"
#include "mozilla/Mutex.h"
#include "nsDataHashtable.h"
#include "nsRefPtrHashtable.h"
#include "nsThreadUtils.h"
BEGIN_QUOTA_NAMESPACE
class OriginInfo;
class QuotaManager;
class QuotaObject
{
friend class OriginInfo;
friend class QuotaManager;
public:
void
AddRef();
void
Release();
void
UpdateSize(int64_t aSize);
bool
MaybeAllocateMoreSpace(int64_t aOffset, int32_t aCount);
private:
QuotaObject(OriginInfo* aOriginInfo, const nsAString& aPath, int64_t aSize)
: mOriginInfo(aOriginInfo), mPath(aPath), mSize(aSize)
{ }
virtual ~QuotaObject()
{ }
nsAutoRefCnt mRefCnt;
OriginInfo* mOriginInfo;
nsString mPath;
int64_t mSize;
};
class OriginInfo
{
friend class QuotaManager;
friend class QuotaObject;
public:
OriginInfo(const nsACString& aOrigin, int64_t aLimit, int64_t aUsage)
: mOrigin(aOrigin), mLimit(aLimit), mUsage(aUsage)
{
mQuotaObjects.Init();
}
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(OriginInfo)
private:
void
#ifdef DEBUG
LockedClearOriginInfos();
#else
LockedClearOriginInfos()
{
mQuotaObjects.EnumerateRead(ClearOriginInfoCallback, nullptr);
}
#endif
static PLDHashOperator
ClearOriginInfoCallback(const nsAString& aKey,
QuotaObject* aValue, void* aUserArg);
nsDataHashtable<nsStringHashKey, QuotaObject*> mQuotaObjects;
nsCString mOrigin;
int64_t mLimit;
int64_t mUsage;
};
class QuotaManager
{
friend class nsAutoPtr<QuotaManager>;
friend class OriginInfo;
friend class QuotaObject;
public:
// Returns a non-owning reference.
static QuotaManager*
GetOrCreate();
// Returns a non-owning reference.
static QuotaManager*
Get();
void
InitQuotaForOrigin(const nsACString& aOrigin,
int64_t aLimit,
int64_t aUsage);
void
DecreaseUsageForOrigin(const nsACString& aOrigin,
int64_t aSize);
void
RemoveQuotaForPattern(const nsACString& aPattern);
already_AddRefed<QuotaObject>
GetQuotaObject(const nsACString& aOrigin,
nsIFile* aFile);
already_AddRefed<QuotaObject>
GetQuotaObject(const nsACString& aOrigin,
const nsAString& aPath);
private:
QuotaManager()
: mQuotaMutex("QuotaManager.mQuotaMutex")
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
mOriginInfos.Init();
}
virtual ~QuotaManager()
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
}
mozilla::Mutex mQuotaMutex;
nsRefPtrHashtable<nsCStringHashKey, OriginInfo> mOriginInfos;
};
END_QUOTA_NAMESPACE
#endif /* mozilla_dom_quota_quotamanager_h__ */

View File

@ -69,6 +69,7 @@ SHARED_LIBRARY_LIBS = \
$(DEPTH)/dom/encoding/$(LIB_PREFIX)domencoding_s.$(LIB_SUFFIX) \
$(DEPTH)/dom/file/$(LIB_PREFIX)domfile_s.$(LIB_SUFFIX) \
$(DEPTH)/dom/power/$(LIB_PREFIX)dom_power_s.$(LIB_SUFFIX) \
$(DEPTH)/dom/quota/$(LIB_PREFIX)domquota_s.$(LIB_SUFFIX) \
$(DEPTH)/dom/settings/$(LIB_PREFIX)jsdomsettings_s.$(LIB_SUFFIX) \
$(DEPTH)/dom/permission/$(LIB_PREFIX)jsdompermissionsettings_s.$(LIB_SUFFIX) \
$(DEPTH)/dom/network/src/$(LIB_PREFIX)dom_network_s.$(LIB_SUFFIX) \

View File

@ -19,6 +19,7 @@ LIBXUL_LIBRARY = 1
EXPORTS = \
nsMIMEInputStream.h \
nsURLHelper.h \
nsFileStreams.h \
$(NULL)
EXPORTS_NAMESPACES = mozilla/net

View File

@ -51,7 +51,9 @@ nsFileStreamBase::~nsFileStreamBase()
Close();
}
NS_IMPL_THREADSAFE_ISUPPORTS1(nsFileStreamBase, nsISeekableStream)
NS_IMPL_THREADSAFE_ISUPPORTS2(nsFileStreamBase,
nsISeekableStream,
nsIFileMetadata)
NS_IMETHODIMP
nsFileStreamBase::Seek(int32_t whence, int64_t offset)
@ -124,6 +126,52 @@ nsFileStreamBase::SetEOF()
return NS_OK;
}
NS_IMETHODIMP
nsFileStreamBase::GetSize(int64_t* _retval)
{
nsresult rv = DoPendingOpen();
NS_ENSURE_SUCCESS(rv, rv);
if (!mFD) {
return NS_BASE_STREAM_CLOSED;
}
PRFileInfo64 info;
if (PR_GetOpenFileInfo64(mFD, &info) == PR_FAILURE) {
return NS_BASE_STREAM_OSERROR;
}
*_retval = int64_t(info.size);
return NS_OK;
}
NS_IMETHODIMP
nsFileStreamBase::GetLastModified(int64_t* _retval)
{
nsresult rv = DoPendingOpen();
NS_ENSURE_SUCCESS(rv, rv);
if (!mFD) {
return NS_BASE_STREAM_CLOSED;
}
PRFileInfo64 info;
if (PR_GetOpenFileInfo64(mFD, &info) == PR_FAILURE) {
return NS_BASE_STREAM_OSERROR;
}
int64_t modTime = int64_t(info.modifyTime);
if (modTime == 0) {
*_retval = 0;
}
else {
*_retval = modTime / int64_t(PR_USEC_PER_MSEC);
}
return NS_OK;
}
nsresult
nsFileStreamBase::Close()
{
@ -934,13 +982,12 @@ nsSafeFileOutputStream::Write(const char *buf, uint32_t count, uint32_t *result)
////////////////////////////////////////////////////////////////////////////////
// nsFileStream
NS_IMPL_ISUPPORTS_INHERITED4(nsFileStream,
NS_IMPL_ISUPPORTS_INHERITED3(nsFileStream,
nsFileStreamBase,
nsIInputStream,
nsIOutputStream,
nsIFileStream,
nsIFileMetadata)
nsIFileStream)
NS_IMETHODIMP
nsFileStream::Init(nsIFile* file, int32_t ioFlags, int32_t perm,
int32_t behaviorFlags)
@ -959,50 +1006,4 @@ nsFileStream::Init(nsIFile* file, int32_t ioFlags, int32_t perm,
mBehaviorFlags & nsIFileStream::DEFER_OPEN);
}
NS_IMETHODIMP
nsFileStream::GetSize(int64_t* _retval)
{
nsresult rv = DoPendingOpen();
NS_ENSURE_SUCCESS(rv, rv);
if (!mFD) {
return NS_BASE_STREAM_CLOSED;
}
PRFileInfo64 info;
if (PR_GetOpenFileInfo64(mFD, &info) == PR_FAILURE) {
return NS_BASE_STREAM_OSERROR;
}
*_retval = int64_t(info.size);
return NS_OK;
}
NS_IMETHODIMP
nsFileStream::GetLastModified(int64_t* _retval)
{
nsresult rv = DoPendingOpen();
NS_ENSURE_SUCCESS(rv, rv);
if (!mFD) {
return NS_BASE_STREAM_CLOSED;
}
PRFileInfo64 info;
if (PR_GetOpenFileInfo64(mFD, &info) == PR_FAILURE) {
return NS_BASE_STREAM_OSERROR;
}
int64_t modTime = int64_t(info.modifyTime);
if (modTime == 0) {
*_retval = 0;
}
else {
*_retval = modTime / int64_t(PR_USEC_PER_MSEC);
}
return NS_OK;
}
////////////////////////////////////////////////////////////////////////////////

View File

@ -24,11 +24,13 @@
////////////////////////////////////////////////////////////////////////////////
class nsFileStreamBase : public nsISeekableStream
class nsFileStreamBase : public nsISeekableStream,
public nsIFileMetadata
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSISEEKABLESTREAM
NS_DECL_NSIFILEMETADATA
nsFileStreamBase();
virtual ~nsFileStreamBase();
@ -124,8 +126,8 @@ public:
NS_IMETHOD IsNonBlocking(bool* _retval)
{
return nsFileStreamBase::IsNonBlocking(_retval);
}
}
// Overrided from nsFileStreamBase
NS_IMETHOD Seek(int32_t aWhence, int64_t aOffset);
@ -260,13 +262,11 @@ protected:
class nsFileStream : public nsFileStreamBase,
public nsIInputStream,
public nsIOutputStream,
public nsIFileStream,
public nsIFileMetadata
public nsIFileStream
{
public:
NS_DECL_ISUPPORTS_INHERITED
NS_DECL_NSIFILESTREAM
NS_DECL_NSIFILEMETADATA
NS_FORWARD_NSIINPUTSTREAM(nsFileStreamBase::)
// Can't use NS_FORWARD_NSIOUTPUTSTREAM due to overlapping methods

View File

@ -36,7 +36,6 @@ XPIDLSRCS = \
mozIStorageCompletionCallback.idl \
mozIStorageBaseStatement.idl \
mozIStorageAsyncStatement.idl \
mozIStorageServiceQuotaManagement.idl \
mozIStorageVacuumParticipant.idl \
$(NULL)
# SEE ABOVE NOTE!

View File

@ -7,6 +7,7 @@
interface mozIStorageConnection;
interface nsIFile;
interface nsIFileURL;
/**
* The mozIStorageService interface is intended to be implemented by
@ -15,7 +16,7 @@ interface nsIFile;
*
* This is the only way to open a database connection.
*/
[scriptable, uuid(fe8e95cb-b377-4c8d-bccb-d9198c67542b)]
[scriptable, uuid(12bfad34-cca3-40fb-8736-d8bf9db61a27)]
interface mozIStorageService : nsISupports {
/**
* Get a connection to a named special database storage.
@ -106,6 +107,16 @@ interface mozIStorageService : nsISupports {
*/
mozIStorageConnection openUnsharedDatabase(in nsIFile aDatabaseFile);
/**
* See openDatabase(). Exactly the same only initialized with a file URL.
* Custom parameters can be passed to SQLite and VFS implementations through
* the query part of the URL.
*
* @param aURL
* A nsIFileURL that represents the database that is to be opened.
*/
mozIStorageConnection openDatabaseWithFileURL(in nsIFileURL aFileURL);
/*
* Utilities
*/

View File

@ -1,99 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "nsISupports.idl"
interface mozIStorageConnection;
interface nsIFile;
[scriptable, function, uuid(ae94f0a5-ebdf-48f4-9959-085e13235d8d)]
interface mozIStorageQuotaCallback : nsISupports
{
/**
* Called when the file size quota for a group of databases is exceeded.
*
* @param aFilename
* The filename of the database that has exceeded the quota.
*
* @param aCurrentSizeLimit
* The current size (in bytes) of the quota.
*
* @param aCurrentTotalSize
* The current size of all databases in the quota group.
*
* @param aUserData
* Any additional data that was provided to the
* setQuotaForFilenamePattern function.
*
* @returns A new quota size. A new quota of 0 will disable the quota callback
* and any quota value less than aCurrentTotalSize will cause the
* database operation to fail with NS_ERROR_FILE_NO_DEVICE_SPACE.
*/
long long quotaExceeded(in ACString aFilename,
in long long aCurrentSizeLimit,
in long long aCurrentTotalSize,
in nsISupports aUserData);
};
/**
* This is a temporary interface that should eventually merge with
* mozIStorageService.
*/
[scriptable, uuid(4d81faf5-fe01-428b-99b8-c94cba12fd72)]
interface mozIStorageServiceQuotaManagement : nsISupports
{
/**
* See mozIStorageService.openDatabase. Exactly the same only with a custom
* SQLite VFS.
*/
mozIStorageConnection openDatabaseWithVFS(in nsIFile aDatabaseFile,
in ACString aVFSName);
/**
* Set a file size quota for a group of databases matching the given filename
* pattern, optionally specifying a callback when the quota is exceeded.
*
* @param aPattern
* A pattern to match filenames for inclusion in the quota system. May
* contain the following special characters:
* '*' Matches any sequence of zero or more characters.
* '?' Matches exactly one character.
* [...] Matches one character from the enclosed list of characters.
* [^...] Matches one character not in the enclosed list.
*
* @param aSizeLimit
* The size limit (in bytes) for the quota group.
*
* @param aCallback
* A callback that will be used when the quota is exceeded.
*
* @param aUserData
* Additional information to be passed to the callback.
*/
void setQuotaForFilenamePattern(in ACString aPattern,
in long long aSizeLimit,
in mozIStorageQuotaCallback aCallback,
in nsISupports aUserData);
/**
* Adds, removes, or updates the file size information maintained by the quota
* system for files not opened through openDatabaseWithVFS().
*
* Use this function when you want files to be included in quota calculations
* that are either a) not SQLite databases, or b) SQLite databases that have
* not been opened.
*
* This function will have no effect on files that do not match an existing
* quota pattern (set previously by setQuotaForFilenamePattern()).
*
* @param aFile
* The file for which quota information should be updated. If the file
* exists then its size information will be added or refreshed. If the
* file does not exist then the file will be removed from tracking
* under the quota system.
*/
void updateQuotaInformationForFile(in nsIFile aFile);
};

View File

@ -24,7 +24,6 @@
#include "mozIStorageStatementCallback.h"
#include "mozIStorageBindingParamsArray.h"
#include "mozIStorageBindingParams.h"
#include "mozIStorageServiceQuotaManagement.h"
#include "mozIStorageVacuumParticipant.h"
#include "mozIStorageCompletionCallback.h"
#include "mozIStorageAsyncStatement.h"

View File

@ -10,6 +10,7 @@
#include "sqlite3.h"
#include "nsThreadUtils.h"
#include "mozilla/Util.h"
#include "mozilla/dom/quota/QuotaManager.h"
/**
* This preference is a workaround to allow users/sysadmins to identify
@ -24,6 +25,7 @@
namespace {
using namespace mozilla;
using namespace mozilla::dom::quota;
struct Histograms {
const char *name;
@ -82,9 +84,17 @@ private:
};
struct telemetry_file {
sqlite3_file base; // Base class. Must be first
Histograms *histograms; // histograms pertaining to this file
sqlite3_file pReal[1]; // This contains the vfs that actually does work
// Base class. Must be first
sqlite3_file base;
// histograms pertaining to this file
Histograms *histograms;
// quota object for this file
nsRefPtr<QuotaObject> quotaObject;
// This contains the vfs that actually does work
sqlite3_file pReal[1];
};
/*
@ -99,6 +109,7 @@ xClose(sqlite3_file *pFile)
if( rc==SQLITE_OK ){
delete p->base.pMethods;
p->base.pMethods = NULL;
p->quotaObject = nullptr;
}
return rc;
}
@ -126,6 +137,9 @@ int
xWrite(sqlite3_file *pFile, const void *zBuf, int iAmt, sqlite_int64 iOfst)
{
telemetry_file *p = (telemetry_file *)pFile;
if (p->quotaObject && !p->quotaObject->MaybeAllocateMoreSpace(iOfst, iAmt)) {
return SQLITE_FULL;
}
IOThreadAutoTimer ioTimer(p->histograms->writeMS);
int rc;
rc = p->pReal->pMethods->xWrite(p->pReal, zBuf, iAmt, iOfst);
@ -144,6 +158,9 @@ xTruncate(sqlite3_file *pFile, sqlite_int64 size)
int rc;
Telemetry::AutoTimer<Telemetry::MOZ_SQLITE_TRUNCATE_MS> timer;
rc = p->pReal->pMethods->xTruncate(p->pReal, size);
if (rc == SQLITE_OK && p->quotaObject) {
p->quotaObject->UpdateSize(size);
}
return rc;
}
@ -300,6 +317,18 @@ xOpen(sqlite3_vfs* vfs, const char *zName, sqlite3_file* pFile,
break;
}
p->histograms = h;
const char* origin;
if ((flags & SQLITE_OPEN_URI) &&
(origin = sqlite3_uri_parameter(zName, "origin"))) {
QuotaManager* quotaManager = QuotaManager::Get();
MOZ_ASSERT(quotaManager);
p->quotaObject = quotaManager->GetQuotaObject(nsDependentCString(origin),
NS_ConvertUTF8toUTF16(zName));
}
rc = orig_vfs->xOpen(orig_vfs, zName, p->pReal, flags, pOutFlags);
if( rc != SQLITE_OK )
return rc;

View File

@ -12,6 +12,7 @@
#include "nsIMemoryReporter.h"
#include "nsThreadUtils.h"
#include "nsIFile.h"
#include "nsIFileURL.h"
#include "mozilla/Telemetry.h"
#include "mozilla/Mutex.h"
#include "mozilla/CondVar.h"
@ -471,34 +472,83 @@ Connection::getAsyncExecutionTarget()
}
nsresult
Connection::initialize(nsIFile *aDatabaseFile,
const char* aVFSName)
Connection::initialize()
{
NS_ASSERTION (!mDBConn, "Initialize called on already opened database!");
SAMPLE_LABEL("storage", "Connection::initialize");
int srv;
nsresult rv;
mDatabaseFile = aDatabaseFile;
if (aDatabaseFile) {
nsAutoString path;
rv = aDatabaseFile->GetPath(path);
NS_ENSURE_SUCCESS(rv, rv);
srv = ::sqlite3_open_v2(NS_ConvertUTF16toUTF8(path).get(), &mDBConn, mFlags,
aVFSName);
}
else {
// in memory database requested, sqlite uses a magic file name
srv = ::sqlite3_open_v2(":memory:", &mDBConn, mFlags, aVFSName);
}
// in memory database requested, sqlite uses a magic file name
int srv = ::sqlite3_open_v2(":memory:", &mDBConn, mFlags, NULL);
if (srv != SQLITE_OK) {
mDBConn = nullptr;
return convertResultCode(srv);
}
return initializeInternal(nullptr);
}
nsresult
Connection::initialize(nsIFile *aDatabaseFile)
{
NS_ASSERTION (aDatabaseFile, "Passed null file!");
NS_ASSERTION (!mDBConn, "Initialize called on already opened database!");
SAMPLE_LABEL("storage", "Connection::initialize");
mDatabaseFile = aDatabaseFile;
nsAutoString path;
nsresult rv = aDatabaseFile->GetPath(path);
NS_ENSURE_SUCCESS(rv, rv);
int srv = ::sqlite3_open_v2(NS_ConvertUTF16toUTF8(path).get(), &mDBConn,
mFlags, NULL);
if (srv != SQLITE_OK) {
mDBConn = nullptr;
return convertResultCode(srv);
}
rv = initializeInternal(aDatabaseFile);
NS_ENSURE_SUCCESS(rv, rv);
mDatabaseFile = aDatabaseFile;
return NS_OK;
}
nsresult
Connection::initialize(nsIFileURL *aFileURL)
{
NS_ASSERTION (aFileURL, "Passed null file URL!");
NS_ASSERTION (!mDBConn, "Initialize called on already opened database!");
SAMPLE_LABEL("storage", "Connection::initialize");
nsCOMPtr<nsIFile> databaseFile;
nsresult rv = aFileURL->GetFile(getter_AddRefs(databaseFile));
NS_ENSURE_SUCCESS(rv, rv);
nsAutoCString spec;
rv = aFileURL->GetSpec(spec);
NS_ENSURE_SUCCESS(rv, rv);
int srv = ::sqlite3_open_v2(spec.get(), &mDBConn, mFlags, NULL);
if (srv != SQLITE_OK) {
mDBConn = nullptr;
return convertResultCode(srv);
}
rv = initializeInternal(databaseFile);
NS_ENSURE_SUCCESS(rv, rv);
mFileURL = aFileURL;
mDatabaseFile = databaseFile;
return NS_OK;
}
nsresult
Connection::initializeInternal(nsIFile* aDatabaseFile)
{
// Properly wrap the database handle's mutex.
sharedDBMutex.initWithMutex(sqlite3_db_mutex(mDBConn));
@ -522,14 +572,14 @@ Connection::initialize(nsIFile *aDatabaseFile,
nsAutoCString pageSizeQuery(MOZ_STORAGE_UNIQUIFY_QUERY_STR
"PRAGMA page_size = ");
pageSizeQuery.AppendInt(pageSize);
rv = ExecuteSimpleSQL(pageSizeQuery);
nsresult rv = ExecuteSimpleSQL(pageSizeQuery);
NS_ENSURE_SUCCESS(rv, rv);
// Get the current page_size, since it may differ from the specified value.
sqlite3_stmt *stmt;
NS_NAMED_LITERAL_CSTRING(pragma_page_size,
MOZ_STORAGE_UNIQUIFY_QUERY_STR "PRAGMA page_size");
srv = prepareStatement(pragma_page_size, &stmt);
int srv = prepareStatement(pragma_page_size, &stmt);
if (srv == SQLITE_OK) {
if (SQLITE_ROW == stepStatement(stmt)) {
pageSize = ::sqlite3_column_int64(stmt, 0);
@ -962,7 +1012,8 @@ Connection::Clone(bool aReadOnly,
nsRefPtr<Connection> clone = new Connection(mStorageService, flags);
NS_ENSURE_TRUE(clone, NS_ERROR_OUT_OF_MEMORY);
nsresult rv = clone->initialize(mDatabaseFile);
nsresult rv = mFileURL ? clone->initialize(mFileURL)
: clone->initialize(mDatabaseFile);
NS_ENSURE_SUCCESS(rv, rv);
// Copy over pragmas from the original connection.

View File

@ -25,6 +25,7 @@
struct PRLock;
class nsIFile;
class nsIFileURL;
class nsIEventTarget;
class nsIThread;
@ -62,19 +63,28 @@ public:
*/
Connection(Service *aService, int aFlags);
/**
* Creates the connection to an in-memory database.
*/
nsresult initialize();
/**
* Creates the connection to the database.
*
* @param aDatabaseFile
* The nsIFile of the location of the database to open, or create if it
* does not exist. Passing in nullptr here creates an in-memory
* database.
* @param aVFSName
* The VFS that SQLite will use when opening this database. NULL means
* "default".
* does not exist.
*/
nsresult initialize(nsIFile *aDatabaseFile,
const char* aVFSName = NULL);
nsresult initialize(nsIFile *aDatabaseFile);
/**
* Creates the connection to the database.
*
* @param aFileURL
* The nsIFileURL of the location of the database to open, or create if it
* does not exist.
*/
nsresult initialize(nsIFileURL *aFileURL);
// fetch the native handle
sqlite3 *GetNativeConnection() { return mDBConn; }
@ -155,6 +165,8 @@ public:
private:
~Connection();
nsresult initializeInternal(nsIFile *aDatabaseFile);
/**
* Sets the database into a closed state so no further actions can be
* performed.
@ -206,6 +218,7 @@ private:
int progressHandler();
sqlite3 *mDBConn;
nsCOMPtr<nsIFileURL> mFileURL;
nsCOMPtr<nsIFile> mDatabaseFile;
/**

View File

@ -24,8 +24,6 @@
#include "mozilla/Preferences.h"
#include "sqlite3.h"
#include "test_quota.h"
#include "test_quota.c"
#ifdef SQLITE_OS_WIN
// "windows.h" was included and it can #define lots of things we care about...
@ -35,61 +33,6 @@
#include "nsIPromptService.h"
#include "nsIMemoryReporter.h"
namespace {
class QuotaCallbackData
{
public:
QuotaCallbackData(mozIStorageQuotaCallback *aCallback,
nsISupports *aUserData)
: callback(aCallback), userData(aUserData)
{
MOZ_COUNT_CTOR(QuotaCallbackData);
}
~QuotaCallbackData()
{
MOZ_COUNT_DTOR(QuotaCallbackData);
}
static void Callback(const char *zFilename,
sqlite3_int64 *piLimit,
sqlite3_int64 iSize,
void *pArg)
{
NS_ASSERTION(zFilename && strlen(zFilename), "Null or empty filename!");
NS_ASSERTION(piLimit, "Null pointer!");
QuotaCallbackData *data = static_cast<QuotaCallbackData*>(pArg);
if (!data) {
// No callback specified, return immediately.
return;
}
NS_ASSERTION(data->callback, "Should never have a null callback!");
nsDependentCString filename(zFilename);
int64_t newLimit;
if (NS_SUCCEEDED(data->callback->QuotaExceeded(filename, *piLimit,
iSize, data->userData,
&newLimit))) {
*piLimit = newLimit;
}
}
static void Destroy(void *aUserData)
{
delete static_cast<QuotaCallbackData*>(aUserData);
}
private:
nsCOMPtr<mozIStorageQuotaCallback> callback;
nsCOMPtr<nsISupports> userData;
};
} // anonymous namespace
////////////////////////////////////////////////////////////////////////////////
//// Defines
@ -345,11 +288,10 @@ private:
////////////////////////////////////////////////////////////////////////////////
//// Service
NS_IMPL_THREADSAFE_ISUPPORTS3(
NS_IMPL_THREADSAFE_ISUPPORTS2(
Service,
mozIStorageService,
nsIObserver,
mozIStorageServiceQuotaManagement
nsIObserver
)
Service *Service::gService = nullptr;
@ -438,10 +380,6 @@ Service::~Service()
// Shutdown the sqlite3 API. Warn if shutdown did not turn out okay, but
// there is nothing actionable we can do in that case.
rc = ::sqlite3_quota_shutdown();
if (rc != SQLITE_OK)
NS_WARNING("sqlite3 did not shutdown cleanly.");
rc = ::sqlite3_shutdown();
if (rc != SQLITE_OK)
NS_WARNING("sqlite3 did not shutdown cleanly.");
@ -636,9 +574,6 @@ Service::initialize()
} else {
NS_WARNING("Failed to register telemetry VFS");
}
rc = ::sqlite3_quota_initialize("telemetry-vfs", 0);
if (rc != SQLITE_OK)
return convertResultCode(rc);
// Set the default value for the toolkit.storage.synchronous pref. It will be
// updated with the user preference on the main thread.
@ -739,28 +674,24 @@ Service::OpenSpecialDatabase(const char *aStorageKey,
// connection to use a memory DB.
}
else if (::strcmp(aStorageKey, "profile") == 0) {
rv = NS_GetSpecialDirectory(NS_APP_STORAGE_50_FILE,
getter_AddRefs(storageFile));
NS_ENSURE_SUCCESS(rv, rv);
nsString filename;
storageFile->GetPath(filename);
nsCString filename8 = NS_ConvertUTF16toUTF8(filename.get());
// fall through to DB initialization
}
else {
return NS_ERROR_INVALID_ARG;
}
Connection *msc = new Connection(this, SQLITE_OPEN_READWRITE);
NS_ENSURE_TRUE(msc, NS_ERROR_OUT_OF_MEMORY);
nsRefPtr<Connection> msc = new Connection(this, SQLITE_OPEN_READWRITE);
rv = msc->initialize(storageFile);
rv = storageFile ? msc->initialize(storageFile) : msc->initialize();
NS_ENSURE_SUCCESS(rv, rv);
NS_ADDREF(*_connection = msc);
msc.forget(_connection);
return NS_OK;
}
NS_IMETHODIMP
@ -774,12 +705,11 @@ Service::OpenDatabase(nsIFile *aDatabaseFile,
int flags = SQLITE_OPEN_READWRITE | SQLITE_OPEN_SHAREDCACHE |
SQLITE_OPEN_CREATE;
nsRefPtr<Connection> msc = new Connection(this, flags);
NS_ENSURE_TRUE(msc, NS_ERROR_OUT_OF_MEMORY);
nsresult rv = msc->initialize(aDatabaseFile);
NS_ENSURE_SUCCESS(rv, rv);
NS_ADDREF(*_connection = msc);
msc.forget(_connection);
return NS_OK;
}
@ -794,12 +724,30 @@ Service::OpenUnsharedDatabase(nsIFile *aDatabaseFile,
int flags = SQLITE_OPEN_READWRITE | SQLITE_OPEN_PRIVATECACHE |
SQLITE_OPEN_CREATE;
nsRefPtr<Connection> msc = new Connection(this, flags);
NS_ENSURE_TRUE(msc, NS_ERROR_OUT_OF_MEMORY);
nsresult rv = msc->initialize(aDatabaseFile);
NS_ENSURE_SUCCESS(rv, rv);
NS_ADDREF(*_connection = msc);
msc.forget(_connection);
return NS_OK;
}
NS_IMETHODIMP
Service::OpenDatabaseWithFileURL(nsIFileURL *aFileURL,
mozIStorageConnection **_connection)
{
NS_ENSURE_ARG(aFileURL);
// Always ensure that SQLITE_OPEN_CREATE is passed in for compatibility
// reasons.
int flags = SQLITE_OPEN_READWRITE | SQLITE_OPEN_SHAREDCACHE |
SQLITE_OPEN_CREATE | SQLITE_OPEN_URI;
nsRefPtr<Connection> msc = new Connection(this, flags);
nsresult rv = msc->initialize(aFileURL);
NS_ENSURE_SUCCESS(rv, rv);
msc.forget(_connection);
return NS_OK;
}
@ -885,67 +833,5 @@ Service::Observe(nsISupports *, const char *aTopic, const PRUnichar *)
return NS_OK;
}
////////////////////////////////////////////////////////////////////////////////
//// mozIStorageServiceQuotaManagement
NS_IMETHODIMP
Service::OpenDatabaseWithVFS(nsIFile *aDatabaseFile,
const nsACString &aVFSName,
mozIStorageConnection **_connection)
{
NS_ENSURE_ARG(aDatabaseFile);
// Always ensure that SQLITE_OPEN_CREATE is passed in for compatibility
// reasons.
int flags = SQLITE_OPEN_READWRITE | SQLITE_OPEN_SHAREDCACHE |
SQLITE_OPEN_CREATE;
nsRefPtr<Connection> msc = new Connection(this, flags);
NS_ENSURE_TRUE(msc, NS_ERROR_OUT_OF_MEMORY);
nsresult rv = msc->initialize(aDatabaseFile,
PromiseFlatCString(aVFSName).get());
NS_ENSURE_SUCCESS(rv, rv);
NS_ADDREF(*_connection = msc);
return NS_OK;
}
NS_IMETHODIMP
Service::SetQuotaForFilenamePattern(const nsACString &aPattern,
int64_t aSizeLimit,
mozIStorageQuotaCallback *aCallback,
nsISupports *aUserData)
{
NS_ENSURE_FALSE(aPattern.IsEmpty(), NS_ERROR_INVALID_ARG);
nsAutoPtr<QuotaCallbackData> data;
if (aSizeLimit && aCallback) {
data = new QuotaCallbackData(aCallback, aUserData);
}
int rc = ::sqlite3_quota_set(PromiseFlatCString(aPattern).get(),
aSizeLimit, QuotaCallbackData::Callback,
data, QuotaCallbackData::Destroy);
NS_ENSURE_TRUE(rc == SQLITE_OK, convertResultCode(rc));
data.forget();
return NS_OK;
}
NS_IMETHODIMP
Service::UpdateQuotaInformationForFile(nsIFile *aFile)
{
NS_ENSURE_ARG_POINTER(aFile);
nsString path;
nsresult rv = aFile->GetPath(path);
NS_ENSURE_SUCCESS(rv, rv);
int rc = ::sqlite3_quota_file(NS_ConvertUTF16toUTF8(path).get());
NS_ENSURE_TRUE(rc == SQLITE_OK, convertResultCode(rc));
return NS_OK;
}
} // namespace storage
} // namespace mozilla

View File

@ -15,7 +15,6 @@
#include "mozilla/Mutex.h"
#include "mozIStorageService.h"
#include "mozIStorageServiceQuotaManagement.h"
class nsIMemoryReporter;
class nsIMemoryMultiReporter;
@ -28,7 +27,6 @@ namespace storage {
class Connection;
class Service : public mozIStorageService
, public nsIObserver
, public mozIStorageServiceQuotaManagement
{
public:
/**
@ -58,7 +56,6 @@ public:
NS_DECL_ISUPPORTS
NS_DECL_MOZISTORAGESERVICE
NS_DECL_NSIOBSERVER
NS_DECL_MOZISTORAGESERVICEQUOTAMANAGEMENT
/**
* Obtains an already AddRefed pointer to XPConnect. This is used by

View File

@ -68,6 +68,7 @@ MAKEFILES_dom="
dom/plugins/base/Makefile
dom/plugins/ipc/Makefile
dom/power/Makefile
dom/quota/Makefile
dom/settings/Makefile
dom/sms/Makefile
dom/sms/interfaces/Makefile