commit 74997f1 Author: Jan Varga Date: Mon Dec 17 20:25:10 2012 +0100 Bug 787804 - Rewrite quota handling (eliminate test_quota.c). r=bent,asuth,vladan --- db/sqlite3/README.MOZILLA | 4 +- db/sqlite3/src/sqlite.def | 1 + db/sqlite3/src/test_quota.c | 2001 -------------------- db/sqlite3/src/test_quota.h | 274 --- dom/Makefile.in | 1 + dom/dom-config.mk | 1 + dom/file/FileStreamWrappers.cpp | 11 - dom/file/LockedFile.cpp | 8 +- dom/file/nsIFileStorage.h | 40 +- dom/indexedDB/FileManager.cpp | 33 +- dom/indexedDB/FileManager.h | 20 +- dom/indexedDB/FileStream.cpp | 321 ---- dom/indexedDB/FileStream.h | 140 -- dom/indexedDB/IDBDatabase.cpp | 6 + dom/indexedDB/IDBFactory.cpp | 28 +- dom/indexedDB/IDBFactory.h | 8 +- dom/indexedDB/IDBFileHandle.cpp | 25 +- dom/indexedDB/IDBObjectStore.cpp | 10 +- dom/indexedDB/IDBTransaction.cpp | 3 +- dom/indexedDB/IndexedDatabaseInlines.h | 13 + dom/indexedDB/IndexedDatabaseManager.cpp | 162 +- dom/indexedDB/IndexedDatabaseManager.h | 11 +- dom/indexedDB/Makefile.in | 2 - dom/indexedDB/OpenDatabaseHelper.cpp | 104 +- dom/indexedDB/OpenDatabaseHelper.h | 12 +- dom/indexedDB/nsIStandardFileStream.idl | 60 - dom/indexedDB/test/Makefile.in | 2 + dom/indexedDB/test/file.js | 21 +- dom/indexedDB/test/test_file_quota.html | 14 +- dom/indexedDB/test/test_filehandle_quota.html | 5 +- dom/quota/FileStreams.cpp | 123 ++ dom/quota/FileStreams.h | 115 ++ dom/quota/Makefile.in | 33 + dom/quota/QuotaCommon.h | 23 + dom/quota/QuotaManager.cpp | 294 +++ dom/quota/QuotaManager.h | 147 ++ layout/build/Makefile.in | 1 + netwerk/base/src/Makefile.in | 1 + netwerk/base/src/nsFileStreams.cpp | 103 +- netwerk/base/src/nsFileStreams.h | 12 +- storage/public/Makefile.in | 1 - storage/public/mozIStorageService.idl | 13 +- .../public/mozIStorageServiceQuotaManagement.idl | 99 - storage/public/storage.h | 1 - storage/src/TelemetryVFS.cpp | 35 +- storage/src/mozStorageConnection.cpp | 85 +- storage/src/mozStorageConnection.h | 27 +- storage/src/mozStorageService.cpp | 168 +- storage/src/mozStorageService.h | 3 - toolkit/toolkit-makefiles.sh | 1 + 50 files changed, 1239 insertions(+), 3387 deletions(-) diff --git dom/Makefile.in dom/Makefile.in index 672e065..47cd253 100644 --- dom/Makefile.in +++ dom/Makefile.in @@ -58,6 +58,7 @@ PARALLEL_DIRS += \ media \ messages \ power \ + quota \ settings \ sms \ mms \ diff --git dom/dom-config.mk dom/dom-config.mk index d0f46cc..1cf57ed 100644 --- dom/dom-config.mk +++ dom/dom-config.mk @@ -8,6 +8,7 @@ DOM_SRCDIRS = \ dom/encoding \ dom/file \ dom/power \ + dom/quota \ dom/media \ dom/network/src \ dom/settings \ diff --git dom/file/FileStreamWrappers.cpp dom/file/FileStreamWrappers.cpp index 2283266..c4cf102 100644 --- dom/file/FileStreamWrappers.cpp +++ dom/file/FileStreamWrappers.cpp @@ -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 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(); diff --git dom/file/LockedFile.cpp dom/file/LockedFile.cpp index 0fca730..926df91 100644 --- dom/file/LockedFile.cpp +++ dom/file/LockedFile.cpp @@ -953,10 +953,10 @@ FinishHelper::Run() } for (uint32_t index = 0; index < mParallelStreams.Length(); index++) { - nsCOMPtr ostream = + nsCOMPtr 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 ostream = do_QueryInterface(mStream); + nsCOMPtr stream = do_QueryInterface(mStream); - if (NS_FAILED(ostream->Close())) { + if (NS_FAILED(stream->Close())) { NS_WARNING("Failed to close stream!"); } diff --git dom/file/nsIFileStorage.h dom/file/nsIFileStorage.h index 92bb608..e985f0a 100644 --- dom/file/nsIFileStorage.h +++ dom/file/nsIFileStorage.h @@ -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__ diff --git dom/indexedDB/FileManager.cpp dom/indexedDB/FileManager.cpp index 9db56e8..4ed6e9e 100644 --- dom/indexedDB/FileManager.cpp +++ dom/indexedDB/FileManager.cpp @@ -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 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 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; } diff --git dom/indexedDB/FileManager.h dom/indexedDB/FileManager.h index 2c72d0a..370d4a8 100644 --- dom/indexedDB/FileManager.h +++ dom/indexedDB/FileManager.h @@ -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 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; diff --git dom/indexedDB/FileStream.cpp dom/indexedDB/FileStream.cpp deleted file mode 100644 index dddf5d5..0000000 --- dom/indexedDB/FileStream.cpp +++ /dev/null @@ -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; -} diff --git dom/indexedDB/FileStream.h dom/indexedDB/FileStream.h deleted file mode 100644 index 09648b1..0000000 --- dom/indexedDB/FileStream.h +++ /dev/null @@ -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__ diff --git dom/indexedDB/IDBDatabase.cpp dom/indexedDB/IDBDatabase.cpp index 63500b0..8842daf 100644 --- dom/indexedDB/IDBDatabase.cpp +++ dom/indexedDB/IDBDatabase.cpp @@ -779,6 +779,12 @@ IDBDatabase::Close() return NS_OK; } +const nsACString& +IDBDatabase::StorageOrigin() +{ + return Origin(); +} + nsISupports* IDBDatabase::StorageId() { diff --git dom/indexedDB/IDBFactory.cpp dom/indexedDB/IDBFactory.cpp index 1007df1..c1f573e 100644 --- dom/indexedDB/IDBFactory.cpp +++ dom/indexedDB/IDBFactory.cpp @@ -253,8 +253,26 @@ IDBFactory::Create(ContentParent* aContentParent, } // static +already_AddRefed +IDBFactory::GetDatabaseFileURL(nsIFile* aDatabaseFile, const nsACString& aOrigin) +{ + nsCOMPtr uri; + nsresult rv = NS_NewFileURI(getter_AddRefs(uri), aDatabaseFile); + NS_ENSURE_SUCCESS(rv, nullptr); + + nsCOMPtr 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 -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 ss = + nsCOMPtr dbFileUrl = GetDatabaseFileURL(dbFile, aOrigin); + NS_ENSURE_TRUE(dbFileUrl, nullptr); + + nsCOMPtr ss = do_GetService(MOZ_STORAGE_SERVICE_CONTRACTID); NS_ENSURE_TRUE(ss, nullptr); nsCOMPtr 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. diff --git dom/indexedDB/IDBFactory.h dom/indexedDB/IDBFactory.h index d5461f7..49dad42 100644 --- dom/indexedDB/IDBFactory.h +++ dom/indexedDB/IDBFactory.h @@ -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 + GetDatabaseFileURL(nsIFile* aDatabaseFile, const nsACString& aOrigin); + static already_AddRefed - GetConnection(const nsAString& aDatabaseFilePath); + GetConnection(const nsAString& aDatabaseFilePath, + const nsACString& aOrigin); static nsresult LoadDatabaseInformation(mozIStorageConnection* aConnection, diff --git dom/indexedDB/IDBFileHandle.cpp dom/indexedDB/IDBFileHandle.cpp index e0340ff..f71fd56 100644 --- dom/indexedDB/IDBFileHandle.cpp +++ dom/indexedDB/IDBFileHandle.cpp @@ -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 IDBFileHandle::CreateStream(nsIFile* aFile, bool aReadOnly) { - nsRefPtr stream = new FileStream(); + const nsACString& origin = mFileStorage->StorageOrigin(); + + nsCOMPtr result; - nsString streamMode; if (aReadOnly) { - streamMode.AssignLiteral("rb"); + nsRefPtr stream = FileInputStream::Create( + origin, aFile, -1, -1, nsIFileInputStream::DEFER_OPEN); + result = NS_ISUPPORTS_CAST(nsIFileInputStream*, stream); } else { - streamMode.AssignLiteral("r+b"); + nsRefPtr 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 result = - NS_ISUPPORTS_CAST(nsIStandardFileStream*, stream); return result.forget(); } diff --git dom/indexedDB/IDBObjectStore.cpp dom/indexedDB/IDBObjectStore.cpp index 746d473..1f16d26 100644 --- dom/indexedDB/IDBObjectStore.cpp +++ dom/indexedDB/IDBObjectStore.cpp @@ -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 outputStream = new FileStream(); - rv = outputStream->Init(nativeFile, NS_LITERAL_STRING("wb"), 0); - NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + nsRefPtr 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); diff --git dom/indexedDB/IDBTransaction.cpp dom/indexedDB/IDBTransaction.cpp index fcef7cc..a5345e2 100644 --- dom/indexedDB/IDBTransaction.cpp +++ dom/indexedDB/IDBTransaction.cpp @@ -352,7 +352,8 @@ IDBTransaction::GetOrCreateConnection(mozIStorageConnection** aResult) if (!mConnection) { nsCOMPtr connection = - IDBFactory::GetConnection(mDatabase->FilePath()); + IDBFactory::GetConnection(mDatabase->FilePath(), + mDatabase->Origin()); NS_ENSURE_TRUE(connection, NS_ERROR_FAILURE); nsresult rv; diff --git dom/indexedDB/IndexedDatabaseInlines.h dom/indexedDB/IndexedDatabaseInlines.h index 62e65d6..f27d60c 100644 --- dom/indexedDB/IndexedDatabaseInlines.h +++ dom/indexedDB/IndexedDatabaseInlines.h @@ -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 diff --git dom/indexedDB/IndexedDatabaseManager.cpp dom/indexedDB/IndexedDatabaseManager.cpp index e4ad647..88f09da 100644 --- dom/indexedDB/IndexedDatabaseManager.cpp +++ dom/indexedDB/IndexedDatabaseManager.cpp @@ -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 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 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 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 subdirsToProcess; nsAutoTArray , 20> unknownFiles; + uint64_t usage = 0; + nsTHashtable validSubdirs; validSubdirs.Init(20); @@ -1068,20 +1026,28 @@ IndexedDatabaseManager::EnsureOriginIsInitialized(const nsACString& aOrigin, continue; } - nsCOMPtr fileManagerDirectory; - rv = directory->Clone(getter_AddRefs(fileManagerDirectory)); + nsCOMPtr 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 >* array; - if (!mFileManagers.Get(aOrigin, &array)) { + if (!mFileManagers.Get(aFileManager->Origin(), &array)) { array = new nsTArray >(); - 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 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(); diff --git dom/indexedDB/IndexedDatabaseManager.h dom/indexedDB/IndexedDatabaseManager.h index f9fbbf2..1ea5425 100644 --- dom/indexedDB/IndexedDatabaseManager.h +++ dom/indexedDB/IndexedDatabaseManager.h @@ -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 mShutdownTimer; - // A single threadsafe instance of our quota callback. Created on the main - // thread during GetOrCreate(). - nsCOMPtr 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 mInitializedOrigins; diff --git dom/indexedDB/Makefile.in dom/indexedDB/Makefile.in index fef0858..09d4853 100644 --- dom/indexedDB/Makefile.in +++ dom/indexedDB/Makefile.in @@ -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 diff --git dom/indexedDB/OpenDatabaseHelper.cpp dom/indexedDB/OpenDatabaseHelper.cpp index e71cad4..4cd7f61 100644 --- dom/indexedDB/OpenDatabaseHelper.cpp +++ dom/indexedDB/OpenDatabaseHelper.cpp @@ -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 fileManagerDirectory; - rv = dbDirectory->Clone(getter_AddRefs(fileManagerDirectory)); + nsCOMPtr 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 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 = 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 dbFileUrl = + IDBFactory::GetDatabaseFileURL(aDBFile, aOrigin); + NS_ENSURE_TRUE(dbFileUrl, NS_ERROR_FAILURE); - nsCOMPtr ss = + nsCOMPtr ss = do_GetService(MOZ_STORAGE_SERVICE_CONTRACTID); NS_ENSURE_TRUE(ss, NS_ERROR_FAILURE); nsCOMPtr 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 fileManagerDirectory; - rv = directory->Clone(getter_AddRefs(fileManagerDirectory)); - NS_ENSURE_SUCCESS(rv, rv); + nsCOMPtr fmDirectory; + rv = directory->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); - rv = fileManagerDirectory->Exists(&exists); + 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; diff --git dom/indexedDB/OpenDatabaseHelper.h dom/indexedDB/OpenDatabaseHelper.h index 587301b..5a3d987 100644 --- dom/indexedDB/OpenDatabaseHelper.h +++ dom/indexedDB/OpenDatabaseHelper.h @@ -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: diff --git dom/indexedDB/nsIStandardFileStream.idl dom/indexedDB/nsIStandardFileStream.idl deleted file mode 100644 index 265c3ed..0000000 --- dom/indexedDB/nsIStandardFileStream.idl +++ /dev/null @@ -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(); -}; diff --git dom/indexedDB/test/Makefile.in dom/indexedDB/test/Makefile.in index 9c79b14..4c9a201 100644 --- dom/indexedDB/test/Makefile.in +++ dom/indexedDB/test/Makefile.in @@ -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 \ diff --git dom/indexedDB/test/file.js dom/indexedDB/test/file.js index 07bd10a..3c6194a 100644 --- dom/indexedDB/test/file.js +++ dom/indexedDB/test/file.js @@ -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); diff --git dom/indexedDB/test/test_file_quota.html dom/indexedDB/test/test_file_quota.html index b07880d..9fbc0c0 100644 --- dom/indexedDB/test/test_file_quota.html +++ dom/indexedDB/test/test_file_quota.html @@ -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; diff --git dom/indexedDB/test/test_filehandle_quota.html dom/indexedDB/test/test_filehandle_quota.html index addaf01..0506279 100644 --- dom/indexedDB/test/test_filehandle_quota.html +++ dom/indexedDB/test/test_filehandle_quota.html @@ -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; diff --git dom/quota/FileStreams.cpp dom/quota/FileStreams.cpp new file mode 100644 index 0000000..9de244f --- /dev/null +++ dom/quota/FileStreams.cpp @@ -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 +NS_IMETHODIMP +FileQuotaStream::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 +NS_IMETHODIMP +FileQuotaStream::Close() +{ + nsresult rv = FileStreamBase::Close(); + NS_ENSURE_SUCCESS(rv, rv); + + mQuotaObject = nullptr; + + return NS_OK; +} + +template +nsresult +FileQuotaStream::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 +NS_IMETHODIMP +FileQuotaStreamWithWrite::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::Create(const nsACString& aOrigin, nsIFile* aFile, + int32_t aIOFlags, int32_t aPerm, + int32_t aBehaviorFlags) +{ + nsRefPtr 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::Create(const nsACString& aOrigin, nsIFile* aFile, + int32_t aIOFlags, int32_t aPerm, + int32_t aBehaviorFlags) +{ + nsRefPtr 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::Create(const nsACString& aOrigin, nsIFile* aFile, int32_t aIOFlags, + int32_t aPerm, int32_t aBehaviorFlags) +{ + nsRefPtr stream = new FileStream(aOrigin); + nsresult rv = stream->Init(aFile, aIOFlags, aPerm, aBehaviorFlags); + NS_ENSURE_SUCCESS(rv, nullptr); + return stream.forget(); +} diff --git dom/quota/FileStreams.h dom/quota/FileStreams.h new file mode 100644 index 0000000..77bfad4 --- /dev/null +++ dom/quota/FileStreams.h @@ -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 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 mQuotaObject; +}; + +template +class FileQuotaStreamWithWrite : public FileQuotaStream +{ +public: + // nsFileStreamBase override + NS_IMETHOD + Write(const char* aBuf, uint32_t aCount, uint32_t* _retval) MOZ_OVERRIDE; + +protected: + FileQuotaStreamWithWrite(const nsACString& aOrigin) + : FileQuotaStream(aOrigin) + { } +}; + +class FileInputStream : public FileQuotaStream +{ +public: + NS_DECL_ISUPPORTS_INHERITED + + static already_AddRefed + Create(const nsACString& aOrigin, nsIFile* aFile, int32_t aIOFlags = -1, + int32_t aPerm = -1, int32_t aBehaviorFlags = 0); + +private: + FileInputStream(const nsACString& aOrigin) + : FileQuotaStream(aOrigin) + { } + + virtual ~FileInputStream() { + Close(); + } +}; + +class FileOutputStream : public FileQuotaStreamWithWrite +{ +public: + NS_DECL_ISUPPORTS_INHERITED + + static already_AddRefed + Create(const nsACString& aOrigin, nsIFile* aFile, int32_t aIOFlags = -1, + int32_t aPerm = -1, int32_t aBehaviorFlags = 0); + +private: + FileOutputStream(const nsACString& aOrigin) + : FileQuotaStreamWithWrite(aOrigin) + { } + + virtual ~FileOutputStream() { + Close(); + } +}; + +class FileStream : public FileQuotaStreamWithWrite +{ +public: + NS_DECL_ISUPPORTS_INHERITED + + static already_AddRefed + Create(const nsACString& aOrigin, nsIFile* aFile, int32_t aIOFlags = -1, + int32_t aPerm = -1, int32_t aBehaviorFlags = 0); + +private: + FileStream(const nsACString& aOrigin) + : FileQuotaStreamWithWrite(aOrigin) + { } + + virtual ~FileStream() { + Close(); + } +}; + +END_QUOTA_NAMESPACE + +#endif /* mozilla_dom_quota_filestreams_h__ */ diff --git dom/quota/Makefile.in dom/quota/Makefile.in new file mode 100644 index 0000000..49be551 --- /dev/null +++ dom/quota/Makefile.in @@ -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 diff --git dom/quota/QuotaCommon.h dom/quota/QuotaCommon.h new file mode 100644 index 0000000..a415d17 --- /dev/null +++ dom/quota/QuotaCommon.h @@ -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__ diff --git dom/quota/QuotaManager.cpp dom/quota/QuotaManager.cpp new file mode 100644 index 0000000..b251606 --- /dev/null +++ dom/quota/QuotaManager.cpp @@ -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 gInstance; + +PLDHashOperator +RemoveQuotaForPatternCallback(const nsACString& aKey, + nsRefPtr& aValue, + void* aUserArg) +{ + NS_ASSERTION(!aKey.IsEmpty(), "Empty key!"); + NS_ASSERTION(aValue, "Null pointer!"); + NS_ASSERTION(aUserArg, "Null pointer!"); + + const nsACString* pattern = + static_cast(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; + 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(&aPattern)); +} + +already_AddRefed +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; + 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 result = info; + return result.forget(); +} + +already_AddRefed +QuotaManager::GetQuotaObject(const nsACString& aOrigin, + const nsAString& aPath) +{ + nsresult rv; + nsCOMPtr 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); +} diff --git dom/quota/QuotaManager.h dom/quota/QuotaManager.h new file mode 100644 index 0000000..e19acdd --- /dev/null +++ dom/quota/QuotaManager.h @@ -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 mQuotaObjects; + + nsCString mOrigin; + int64_t mLimit; + int64_t mUsage; +}; + +class QuotaManager +{ + friend class nsAutoPtr; + 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 + GetQuotaObject(const nsACString& aOrigin, + nsIFile* aFile); + + already_AddRefed + 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 mOriginInfos; +}; + +END_QUOTA_NAMESPACE + +#endif /* mozilla_dom_quota_quotamanager_h__ */ diff --git layout/build/Makefile.in layout/build/Makefile.in index e6b32da..496b55f 100644 --- layout/build/Makefile.in +++ layout/build/Makefile.in @@ -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) \ diff --git netwerk/base/src/Makefile.in netwerk/base/src/Makefile.in index 0c0d60e..e8cef48 100644 --- netwerk/base/src/Makefile.in +++ netwerk/base/src/Makefile.in @@ -19,6 +19,7 @@ LIBXUL_LIBRARY = 1 EXPORTS = \ nsMIMEInputStream.h \ nsURLHelper.h \ + nsFileStreams.h \ $(NULL) EXPORTS_NAMESPACES = mozilla/net diff --git netwerk/base/src/nsFileStreams.cpp netwerk/base/src/nsFileStreams.cpp index 2420ffc..ecc26aa 100644 --- netwerk/base/src/nsFileStreams.cpp +++ netwerk/base/src/nsFileStreams.cpp @@ -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; -} - //////////////////////////////////////////////////////////////////////////////// diff --git netwerk/base/src/nsFileStreams.h netwerk/base/src/nsFileStreams.h index 13e5b45..1aa6a82 100644 --- netwerk/base/src/nsFileStreams.h +++ netwerk/base/src/nsFileStreams.h @@ -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 diff --git storage/public/Makefile.in storage/public/Makefile.in index c485d4e..c05e6f3 100644 --- storage/public/Makefile.in +++ storage/public/Makefile.in @@ -36,7 +36,6 @@ XPIDLSRCS = \ mozIStorageCompletionCallback.idl \ mozIStorageBaseStatement.idl \ mozIStorageAsyncStatement.idl \ - mozIStorageServiceQuotaManagement.idl \ mozIStorageVacuumParticipant.idl \ $(NULL) # SEE ABOVE NOTE! diff --git storage/public/mozIStorageService.idl storage/public/mozIStorageService.idl index 3087a11..483649b 100644 --- storage/public/mozIStorageService.idl +++ storage/public/mozIStorageService.idl @@ -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 */ diff --git storage/public/mozIStorageServiceQuotaManagement.idl storage/public/mozIStorageServiceQuotaManagement.idl deleted file mode 100644 index ee5086b..0000000 --- storage/public/mozIStorageServiceQuotaManagement.idl +++ /dev/null @@ -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); -}; diff --git storage/public/storage.h storage/public/storage.h index 8e571e2..08f39f3 100644 --- storage/public/storage.h +++ storage/public/storage.h @@ -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" diff --git storage/src/TelemetryVFS.cpp storage/src/TelemetryVFS.cpp index 60de5c4..e4fce09 100644 --- storage/src/TelemetryVFS.cpp +++ storage/src/TelemetryVFS.cpp @@ -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; + + // 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 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; diff --git storage/src/mozStorageConnection.cpp storage/src/mozStorageConnection.cpp index 3afd3e1b..430824a 100644 --- storage/src/mozStorageConnection.cpp +++ storage/src/mozStorageConnection.cpp @@ -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; + // 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; - if (aDatabaseFile) { - nsAutoString path; - rv = aDatabaseFile->GetPath(path); - NS_ENSURE_SUCCESS(rv, rv); + nsAutoString path; + nsresult 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); + 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 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 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. diff --git storage/src/mozStorageConnection.h storage/src/mozStorageConnection.h index b71f5db..97f5cf8 100644 --- storage/src/mozStorageConnection.h +++ storage/src/mozStorageConnection.h @@ -25,6 +25,7 @@ struct PRLock; class nsIFile; +class nsIFileURL; class nsIEventTarget; class nsIThread; @@ -63,18 +64,27 @@ 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 mFileURL; nsCOMPtr mDatabaseFile; /** diff --git storage/src/mozStorageService.cpp storage/src/mozStorageService.cpp index 00661d6..862a7da 100644 --- storage/src/mozStorageService.cpp +++ storage/src/mozStorageService.cpp @@ -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(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(aUserData); - } - -private: - nsCOMPtr callback; - nsCOMPtr 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 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 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 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 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 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 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 diff --git storage/src/mozStorageService.h storage/src/mozStorageService.h index 21c1ff8..3f5a546 100644 --- storage/src/mozStorageService.h +++ storage/src/mozStorageService.h @@ -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 diff --git toolkit/toolkit-makefiles.sh toolkit/toolkit-makefiles.sh index 6a7d714..8f1bbe0 100644 --- toolkit/toolkit-makefiles.sh +++ toolkit/toolkit-makefiles.sh @@ -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