зеркало из https://github.com/mozilla/pjs.git
Merge mozilla-central and mozilla-inbound
This commit is contained in:
Коммит
3f360365bf
|
@ -226,7 +226,7 @@ function onIndexedDBClear()
|
||||||
initIndexedDBRow();
|
initIndexedDBRow();
|
||||||
}
|
}
|
||||||
|
|
||||||
function onIndexedDBUsageCallback(uri, usage)
|
function onIndexedDBUsageCallback(uri, usage, fileUsage)
|
||||||
{
|
{
|
||||||
if (!uri.equals(gPermURI)) {
|
if (!uri.equals(gPermURI)) {
|
||||||
throw new Error("Callback received for bad URI: " + uri);
|
throw new Error("Callback received for bad URI: " + uri);
|
||||||
|
|
|
@ -53,6 +53,9 @@
|
||||||
#include "nsIXMLHttpRequest.h"
|
#include "nsIXMLHttpRequest.h"
|
||||||
#include "prmem.h"
|
#include "prmem.h"
|
||||||
#include "nsAutoPtr.h"
|
#include "nsAutoPtr.h"
|
||||||
|
#include "mozilla/dom/indexedDB/FileInfo.h"
|
||||||
|
#include "mozilla/dom/indexedDB/FileManager.h"
|
||||||
|
#include "mozilla/dom/indexedDB/IndexedDatabaseManager.h"
|
||||||
|
|
||||||
#include "mozilla/GuardObjects.h"
|
#include "mozilla/GuardObjects.h"
|
||||||
|
|
||||||
|
@ -67,12 +70,13 @@ class nsIBlobBuilder;
|
||||||
|
|
||||||
nsresult NS_NewBlobBuilder(nsISupports* *aSupports);
|
nsresult NS_NewBlobBuilder(nsISupports* *aSupports);
|
||||||
|
|
||||||
|
using namespace mozilla::dom;
|
||||||
|
|
||||||
class nsDOMFileBase : public nsIDOMFile,
|
class nsDOMFileBase : public nsIDOMFile,
|
||||||
public nsIXHRSendable,
|
public nsIXHRSendable,
|
||||||
public nsIMutable
|
public nsIMutable
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
||||||
nsDOMFileBase(const nsAString& aName, const nsAString& aContentType,
|
nsDOMFileBase(const nsAString& aName, const nsAString& aContentType,
|
||||||
PRUint64 aLength)
|
PRUint64 aLength)
|
||||||
: mIsFile(true), mImmutable(false), mContentType(aContentType),
|
: mIsFile(true), mImmutable(false), mContentType(aContentType),
|
||||||
|
@ -119,6 +123,21 @@ protected:
|
||||||
return mLength == PR_UINT64_MAX;
|
return mLength == PR_UINT64_MAX;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
virtual bool IsStoredFile()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual bool IsWholeFile()
|
||||||
|
{
|
||||||
|
NS_NOTREACHED("Should only be called on dom blobs backed by files!");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
indexedDB::FileInfo*
|
||||||
|
GetFileInfoInternal(indexedDB::FileManager* aFileManager,
|
||||||
|
PRUint32 aStartIndex);
|
||||||
|
|
||||||
bool mIsFile;
|
bool mIsFile;
|
||||||
bool mImmutable;
|
bool mImmutable;
|
||||||
nsString mContentType;
|
nsString mContentType;
|
||||||
|
@ -126,6 +145,9 @@ protected:
|
||||||
|
|
||||||
PRUint64 mStart;
|
PRUint64 mStart;
|
||||||
PRUint64 mLength;
|
PRUint64 mLength;
|
||||||
|
|
||||||
|
// Protected by IndexedDatabaseManager::FileMutex()
|
||||||
|
nsTArray<nsRefPtr<indexedDB::FileInfo> > mFileInfos;
|
||||||
};
|
};
|
||||||
|
|
||||||
class nsDOMFileFile : public nsDOMFileBase,
|
class nsDOMFileFile : public nsDOMFileBase,
|
||||||
|
@ -135,7 +157,7 @@ public:
|
||||||
// Create as a file
|
// Create as a file
|
||||||
nsDOMFileFile(nsIFile *aFile)
|
nsDOMFileFile(nsIFile *aFile)
|
||||||
: nsDOMFileBase(EmptyString(), EmptyString(), PR_UINT64_MAX),
|
: nsDOMFileBase(EmptyString(), EmptyString(), PR_UINT64_MAX),
|
||||||
mFile(aFile), mWholeFile(true)
|
mFile(aFile), mWholeFile(true), mStoredFile(false)
|
||||||
{
|
{
|
||||||
NS_ASSERTION(mFile, "must have file");
|
NS_ASSERTION(mFile, "must have file");
|
||||||
// Lazily get the content type and size
|
// Lazily get the content type and size
|
||||||
|
@ -147,16 +169,37 @@ public:
|
||||||
nsDOMFileFile(nsIFile *aFile, const nsAString& aContentType,
|
nsDOMFileFile(nsIFile *aFile, const nsAString& aContentType,
|
||||||
nsISupports *aCacheToken = nsnull)
|
nsISupports *aCacheToken = nsnull)
|
||||||
: nsDOMFileBase(aContentType, PR_UINT64_MAX),
|
: nsDOMFileBase(aContentType, PR_UINT64_MAX),
|
||||||
mFile(aFile), mWholeFile(true),
|
mFile(aFile), mWholeFile(true), mStoredFile(false),
|
||||||
mCacheToken(aCacheToken)
|
mCacheToken(aCacheToken)
|
||||||
{
|
{
|
||||||
NS_ASSERTION(mFile, "must have file");
|
NS_ASSERTION(mFile, "must have file");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Create as a stored file
|
||||||
|
nsDOMFileFile(const nsAString& aName, const nsAString& aContentType,
|
||||||
|
PRUint64 aLength, nsIFile* aFile,
|
||||||
|
indexedDB::FileInfo* aFileInfo)
|
||||||
|
: nsDOMFileBase(aName, aContentType, aLength),
|
||||||
|
mFile(aFile), mWholeFile(true), mStoredFile(true)
|
||||||
|
{
|
||||||
|
NS_ASSERTION(mFile, "must have file");
|
||||||
|
mFileInfos.AppendElement(aFileInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create as a stored blob
|
||||||
|
nsDOMFileFile(const nsAString& aContentType, PRUint64 aLength,
|
||||||
|
nsIFile* aFile, indexedDB::FileInfo* aFileInfo)
|
||||||
|
: nsDOMFileBase(aContentType, aLength),
|
||||||
|
mFile(aFile), mWholeFile(true), mStoredFile(true)
|
||||||
|
{
|
||||||
|
NS_ASSERTION(mFile, "must have file");
|
||||||
|
mFileInfos.AppendElement(aFileInfo);
|
||||||
|
}
|
||||||
|
|
||||||
// Create as a file to be later initialized
|
// Create as a file to be later initialized
|
||||||
nsDOMFileFile()
|
nsDOMFileFile()
|
||||||
: nsDOMFileBase(EmptyString(), EmptyString(), PR_UINT64_MAX),
|
: nsDOMFileBase(EmptyString(), EmptyString(), PR_UINT64_MAX),
|
||||||
mWholeFile(true)
|
mWholeFile(true), mStoredFile(false)
|
||||||
{
|
{
|
||||||
// Lazily get the content type and size
|
// Lazily get the content type and size
|
||||||
mContentType.SetIsVoid(true);
|
mContentType.SetIsVoid(true);
|
||||||
|
@ -188,17 +231,47 @@ protected:
|
||||||
const nsAString& aContentType)
|
const nsAString& aContentType)
|
||||||
: nsDOMFileBase(aContentType, aOther->mStart + aStart, aLength),
|
: nsDOMFileBase(aContentType, aOther->mStart + aStart, aLength),
|
||||||
mFile(aOther->mFile), mWholeFile(false),
|
mFile(aOther->mFile), mWholeFile(false),
|
||||||
mCacheToken(aOther->mCacheToken)
|
mStoredFile(aOther->mStoredFile), mCacheToken(aOther->mCacheToken)
|
||||||
{
|
{
|
||||||
NS_ASSERTION(mFile, "must have file");
|
NS_ASSERTION(mFile, "must have file");
|
||||||
mImmutable = aOther->mImmutable;
|
mImmutable = aOther->mImmutable;
|
||||||
|
|
||||||
|
if (mStoredFile) {
|
||||||
|
indexedDB::FileInfo* fileInfo;
|
||||||
|
|
||||||
|
if (!indexedDB::IndexedDatabaseManager::IsClosed()) {
|
||||||
|
indexedDB::IndexedDatabaseManager::FileMutex().Lock();
|
||||||
|
}
|
||||||
|
|
||||||
|
NS_ASSERTION(!aOther->mFileInfos.IsEmpty(),
|
||||||
|
"A stored file must have at least one file info!");
|
||||||
|
|
||||||
|
fileInfo = aOther->mFileInfos.ElementAt(0);
|
||||||
|
|
||||||
|
if (!indexedDB::IndexedDatabaseManager::IsClosed()) {
|
||||||
|
indexedDB::IndexedDatabaseManager::FileMutex().Unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
mFileInfos.AppendElement(fileInfo);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
virtual already_AddRefed<nsIDOMBlob>
|
virtual already_AddRefed<nsIDOMBlob>
|
||||||
CreateSlice(PRUint64 aStart, PRUint64 aLength,
|
CreateSlice(PRUint64 aStart, PRUint64 aLength,
|
||||||
const nsAString& aContentType);
|
const nsAString& aContentType);
|
||||||
|
|
||||||
|
virtual bool IsStoredFile()
|
||||||
|
{
|
||||||
|
return mStoredFile;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual bool IsWholeFile()
|
||||||
|
{
|
||||||
|
return mWholeFile;
|
||||||
|
}
|
||||||
|
|
||||||
nsCOMPtr<nsIFile> mFile;
|
nsCOMPtr<nsIFile> mFile;
|
||||||
bool mWholeFile;
|
bool mWholeFile;
|
||||||
|
bool mStoredFile;
|
||||||
nsCOMPtr<nsISupports> mCacheToken;
|
nsCOMPtr<nsISupports> mCacheToken;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -39,15 +39,28 @@
|
||||||
|
|
||||||
%{C++
|
%{C++
|
||||||
#include "jsapi.h"
|
#include "jsapi.h"
|
||||||
|
|
||||||
|
namespace mozilla {
|
||||||
|
namespace dom {
|
||||||
|
namespace indexedDB {
|
||||||
|
class FileInfo;
|
||||||
|
class FileManager;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
%}
|
%}
|
||||||
|
|
||||||
|
[ptr] native FileInfo(mozilla::dom::indexedDB::FileInfo);
|
||||||
|
[ptr] native FileManager(mozilla::dom::indexedDB::FileManager);
|
||||||
|
|
||||||
interface nsIDOMFileError;
|
interface nsIDOMFileError;
|
||||||
interface nsIInputStream;
|
interface nsIInputStream;
|
||||||
interface nsIURI;
|
interface nsIURI;
|
||||||
interface nsIPrincipal;
|
interface nsIPrincipal;
|
||||||
interface nsIDOMBlob;
|
interface nsIDOMBlob;
|
||||||
|
|
||||||
[scriptable, builtinclass, uuid(d5237f31-443a-460b-9e42-449a135346f0)]
|
[scriptable, builtinclass, uuid(f62c6887-e3bc-495a-802c-287e12e969a0)]
|
||||||
interface nsIDOMBlob : nsISupports
|
interface nsIDOMBlob : nsISupports
|
||||||
{
|
{
|
||||||
readonly attribute unsigned long long size;
|
readonly attribute unsigned long long size;
|
||||||
|
@ -61,6 +74,18 @@ interface nsIDOMBlob : nsISupports
|
||||||
[optional_argc] nsIDOMBlob mozSlice([optional] in long long start,
|
[optional_argc] nsIDOMBlob mozSlice([optional] in long long start,
|
||||||
[optional] in long long end,
|
[optional] in long long end,
|
||||||
[optional] in DOMString contentType);
|
[optional] in DOMString contentType);
|
||||||
|
|
||||||
|
// Get internal id of stored file. Returns -1 if it is not a stored file.
|
||||||
|
// Intended only for testing. It can be called on any thread.
|
||||||
|
[notxpcom] long long getFileId();
|
||||||
|
|
||||||
|
// Called when the blob was successfully stored in a database or when
|
||||||
|
// the blob is initialized from a database. It can be called on any thread.
|
||||||
|
[notxpcom] void addFileInfo(in FileInfo aFileInfo);
|
||||||
|
|
||||||
|
// Called before the blob is stored in a database to decide if it can be
|
||||||
|
// shared or needs to be copied. It can be called on any thread.
|
||||||
|
[notxpcom] FileInfo getFileInfo(in FileManager aFileManager);
|
||||||
};
|
};
|
||||||
|
|
||||||
[scriptable, builtinclass, uuid(b096ef67-7b77-47f8-8e70-5d8ee36416bf)]
|
[scriptable, builtinclass, uuid(b096ef67-7b77-47f8-8e70-5d8ee36416bf)]
|
||||||
|
|
|
@ -298,6 +298,76 @@ nsDOMFileBase::GetInternalUrl(nsIPrincipal* aPrincipal, nsAString& aURL)
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
NS_IMETHODIMP_(PRInt64)
|
||||||
|
nsDOMFileBase::GetFileId()
|
||||||
|
{
|
||||||
|
PRInt64 id = -1;
|
||||||
|
|
||||||
|
if (IsStoredFile() && IsWholeFile()) {
|
||||||
|
if (!indexedDB::IndexedDatabaseManager::IsClosed()) {
|
||||||
|
indexedDB::IndexedDatabaseManager::FileMutex().Lock();
|
||||||
|
}
|
||||||
|
|
||||||
|
NS_ASSERTION(!mFileInfos.IsEmpty(),
|
||||||
|
"A stored file must have at least one file info!");
|
||||||
|
|
||||||
|
nsRefPtr<indexedDB::FileInfo>& fileInfo = mFileInfos.ElementAt(0);
|
||||||
|
if (fileInfo) {
|
||||||
|
id = fileInfo->Id();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!indexedDB::IndexedDatabaseManager::IsClosed()) {
|
||||||
|
indexedDB::IndexedDatabaseManager::FileMutex().Unlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
NS_IMETHODIMP_(void)
|
||||||
|
nsDOMFileBase::AddFileInfo(indexedDB::FileInfo* aFileInfo)
|
||||||
|
{
|
||||||
|
if (indexedDB::IndexedDatabaseManager::IsClosed()) {
|
||||||
|
NS_ERROR("Shouldn't be called after shutdown!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
nsRefPtr<indexedDB::FileInfo> fileInfo = aFileInfo;
|
||||||
|
|
||||||
|
MutexAutoLock lock(indexedDB::IndexedDatabaseManager::FileMutex());
|
||||||
|
|
||||||
|
NS_ASSERTION(!mFileInfos.Contains(aFileInfo),
|
||||||
|
"Adding the same file info agan?!");
|
||||||
|
|
||||||
|
nsRefPtr<indexedDB::FileInfo>* element = mFileInfos.AppendElement();
|
||||||
|
element->swap(fileInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
NS_IMETHODIMP_(indexedDB::FileInfo*)
|
||||||
|
nsDOMFileBase::GetFileInfo(indexedDB::FileManager* aFileManager)
|
||||||
|
{
|
||||||
|
if (indexedDB::IndexedDatabaseManager::IsClosed()) {
|
||||||
|
NS_ERROR("Shouldn't be called after shutdown!");
|
||||||
|
return nsnull;
|
||||||
|
}
|
||||||
|
|
||||||
|
// A slice created from a stored file must keep the file info alive.
|
||||||
|
// However, we don't support sharing of slices yet, so the slice must be
|
||||||
|
// copied again. That's why we have to ignore the first file info.
|
||||||
|
PRUint32 startIndex = IsStoredFile() && !IsWholeFile() ? 1 : 0;
|
||||||
|
|
||||||
|
MutexAutoLock lock(indexedDB::IndexedDatabaseManager::FileMutex());
|
||||||
|
|
||||||
|
for (PRUint32 i = startIndex; i < mFileInfos.Length(); i++) {
|
||||||
|
nsRefPtr<indexedDB::FileInfo>& fileInfo = mFileInfos.ElementAt(i);
|
||||||
|
if (fileInfo->Manager() == aFileManager) {
|
||||||
|
return fileInfo;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nsnull;
|
||||||
|
}
|
||||||
|
|
||||||
NS_IMETHODIMP
|
NS_IMETHODIMP
|
||||||
nsDOMFileBase::GetSendInfo(nsIInputStream** aBody,
|
nsDOMFileBase::GetSendInfo(nsIInputStream** aBody,
|
||||||
nsACString& aContentType,
|
nsACString& aContentType,
|
||||||
|
|
|
@ -11,7 +11,7 @@ To move to a new version:
|
||||||
|
|
||||||
Simply copy the sqlite3.h and sqlite3.c files from the amalgamation of sqlite.
|
Simply copy the sqlite3.h and sqlite3.c files from the amalgamation of sqlite.
|
||||||
|
|
||||||
Also copy test_quota.c from the full source package.
|
Also copy test_quota.h and test_quota.c from the full source package.
|
||||||
|
|
||||||
Be sure to update SQLITE_VERSION accordingly in $(topsrcdir)/configure.in as
|
Be sure to update SQLITE_VERSION accordingly in $(topsrcdir)/configure.in as
|
||||||
well as the version number at the top of this file.
|
well as the version number at the top of this file.
|
||||||
|
|
|
@ -181,6 +181,7 @@ EXPORTS
|
||||||
sqlite3_vfs_unregister
|
sqlite3_vfs_unregister
|
||||||
sqlite3_vfs_register
|
sqlite3_vfs_register
|
||||||
sqlite3_vmprintf
|
sqlite3_vmprintf
|
||||||
|
sqlite3_win32_utf8_to_mbcs
|
||||||
#ifdef SQLITE_DEBUG
|
#ifdef SQLITE_DEBUG
|
||||||
sqlite3_mutex_held
|
sqlite3_mutex_held
|
||||||
sqlite3_mutex_notheld
|
sqlite3_mutex_notheld
|
||||||
|
|
|
@ -27,7 +27,7 @@
|
||||||
** files within the group is less than the new quota, then the write
|
** files within the group is less than the new quota, then the write
|
||||||
** continues as if nothing had happened.
|
** continues as if nothing had happened.
|
||||||
*/
|
*/
|
||||||
#include "sqlite3.h"
|
#include "test_quota.h"
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
|
||||||
|
@ -45,20 +45,6 @@
|
||||||
#endif /* SQLITE_THREADSAFE==0 */
|
#endif /* SQLITE_THREADSAFE==0 */
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
** For an build without mutexes, no-op the mutex calls.
|
|
||||||
*/
|
|
||||||
#if defined(SQLITE_THREADSAFE) && SQLITE_THREADSAFE==0
|
|
||||||
#define sqlite3_mutex_alloc(X) ((sqlite3_mutex*)8)
|
|
||||||
#define sqlite3_mutex_free(X)
|
|
||||||
#define sqlite3_mutex_enter(X)
|
|
||||||
#define sqlite3_mutex_try(X) SQLITE_OK
|
|
||||||
#define sqlite3_mutex_leave(X)
|
|
||||||
#define sqlite3_mutex_held(X) ((void)(X),1)
|
|
||||||
#define sqlite3_mutex_notheld(X) ((void)(X),1)
|
|
||||||
#endif /* SQLITE_THREADSAFE==0 */
|
|
||||||
|
|
||||||
|
|
||||||
/************************ Object Definitions ******************************/
|
/************************ Object Definitions ******************************/
|
||||||
|
|
||||||
/* Forward declaration of all object types */
|
/* Forward declaration of all object types */
|
||||||
|
@ -125,6 +111,18 @@ struct quotaConn {
|
||||||
/* The underlying VFS sqlite3_file is appended to this object */
|
/* The underlying VFS sqlite3_file is appended to this object */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
** An instance of the following object records the state of an
|
||||||
|
** open file. This object is opaque to all users - the internal
|
||||||
|
** structure is only visible to the functions below.
|
||||||
|
*/
|
||||||
|
struct quota_FILE {
|
||||||
|
FILE *f; /* Open stdio file pointer */
|
||||||
|
sqlite3_int64 iOfst; /* Current offset into the file */
|
||||||
|
quotaFile *pFile; /* The file record in the quota system */
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
/************************* Global Variables **********************************/
|
/************************* Global Variables **********************************/
|
||||||
/*
|
/*
|
||||||
** All global variables used by this file are containing within the following
|
** All global variables used by this file are containing within the following
|
||||||
|
@ -239,9 +237,11 @@ static void quotaGroupDeref(quotaGroup *pGroup){
|
||||||
**
|
**
|
||||||
** [^...] Matches one character not in the enclosed list.
|
** [^...] Matches one character not in the enclosed list.
|
||||||
**
|
**
|
||||||
|
** / Matches "/" or "\\"
|
||||||
|
**
|
||||||
*/
|
*/
|
||||||
static int quotaStrglob(const char *zGlob, const char *z){
|
static int quotaStrglob(const char *zGlob, const char *z){
|
||||||
int c, c2;
|
int c, c2, cx;
|
||||||
int invert;
|
int invert;
|
||||||
int seen;
|
int seen;
|
||||||
|
|
||||||
|
@ -258,8 +258,9 @@ static int quotaStrglob(const char *zGlob, const char *z){
|
||||||
}
|
}
|
||||||
return (*z)!=0;
|
return (*z)!=0;
|
||||||
}
|
}
|
||||||
|
cx = (c=='/') ? '\\' : c;
|
||||||
while( (c2 = (*(z++)))!=0 ){
|
while( (c2 = (*(z++)))!=0 ){
|
||||||
while( c2!=c ){
|
while( c2!=c && c2!=cx ){
|
||||||
c2 = *(z++);
|
c2 = *(z++);
|
||||||
if( c2==0 ) return 0;
|
if( c2==0 ) return 0;
|
||||||
}
|
}
|
||||||
|
@ -297,6 +298,9 @@ static int quotaStrglob(const char *zGlob, const char *z){
|
||||||
c2 = *(zGlob++);
|
c2 = *(zGlob++);
|
||||||
}
|
}
|
||||||
if( c2==0 || (seen ^ invert)==0 ) return 0;
|
if( c2==0 || (seen ^ invert)==0 ) return 0;
|
||||||
|
}else if( c=='/' ){
|
||||||
|
if( z[0]!='/' && z[0]!='\\' ) return 0;
|
||||||
|
z++;
|
||||||
}else{
|
}else{
|
||||||
if( c!=(*(z++)) ) return 0;
|
if( c!=(*(z++)) ) return 0;
|
||||||
}
|
}
|
||||||
|
@ -327,14 +331,131 @@ static sqlite3_file *quotaSubOpen(sqlite3_file *pConn){
|
||||||
/* Find a file in a quota group and return a pointer to that file.
|
/* Find a file in a quota group and return a pointer to that file.
|
||||||
** Return NULL if the file is not in the group.
|
** Return NULL if the file is not in the group.
|
||||||
*/
|
*/
|
||||||
static quotaFile *quotaFindFile(quotaGroup *pGroup, const char *zName){
|
static quotaFile *quotaFindFile(
|
||||||
|
quotaGroup *pGroup, /* Group in which to look for the file */
|
||||||
|
const char *zName, /* Full pathname of the file */
|
||||||
|
int createFlag /* Try to create the file if not found */
|
||||||
|
){
|
||||||
quotaFile *pFile = pGroup->pFiles;
|
quotaFile *pFile = pGroup->pFiles;
|
||||||
while( pFile && strcmp(pFile->zFilename, zName)!=0 ){
|
while( pFile && strcmp(pFile->zFilename, zName)!=0 ){
|
||||||
pFile = pFile->pNext;
|
pFile = pFile->pNext;
|
||||||
}
|
}
|
||||||
|
if( pFile==0 && createFlag ){
|
||||||
|
int nName = strlen(zName);
|
||||||
|
pFile = (quotaFile *)sqlite3_malloc( sizeof(*pFile) + nName + 1 );
|
||||||
|
if( pFile ){
|
||||||
|
memset(pFile, 0, sizeof(*pFile));
|
||||||
|
pFile->zFilename = (char*)&pFile[1];
|
||||||
|
memcpy(pFile->zFilename, zName, nName+1);
|
||||||
|
pFile->pNext = pGroup->pFiles;
|
||||||
|
if( pGroup->pFiles ) pGroup->pFiles->ppPrev = &pFile->pNext;
|
||||||
|
pFile->ppPrev = &pGroup->pFiles;
|
||||||
|
pGroup->pFiles = pFile;
|
||||||
|
pFile->pGroup = pGroup;
|
||||||
|
}
|
||||||
|
}
|
||||||
return pFile;
|
return pFile;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Figure out if we are dealing with Unix, Windows, or some other
|
||||||
|
** operating system. After the following block of preprocess macros,
|
||||||
|
** all of SQLITE_OS_UNIX, SQLITE_OS_WIN, SQLITE_OS_OS2, and SQLITE_OS_OTHER
|
||||||
|
** will defined to either 1 or 0. One of the four will be 1. The other
|
||||||
|
** three will be 0.
|
||||||
|
*/
|
||||||
|
#if defined(SQLITE_OS_OTHER)
|
||||||
|
# if SQLITE_OS_OTHER==1
|
||||||
|
# undef SQLITE_OS_UNIX
|
||||||
|
# define SQLITE_OS_UNIX 0
|
||||||
|
# undef SQLITE_OS_WIN
|
||||||
|
# define SQLITE_OS_WIN 0
|
||||||
|
# undef SQLITE_OS_OS2
|
||||||
|
# define SQLITE_OS_OS2 0
|
||||||
|
# else
|
||||||
|
# undef SQLITE_OS_OTHER
|
||||||
|
# endif
|
||||||
|
#endif
|
||||||
|
#if !defined(SQLITE_OS_UNIX) && !defined(SQLITE_OS_OTHER)
|
||||||
|
# define SQLITE_OS_OTHER 0
|
||||||
|
# ifndef SQLITE_OS_WIN
|
||||||
|
# if defined(_WIN32) || defined(WIN32) || defined(__CYGWIN__) \
|
||||||
|
|| defined(__MINGW32__) || defined(__BORLANDC__)
|
||||||
|
# define SQLITE_OS_WIN 1
|
||||||
|
# define SQLITE_OS_UNIX 0
|
||||||
|
# define SQLITE_OS_OS2 0
|
||||||
|
# elif defined(__EMX__) || defined(_OS2) || defined(OS2) \
|
||||||
|
|| defined(_OS2_) || defined(__OS2__)
|
||||||
|
# define SQLITE_OS_WIN 0
|
||||||
|
# define SQLITE_OS_UNIX 0
|
||||||
|
# define SQLITE_OS_OS2 1
|
||||||
|
# else
|
||||||
|
# define SQLITE_OS_WIN 0
|
||||||
|
# define SQLITE_OS_UNIX 1
|
||||||
|
# define SQLITE_OS_OS2 0
|
||||||
|
# endif
|
||||||
|
# else
|
||||||
|
# define SQLITE_OS_UNIX 0
|
||||||
|
# define SQLITE_OS_OS2 0
|
||||||
|
# endif
|
||||||
|
#else
|
||||||
|
# ifndef SQLITE_OS_WIN
|
||||||
|
# define SQLITE_OS_WIN 0
|
||||||
|
# endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if SQLITE_OS_UNIX
|
||||||
|
# include <unistd.h>
|
||||||
|
#endif
|
||||||
|
#if SQLITE_OS_WIN
|
||||||
|
# include <windows.h>
|
||||||
|
# include <io.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Translate UTF8 to MBCS for use in fopen() calls. Return a pointer to the
|
||||||
|
** translated text.. Call quota_mbcs_free() to deallocate any memory
|
||||||
|
** used to store the returned pointer when done.
|
||||||
|
*/
|
||||||
|
static char *quota_utf8_to_mbcs(const char *zUtf8){
|
||||||
|
#if SQLITE_OS_WIN
|
||||||
|
int n; /* Bytes in zUtf8 */
|
||||||
|
int nWide; /* number of UTF-16 characters */
|
||||||
|
int nMbcs; /* Bytes of MBCS */
|
||||||
|
LPWSTR zTmpWide; /* The UTF16 text */
|
||||||
|
char *zMbcs; /* The MBCS text */
|
||||||
|
int codepage; /* Code page used by fopen() */
|
||||||
|
|
||||||
|
n = strlen(zUtf8);
|
||||||
|
nWide = MultiByteToWideChar(CP_UTF8, 0, zUtf8, -1, NULL, 0);
|
||||||
|
if( nWide==0 ) return 0;
|
||||||
|
zTmpWide = (LPWSTR)sqlite3_malloc( (nWide+1)*sizeof(zTmpWide[0]) );
|
||||||
|
if( zTmpWide==0 ) return 0;
|
||||||
|
MultiByteToWideChar(CP_UTF8, 0, zUtf8, -1, zTmpWide, nWide);
|
||||||
|
codepage = AreFileApisANSI() ? CP_ACP : CP_OEMCP;
|
||||||
|
nMbcs = WideCharToMultiByte(codepage, 0, zTmpWide, nWide, 0, 0, 0, 0);
|
||||||
|
zMbcs = nMbcs ? (char*)sqlite3_malloc( nMbcs+1 ) : 0;
|
||||||
|
if( zMbcs ){
|
||||||
|
WideCharToMultiByte(codepage, 0, zTmpWide, nWide, zMbcs, nMbcs, 0, 0);
|
||||||
|
}
|
||||||
|
sqlite3_free(zTmpWide);
|
||||||
|
return zMbcs;
|
||||||
|
#else
|
||||||
|
return (char*)zUtf8; /* No-op on unix */
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Deallocate any memory allocated by quota_utf8_to_mbcs().
|
||||||
|
*/
|
||||||
|
static void quota_mbcs_free(char *zOld){
|
||||||
|
#if SQLITE_OS_WIN
|
||||||
|
sqlite3_free(zOld);
|
||||||
|
#else
|
||||||
|
/* No-op on unix */
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
/************************* VFS Method Wrappers *****************************/
|
/************************* VFS Method Wrappers *****************************/
|
||||||
/*
|
/*
|
||||||
** This is the xOpen method used for the "quota" VFS.
|
** This is the xOpen method used for the "quota" VFS.
|
||||||
|
@ -378,25 +499,13 @@ static int quotaOpen(
|
||||||
pSubOpen = quotaSubOpen(pConn);
|
pSubOpen = quotaSubOpen(pConn);
|
||||||
rc = pOrigVfs->xOpen(pOrigVfs, zName, pSubOpen, flags, pOutFlags);
|
rc = pOrigVfs->xOpen(pOrigVfs, zName, pSubOpen, flags, pOutFlags);
|
||||||
if( rc==SQLITE_OK ){
|
if( rc==SQLITE_OK ){
|
||||||
pFile = quotaFindFile(pGroup, zName);
|
pFile = quotaFindFile(pGroup, zName, 1);
|
||||||
if( pFile==0 ){
|
if( pFile==0 ){
|
||||||
int nName = strlen(zName);
|
quotaLeave();
|
||||||
pFile = (quotaFile *)sqlite3_malloc( sizeof(*pFile) + nName + 1 );
|
pSubOpen->pMethods->xClose(pSubOpen);
|
||||||
if( pFile==0 ){
|
return SQLITE_NOMEM;
|
||||||
quotaLeave();
|
|
||||||
pSubOpen->pMethods->xClose(pSubOpen);
|
|
||||||
return SQLITE_NOMEM;
|
|
||||||
}
|
|
||||||
memset(pFile, 0, sizeof(*pFile));
|
|
||||||
pFile->zFilename = (char*)&pFile[1];
|
|
||||||
memcpy(pFile->zFilename, zName, nName+1);
|
|
||||||
pFile->pNext = pGroup->pFiles;
|
|
||||||
if( pGroup->pFiles ) pGroup->pFiles->ppPrev = &pFile->pNext;
|
|
||||||
pFile->ppPrev = &pGroup->pFiles;
|
|
||||||
pGroup->pFiles = pFile;
|
|
||||||
pFile->pGroup = pGroup;
|
|
||||||
pFile->deleteOnClose = (flags & SQLITE_OPEN_DELETEONCLOSE)!=0;
|
|
||||||
}
|
}
|
||||||
|
pFile->deleteOnClose = (flags & SQLITE_OPEN_DELETEONCLOSE)!=0;
|
||||||
pFile->nRef++;
|
pFile->nRef++;
|
||||||
pQuotaOpen->pFile = pFile;
|
pQuotaOpen->pFile = pFile;
|
||||||
if( pSubOpen->pMethods->iVersion==1 ){
|
if( pSubOpen->pMethods->iVersion==1 ){
|
||||||
|
@ -437,7 +546,7 @@ static int quotaDelete(
|
||||||
quotaEnter();
|
quotaEnter();
|
||||||
pGroup = quotaGroupFind(zName);
|
pGroup = quotaGroupFind(zName);
|
||||||
if( pGroup ){
|
if( pGroup ){
|
||||||
pFile = quotaFindFile(pGroup, zName);
|
pFile = quotaFindFile(pGroup, zName, 0);
|
||||||
if( pFile ){
|
if( pFile ){
|
||||||
if( pFile->nRef ){
|
if( pFile->nRef ){
|
||||||
pFile->deleteOnClose = 1;
|
pFile->deleteOnClose = 1;
|
||||||
|
@ -469,7 +578,10 @@ static int quotaClose(sqlite3_file *pConn){
|
||||||
pFile->nRef--;
|
pFile->nRef--;
|
||||||
if( pFile->nRef==0 ){
|
if( pFile->nRef==0 ){
|
||||||
quotaGroup *pGroup = pFile->pGroup;
|
quotaGroup *pGroup = pFile->pGroup;
|
||||||
if( pFile->deleteOnClose ) quotaRemoveFile(pFile);
|
if( pFile->deleteOnClose ){
|
||||||
|
gQuota.pOrigVfs->xDelete(gQuota.pOrigVfs, pFile->zFilename, 0);
|
||||||
|
quotaRemoveFile(pFile);
|
||||||
|
}
|
||||||
quotaGroupDeref(pGroup);
|
quotaGroupDeref(pGroup);
|
||||||
}
|
}
|
||||||
quotaLeave();
|
quotaLeave();
|
||||||
|
@ -603,7 +715,13 @@ static int quotaCheckReservedLock(sqlite3_file *pConn, int *pResOut){
|
||||||
*/
|
*/
|
||||||
static int quotaFileControl(sqlite3_file *pConn, int op, void *pArg){
|
static int quotaFileControl(sqlite3_file *pConn, int op, void *pArg){
|
||||||
sqlite3_file *pSubOpen = quotaSubOpen(pConn);
|
sqlite3_file *pSubOpen = quotaSubOpen(pConn);
|
||||||
return pSubOpen->pMethods->xFileControl(pSubOpen, op, pArg);
|
int rc = pSubOpen->pMethods->xFileControl(pSubOpen, op, pArg);
|
||||||
|
#if defined(SQLITE_FCNTL_VFSNAME)
|
||||||
|
if( op==SQLITE_FCNTL_VFSNAME && rc==SQLITE_OK ){
|
||||||
|
*(char**)pArg = sqlite3_mprintf("quota/%z", *(char**)pArg);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Pass xSectorSize requests through to the original VFS unchanged.
|
/* Pass xSectorSize requests through to the original VFS unchanged.
|
||||||
|
@ -819,8 +937,8 @@ int sqlite3_quota_file(const char *zFilename){
|
||||||
int rc;
|
int rc;
|
||||||
int outFlags = 0;
|
int outFlags = 0;
|
||||||
sqlite3_int64 iSize;
|
sqlite3_int64 iSize;
|
||||||
fd = (sqlite3_file*)
|
fd = (sqlite3_file*)sqlite3_malloc(gQuota.sThisVfs.szOsFile +
|
||||||
sqlite3_malloc(gQuota.sThisVfs.szOsFile + gQuota.sThisVfs.mxPathname+1);
|
gQuota.sThisVfs.mxPathname+1);
|
||||||
if( fd==0 ) return SQLITE_NOMEM;
|
if( fd==0 ) return SQLITE_NOMEM;
|
||||||
zFull = gQuota.sThisVfs.szOsFile + (char*)fd;
|
zFull = gQuota.sThisVfs.szOsFile + (char*)fd;
|
||||||
rc = gQuota.pOrigVfs->xFullPathname(gQuota.pOrigVfs, zFilename,
|
rc = gQuota.pOrigVfs->xFullPathname(gQuota.pOrigVfs, zFilename,
|
||||||
|
@ -838,7 +956,7 @@ int sqlite3_quota_file(const char *zFilename){
|
||||||
quotaEnter();
|
quotaEnter();
|
||||||
pGroup = quotaGroupFind(zFull);
|
pGroup = quotaGroupFind(zFull);
|
||||||
if( pGroup ){
|
if( pGroup ){
|
||||||
pFile = quotaFindFile(pGroup, zFull);
|
pFile = quotaFindFile(pGroup, zFull, 0);
|
||||||
if( pFile ) quotaRemoveFile(pFile);
|
if( pFile ) quotaRemoveFile(pFile);
|
||||||
}
|
}
|
||||||
quotaLeave();
|
quotaLeave();
|
||||||
|
@ -847,6 +965,220 @@ int sqlite3_quota_file(const char *zFilename){
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Open a potentially quotaed file for I/O.
|
||||||
|
*/
|
||||||
|
quota_FILE *sqlite3_quota_fopen(const char *zFilename, const char *zMode){
|
||||||
|
quota_FILE *p = 0;
|
||||||
|
char *zFull = 0;
|
||||||
|
char *zFullTranslated;
|
||||||
|
int rc;
|
||||||
|
quotaGroup *pGroup;
|
||||||
|
quotaFile *pFile;
|
||||||
|
|
||||||
|
zFull = (char*)sqlite3_malloc(gQuota.sThisVfs.mxPathname + 1);
|
||||||
|
if( zFull==0 ) return 0;
|
||||||
|
rc = gQuota.pOrigVfs->xFullPathname(gQuota.pOrigVfs, zFilename,
|
||||||
|
gQuota.sThisVfs.mxPathname+1, zFull);
|
||||||
|
if( rc ) goto quota_fopen_error;
|
||||||
|
p = (quota_FILE*)sqlite3_malloc(sizeof(*p));
|
||||||
|
if( p==0 ) goto quota_fopen_error;
|
||||||
|
memset(p, 0, sizeof(*p));
|
||||||
|
zFullTranslated = quota_utf8_to_mbcs(zFull);
|
||||||
|
if( zFullTranslated==0 ) goto quota_fopen_error;
|
||||||
|
p->f = fopen(zFullTranslated, zMode);
|
||||||
|
quota_mbcs_free(zFullTranslated);
|
||||||
|
if( p->f==0 ) goto quota_fopen_error;
|
||||||
|
quotaEnter();
|
||||||
|
pGroup = quotaGroupFind(zFull);
|
||||||
|
if( pGroup ){
|
||||||
|
pFile = quotaFindFile(pGroup, zFull, 1);
|
||||||
|
if( pFile==0 ){
|
||||||
|
quotaLeave();
|
||||||
|
goto quota_fopen_error;
|
||||||
|
}
|
||||||
|
pFile->nRef++;
|
||||||
|
p->pFile = pFile;
|
||||||
|
}
|
||||||
|
quotaLeave();
|
||||||
|
sqlite3_free(zFull);
|
||||||
|
return p;
|
||||||
|
|
||||||
|
quota_fopen_error:
|
||||||
|
sqlite3_free(zFull);
|
||||||
|
if( p && p->f ) fclose(p->f);
|
||||||
|
sqlite3_free(p);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Read content from a quota_FILE
|
||||||
|
*/
|
||||||
|
size_t sqlite3_quota_fread(
|
||||||
|
void *pBuf, /* Store the content here */
|
||||||
|
size_t size, /* Size of each element */
|
||||||
|
size_t nmemb, /* Number of elements to read */
|
||||||
|
quota_FILE *p /* Read from this quota_FILE object */
|
||||||
|
){
|
||||||
|
return fread(pBuf, size, nmemb, p->f);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Write content into a quota_FILE. Invoke the quota callback and block
|
||||||
|
** the write if we exceed quota.
|
||||||
|
*/
|
||||||
|
size_t sqlite3_quota_fwrite(
|
||||||
|
void *pBuf, /* Take content to write from here */
|
||||||
|
size_t size, /* Size of each element */
|
||||||
|
size_t nmemb, /* Number of elements */
|
||||||
|
quota_FILE *p /* Write to this quota_FILE objecct */
|
||||||
|
){
|
||||||
|
sqlite3_int64 iOfst;
|
||||||
|
sqlite3_int64 iEnd;
|
||||||
|
sqlite3_int64 szNew;
|
||||||
|
quotaFile *pFile;
|
||||||
|
|
||||||
|
iOfst = ftell(p->f);
|
||||||
|
iEnd = iOfst + size*nmemb;
|
||||||
|
pFile = p->pFile;
|
||||||
|
if( pFile && pFile->iSize<iEnd ){
|
||||||
|
quotaGroup *pGroup = pFile->pGroup;
|
||||||
|
quotaEnter();
|
||||||
|
szNew = pGroup->iSize - pFile->iSize + iEnd;
|
||||||
|
if( szNew>pGroup->iLimit && pGroup->iLimit>0 ){
|
||||||
|
if( pGroup->xCallback ){
|
||||||
|
pGroup->xCallback(pFile->zFilename, &pGroup->iLimit, szNew,
|
||||||
|
pGroup->pArg);
|
||||||
|
}
|
||||||
|
if( szNew>pGroup->iLimit && pGroup->iLimit>0 ){
|
||||||
|
iEnd = pGroup->iLimit - pGroup->iSize + pFile->iSize;
|
||||||
|
nmemb = (iEnd - iOfst)/size;
|
||||||
|
iEnd = iOfst + size*nmemb;
|
||||||
|
szNew = pGroup->iSize - pFile->iSize + iEnd;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pGroup->iSize = szNew;
|
||||||
|
pFile->iSize = iEnd;
|
||||||
|
quotaLeave();
|
||||||
|
}
|
||||||
|
return fwrite(pBuf, size, nmemb, p->f);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Close an open quota_FILE stream.
|
||||||
|
*/
|
||||||
|
int sqlite3_quota_fclose(quota_FILE *p){
|
||||||
|
int rc;
|
||||||
|
quotaFile *pFile;
|
||||||
|
rc = fclose(p->f);
|
||||||
|
pFile = p->pFile;
|
||||||
|
if( pFile ){
|
||||||
|
quotaEnter();
|
||||||
|
pFile->nRef--;
|
||||||
|
if( pFile->nRef==0 ){
|
||||||
|
quotaGroup *pGroup = pFile->pGroup;
|
||||||
|
if( pFile->deleteOnClose ){
|
||||||
|
gQuota.pOrigVfs->xDelete(gQuota.pOrigVfs, pFile->zFilename, 0);
|
||||||
|
quotaRemoveFile(pFile);
|
||||||
|
}
|
||||||
|
quotaGroupDeref(pGroup);
|
||||||
|
}
|
||||||
|
quotaLeave();
|
||||||
|
}
|
||||||
|
sqlite3_free(p);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Flush memory buffers for a quota_FILE to disk.
|
||||||
|
*/
|
||||||
|
int sqlite3_quota_fflush(quota_FILE *p, int doFsync){
|
||||||
|
int rc;
|
||||||
|
rc = fflush(p->f);
|
||||||
|
if( rc==0 && doFsync ){
|
||||||
|
#if SQLITE_OS_UNIX
|
||||||
|
rc = fsync(fileno(p->f));
|
||||||
|
#endif
|
||||||
|
#if SQLITE_OS_WIN
|
||||||
|
rc = _commit(_fileno(p->f));
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
return rc!=0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Seek on a quota_FILE stream.
|
||||||
|
*/
|
||||||
|
int sqlite3_quota_fseek(quota_FILE *p, long offset, int whence){
|
||||||
|
return fseek(p->f, offset, whence);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** rewind a quota_FILE stream.
|
||||||
|
*/
|
||||||
|
void sqlite3_quota_rewind(quota_FILE *p){
|
||||||
|
rewind(p->f);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Tell the current location of a quota_FILE stream.
|
||||||
|
*/
|
||||||
|
long sqlite3_quota_ftell(quota_FILE *p){
|
||||||
|
return ftell(p->f);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Remove a managed file. Update quotas accordingly.
|
||||||
|
*/
|
||||||
|
int sqlite3_quota_remove(const char *zFilename){
|
||||||
|
char *zFull; /* Full pathname for zFilename */
|
||||||
|
int nFull; /* Number of bytes in zFilename */
|
||||||
|
int rc; /* Result code */
|
||||||
|
quotaGroup *pGroup; /* Group containing zFilename */
|
||||||
|
quotaFile *pFile; /* A file in the group */
|
||||||
|
quotaFile *pNextFile; /* next file in the group */
|
||||||
|
int diff; /* Difference between filenames */
|
||||||
|
char c; /* First character past end of pattern */
|
||||||
|
|
||||||
|
zFull = (char*)sqlite3_malloc(gQuota.sThisVfs.mxPathname + 1);
|
||||||
|
if( zFull==0 ) return SQLITE_NOMEM;
|
||||||
|
rc = gQuota.pOrigVfs->xFullPathname(gQuota.pOrigVfs, zFilename,
|
||||||
|
gQuota.sThisVfs.mxPathname+1, zFull);
|
||||||
|
if( rc ){
|
||||||
|
sqlite3_free(zFull);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Figure out the length of the full pathname. If the name ends with
|
||||||
|
** / (or \ on windows) then remove the trailing /.
|
||||||
|
*/
|
||||||
|
nFull = strlen(zFull);
|
||||||
|
if( nFull>0 && (zFull[nFull-1]=='/' || zFull[nFull-1]=='\\') ){
|
||||||
|
nFull--;
|
||||||
|
zFull[nFull] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
quotaEnter();
|
||||||
|
pGroup = quotaGroupFind(zFull);
|
||||||
|
if( pGroup ){
|
||||||
|
for(pFile=pGroup->pFiles; pFile && rc==SQLITE_OK; pFile=pNextFile){
|
||||||
|
pNextFile = pFile->pNext;
|
||||||
|
diff = memcmp(zFull, pFile->zFilename, nFull);
|
||||||
|
if( diff==0 && ((c = pFile->zFilename[nFull])==0 || c=='/' || c=='\\') ){
|
||||||
|
if( pFile->nRef ){
|
||||||
|
pFile->deleteOnClose = 1;
|
||||||
|
}else{
|
||||||
|
rc = gQuota.pOrigVfs->xDelete(gQuota.pOrigVfs, pFile->zFilename, 0);
|
||||||
|
quotaRemoveFile(pFile);
|
||||||
|
quotaGroupDeref(pGroup);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
quotaLeave();
|
||||||
|
sqlite3_free(zFull);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
/***************************** Test Code ***********************************/
|
/***************************** Test Code ***********************************/
|
||||||
#ifdef SQLITE_TEST
|
#ifdef SQLITE_TEST
|
||||||
|
@ -1075,9 +1407,13 @@ static int test_quota_dump(
|
||||||
Tcl_ListObjAppendElement(interp, pGroupTerm,
|
Tcl_ListObjAppendElement(interp, pGroupTerm,
|
||||||
Tcl_NewWideIntObj(pGroup->iSize));
|
Tcl_NewWideIntObj(pGroup->iSize));
|
||||||
for(pFile=pGroup->pFiles; pFile; pFile=pFile->pNext){
|
for(pFile=pGroup->pFiles; pFile; pFile=pFile->pNext){
|
||||||
|
int i;
|
||||||
|
char zTemp[1000];
|
||||||
pFileTerm = Tcl_NewObj();
|
pFileTerm = Tcl_NewObj();
|
||||||
|
sqlite3_snprintf(sizeof(zTemp), zTemp, "%s", pFile->zFilename);
|
||||||
|
for(i=0; zTemp[i]; i++){ if( zTemp[i]=='\\' ) zTemp[i] = '/'; }
|
||||||
Tcl_ListObjAppendElement(interp, pFileTerm,
|
Tcl_ListObjAppendElement(interp, pFileTerm,
|
||||||
Tcl_NewStringObj(pFile->zFilename, -1));
|
Tcl_NewStringObj(zTemp, -1));
|
||||||
Tcl_ListObjAppendElement(interp, pFileTerm,
|
Tcl_ListObjAppendElement(interp, pFileTerm,
|
||||||
Tcl_NewWideIntObj(pFile->iSize));
|
Tcl_NewWideIntObj(pFile->iSize));
|
||||||
Tcl_ListObjAppendElement(interp, pFileTerm,
|
Tcl_ListObjAppendElement(interp, pFileTerm,
|
||||||
|
@ -1093,6 +1429,272 @@ static int test_quota_dump(
|
||||||
return TCL_OK;
|
return TCL_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** tclcmd: sqlite3_quota_fopen FILENAME MODE
|
||||||
|
*/
|
||||||
|
static int test_quota_fopen(
|
||||||
|
void * clientData,
|
||||||
|
Tcl_Interp *interp,
|
||||||
|
int objc,
|
||||||
|
Tcl_Obj *CONST objv[]
|
||||||
|
){
|
||||||
|
const char *zFilename; /* File pattern to configure */
|
||||||
|
const char *zMode; /* Mode string */
|
||||||
|
quota_FILE *p; /* Open string object */
|
||||||
|
char zReturn[50]; /* Name of pointer to return */
|
||||||
|
|
||||||
|
/* Process arguments */
|
||||||
|
if( objc!=3 ){
|
||||||
|
Tcl_WrongNumArgs(interp, 1, objv, "FILENAME MODE");
|
||||||
|
return TCL_ERROR;
|
||||||
|
}
|
||||||
|
zFilename = Tcl_GetString(objv[1]);
|
||||||
|
zMode = Tcl_GetString(objv[2]);
|
||||||
|
p = sqlite3_quota_fopen(zFilename, zMode);
|
||||||
|
sqlite3_snprintf(sizeof(zReturn), zReturn, "%p", p);
|
||||||
|
Tcl_SetResult(interp, zReturn, TCL_VOLATILE);
|
||||||
|
return TCL_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Defined in test1.c */
|
||||||
|
extern void *sqlite3TestTextToPtr(const char*);
|
||||||
|
|
||||||
|
/*
|
||||||
|
** tclcmd: sqlite3_quota_fread HANDLE SIZE NELEM
|
||||||
|
*/
|
||||||
|
static int test_quota_fread(
|
||||||
|
void * clientData,
|
||||||
|
Tcl_Interp *interp,
|
||||||
|
int objc,
|
||||||
|
Tcl_Obj *CONST objv[]
|
||||||
|
){
|
||||||
|
quota_FILE *p;
|
||||||
|
char *zBuf;
|
||||||
|
int sz;
|
||||||
|
int nElem;
|
||||||
|
int got;
|
||||||
|
|
||||||
|
if( objc!=4 ){
|
||||||
|
Tcl_WrongNumArgs(interp, 1, objv, "HANDLE SIZE NELEM");
|
||||||
|
return TCL_ERROR;
|
||||||
|
}
|
||||||
|
p = sqlite3TestTextToPtr(Tcl_GetString(objv[1]));
|
||||||
|
if( Tcl_GetIntFromObj(interp, objv[2], &sz) ) return TCL_ERROR;
|
||||||
|
if( Tcl_GetIntFromObj(interp, objv[3], &nElem) ) return TCL_ERROR;
|
||||||
|
zBuf = (char*)sqlite3_malloc( sz*nElem + 1 );
|
||||||
|
if( zBuf==0 ){
|
||||||
|
Tcl_SetResult(interp, "out of memory", TCL_STATIC);
|
||||||
|
return TCL_ERROR;
|
||||||
|
}
|
||||||
|
got = sqlite3_quota_fread(zBuf, sz, nElem, p);
|
||||||
|
if( got<0 ) got = 0;
|
||||||
|
zBuf[got*sz] = 0;
|
||||||
|
Tcl_SetResult(interp, zBuf, TCL_VOLATILE);
|
||||||
|
sqlite3_free(zBuf);
|
||||||
|
return TCL_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** tclcmd: sqlite3_quota_fwrite HANDLE SIZE NELEM CONTENT
|
||||||
|
*/
|
||||||
|
static int test_quota_fwrite(
|
||||||
|
void * clientData,
|
||||||
|
Tcl_Interp *interp,
|
||||||
|
int objc,
|
||||||
|
Tcl_Obj *CONST objv[]
|
||||||
|
){
|
||||||
|
quota_FILE *p;
|
||||||
|
char *zBuf;
|
||||||
|
int sz;
|
||||||
|
int nElem;
|
||||||
|
int got;
|
||||||
|
|
||||||
|
if( objc!=5 ){
|
||||||
|
Tcl_WrongNumArgs(interp, 1, objv, "HANDLE SIZE NELEM CONTENT");
|
||||||
|
return TCL_ERROR;
|
||||||
|
}
|
||||||
|
p = sqlite3TestTextToPtr(Tcl_GetString(objv[1]));
|
||||||
|
if( Tcl_GetIntFromObj(interp, objv[2], &sz) ) return TCL_ERROR;
|
||||||
|
if( Tcl_GetIntFromObj(interp, objv[3], &nElem) ) return TCL_ERROR;
|
||||||
|
zBuf = Tcl_GetString(objv[4]);
|
||||||
|
got = sqlite3_quota_fwrite(zBuf, sz, nElem, p);
|
||||||
|
Tcl_SetObjResult(interp, Tcl_NewIntObj(got));
|
||||||
|
return TCL_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** tclcmd: sqlite3_quota_fclose HANDLE
|
||||||
|
*/
|
||||||
|
static int test_quota_fclose(
|
||||||
|
void * clientData,
|
||||||
|
Tcl_Interp *interp,
|
||||||
|
int objc,
|
||||||
|
Tcl_Obj *CONST objv[]
|
||||||
|
){
|
||||||
|
quota_FILE *p;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
if( objc!=2 ){
|
||||||
|
Tcl_WrongNumArgs(interp, 1, objv, "HANDLE");
|
||||||
|
return TCL_ERROR;
|
||||||
|
}
|
||||||
|
p = sqlite3TestTextToPtr(Tcl_GetString(objv[1]));
|
||||||
|
rc = sqlite3_quota_fclose(p);
|
||||||
|
Tcl_SetObjResult(interp, Tcl_NewIntObj(rc));
|
||||||
|
return TCL_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** tclcmd: sqlite3_quota_fflush HANDLE ?HARDSYNC?
|
||||||
|
*/
|
||||||
|
static int test_quota_fflush(
|
||||||
|
void * clientData,
|
||||||
|
Tcl_Interp *interp,
|
||||||
|
int objc,
|
||||||
|
Tcl_Obj *CONST objv[]
|
||||||
|
){
|
||||||
|
quota_FILE *p;
|
||||||
|
int rc;
|
||||||
|
int doSync = 0;
|
||||||
|
|
||||||
|
if( objc!=2 && objc!=3 ){
|
||||||
|
Tcl_WrongNumArgs(interp, 1, objv, "HANDLE ?HARDSYNC?");
|
||||||
|
return TCL_ERROR;
|
||||||
|
}
|
||||||
|
p = sqlite3TestTextToPtr(Tcl_GetString(objv[1]));
|
||||||
|
if( objc==3 ){
|
||||||
|
if( Tcl_GetBooleanFromObj(interp, objv[2], &doSync) ) return TCL_ERROR;
|
||||||
|
}
|
||||||
|
rc = sqlite3_quota_fflush(p, doSync);
|
||||||
|
Tcl_SetObjResult(interp, Tcl_NewIntObj(rc));
|
||||||
|
return TCL_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** tclcmd: sqlite3_quota_fseek HANDLE OFFSET WHENCE
|
||||||
|
*/
|
||||||
|
static int test_quota_fseek(
|
||||||
|
void * clientData,
|
||||||
|
Tcl_Interp *interp,
|
||||||
|
int objc,
|
||||||
|
Tcl_Obj *CONST objv[]
|
||||||
|
){
|
||||||
|
quota_FILE *p;
|
||||||
|
int ofst;
|
||||||
|
const char *zWhence;
|
||||||
|
int whence;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
if( objc!=4 ){
|
||||||
|
Tcl_WrongNumArgs(interp, 1, objv, "HANDLE OFFSET WHENCE");
|
||||||
|
return TCL_ERROR;
|
||||||
|
}
|
||||||
|
p = sqlite3TestTextToPtr(Tcl_GetString(objv[1]));
|
||||||
|
if( Tcl_GetIntFromObj(interp, objv[2], &ofst) ) return TCL_ERROR;
|
||||||
|
zWhence = Tcl_GetString(objv[3]);
|
||||||
|
if( strcmp(zWhence, "SEEK_SET")==0 ){
|
||||||
|
whence = SEEK_SET;
|
||||||
|
}else if( strcmp(zWhence, "SEEK_CUR")==0 ){
|
||||||
|
whence = SEEK_CUR;
|
||||||
|
}else if( strcmp(zWhence, "SEEK_END")==0 ){
|
||||||
|
whence = SEEK_END;
|
||||||
|
}else{
|
||||||
|
Tcl_AppendResult(interp,
|
||||||
|
"WHENCE should be SEEK_SET, SEEK_CUR, or SEEK_END", (char*)0);
|
||||||
|
return TCL_ERROR;
|
||||||
|
}
|
||||||
|
rc = sqlite3_quota_fseek(p, ofst, whence);
|
||||||
|
Tcl_SetObjResult(interp, Tcl_NewIntObj(rc));
|
||||||
|
return TCL_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** tclcmd: sqlite3_quota_rewind HANDLE
|
||||||
|
*/
|
||||||
|
static int test_quota_rewind(
|
||||||
|
void * clientData,
|
||||||
|
Tcl_Interp *interp,
|
||||||
|
int objc,
|
||||||
|
Tcl_Obj *CONST objv[]
|
||||||
|
){
|
||||||
|
quota_FILE *p;
|
||||||
|
if( objc!=2 ){
|
||||||
|
Tcl_WrongNumArgs(interp, 1, objv, "HANDLE");
|
||||||
|
return TCL_ERROR;
|
||||||
|
}
|
||||||
|
p = sqlite3TestTextToPtr(Tcl_GetString(objv[1]));
|
||||||
|
sqlite3_quota_rewind(p);
|
||||||
|
return TCL_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** tclcmd: sqlite3_quota_ftell HANDLE
|
||||||
|
*/
|
||||||
|
static int test_quota_ftell(
|
||||||
|
void * clientData,
|
||||||
|
Tcl_Interp *interp,
|
||||||
|
int objc,
|
||||||
|
Tcl_Obj *CONST objv[]
|
||||||
|
){
|
||||||
|
quota_FILE *p;
|
||||||
|
sqlite3_int64 x;
|
||||||
|
if( objc!=2 ){
|
||||||
|
Tcl_WrongNumArgs(interp, 1, objv, "HANDLE");
|
||||||
|
return TCL_ERROR;
|
||||||
|
}
|
||||||
|
p = sqlite3TestTextToPtr(Tcl_GetString(objv[1]));
|
||||||
|
x = sqlite3_quota_ftell(p);
|
||||||
|
Tcl_SetObjResult(interp, Tcl_NewWideIntObj(x));
|
||||||
|
return TCL_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** tclcmd: sqlite3_quota_remove FILENAME
|
||||||
|
*/
|
||||||
|
static int test_quota_remove(
|
||||||
|
void * clientData,
|
||||||
|
Tcl_Interp *interp,
|
||||||
|
int objc,
|
||||||
|
Tcl_Obj *CONST objv[]
|
||||||
|
){
|
||||||
|
const char *zFilename; /* File pattern to configure */
|
||||||
|
int rc;
|
||||||
|
if( objc!=2 ){
|
||||||
|
Tcl_WrongNumArgs(interp, 1, objv, "FILENAME");
|
||||||
|
return TCL_ERROR;
|
||||||
|
}
|
||||||
|
zFilename = Tcl_GetString(objv[1]);
|
||||||
|
rc = sqlite3_quota_remove(zFilename);
|
||||||
|
Tcl_SetObjResult(interp, Tcl_NewIntObj(rc));
|
||||||
|
return TCL_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** tclcmd: sqlite3_quota_glob PATTERN TEXT
|
||||||
|
**
|
||||||
|
** Test the glob pattern matching. Return 1 if TEXT matches PATTERN
|
||||||
|
** and return 0 if it does not.
|
||||||
|
*/
|
||||||
|
static int test_quota_glob(
|
||||||
|
void * clientData,
|
||||||
|
Tcl_Interp *interp,
|
||||||
|
int objc,
|
||||||
|
Tcl_Obj *CONST objv[]
|
||||||
|
){
|
||||||
|
const char *zPattern; /* The glob pattern */
|
||||||
|
const char *zText; /* Text to compare agains the pattern */
|
||||||
|
int rc;
|
||||||
|
if( objc!=3 ){
|
||||||
|
Tcl_WrongNumArgs(interp, 1, objv, "PATTERN TEXT");
|
||||||
|
return TCL_ERROR;
|
||||||
|
}
|
||||||
|
zPattern = Tcl_GetString(objv[1]);
|
||||||
|
zText = Tcl_GetString(objv[2]);
|
||||||
|
rc = quotaStrglob(zPattern, zText);
|
||||||
|
Tcl_SetObjResult(interp, Tcl_NewIntObj(rc));
|
||||||
|
return TCL_OK;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** This routine registers the custom TCL commands defined in this
|
** This routine registers the custom TCL commands defined in this
|
||||||
** module. This should be the only procedure visible from outside
|
** module. This should be the only procedure visible from outside
|
||||||
|
@ -1104,10 +1706,20 @@ int Sqlitequota_Init(Tcl_Interp *interp){
|
||||||
Tcl_ObjCmdProc *xProc;
|
Tcl_ObjCmdProc *xProc;
|
||||||
} aCmd[] = {
|
} aCmd[] = {
|
||||||
{ "sqlite3_quota_initialize", test_quota_initialize },
|
{ "sqlite3_quota_initialize", test_quota_initialize },
|
||||||
{ "sqlite3_quota_shutdown", test_quota_shutdown },
|
{ "sqlite3_quota_shutdown", test_quota_shutdown },
|
||||||
{ "sqlite3_quota_set", test_quota_set },
|
{ "sqlite3_quota_set", test_quota_set },
|
||||||
{ "sqlite3_quota_file", test_quota_file },
|
{ "sqlite3_quota_file", test_quota_file },
|
||||||
{ "sqlite3_quota_dump", test_quota_dump },
|
{ "sqlite3_quota_dump", test_quota_dump },
|
||||||
|
{ "sqlite3_quota_fopen", test_quota_fopen },
|
||||||
|
{ "sqlite3_quota_fread", test_quota_fread },
|
||||||
|
{ "sqlite3_quota_fwrite", test_quota_fwrite },
|
||||||
|
{ "sqlite3_quota_fclose", test_quota_fclose },
|
||||||
|
{ "sqlite3_quota_fflush", test_quota_fflush },
|
||||||
|
{ "sqlite3_quota_fseek", test_quota_fseek },
|
||||||
|
{ "sqlite3_quota_rewind", test_quota_rewind },
|
||||||
|
{ "sqlite3_quota_ftell", test_quota_ftell },
|
||||||
|
{ "sqlite3_quota_remove", test_quota_remove },
|
||||||
|
{ "sqlite3_quota_glob", test_quota_glob },
|
||||||
};
|
};
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,209 @@
|
||||||
|
/*
|
||||||
|
** 2011 December 1
|
||||||
|
**
|
||||||
|
** The author disclaims copyright to this source code. In place of
|
||||||
|
** a legal notice, here is a blessing:
|
||||||
|
**
|
||||||
|
** May you do good and not evil.
|
||||||
|
** May you find forgiveness for yourself and forgive others.
|
||||||
|
** May you share freely, never taking more than you give.
|
||||||
|
**
|
||||||
|
*************************************************************************
|
||||||
|
**
|
||||||
|
** This file contains the interface definition for the quota a VFS shim.
|
||||||
|
**
|
||||||
|
** This particular shim enforces a quota system on files. One or more
|
||||||
|
** database files are in a "quota group" that is defined by a GLOB
|
||||||
|
** pattern. A quota is set for the combined size of all files in the
|
||||||
|
** the group. A quota of zero means "no limit". If the total size
|
||||||
|
** of all files in the quota group is greater than the limit, then
|
||||||
|
** write requests that attempt to enlarge a file fail with SQLITE_FULL.
|
||||||
|
**
|
||||||
|
** However, before returning SQLITE_FULL, the write requests invoke
|
||||||
|
** a callback function that is configurable for each quota group.
|
||||||
|
** This callback has the opportunity to enlarge the quota. If the
|
||||||
|
** callback does enlarge the quota such that the total size of all
|
||||||
|
** files within the group is less than the new quota, then the write
|
||||||
|
** continues as if nothing had happened.
|
||||||
|
*/
|
||||||
|
#ifndef _QUOTA_H_
|
||||||
|
#include "sqlite3.h"
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
/* Make this callable from C++ */
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Initialize the quota VFS shim. Use the VFS named zOrigVfsName
|
||||||
|
** as the VFS that does the actual work. Use the default if
|
||||||
|
** zOrigVfsName==NULL.
|
||||||
|
**
|
||||||
|
** The quota VFS shim is named "quota". It will become the default
|
||||||
|
** VFS if makeDefault is non-zero.
|
||||||
|
**
|
||||||
|
** THIS ROUTINE IS NOT THREADSAFE. Call this routine exactly once
|
||||||
|
** during start-up.
|
||||||
|
*/
|
||||||
|
int sqlite3_quota_initialize(const char *zOrigVfsName, int makeDefault);
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Shutdown the quota system.
|
||||||
|
**
|
||||||
|
** All SQLite database connections must be closed before calling this
|
||||||
|
** routine.
|
||||||
|
**
|
||||||
|
** THIS ROUTINE IS NOT THREADSAFE. Call this routine exactly once while
|
||||||
|
** shutting down in order to free all remaining quota groups.
|
||||||
|
*/
|
||||||
|
int sqlite3_quota_shutdown(void);
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Create or destroy a quota group.
|
||||||
|
**
|
||||||
|
** The quota group is defined by the zPattern. When calling this routine
|
||||||
|
** with a zPattern for a quota group that already exists, this routine
|
||||||
|
** merely updates the iLimit, xCallback, and pArg values for that quota
|
||||||
|
** group. If zPattern is new, then a new quota group is created.
|
||||||
|
**
|
||||||
|
** The zPattern is always compared against the full pathname of the file.
|
||||||
|
** Even if APIs are called with relative pathnames, SQLite converts the
|
||||||
|
** name to a full pathname before comparing it against zPattern. zPattern
|
||||||
|
** is a glob pattern with the following matching rules:
|
||||||
|
**
|
||||||
|
** '*' Matches any sequence of zero or more characters.
|
||||||
|
**
|
||||||
|
** '?' Matches exactly one character.
|
||||||
|
**
|
||||||
|
** [...] Matches one character from the enclosed list of
|
||||||
|
** characters. "]" can be part of the list if it is
|
||||||
|
** the first character. Within the list "X-Y" matches
|
||||||
|
** characters X or Y or any character in between the
|
||||||
|
** two. Ex: "[0-9]" matches any digit.
|
||||||
|
**
|
||||||
|
** [^...] Matches one character not in the enclosed list.
|
||||||
|
**
|
||||||
|
** / Matches either / or \. This allows glob patterns
|
||||||
|
** containing / to work on both unix and windows.
|
||||||
|
**
|
||||||
|
** Note that, unlike unix shell globbing, the directory separator "/"
|
||||||
|
** can match a wildcard. So, for example, the pattern "/abc/xyz/" "*"
|
||||||
|
** matches any files anywhere in the directory hierarchy beneath
|
||||||
|
** /abc/xyz.
|
||||||
|
**
|
||||||
|
** The glob algorithm works on bytes. Multi-byte UTF8 characters are
|
||||||
|
** matched as if each byte were a separate character.
|
||||||
|
**
|
||||||
|
** If the iLimit for a quota group is set to zero, then the quota group
|
||||||
|
** is disabled and will be deleted when the last database connection using
|
||||||
|
** the quota group is closed.
|
||||||
|
**
|
||||||
|
** Calling this routine on a zPattern that does not exist and with a
|
||||||
|
** zero iLimit is a no-op.
|
||||||
|
**
|
||||||
|
** A quota group must exist with a non-zero iLimit prior to opening
|
||||||
|
** database connections if those connections are to participate in the
|
||||||
|
** quota group. Creating a quota group does not affect database connections
|
||||||
|
** that are already open.
|
||||||
|
**
|
||||||
|
** The patterns that define the various quota groups should be distinct.
|
||||||
|
** If the same filename matches more than one quota group pattern, then
|
||||||
|
** the behavior of this package is undefined.
|
||||||
|
*/
|
||||||
|
int sqlite3_quota_set(
|
||||||
|
const char *zPattern, /* The filename pattern */
|
||||||
|
sqlite3_int64 iLimit, /* New quota to set for this quota group */
|
||||||
|
void (*xCallback)( /* Callback invoked when going over quota */
|
||||||
|
const char *zFilename, /* Name of file whose size increases */
|
||||||
|
sqlite3_int64 *piLimit, /* IN/OUT: The current limit */
|
||||||
|
sqlite3_int64 iSize, /* Total size of all files in the group */
|
||||||
|
void *pArg /* Client data */
|
||||||
|
),
|
||||||
|
void *pArg, /* client data passed thru to callback */
|
||||||
|
void (*xDestroy)(void*) /* Optional destructor for pArg */
|
||||||
|
);
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Bring the named file under quota management, assuming its name matches
|
||||||
|
** the glob pattern of some quota group. Or if it is already under
|
||||||
|
** management, update its size. If zFilename does not match the glob
|
||||||
|
** pattern of any quota group, this routine is a no-op.
|
||||||
|
*/
|
||||||
|
int sqlite3_quota_file(const char *zFilename);
|
||||||
|
|
||||||
|
/*
|
||||||
|
** The following object serves the same role as FILE in the standard C
|
||||||
|
** library. It represents an open connection to a file on disk for I/O.
|
||||||
|
**
|
||||||
|
** A single quota_FILE should not be used by two or more threads at the
|
||||||
|
** same time. Multiple threads can be using different quota_FILE objects
|
||||||
|
** simultaneously, but not the same quota_FILE object.
|
||||||
|
*/
|
||||||
|
typedef struct quota_FILE quota_FILE;
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Create a new quota_FILE object used to read and/or write to the
|
||||||
|
** file zFilename. The zMode parameter is as with standard library zMode.
|
||||||
|
*/
|
||||||
|
quota_FILE *sqlite3_quota_fopen(const char *zFilename, const char *zMode);
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Perform I/O against a quota_FILE object. When doing writes, the
|
||||||
|
** quota mechanism may result in a short write, in order to prevent
|
||||||
|
** the sum of sizes of all files from going over quota.
|
||||||
|
*/
|
||||||
|
size_t sqlite3_quota_fread(void*, size_t, size_t, quota_FILE*);
|
||||||
|
size_t sqlite3_quota_fwrite(void*, size_t, size_t, quota_FILE*);
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Flush all written content held in memory buffers out to disk.
|
||||||
|
** This is the equivalent of fflush() in the standard library.
|
||||||
|
**
|
||||||
|
** If the hardSync parameter is true (non-zero) then this routine
|
||||||
|
** also forces OS buffers to disk - the equivalent of fsync().
|
||||||
|
**
|
||||||
|
** This routine return zero on success and non-zero if something goes
|
||||||
|
** wrong.
|
||||||
|
*/
|
||||||
|
int sqlite3_quota_fflush(quota_FILE*, int hardSync);
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Close a quota_FILE object and free all associated resources. The
|
||||||
|
** file remains under quota management.
|
||||||
|
*/
|
||||||
|
int sqlite3_quota_fclose(quota_FILE*);
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Move the read/write pointer for a quota_FILE object. Or tell the
|
||||||
|
** current location of the read/write pointer.
|
||||||
|
*/
|
||||||
|
int sqlite3_quota_fseek(quota_FILE*, long, int);
|
||||||
|
void sqlite3_quota_rewind(quota_FILE*);
|
||||||
|
long sqlite3_quota_ftell(quota_FILE*);
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Delete a file from the disk, if that file is under quota management.
|
||||||
|
** Adjust quotas accordingly.
|
||||||
|
**
|
||||||
|
** If zFilename is the name of a directory that matches one of the
|
||||||
|
** quota glob patterns, then all files under quota management that
|
||||||
|
** are contained within that directory are deleted.
|
||||||
|
**
|
||||||
|
** A standard SQLite result code is returned (SQLITE_OK, SQLITE_NOMEM, etc.)
|
||||||
|
** When deleting a directory of files, if the deletion of any one
|
||||||
|
** file fails (for example due to an I/O error), then this routine
|
||||||
|
** returns immediately, with the error code, and does not try to
|
||||||
|
** delete any of the other files in the specified directory.
|
||||||
|
**
|
||||||
|
** All files are removed from quota management and deleted from disk.
|
||||||
|
** However, no attempt is made to remove empty directories.
|
||||||
|
**
|
||||||
|
** This routine is a no-op for files that are not under quota management.
|
||||||
|
*/
|
||||||
|
int sqlite3_quota_remove(const char *zFilename);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
} /* end of the 'extern "C"' block */
|
||||||
|
#endif
|
||||||
|
#endif /* _QUOTA_H_ */
|
|
@ -45,6 +45,7 @@ namespace dom {
|
||||||
enum StructuredCloneTags {
|
enum StructuredCloneTags {
|
||||||
SCTAG_BASE = JS_SCTAG_USER_MIN,
|
SCTAG_BASE = JS_SCTAG_USER_MIN,
|
||||||
SCTAG_DOM_BLOB,
|
SCTAG_DOM_BLOB,
|
||||||
|
SCTAG_DOM_FILE,
|
||||||
SCTAG_DOM_FILELIST,
|
SCTAG_DOM_FILELIST,
|
||||||
SCTAG_DOM_MAX
|
SCTAG_DOM_MAX
|
||||||
};
|
};
|
||||||
|
|
|
@ -83,6 +83,8 @@
|
||||||
#include "nsIIOService.h"
|
#include "nsIIOService.h"
|
||||||
|
|
||||||
#include "mozilla/dom/Element.h"
|
#include "mozilla/dom/Element.h"
|
||||||
|
#include "mozilla/dom/indexedDB/FileInfo.h"
|
||||||
|
#include "mozilla/dom/indexedDB/IndexedDatabaseManager.h"
|
||||||
#include "sampler.h"
|
#include "sampler.h"
|
||||||
|
|
||||||
using namespace mozilla::dom;
|
using namespace mozilla::dom;
|
||||||
|
@ -1922,3 +1924,51 @@ nsDOMWindowUtils::CheckAndClearPaintedState(nsIDOMElement* aElement, bool* aResu
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
NS_IMETHODIMP
|
||||||
|
nsDOMWindowUtils::GetFileId(nsIDOMBlob* aBlob, PRInt64* aResult)
|
||||||
|
{
|
||||||
|
if (!IsUniversalXPConnectCapable()) {
|
||||||
|
return NS_ERROR_DOM_SECURITY_ERR;
|
||||||
|
}
|
||||||
|
|
||||||
|
*aResult = aBlob->GetFileId();
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
NS_IMETHODIMP
|
||||||
|
nsDOMWindowUtils::GetFileReferences(const nsAString& aDatabaseName,
|
||||||
|
PRInt64 aId, PRInt32* aRefCnt,
|
||||||
|
PRInt32* aDBRefCnt, PRInt32* aSliceRefCnt,
|
||||||
|
bool* aResult)
|
||||||
|
{
|
||||||
|
if (!IsUniversalXPConnectCapable()) {
|
||||||
|
return NS_ERROR_DOM_SECURITY_ERR;
|
||||||
|
}
|
||||||
|
|
||||||
|
NS_ENSURE_TRUE(mWindow, NS_ERROR_FAILURE);
|
||||||
|
|
||||||
|
nsCString origin;
|
||||||
|
nsresult rv = indexedDB::IndexedDatabaseManager::GetASCIIOriginFromWindow(
|
||||||
|
mWindow, origin);
|
||||||
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
|
nsRefPtr<indexedDB::IndexedDatabaseManager> mgr =
|
||||||
|
indexedDB::IndexedDatabaseManager::GetOrCreate();
|
||||||
|
NS_ENSURE_TRUE(mgr, NS_ERROR_FAILURE);
|
||||||
|
|
||||||
|
nsRefPtr<indexedDB::FileManager> fileManager =
|
||||||
|
mgr->GetOrCreateFileManager(origin, aDatabaseName);
|
||||||
|
NS_ENSURE_TRUE(fileManager, NS_ERROR_FAILURE);
|
||||||
|
|
||||||
|
nsRefPtr<indexedDB::FileInfo> fileInfo = fileManager->GetFileInfo(aId);
|
||||||
|
if (fileInfo) {
|
||||||
|
fileInfo->GetReferences(aRefCnt, aDBRefCnt, aSliceRefCnt);
|
||||||
|
*aRefCnt--;
|
||||||
|
*aResult = true;
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
*aRefCnt = *aDBRefCnt = *aSliceRefCnt = -1;
|
||||||
|
*aResult = false;
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
|
@ -77,9 +77,9 @@ private:
|
||||||
// something fails.
|
// something fails.
|
||||||
inline
|
inline
|
||||||
nsresult
|
nsresult
|
||||||
ConvertCloneBuffersToArrayInternal(
|
ConvertCloneReadInfosToArrayInternal(
|
||||||
JSContext* aCx,
|
JSContext* aCx,
|
||||||
nsTArray<JSAutoStructuredCloneBuffer>& aBuffers,
|
nsTArray<StructuredCloneReadInfo>& aReadInfos,
|
||||||
jsval* aResult)
|
jsval* aResult)
|
||||||
{
|
{
|
||||||
JSObject* array = JS_NewArrayObject(aCx, 0, nsnull);
|
JSObject* array = JS_NewArrayObject(aCx, 0, nsnull);
|
||||||
|
@ -88,17 +88,18 @@ ConvertCloneBuffersToArrayInternal(
|
||||||
return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
|
return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!aBuffers.IsEmpty()) {
|
if (!aReadInfos.IsEmpty()) {
|
||||||
if (!JS_SetArrayLength(aCx, array, jsuint(aBuffers.Length()))) {
|
if (!JS_SetArrayLength(aCx, array, jsuint(aReadInfos.Length()))) {
|
||||||
NS_WARNING("Failed to set array length!");
|
NS_WARNING("Failed to set array length!");
|
||||||
return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
|
return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (uint32 index = 0, count = aBuffers.Length(); index < count; index++) {
|
for (uint32 index = 0, count = aReadInfos.Length(); index < count;
|
||||||
JSAutoStructuredCloneBuffer& buffer = aBuffers[index];
|
index++) {
|
||||||
|
StructuredCloneReadInfo& readInfo = aReadInfos[index];
|
||||||
|
|
||||||
jsval val;
|
jsval val;
|
||||||
if (!IDBObjectStore::DeserializeValue(aCx, buffer, &val)) {
|
if (!IDBObjectStore::DeserializeValue(aCx, readInfo, &val)) {
|
||||||
NS_WARNING("Failed to decode!");
|
NS_WARNING("Failed to decode!");
|
||||||
return NS_ERROR_DOM_DATA_CLONE_ERR;
|
return NS_ERROR_DOM_DATA_CLONE_ERR;
|
||||||
}
|
}
|
||||||
|
@ -504,9 +505,9 @@ AsyncConnectionHelper::ReleaseMainThreadObjects()
|
||||||
|
|
||||||
// static
|
// static
|
||||||
nsresult
|
nsresult
|
||||||
AsyncConnectionHelper::ConvertCloneBuffersToArray(
|
AsyncConnectionHelper::ConvertCloneReadInfosToArray(
|
||||||
JSContext* aCx,
|
JSContext* aCx,
|
||||||
nsTArray<JSAutoStructuredCloneBuffer>& aBuffers,
|
nsTArray<StructuredCloneReadInfo>& aReadInfos,
|
||||||
jsval* aResult)
|
jsval* aResult)
|
||||||
{
|
{
|
||||||
NS_ASSERTION(aCx, "Null context!");
|
NS_ASSERTION(aCx, "Null context!");
|
||||||
|
@ -514,12 +515,12 @@ AsyncConnectionHelper::ConvertCloneBuffersToArray(
|
||||||
|
|
||||||
JSAutoRequest ar(aCx);
|
JSAutoRequest ar(aCx);
|
||||||
|
|
||||||
nsresult rv = ConvertCloneBuffersToArrayInternal(aCx, aBuffers, aResult);
|
nsresult rv = ConvertCloneReadInfosToArrayInternal(aCx, aReadInfos, aResult);
|
||||||
|
|
||||||
for (PRUint32 index = 0; index < aBuffers.Length(); index++) {
|
for (PRUint32 index = 0; index < aReadInfos.Length(); index++) {
|
||||||
aBuffers[index].clear();
|
aReadInfos[index].mCloneBuffer.clear();
|
||||||
}
|
}
|
||||||
aBuffers.Clear();
|
aReadInfos.Clear();
|
||||||
|
|
||||||
return rv;
|
return rv;
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,6 +41,7 @@
|
||||||
#define mozilla_dom_indexeddb_asyncconnectionhelper_h__
|
#define mozilla_dom_indexeddb_asyncconnectionhelper_h__
|
||||||
|
|
||||||
// Only meant to be included in IndexedDB source files, not exported.
|
// Only meant to be included in IndexedDB source files, not exported.
|
||||||
|
#include "DatabaseInfo.h"
|
||||||
#include "IndexedDatabase.h"
|
#include "IndexedDatabase.h"
|
||||||
#include "IDBDatabase.h"
|
#include "IDBDatabase.h"
|
||||||
#include "IDBRequest.h"
|
#include "IDBRequest.h"
|
||||||
|
@ -198,9 +199,9 @@ protected:
|
||||||
/**
|
/**
|
||||||
* Helper to make a JS array object out of an array of clone buffers.
|
* Helper to make a JS array object out of an array of clone buffers.
|
||||||
*/
|
*/
|
||||||
static nsresult ConvertCloneBuffersToArray(
|
static nsresult ConvertCloneReadInfosToArray(
|
||||||
JSContext* aCx,
|
JSContext* aCx,
|
||||||
nsTArray<JSAutoStructuredCloneBuffer>& aBuffers,
|
nsTArray<StructuredCloneReadInfo>& aReadInfos,
|
||||||
jsval* aResult);
|
jsval* aResult);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
|
@ -205,9 +205,6 @@ CheckPermissionsHelper::Observe(nsISupports* aSubject,
|
||||||
mPromptResult = nsDependentString(aData).ToInteger(&rv);
|
mPromptResult = nsDependentString(aData).ToInteger(&rv);
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
IndexedDatabaseManager* mgr = IndexedDatabaseManager::Get();
|
|
||||||
NS_ASSERTION(mgr, "This should never be null!");
|
|
||||||
|
|
||||||
rv = NS_DispatchToCurrentThread(this);
|
rv = NS_DispatchToCurrentThread(this);
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,138 @@
|
||||||
|
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||||
|
/* vim: set ts=2 et sw=2 tw=80: */
|
||||||
|
/* ***** BEGIN LICENSE BLOCK *****
|
||||||
|
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||||
|
*
|
||||||
|
* The contents of this file are subject to the Mozilla Public License Version
|
||||||
|
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
* http://www.mozilla.org/MPL/
|
||||||
|
*
|
||||||
|
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||||
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||||
|
* for the specific language governing rights and limitations under the
|
||||||
|
* License.
|
||||||
|
*
|
||||||
|
* The Original Code is Indexed Database.
|
||||||
|
*
|
||||||
|
* The Initial Developer of the Original Code is
|
||||||
|
* The Mozilla Foundation.
|
||||||
|
* Portions created by the Initial Developer are Copyright (C) 2011
|
||||||
|
* the Initial Developer. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Contributor(s):
|
||||||
|
*
|
||||||
|
* Alternatively, the contents of this file may be used under the terms of
|
||||||
|
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||||
|
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||||
|
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||||
|
* of those above. If you wish to allow use of your version of this file only
|
||||||
|
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||||
|
* use your version of this file under the terms of the MPL, indicate your
|
||||||
|
* decision by deleting the provisions above and replace them with the notice
|
||||||
|
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||||
|
* the provisions above, a recipient may use your version of this file under
|
||||||
|
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||||
|
*
|
||||||
|
* ***** END LICENSE BLOCK ***** */
|
||||||
|
|
||||||
|
#include "IndexedDatabaseManager.h"
|
||||||
|
#include "FileInfo.h"
|
||||||
|
|
||||||
|
USING_INDEXEDDB_NAMESPACE
|
||||||
|
|
||||||
|
// static
|
||||||
|
FileInfo*
|
||||||
|
FileInfo::Create(FileManager* aFileManager, PRInt64 aId)
|
||||||
|
{
|
||||||
|
NS_ASSERTION(aId > 0, "Wrong id!");
|
||||||
|
|
||||||
|
if (aId <= PR_INT16_MAX) {
|
||||||
|
return new FileInfo16(aFileManager, aId);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (aId <= PR_INT32_MAX) {
|
||||||
|
return new FileInfo32(aFileManager, aId);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new FileInfo64(aFileManager, aId);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
FileInfo::GetReferences(PRInt32* aRefCnt, PRInt32* aDBRefCnt,
|
||||||
|
PRInt32* aSliceRefCnt)
|
||||||
|
{
|
||||||
|
if (IndexedDatabaseManager::IsClosed()) {
|
||||||
|
NS_ERROR("Shouldn't be called after shutdown!");
|
||||||
|
|
||||||
|
if (aRefCnt) {
|
||||||
|
*aRefCnt = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (aDBRefCnt) {
|
||||||
|
*aDBRefCnt = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (aSliceRefCnt) {
|
||||||
|
*aSliceRefCnt = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
MutexAutoLock lock(IndexedDatabaseManager::FileMutex());
|
||||||
|
|
||||||
|
if (aRefCnt) {
|
||||||
|
*aRefCnt = mRefCnt;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (aDBRefCnt) {
|
||||||
|
*aDBRefCnt = mDBRefCnt;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (aSliceRefCnt) {
|
||||||
|
*aSliceRefCnt = mSliceRefCnt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
FileInfo::UpdateReferences(nsAutoRefCnt& aRefCount, PRInt32 aDelta,
|
||||||
|
bool aClear)
|
||||||
|
{
|
||||||
|
if (IndexedDatabaseManager::IsClosed()) {
|
||||||
|
NS_ERROR("Shouldn't be called after shutdown!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
MutexAutoLock lock(IndexedDatabaseManager::FileMutex());
|
||||||
|
|
||||||
|
aRefCount = aClear ? 0 : aRefCount + aDelta;
|
||||||
|
|
||||||
|
if (mRefCnt + mDBRefCnt + mSliceRefCnt > 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
mFileManager->mFileInfos.Remove(Id());
|
||||||
|
}
|
||||||
|
|
||||||
|
Cleanup();
|
||||||
|
|
||||||
|
delete this;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
FileInfo::Cleanup()
|
||||||
|
{
|
||||||
|
if (IndexedDatabaseManager::IsShuttingDown() ||
|
||||||
|
mFileManager->Invalidated()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
nsRefPtr<IndexedDatabaseManager> mgr = IndexedDatabaseManager::Get();
|
||||||
|
NS_ASSERTION(mgr, "Shouldn't be null!");
|
||||||
|
|
||||||
|
if (NS_FAILED(mgr->AsyncDeleteFile(mFileManager, Id()))) {
|
||||||
|
NS_WARNING("Failed to delete file asynchronously!");
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,155 @@
|
||||||
|
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||||
|
/* vim: set ts=2 et sw=2 tw=80: */
|
||||||
|
/* ***** BEGIN LICENSE BLOCK *****
|
||||||
|
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||||
|
*
|
||||||
|
* The contents of this file are subject to the Mozilla Public License Version
|
||||||
|
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
* http://www.mozilla.org/MPL/
|
||||||
|
*
|
||||||
|
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||||
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||||
|
* for the specific language governing rights and limitations under the
|
||||||
|
* License.
|
||||||
|
*
|
||||||
|
* The Original Code is Indexed Database.
|
||||||
|
*
|
||||||
|
* The Initial Developer of the Original Code is
|
||||||
|
* The Mozilla Foundation.
|
||||||
|
* Portions created by the Initial Developer are Copyright (C) 2011
|
||||||
|
* the Initial Developer. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Contributor(s):
|
||||||
|
*
|
||||||
|
* Alternatively, the contents of this file may be used under the terms of
|
||||||
|
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||||
|
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||||
|
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||||
|
* of those above. If you wish to allow use of your version of this file only
|
||||||
|
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||||
|
* use your version of this file under the terms of the MPL, indicate your
|
||||||
|
* decision by deleting the provisions above and replace them with the notice
|
||||||
|
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||||
|
* the provisions above, a recipient may use your version of this file under
|
||||||
|
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||||
|
*
|
||||||
|
* ***** END LICENSE BLOCK ***** */
|
||||||
|
|
||||||
|
#ifndef mozilla_dom_indexeddb_fileinfo_h__
|
||||||
|
#define mozilla_dom_indexeddb_fileinfo_h__
|
||||||
|
|
||||||
|
#include "IndexedDatabase.h"
|
||||||
|
#include "nsAtomicRefcnt.h"
|
||||||
|
#include "nsThreadUtils.h"
|
||||||
|
#include "FileManager.h"
|
||||||
|
#include "IndexedDatabaseManager.h"
|
||||||
|
|
||||||
|
BEGIN_INDEXEDDB_NAMESPACE
|
||||||
|
|
||||||
|
class FileInfo
|
||||||
|
{
|
||||||
|
friend class FileManager;
|
||||||
|
|
||||||
|
public:
|
||||||
|
FileInfo(FileManager* aFileManager)
|
||||||
|
: mFileManager(aFileManager)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
virtual ~FileInfo()
|
||||||
|
{
|
||||||
|
#ifdef DEBUG
|
||||||
|
NS_ASSERTION(NS_IsMainThread(), "File info destroyed on wrong thread!");
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
static
|
||||||
|
FileInfo* Create(FileManager* aFileManager, PRInt64 aId);
|
||||||
|
|
||||||
|
void AddRef()
|
||||||
|
{
|
||||||
|
if (IndexedDatabaseManager::IsClosed()) {
|
||||||
|
NS_AtomicIncrementRefcnt(mRefCnt);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
UpdateReferences(mRefCnt, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Release()
|
||||||
|
{
|
||||||
|
if (IndexedDatabaseManager::IsClosed()) {
|
||||||
|
nsrefcnt count = NS_AtomicDecrementRefcnt(mRefCnt);
|
||||||
|
if (count == 0) {
|
||||||
|
mRefCnt = 1;
|
||||||
|
delete this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
UpdateReferences(mRefCnt, -1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void UpdateDBRefs(PRInt32 aDelta)
|
||||||
|
{
|
||||||
|
UpdateReferences(mDBRefCnt, aDelta);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ClearDBRefs()
|
||||||
|
{
|
||||||
|
UpdateReferences(mDBRefCnt, 0, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void UpdateSliceRefs(PRInt32 aDelta)
|
||||||
|
{
|
||||||
|
UpdateReferences(mSliceRefCnt, aDelta);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GetReferences(PRInt32* aRefCnt, PRInt32* aDBRefCnt,
|
||||||
|
PRInt32* aSliceRefCnt);
|
||||||
|
|
||||||
|
FileManager* Manager() const
|
||||||
|
{
|
||||||
|
return mFileManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual PRInt64 Id() const = 0;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void UpdateReferences(nsAutoRefCnt& aRefCount, PRInt32 aDelta,
|
||||||
|
bool aClear = false);
|
||||||
|
void Cleanup();
|
||||||
|
|
||||||
|
nsAutoRefCnt mRefCnt;
|
||||||
|
nsAutoRefCnt mDBRefCnt;
|
||||||
|
nsAutoRefCnt mSliceRefCnt;
|
||||||
|
|
||||||
|
nsRefPtr<FileManager> mFileManager;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define FILEINFO_SUBCLASS(_bits) \
|
||||||
|
class FileInfo##_bits : public FileInfo \
|
||||||
|
{ \
|
||||||
|
public: \
|
||||||
|
FileInfo##_bits(FileManager* aFileManager, PRInt64 aId) \
|
||||||
|
: FileInfo(aFileManager), mId(aId) \
|
||||||
|
{ } \
|
||||||
|
\
|
||||||
|
virtual PRInt64 Id() const \
|
||||||
|
{ \
|
||||||
|
return mId; \
|
||||||
|
} \
|
||||||
|
\
|
||||||
|
private: \
|
||||||
|
PRInt##_bits mId; \
|
||||||
|
};
|
||||||
|
|
||||||
|
FILEINFO_SUBCLASS(16);
|
||||||
|
FILEINFO_SUBCLASS(32);
|
||||||
|
FILEINFO_SUBCLASS(64);
|
||||||
|
|
||||||
|
#undef FILEINFO_SUBCLASS
|
||||||
|
|
||||||
|
END_INDEXEDDB_NAMESPACE
|
||||||
|
|
||||||
|
#endif // mozilla_dom_indexeddb_fileinfo_h__
|
|
@ -0,0 +1,309 @@
|
||||||
|
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||||
|
/* vim: set ts=2 et sw=2 tw=80: */
|
||||||
|
/* ***** BEGIN LICENSE BLOCK *****
|
||||||
|
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||||
|
*
|
||||||
|
* The contents of this file are subject to the Mozilla Public License Version
|
||||||
|
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
* http://www.mozilla.org/MPL/
|
||||||
|
*
|
||||||
|
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||||
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||||
|
* for the specific language governing rights and limitations under the
|
||||||
|
* License.
|
||||||
|
*
|
||||||
|
* The Original Code is Indexed Database.
|
||||||
|
*
|
||||||
|
* The Initial Developer of the Original Code is
|
||||||
|
* The Mozilla Foundation.
|
||||||
|
* Portions created by the Initial Developer are Copyright (C) 2011
|
||||||
|
* the Initial Developer. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Contributor(s):
|
||||||
|
*
|
||||||
|
* Alternatively, the contents of this file may be used under the terms of
|
||||||
|
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||||
|
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||||
|
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||||
|
* of those above. If you wish to allow use of your version of this file only
|
||||||
|
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||||
|
* use your version of this file under the terms of the MPL, indicate your
|
||||||
|
* decision by deleting the provisions above and replace them with the notice
|
||||||
|
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||||
|
* the provisions above, a recipient may use your version of this file under
|
||||||
|
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||||
|
*
|
||||||
|
* ***** END LICENSE BLOCK ***** */
|
||||||
|
|
||||||
|
#include "FileManager.h"
|
||||||
|
|
||||||
|
#include "mozIStorageConnection.h"
|
||||||
|
#include "mozIStorageServiceQuotaManagement.h"
|
||||||
|
#include "mozIStorageStatement.h"
|
||||||
|
#include "nsISimpleEnumerator.h"
|
||||||
|
#include "mozStorageCID.h"
|
||||||
|
#include "nsContentUtils.h"
|
||||||
|
|
||||||
|
#include "FileInfo.h"
|
||||||
|
#include "IndexedDatabaseManager.h"
|
||||||
|
|
||||||
|
USING_INDEXEDDB_NAMESPACE
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
PLDHashOperator
|
||||||
|
EnumerateToTArray(const PRUint64& aKey,
|
||||||
|
FileInfo* aValue,
|
||||||
|
void* aUserArg)
|
||||||
|
{
|
||||||
|
NS_ASSERTION(aValue, "Null pointer!");
|
||||||
|
NS_ASSERTION(aUserArg, "Null pointer!");
|
||||||
|
|
||||||
|
nsTArray<FileInfo*>* array =
|
||||||
|
static_cast<nsTArray<FileInfo*>*>(aUserArg);
|
||||||
|
|
||||||
|
array->AppendElement(aValue);
|
||||||
|
|
||||||
|
return PL_DHASH_NEXT;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // anonymous namespace
|
||||||
|
|
||||||
|
nsresult
|
||||||
|
FileManager::Init()
|
||||||
|
{
|
||||||
|
NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
|
||||||
|
|
||||||
|
NS_ENSURE_TRUE(mFileInfos.Init(), NS_ERROR_OUT_OF_MEMORY);
|
||||||
|
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
nsresult
|
||||||
|
FileManager::InitDirectory(nsIFile* aDirectory,
|
||||||
|
mozIStorageConnection* aConnection)
|
||||||
|
{
|
||||||
|
NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
|
||||||
|
|
||||||
|
bool exists;
|
||||||
|
nsresult rv = aDirectory->Exists(&exists);
|
||||||
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
|
if (exists) {
|
||||||
|
bool isDirectory;
|
||||||
|
rv = aDirectory->IsDirectory(&isDirectory);
|
||||||
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
NS_ENSURE_TRUE(isDirectory, NS_ERROR_FAILURE);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
rv = aDirectory->Create(nsIFile::DIRECTORY_TYPE, 0755);
|
||||||
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
}
|
||||||
|
|
||||||
|
rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
|
||||||
|
"CREATE VIRTUAL TABLE fs USING filesystem;"
|
||||||
|
));
|
||||||
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
|
nsCOMPtr<mozIStorageStatement> stmt;
|
||||||
|
rv = aConnection->CreateStatement(NS_LITERAL_CSTRING(
|
||||||
|
"SELECT name, (name IN (SELECT id FROM file)) FROM fs "
|
||||||
|
"WHERE path = :path"
|
||||||
|
), getter_AddRefs(stmt));
|
||||||
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
|
nsString path;
|
||||||
|
rv = aDirectory->GetPath(path);
|
||||||
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
|
rv = stmt->BindStringByName(NS_LITERAL_CSTRING("path"), path);
|
||||||
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
|
nsCOMPtr<mozIStorageServiceQuotaManagement> ss =
|
||||||
|
do_GetService(MOZ_STORAGE_SERVICE_CONTRACTID);
|
||||||
|
NS_ENSURE_TRUE(ss, NS_ERROR_FAILURE);
|
||||||
|
|
||||||
|
bool hasResult;
|
||||||
|
while (NS_SUCCEEDED(stmt->ExecuteStep(&hasResult)) && hasResult) {
|
||||||
|
nsString name;
|
||||||
|
rv = stmt->GetString(0, name);
|
||||||
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
|
PRInt32 flag = stmt->AsInt32(1);
|
||||||
|
|
||||||
|
nsCOMPtr<nsIFile> file;
|
||||||
|
rv = aDirectory->Clone(getter_AddRefs(file));
|
||||||
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
|
rv = file->Append(name);
|
||||||
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
|
if (flag) {
|
||||||
|
rv = ss->UpdateQuotaInformationForFile(file);
|
||||||
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
} else {
|
||||||
|
rv = file->Remove(false);
|
||||||
|
if (NS_FAILED(rv)) {
|
||||||
|
NS_WARNING("Failed to remove orphaned file!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
|
||||||
|
"DROP TABLE fs;"
|
||||||
|
));
|
||||||
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
|
rv = aDirectory->GetPath(mDirectoryPath);
|
||||||
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
|
rv = aDirectory->GetLeafName(mDirectoryName);
|
||||||
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
nsresult
|
||||||
|
FileManager::Load(mozIStorageConnection* aConnection)
|
||||||
|
{
|
||||||
|
NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
|
||||||
|
|
||||||
|
nsCOMPtr<mozIStorageStatement> stmt;
|
||||||
|
nsresult rv = aConnection->CreateStatement(NS_LITERAL_CSTRING(
|
||||||
|
"SELECT id, refcount "
|
||||||
|
"FROM file"
|
||||||
|
), getter_AddRefs(stmt));
|
||||||
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
|
bool hasResult;
|
||||||
|
while (NS_SUCCEEDED(stmt->ExecuteStep(&hasResult)) && hasResult) {
|
||||||
|
PRInt64 id;
|
||||||
|
rv = stmt->GetInt64(0, &id);
|
||||||
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
|
PRInt32 refcount;
|
||||||
|
rv = stmt->GetInt32(1, &refcount);
|
||||||
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
|
NS_ASSERTION(refcount, "This shouldn't happen!");
|
||||||
|
|
||||||
|
nsRefPtr<FileInfo> fileInfo = FileInfo::Create(this, id);
|
||||||
|
fileInfo->mDBRefCnt = refcount;
|
||||||
|
|
||||||
|
if (!mFileInfos.Put(id, fileInfo)) {
|
||||||
|
NS_WARNING("Out of memory?");
|
||||||
|
return NS_ERROR_OUT_OF_MEMORY;
|
||||||
|
}
|
||||||
|
|
||||||
|
mLastFileId = NS_MAX(id, mLastFileId);
|
||||||
|
}
|
||||||
|
|
||||||
|
mLoaded = true;
|
||||||
|
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
nsresult
|
||||||
|
FileManager::Invalidate()
|
||||||
|
{
|
||||||
|
if (IndexedDatabaseManager::IsClosed()) {
|
||||||
|
NS_ERROR("Shouldn't be called after shutdown!");
|
||||||
|
return NS_ERROR_UNEXPECTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
nsTArray<FileInfo*> fileInfos;
|
||||||
|
{
|
||||||
|
MutexAutoLock lock(IndexedDatabaseManager::FileMutex());
|
||||||
|
|
||||||
|
NS_ASSERTION(!mInvalidated, "Invalidate more than once?!");
|
||||||
|
mInvalidated = true;
|
||||||
|
|
||||||
|
fileInfos.SetCapacity(mFileInfos.Count());
|
||||||
|
mFileInfos.EnumerateRead(EnumerateToTArray, &fileInfos);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (PRUint32 i = 0; i < fileInfos.Length(); i++) {
|
||||||
|
FileInfo* fileInfo = fileInfos.ElementAt(i);
|
||||||
|
fileInfo->ClearDBRefs();
|
||||||
|
}
|
||||||
|
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
already_AddRefed<nsIFile>
|
||||||
|
FileManager::GetDirectory()
|
||||||
|
{
|
||||||
|
nsCOMPtr<nsILocalFile> directory =
|
||||||
|
do_CreateInstance(NS_LOCAL_FILE_CONTRACTID);
|
||||||
|
NS_ENSURE_TRUE(directory, nsnull);
|
||||||
|
|
||||||
|
nsresult rv = directory->InitWithPath(mDirectoryPath);
|
||||||
|
NS_ENSURE_SUCCESS(rv, nsnull);
|
||||||
|
|
||||||
|
return directory.forget();
|
||||||
|
}
|
||||||
|
|
||||||
|
already_AddRefed<FileInfo>
|
||||||
|
FileManager::GetFileInfo(PRInt64 aId)
|
||||||
|
{
|
||||||
|
if (IndexedDatabaseManager::IsClosed()) {
|
||||||
|
NS_ERROR("Shouldn't be called after shutdown!");
|
||||||
|
return nsnull;
|
||||||
|
}
|
||||||
|
|
||||||
|
FileInfo* fileInfo = nsnull;
|
||||||
|
{
|
||||||
|
MutexAutoLock lock(IndexedDatabaseManager::FileMutex());
|
||||||
|
fileInfo = mFileInfos.Get(aId);
|
||||||
|
}
|
||||||
|
nsRefPtr<FileInfo> result = fileInfo;
|
||||||
|
return result.forget();
|
||||||
|
}
|
||||||
|
|
||||||
|
already_AddRefed<FileInfo>
|
||||||
|
FileManager::GetNewFileInfo()
|
||||||
|
{
|
||||||
|
if (IndexedDatabaseManager::IsClosed()) {
|
||||||
|
NS_ERROR("Shouldn't be called after shutdown!");
|
||||||
|
return nsnull;
|
||||||
|
}
|
||||||
|
|
||||||
|
nsAutoPtr<FileInfo> fileInfo;
|
||||||
|
|
||||||
|
{
|
||||||
|
MutexAutoLock lock(IndexedDatabaseManager::FileMutex());
|
||||||
|
|
||||||
|
PRInt64 id = mLastFileId + 1;
|
||||||
|
|
||||||
|
fileInfo = FileInfo::Create(this, id);
|
||||||
|
|
||||||
|
if (!mFileInfos.Put(id, fileInfo)) {
|
||||||
|
NS_WARNING("Out of memory?");
|
||||||
|
return nsnull;
|
||||||
|
}
|
||||||
|
|
||||||
|
mLastFileId = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
nsRefPtr<FileInfo> result = fileInfo.forget();
|
||||||
|
return result.forget();
|
||||||
|
}
|
||||||
|
|
||||||
|
already_AddRefed<nsIFile>
|
||||||
|
FileManager::GetFileForId(nsIFile* aDirectory, PRInt64 aId)
|
||||||
|
{
|
||||||
|
NS_ASSERTION(aDirectory, "Null pointer!");
|
||||||
|
|
||||||
|
nsAutoString id;
|
||||||
|
id.AppendInt(aId);
|
||||||
|
|
||||||
|
nsCOMPtr<nsIFile> file;
|
||||||
|
nsresult rv = aDirectory->Clone(getter_AddRefs(file));
|
||||||
|
NS_ENSURE_SUCCESS(rv, nsnull);
|
||||||
|
|
||||||
|
rv = file->Append(id);
|
||||||
|
NS_ENSURE_SUCCESS(rv, nsnull);
|
||||||
|
|
||||||
|
return file.forget();
|
||||||
|
}
|
|
@ -0,0 +1,136 @@
|
||||||
|
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||||
|
/* vim: set ts=2 et sw=2 tw=80: */
|
||||||
|
/* ***** BEGIN LICENSE BLOCK *****
|
||||||
|
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||||
|
*
|
||||||
|
* The contents of this file are subject to the Mozilla Public License Version
|
||||||
|
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
* http://www.mozilla.org/MPL/
|
||||||
|
*
|
||||||
|
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||||
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||||
|
* for the specific language governing rights and limitations under the
|
||||||
|
* License.
|
||||||
|
*
|
||||||
|
* The Original Code is Indexed Database.
|
||||||
|
*
|
||||||
|
* The Initial Developer of the Original Code is
|
||||||
|
* The Mozilla Foundation.
|
||||||
|
* Portions created by the Initial Developer are Copyright (C) 2011
|
||||||
|
* the Initial Developer. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Contributor(s):
|
||||||
|
*
|
||||||
|
* Alternatively, the contents of this file may be used under the terms of
|
||||||
|
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||||
|
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||||
|
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||||
|
* of those above. If you wish to allow use of your version of this file only
|
||||||
|
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||||
|
* use your version of this file under the terms of the MPL, indicate your
|
||||||
|
* decision by deleting the provisions above and replace them with the notice
|
||||||
|
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||||
|
* the provisions above, a recipient may use your version of this file under
|
||||||
|
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||||
|
*
|
||||||
|
* ***** END LICENSE BLOCK ***** */
|
||||||
|
|
||||||
|
#ifndef mozilla_dom_indexeddb_filemanager_h__
|
||||||
|
#define mozilla_dom_indexeddb_filemanager_h__
|
||||||
|
|
||||||
|
#include "IndexedDatabase.h"
|
||||||
|
#include "nsIFile.h"
|
||||||
|
#include "nsILocalFile.h"
|
||||||
|
#include "nsIDOMFile.h"
|
||||||
|
#include "nsDataHashtable.h"
|
||||||
|
|
||||||
|
class mozIStorageConnection;
|
||||||
|
|
||||||
|
BEGIN_INDEXEDDB_NAMESPACE
|
||||||
|
|
||||||
|
class FileInfo;
|
||||||
|
|
||||||
|
class FileManager
|
||||||
|
{
|
||||||
|
friend class FileInfo;
|
||||||
|
|
||||||
|
public:
|
||||||
|
FileManager(const nsACString& aOrigin,
|
||||||
|
const nsAString& aDatabaseName)
|
||||||
|
: mOrigin(aOrigin), mDatabaseName(aDatabaseName), mLastFileId(0),
|
||||||
|
mLoaded(false), mInvalidated(false)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
~FileManager()
|
||||||
|
{ }
|
||||||
|
|
||||||
|
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(FileManager)
|
||||||
|
|
||||||
|
const nsACString& Origin() const
|
||||||
|
{
|
||||||
|
return mOrigin;
|
||||||
|
}
|
||||||
|
|
||||||
|
const nsAString& DatabaseName() const
|
||||||
|
{
|
||||||
|
return mDatabaseName;
|
||||||
|
}
|
||||||
|
|
||||||
|
const nsAString& DirectoryName() const
|
||||||
|
{
|
||||||
|
return mDirectoryName;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsDirectoryInited() const
|
||||||
|
{
|
||||||
|
return !mDirectoryPath.IsEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Loaded() const
|
||||||
|
{
|
||||||
|
return mLoaded;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Invalidated() const
|
||||||
|
{
|
||||||
|
return mInvalidated;
|
||||||
|
}
|
||||||
|
|
||||||
|
nsresult Init();
|
||||||
|
|
||||||
|
nsresult InitDirectory(nsIFile* aDirectory,
|
||||||
|
mozIStorageConnection* aConnection);
|
||||||
|
|
||||||
|
nsresult Load(mozIStorageConnection* aConnection);
|
||||||
|
|
||||||
|
nsresult Invalidate();
|
||||||
|
|
||||||
|
already_AddRefed<nsIFile> GetDirectory();
|
||||||
|
|
||||||
|
already_AddRefed<FileInfo> GetFileInfo(PRInt64 aId);
|
||||||
|
|
||||||
|
already_AddRefed<FileInfo> GetNewFileInfo();
|
||||||
|
|
||||||
|
static already_AddRefed<nsIFile> GetFileForId(nsIFile* aDirectory,
|
||||||
|
PRInt64 aId);
|
||||||
|
|
||||||
|
private:
|
||||||
|
nsCString mOrigin;
|
||||||
|
nsString mDatabaseName;
|
||||||
|
|
||||||
|
nsString mDirectoryPath;
|
||||||
|
nsString mDirectoryName;
|
||||||
|
|
||||||
|
PRInt64 mLastFileId;
|
||||||
|
|
||||||
|
// Protected by IndexedDatabaseManager::FileMutex()
|
||||||
|
nsDataHashtable<nsUint64HashKey, FileInfo*> mFileInfos;
|
||||||
|
|
||||||
|
bool mLoaded;
|
||||||
|
bool mInvalidated;
|
||||||
|
};
|
||||||
|
|
||||||
|
END_INDEXEDDB_NAMESPACE
|
||||||
|
|
||||||
|
#endif // mozilla_dom_indexeddb_filemanager_h__
|
|
@ -49,7 +49,6 @@
|
||||||
#include "nsThreadUtils.h"
|
#include "nsThreadUtils.h"
|
||||||
|
|
||||||
#include "AsyncConnectionHelper.h"
|
#include "AsyncConnectionHelper.h"
|
||||||
#include "DatabaseInfo.h"
|
|
||||||
#include "IDBEvents.h"
|
#include "IDBEvents.h"
|
||||||
#include "IDBIndex.h"
|
#include "IDBIndex.h"
|
||||||
#include "IDBObjectStore.h"
|
#include "IDBObjectStore.h"
|
||||||
|
@ -87,7 +86,7 @@ public:
|
||||||
|
|
||||||
~ContinueHelper()
|
~ContinueHelper()
|
||||||
{
|
{
|
||||||
IDBObjectStore::ClearStructuredCloneBuffer(mCloneBuffer);
|
IDBObjectStore::ClearStructuredCloneBuffer(mCloneReadInfo.mCloneBuffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
nsresult DoDatabaseWork(mozIStorageConnection* aConnection);
|
nsresult DoDatabaseWork(mozIStorageConnection* aConnection);
|
||||||
|
@ -112,7 +111,7 @@ protected:
|
||||||
PRInt32 mCount;
|
PRInt32 mCount;
|
||||||
Key mKey;
|
Key mKey;
|
||||||
Key mObjectKey;
|
Key mObjectKey;
|
||||||
JSAutoStructuredCloneBuffer mCloneBuffer;
|
StructuredCloneReadInfo mCloneReadInfo;
|
||||||
};
|
};
|
||||||
|
|
||||||
class ContinueObjectStoreHelper : public ContinueHelper
|
class ContinueObjectStoreHelper : public ContinueHelper
|
||||||
|
@ -165,7 +164,7 @@ IDBCursor::Create(IDBRequest* aRequest,
|
||||||
const nsACString& aContinueQuery,
|
const nsACString& aContinueQuery,
|
||||||
const nsACString& aContinueToQuery,
|
const nsACString& aContinueToQuery,
|
||||||
const Key& aKey,
|
const Key& aKey,
|
||||||
JSAutoStructuredCloneBuffer& aCloneBuffer)
|
StructuredCloneReadInfo& aCloneReadInfo)
|
||||||
{
|
{
|
||||||
NS_ASSERTION(aObjectStore, "Null pointer!");
|
NS_ASSERTION(aObjectStore, "Null pointer!");
|
||||||
NS_ASSERTION(!aKey.IsUnset(), "Bad key!");
|
NS_ASSERTION(!aKey.IsUnset(), "Bad key!");
|
||||||
|
@ -178,7 +177,7 @@ IDBCursor::Create(IDBRequest* aRequest,
|
||||||
cursor->mObjectStore = aObjectStore;
|
cursor->mObjectStore = aObjectStore;
|
||||||
cursor->mType = OBJECTSTORE;
|
cursor->mType = OBJECTSTORE;
|
||||||
cursor->mKey = aKey;
|
cursor->mKey = aKey;
|
||||||
cursor->mCloneBuffer.swap(aCloneBuffer);
|
cursor->mCloneReadInfo.Swap(aCloneReadInfo);
|
||||||
|
|
||||||
return cursor.forget();
|
return cursor.forget();
|
||||||
}
|
}
|
||||||
|
@ -224,7 +223,7 @@ IDBCursor::Create(IDBRequest* aRequest,
|
||||||
const nsACString& aContinueToQuery,
|
const nsACString& aContinueToQuery,
|
||||||
const Key& aKey,
|
const Key& aKey,
|
||||||
const Key& aObjectKey,
|
const Key& aObjectKey,
|
||||||
JSAutoStructuredCloneBuffer& aCloneBuffer)
|
StructuredCloneReadInfo& aCloneReadInfo)
|
||||||
{
|
{
|
||||||
NS_ASSERTION(aIndex, "Null pointer!");
|
NS_ASSERTION(aIndex, "Null pointer!");
|
||||||
NS_ASSERTION(!aKey.IsUnset(), "Bad key!");
|
NS_ASSERTION(!aKey.IsUnset(), "Bad key!");
|
||||||
|
@ -240,7 +239,7 @@ IDBCursor::Create(IDBRequest* aRequest,
|
||||||
cursor->mType = INDEXOBJECT;
|
cursor->mType = INDEXOBJECT;
|
||||||
cursor->mKey = aKey;
|
cursor->mKey = aKey;
|
||||||
cursor->mObjectKey = aObjectKey;
|
cursor->mObjectKey = aObjectKey;
|
||||||
cursor->mCloneBuffer.swap(aCloneBuffer);
|
cursor->mCloneReadInfo.Swap(aCloneReadInfo);
|
||||||
|
|
||||||
return cursor.forget();
|
return cursor.forget();
|
||||||
}
|
}
|
||||||
|
@ -300,7 +299,7 @@ IDBCursor::~IDBCursor()
|
||||||
if (mRooted) {
|
if (mRooted) {
|
||||||
NS_DROP_JS_OBJECTS(this, IDBCursor);
|
NS_DROP_JS_OBJECTS(this, IDBCursor);
|
||||||
}
|
}
|
||||||
IDBObjectStore::ClearStructuredCloneBuffer(mCloneBuffer);
|
IDBObjectStore::ClearStructuredCloneBuffer(mCloneReadInfo.mCloneBuffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
nsresult
|
nsresult
|
||||||
|
@ -529,12 +528,12 @@ IDBCursor::GetValue(JSContext* aCx,
|
||||||
mRooted = true;
|
mRooted = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!IDBObjectStore::DeserializeValue(aCx, mCloneBuffer, &mCachedValue)) {
|
if (!IDBObjectStore::DeserializeValue(aCx, mCloneReadInfo, &mCachedValue)) {
|
||||||
mCachedValue = JSVAL_VOID;
|
mCachedValue = JSVAL_VOID;
|
||||||
return NS_ERROR_DOM_DATA_CLONE_ERR;
|
return NS_ERROR_DOM_DATA_CLONE_ERR;
|
||||||
}
|
}
|
||||||
|
|
||||||
mCloneBuffer.clear();
|
mCloneReadInfo.mCloneBuffer.clear();
|
||||||
mHaveCachedValue = true;
|
mHaveCachedValue = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -762,8 +761,8 @@ ContinueHelper::GetSuccessResult(JSContext* aCx,
|
||||||
mCursor->mObjectKey = mObjectKey;
|
mCursor->mObjectKey = mObjectKey;
|
||||||
mCursor->mContinueToKey.Unset();
|
mCursor->mContinueToKey.Unset();
|
||||||
|
|
||||||
mCursor->mCloneBuffer.swap(mCloneBuffer);
|
mCursor->mCloneReadInfo.Swap(mCloneReadInfo);
|
||||||
mCloneBuffer.clear();
|
mCloneReadInfo.mCloneBuffer.clear();
|
||||||
|
|
||||||
nsresult rv = WrapNative(aCx, mCursor, aVal);
|
nsresult rv = WrapNative(aCx, mCursor, aVal);
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
@ -811,8 +810,8 @@ ContinueObjectStoreHelper::GatherResultsFromStatement(
|
||||||
nsresult rv = mKey.SetFromStatement(aStatement, 0);
|
nsresult rv = mKey.SetFromStatement(aStatement, 0);
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
rv = IDBObjectStore::GetStructuredCloneDataFromStatement(aStatement, 1,
|
rv = IDBObjectStore::GetStructuredCloneReadInfoFromStatement(aStatement, 1, 2,
|
||||||
mCloneBuffer);
|
mDatabase->Manager(), mCloneReadInfo);
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
|
@ -881,8 +880,8 @@ ContinueIndexObjectHelper::GatherResultsFromStatement(
|
||||||
rv = mObjectKey.SetFromStatement(aStatement, 1);
|
rv = mObjectKey.SetFromStatement(aStatement, 1);
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
rv = IDBObjectStore::GetStructuredCloneDataFromStatement(aStatement, 2,
|
rv = IDBObjectStore::GetStructuredCloneReadInfoFromStatement(aStatement, 2, 3,
|
||||||
mCloneBuffer);
|
mDatabase->Manager(), mCloneReadInfo);
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
|
|
|
@ -88,7 +88,7 @@ public:
|
||||||
const nsACString& aContinueQuery,
|
const nsACString& aContinueQuery,
|
||||||
const nsACString& aContinueToQuery,
|
const nsACString& aContinueToQuery,
|
||||||
const Key& aKey,
|
const Key& aKey,
|
||||||
JSAutoStructuredCloneBuffer& aCloneBuffer);
|
StructuredCloneReadInfo& aCloneReadInfo);
|
||||||
|
|
||||||
// For INDEXKEY cursors.
|
// For INDEXKEY cursors.
|
||||||
static
|
static
|
||||||
|
@ -115,7 +115,7 @@ public:
|
||||||
const nsACString& aContinueToQuery,
|
const nsACString& aContinueToQuery,
|
||||||
const Key& aKey,
|
const Key& aKey,
|
||||||
const Key& aObjectKey,
|
const Key& aObjectKey,
|
||||||
JSAutoStructuredCloneBuffer& aCloneBuffer);
|
StructuredCloneReadInfo& aCloneReadInfo);
|
||||||
|
|
||||||
enum Type
|
enum Type
|
||||||
{
|
{
|
||||||
|
@ -169,7 +169,7 @@ protected:
|
||||||
|
|
||||||
Key mKey;
|
Key mKey;
|
||||||
Key mObjectKey;
|
Key mObjectKey;
|
||||||
JSAutoStructuredCloneBuffer mCloneBuffer;
|
StructuredCloneReadInfo mCloneReadInfo;
|
||||||
Key mContinueToKey;
|
Key mContinueToKey;
|
||||||
|
|
||||||
bool mHaveCachedKey;
|
bool mHaveCachedKey;
|
||||||
|
|
|
@ -43,6 +43,7 @@
|
||||||
#include "mozilla/Mutex.h"
|
#include "mozilla/Mutex.h"
|
||||||
#include "mozilla/storage.h"
|
#include "mozilla/storage.h"
|
||||||
#include "nsDOMClassInfo.h"
|
#include "nsDOMClassInfo.h"
|
||||||
|
#include "nsDOMLists.h"
|
||||||
#include "nsEventDispatcher.h"
|
#include "nsEventDispatcher.h"
|
||||||
#include "nsJSUtils.h"
|
#include "nsJSUtils.h"
|
||||||
#include "nsProxyRelease.h"
|
#include "nsProxyRelease.h"
|
||||||
|
@ -151,7 +152,8 @@ already_AddRefed<IDBDatabase>
|
||||||
IDBDatabase::Create(nsIScriptContext* aScriptContext,
|
IDBDatabase::Create(nsIScriptContext* aScriptContext,
|
||||||
nsPIDOMWindow* aOwner,
|
nsPIDOMWindow* aOwner,
|
||||||
already_AddRefed<DatabaseInfo> aDatabaseInfo,
|
already_AddRefed<DatabaseInfo> aDatabaseInfo,
|
||||||
const nsACString& aASCIIOrigin)
|
const nsACString& aASCIIOrigin,
|
||||||
|
FileManager* aFileManager)
|
||||||
{
|
{
|
||||||
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
|
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
|
||||||
NS_ASSERTION(!aASCIIOrigin.IsEmpty(), "Empty origin!");
|
NS_ASSERTION(!aASCIIOrigin.IsEmpty(), "Empty origin!");
|
||||||
|
@ -169,6 +171,7 @@ IDBDatabase::Create(nsIScriptContext* aScriptContext,
|
||||||
db->mFilePath = databaseInfo->filePath;
|
db->mFilePath = databaseInfo->filePath;
|
||||||
databaseInfo.swap(db->mDatabaseInfo);
|
databaseInfo.swap(db->mDatabaseInfo);
|
||||||
db->mASCIIOrigin = aASCIIOrigin;
|
db->mASCIIOrigin = aASCIIOrigin;
|
||||||
|
db->mFileManager = aFileManager;
|
||||||
|
|
||||||
IndexedDatabaseManager* mgr = IndexedDatabaseManager::Get();
|
IndexedDatabaseManager* mgr = IndexedDatabaseManager::Get();
|
||||||
NS_ASSERTION(mgr, "This should never be null!");
|
NS_ASSERTION(mgr, "This should never be null!");
|
||||||
|
|
|
@ -41,12 +41,12 @@
|
||||||
#define mozilla_dom_indexeddb_idbdatabase_h__
|
#define mozilla_dom_indexeddb_idbdatabase_h__
|
||||||
|
|
||||||
#include "mozilla/dom/indexedDB/IndexedDatabase.h"
|
#include "mozilla/dom/indexedDB/IndexedDatabase.h"
|
||||||
|
#include "mozilla/dom/indexedDB/FileManager.h"
|
||||||
|
|
||||||
#include "nsIIDBDatabase.h"
|
#include "nsIIDBDatabase.h"
|
||||||
|
|
||||||
#include "nsCycleCollectionParticipant.h"
|
#include "nsCycleCollectionParticipant.h"
|
||||||
#include "nsDOMEventTargetHelper.h"
|
#include "nsDOMEventTargetHelper.h"
|
||||||
#include "nsDOMLists.h"
|
|
||||||
#include "nsIDocument.h"
|
#include "nsIDocument.h"
|
||||||
|
|
||||||
class nsIScriptContext;
|
class nsIScriptContext;
|
||||||
|
@ -78,7 +78,8 @@ public:
|
||||||
Create(nsIScriptContext* aScriptContext,
|
Create(nsIScriptContext* aScriptContext,
|
||||||
nsPIDOMWindow* aOwner,
|
nsPIDOMWindow* aOwner,
|
||||||
already_AddRefed<DatabaseInfo> aDatabaseInfo,
|
already_AddRefed<DatabaseInfo> aDatabaseInfo,
|
||||||
const nsACString& aASCIIOrigin);
|
const nsACString& aASCIIOrigin,
|
||||||
|
FileManager* aFileManager);
|
||||||
|
|
||||||
// nsIDOMEventTarget
|
// nsIDOMEventTarget
|
||||||
virtual nsresult PostHandleEvent(nsEventChainPostVisitor& aVisitor);
|
virtual nsresult PostHandleEvent(nsEventChainPostVisitor& aVisitor);
|
||||||
|
@ -141,6 +142,11 @@ public:
|
||||||
void EnterSetVersionTransaction();
|
void EnterSetVersionTransaction();
|
||||||
void ExitSetVersionTransaction();
|
void ExitSetVersionTransaction();
|
||||||
|
|
||||||
|
FileManager* Manager() const
|
||||||
|
{
|
||||||
|
return mFileManager;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
IDBDatabase();
|
IDBDatabase();
|
||||||
~IDBDatabase();
|
~IDBDatabase();
|
||||||
|
@ -158,6 +164,8 @@ private:
|
||||||
bool mClosed;
|
bool mClosed;
|
||||||
bool mRunningVersionChange;
|
bool mRunningVersionChange;
|
||||||
|
|
||||||
|
nsRefPtr<FileManager> mFileManager;
|
||||||
|
|
||||||
// Only touched on the main thread.
|
// Only touched on the main thread.
|
||||||
nsRefPtr<nsDOMEventListenerWrapper> mOnAbortListener;
|
nsRefPtr<nsDOMEventListenerWrapper> mOnAbortListener;
|
||||||
nsRefPtr<nsDOMEventListenerWrapper> mOnErrorListener;
|
nsRefPtr<nsDOMEventListenerWrapper> mOnErrorListener;
|
||||||
|
|
|
@ -104,7 +104,7 @@ public:
|
||||||
|
|
||||||
~GetHelper()
|
~GetHelper()
|
||||||
{
|
{
|
||||||
IDBObjectStore::ClearStructuredCloneBuffer(mCloneBuffer);
|
IDBObjectStore::ClearStructuredCloneBuffer(mCloneReadInfo.mCloneBuffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
nsresult DoDatabaseWork(mozIStorageConnection* aConnection);
|
nsresult DoDatabaseWork(mozIStorageConnection* aConnection);
|
||||||
|
@ -113,12 +113,12 @@ public:
|
||||||
|
|
||||||
void ReleaseMainThreadObjects()
|
void ReleaseMainThreadObjects()
|
||||||
{
|
{
|
||||||
IDBObjectStore::ClearStructuredCloneBuffer(mCloneBuffer);
|
IDBObjectStore::ClearStructuredCloneBuffer(mCloneReadInfo.mCloneBuffer);
|
||||||
GetKeyHelper::ReleaseMainThreadObjects();
|
GetKeyHelper::ReleaseMainThreadObjects();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
JSAutoStructuredCloneBuffer mCloneBuffer;
|
StructuredCloneReadInfo mCloneReadInfo;
|
||||||
};
|
};
|
||||||
|
|
||||||
class GetAllKeysHelper : public GetKeyHelper
|
class GetAllKeysHelper : public GetKeyHelper
|
||||||
|
@ -154,8 +154,9 @@ public:
|
||||||
|
|
||||||
~GetAllHelper()
|
~GetAllHelper()
|
||||||
{
|
{
|
||||||
for (PRUint32 index = 0; index < mCloneBuffers.Length(); index++) {
|
for (PRUint32 index = 0; index < mCloneReadInfos.Length(); index++) {
|
||||||
IDBObjectStore::ClearStructuredCloneBuffer(mCloneBuffers[index]);
|
IDBObjectStore::ClearStructuredCloneBuffer(
|
||||||
|
mCloneReadInfos[index].mCloneBuffer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -165,15 +166,16 @@ public:
|
||||||
|
|
||||||
void ReleaseMainThreadObjects()
|
void ReleaseMainThreadObjects()
|
||||||
{
|
{
|
||||||
for (PRUint32 index = 0; index < mCloneBuffers.Length(); index++) {
|
for (PRUint32 index = 0; index < mCloneReadInfos.Length(); index++) {
|
||||||
IDBObjectStore::ClearStructuredCloneBuffer(mCloneBuffers[index]);
|
IDBObjectStore::ClearStructuredCloneBuffer(
|
||||||
|
mCloneReadInfos[index].mCloneBuffer);
|
||||||
}
|
}
|
||||||
GetKeyHelper::ReleaseMainThreadObjects();
|
GetKeyHelper::ReleaseMainThreadObjects();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
const PRUint32 mLimit;
|
const PRUint32 mLimit;
|
||||||
nsTArray<JSAutoStructuredCloneBuffer> mCloneBuffers;
|
nsTArray<StructuredCloneReadInfo> mCloneReadInfos;
|
||||||
};
|
};
|
||||||
|
|
||||||
class OpenKeyCursorHelper : public AsyncConnectionHelper
|
class OpenKeyCursorHelper : public AsyncConnectionHelper
|
||||||
|
@ -227,7 +229,7 @@ public:
|
||||||
|
|
||||||
~OpenCursorHelper()
|
~OpenCursorHelper()
|
||||||
{
|
{
|
||||||
IDBObjectStore::ClearStructuredCloneBuffer(mCloneBuffer);
|
IDBObjectStore::ClearStructuredCloneBuffer(mCloneReadInfo.mCloneBuffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
nsresult DoDatabaseWork(mozIStorageConnection* aConnection);
|
nsresult DoDatabaseWork(mozIStorageConnection* aConnection);
|
||||||
|
@ -250,7 +252,7 @@ private:
|
||||||
// Out-params.
|
// Out-params.
|
||||||
Key mKey;
|
Key mKey;
|
||||||
Key mObjectKey;
|
Key mObjectKey;
|
||||||
JSAutoStructuredCloneBuffer mCloneBuffer;
|
StructuredCloneReadInfo mCloneReadInfo;
|
||||||
nsCString mContinueQuery;
|
nsCString mContinueQuery;
|
||||||
nsCString mContinueToQuery;
|
nsCString mContinueToQuery;
|
||||||
Key mRangeKey;
|
Key mRangeKey;
|
||||||
|
@ -793,7 +795,7 @@ GetHelper::DoDatabaseWork(mozIStorageConnection* /* aConnection */)
|
||||||
|
|
||||||
NS_NAMED_LITERAL_CSTRING(indexId, "index_id");
|
NS_NAMED_LITERAL_CSTRING(indexId, "index_id");
|
||||||
|
|
||||||
nsCString query = NS_LITERAL_CSTRING("SELECT data FROM ") + objectTable +
|
nsCString query = NS_LITERAL_CSTRING("SELECT data, file_ids FROM ") + objectTable +
|
||||||
NS_LITERAL_CSTRING(" INNER JOIN ") + joinTable +
|
NS_LITERAL_CSTRING(" INNER JOIN ") + joinTable +
|
||||||
NS_LITERAL_CSTRING(" ON ") + objectTable +
|
NS_LITERAL_CSTRING(" ON ") + objectTable +
|
||||||
NS_LITERAL_CSTRING(".id = ") + joinTable +
|
NS_LITERAL_CSTRING(".id = ") + joinTable +
|
||||||
|
@ -818,8 +820,8 @@ GetHelper::DoDatabaseWork(mozIStorageConnection* /* aConnection */)
|
||||||
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
|
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
|
||||||
|
|
||||||
if (hasResult) {
|
if (hasResult) {
|
||||||
rv = IDBObjectStore::GetStructuredCloneDataFromStatement(stmt, 0,
|
rv = IDBObjectStore::GetStructuredCloneReadInfoFromStatement(stmt, 0, 1,
|
||||||
mCloneBuffer);
|
mDatabase->Manager(), mCloneReadInfo);
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -830,9 +832,9 @@ nsresult
|
||||||
GetHelper::GetSuccessResult(JSContext* aCx,
|
GetHelper::GetSuccessResult(JSContext* aCx,
|
||||||
jsval* aVal)
|
jsval* aVal)
|
||||||
{
|
{
|
||||||
bool result = IDBObjectStore::DeserializeValue(aCx, mCloneBuffer, aVal);
|
bool result = IDBObjectStore::DeserializeValue(aCx, mCloneReadInfo, aVal);
|
||||||
|
|
||||||
mCloneBuffer.clear();
|
mCloneReadInfo.mCloneBuffer.clear();
|
||||||
|
|
||||||
NS_ENSURE_TRUE(result, NS_ERROR_FAILURE);
|
NS_ENSURE_TRUE(result, NS_ERROR_FAILURE);
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
|
@ -1003,7 +1005,7 @@ GetAllHelper::DoDatabaseWork(mozIStorageConnection* /* aConnection */)
|
||||||
limitClause.AppendInt(mLimit);
|
limitClause.AppendInt(mLimit);
|
||||||
}
|
}
|
||||||
|
|
||||||
nsCString query = NS_LITERAL_CSTRING("SELECT data FROM ") + dataTableName +
|
nsCString query = NS_LITERAL_CSTRING("SELECT data, file_ids FROM ") + dataTableName +
|
||||||
NS_LITERAL_CSTRING(" INNER JOIN ") + indexTableName +
|
NS_LITERAL_CSTRING(" INNER JOIN ") + indexTableName +
|
||||||
NS_LITERAL_CSTRING(" ON ") + dataTableName +
|
NS_LITERAL_CSTRING(" ON ") + dataTableName +
|
||||||
NS_LITERAL_CSTRING(".id = ") + indexTableName +
|
NS_LITERAL_CSTRING(".id = ") + indexTableName +
|
||||||
|
@ -1025,18 +1027,19 @@ GetAllHelper::DoDatabaseWork(mozIStorageConnection* /* aConnection */)
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
}
|
}
|
||||||
|
|
||||||
mCloneBuffers.SetCapacity(50);
|
mCloneReadInfos.SetCapacity(50);
|
||||||
|
|
||||||
bool hasResult;
|
bool hasResult;
|
||||||
while(NS_SUCCEEDED((rv = stmt->ExecuteStep(&hasResult))) && hasResult) {
|
while(NS_SUCCEEDED((rv = stmt->ExecuteStep(&hasResult))) && hasResult) {
|
||||||
if (mCloneBuffers.Capacity() == mCloneBuffers.Length()) {
|
if (mCloneReadInfos.Capacity() == mCloneReadInfos.Length()) {
|
||||||
mCloneBuffers.SetCapacity(mCloneBuffers.Capacity() * 2);
|
mCloneReadInfos.SetCapacity(mCloneReadInfos.Capacity() * 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
JSAutoStructuredCloneBuffer* buffer = mCloneBuffers.AppendElement();
|
StructuredCloneReadInfo* readInfo = mCloneReadInfos.AppendElement();
|
||||||
NS_ASSERTION(buffer, "This shouldn't fail!");
|
NS_ASSERTION(readInfo, "This shouldn't fail!");
|
||||||
|
|
||||||
rv = IDBObjectStore::GetStructuredCloneDataFromStatement(stmt, 0, *buffer);
|
rv = IDBObjectStore::GetStructuredCloneReadInfoFromStatement(stmt, 0, 1,
|
||||||
|
mDatabase->Manager(), *readInfo);
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
}
|
}
|
||||||
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
|
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
|
||||||
|
@ -1048,12 +1051,12 @@ nsresult
|
||||||
GetAllHelper::GetSuccessResult(JSContext* aCx,
|
GetAllHelper::GetSuccessResult(JSContext* aCx,
|
||||||
jsval* aVal)
|
jsval* aVal)
|
||||||
{
|
{
|
||||||
NS_ASSERTION(mCloneBuffers.Length() <= mLimit, "Too many results!");
|
NS_ASSERTION(mCloneReadInfos.Length() <= mLimit, "Too many results!");
|
||||||
|
|
||||||
nsresult rv = ConvertCloneBuffersToArray(aCx, mCloneBuffers, aVal);
|
nsresult rv = ConvertCloneReadInfosToArray(aCx, mCloneReadInfos, aVal);
|
||||||
|
|
||||||
for (PRUint32 index = 0; index < mCloneBuffers.Length(); index++) {
|
for (PRUint32 index = 0; index < mCloneReadInfos.Length(); index++) {
|
||||||
mCloneBuffers[index].clear();
|
mCloneReadInfos[index].mCloneBuffer.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
@ -1317,9 +1320,10 @@ OpenCursorHelper::DoDatabaseWork(mozIStorageConnection* aConnection)
|
||||||
NS_NAMED_LITERAL_CSTRING(commaspace, ", ");
|
NS_NAMED_LITERAL_CSTRING(commaspace, ", ");
|
||||||
|
|
||||||
nsCString data = objectTable + NS_LITERAL_CSTRING(".data");
|
nsCString data = objectTable + NS_LITERAL_CSTRING(".data");
|
||||||
|
nsCString fileIds = objectTable + NS_LITERAL_CSTRING(".file_ids");
|
||||||
|
|
||||||
nsCString firstQuery = NS_LITERAL_CSTRING("SELECT ") + value + commaspace +
|
nsCString firstQuery = NS_LITERAL_CSTRING("SELECT ") + value + commaspace +
|
||||||
keyValue + commaspace + data +
|
keyValue + commaspace + data + commaspace + fileIds +
|
||||||
NS_LITERAL_CSTRING(" FROM ") + indexTable +
|
NS_LITERAL_CSTRING(" FROM ") + indexTable +
|
||||||
NS_LITERAL_CSTRING(" INNER JOIN ") + objectTable +
|
NS_LITERAL_CSTRING(" INNER JOIN ") + objectTable +
|
||||||
NS_LITERAL_CSTRING(" ON ") + indexTable + dot +
|
NS_LITERAL_CSTRING(" ON ") + indexTable + dot +
|
||||||
|
@ -1359,13 +1363,14 @@ OpenCursorHelper::DoDatabaseWork(mozIStorageConnection* aConnection)
|
||||||
rv = mObjectKey.SetFromStatement(stmt, 1);
|
rv = mObjectKey.SetFromStatement(stmt, 1);
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
rv = IDBObjectStore::GetStructuredCloneDataFromStatement(stmt, 2,
|
rv = IDBObjectStore::GetStructuredCloneReadInfoFromStatement(stmt, 2, 3,
|
||||||
mCloneBuffer);
|
mDatabase->Manager(), mCloneReadInfo);
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
// Now we need to make the query to get the next match.
|
// Now we need to make the query to get the next match.
|
||||||
nsCAutoString queryStart = NS_LITERAL_CSTRING("SELECT ") + value +
|
nsCAutoString queryStart = NS_LITERAL_CSTRING("SELECT ") + value +
|
||||||
commaspace + keyValue + commaspace + data +
|
commaspace + keyValue + commaspace + data +
|
||||||
|
commaspace + fileIds +
|
||||||
NS_LITERAL_CSTRING(" FROM ") + indexTable +
|
NS_LITERAL_CSTRING(" FROM ") + indexTable +
|
||||||
NS_LITERAL_CSTRING(" INNER JOIN ") + objectTable +
|
NS_LITERAL_CSTRING(" INNER JOIN ") + objectTable +
|
||||||
NS_LITERAL_CSTRING(" ON ") + indexTable + dot +
|
NS_LITERAL_CSTRING(" ON ") + indexTable + dot +
|
||||||
|
@ -1458,10 +1463,10 @@ OpenCursorHelper::GetSuccessResult(JSContext* aCx,
|
||||||
nsRefPtr<IDBCursor> cursor =
|
nsRefPtr<IDBCursor> cursor =
|
||||||
IDBCursor::Create(mRequest, mTransaction, mIndex, mDirection, mRangeKey,
|
IDBCursor::Create(mRequest, mTransaction, mIndex, mDirection, mRangeKey,
|
||||||
mContinueQuery, mContinueToQuery, mKey, mObjectKey,
|
mContinueQuery, mContinueToQuery, mKey, mObjectKey,
|
||||||
mCloneBuffer);
|
mCloneReadInfo);
|
||||||
NS_ENSURE_TRUE(cursor, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
|
NS_ENSURE_TRUE(cursor, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
|
||||||
|
|
||||||
NS_ASSERTION(!mCloneBuffer.data(), "Should have swapped!");
|
NS_ASSERTION(!mCloneReadInfo.mCloneBuffer.data(), "Should have swapped!");
|
||||||
|
|
||||||
return WrapNative(aCx, cursor, aVal);
|
return WrapNative(aCx, cursor, aVal);
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,15 +42,19 @@
|
||||||
#include "nsIJSContextStack.h"
|
#include "nsIJSContextStack.h"
|
||||||
|
|
||||||
#include "jsclone.h"
|
#include "jsclone.h"
|
||||||
|
#include "mozilla/dom/StructuredCloneTags.h"
|
||||||
#include "mozilla/storage.h"
|
#include "mozilla/storage.h"
|
||||||
#include "nsCharSeparatedTokenizer.h"
|
#include "nsCharSeparatedTokenizer.h"
|
||||||
#include "nsContentUtils.h"
|
#include "nsContentUtils.h"
|
||||||
#include "nsDOMClassInfo.h"
|
#include "nsDOMClassInfo.h"
|
||||||
|
#include "nsDOMFile.h"
|
||||||
|
#include "nsDOMLists.h"
|
||||||
#include "nsEventDispatcher.h"
|
#include "nsEventDispatcher.h"
|
||||||
#include "nsJSUtils.h"
|
#include "nsJSUtils.h"
|
||||||
#include "nsServiceManagerUtils.h"
|
#include "nsServiceManagerUtils.h"
|
||||||
#include "nsThreadUtils.h"
|
#include "nsThreadUtils.h"
|
||||||
#include "snappy/snappy.h"
|
#include "snappy/snappy.h"
|
||||||
|
#include "test_quota.h"
|
||||||
|
|
||||||
#include "AsyncConnectionHelper.h"
|
#include "AsyncConnectionHelper.h"
|
||||||
#include "IDBCursor.h"
|
#include "IDBCursor.h"
|
||||||
|
@ -60,6 +64,8 @@
|
||||||
#include "IDBTransaction.h"
|
#include "IDBTransaction.h"
|
||||||
#include "DatabaseInfo.h"
|
#include "DatabaseInfo.h"
|
||||||
|
|
||||||
|
#define FILE_COPY_BUFFER_SIZE 32768
|
||||||
|
|
||||||
USING_INDEXEDDB_NAMESPACE
|
USING_INDEXEDDB_NAMESPACE
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
@ -70,21 +76,20 @@ public:
|
||||||
AddHelper(IDBTransaction* aTransaction,
|
AddHelper(IDBTransaction* aTransaction,
|
||||||
IDBRequest* aRequest,
|
IDBRequest* aRequest,
|
||||||
IDBObjectStore* aObjectStore,
|
IDBObjectStore* aObjectStore,
|
||||||
JSAutoStructuredCloneBuffer& aCloneBuffer,
|
StructuredCloneWriteInfo& aCloneWriteInfo,
|
||||||
const Key& aKey,
|
const Key& aKey,
|
||||||
bool aOverwrite,
|
bool aOverwrite,
|
||||||
nsTArray<IndexUpdateInfo>& aIndexUpdateInfo,
|
nsTArray<IndexUpdateInfo>& aIndexUpdateInfo)
|
||||||
PRUint64 aOffsetToKeyProp)
|
|
||||||
: AsyncConnectionHelper(aTransaction, aRequest), mObjectStore(aObjectStore),
|
: AsyncConnectionHelper(aTransaction, aRequest), mObjectStore(aObjectStore),
|
||||||
mKey(aKey), mOverwrite(aOverwrite), mOffsetToKeyProp(aOffsetToKeyProp)
|
mKey(aKey), mOverwrite(aOverwrite)
|
||||||
{
|
{
|
||||||
mCloneBuffer.swap(aCloneBuffer);
|
mCloneWriteInfo.Swap(aCloneWriteInfo);
|
||||||
mIndexUpdateInfo.SwapElements(aIndexUpdateInfo);
|
mIndexUpdateInfo.SwapElements(aIndexUpdateInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
~AddHelper()
|
~AddHelper()
|
||||||
{
|
{
|
||||||
IDBObjectStore::ClearStructuredCloneBuffer(mCloneBuffer);
|
IDBObjectStore::ClearStructuredCloneBuffer(mCloneWriteInfo.mCloneBuffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
nsresult DoDatabaseWork(mozIStorageConnection* aConnection);
|
nsresult DoDatabaseWork(mozIStorageConnection* aConnection);
|
||||||
|
@ -94,7 +99,7 @@ public:
|
||||||
void ReleaseMainThreadObjects()
|
void ReleaseMainThreadObjects()
|
||||||
{
|
{
|
||||||
mObjectStore = nsnull;
|
mObjectStore = nsnull;
|
||||||
IDBObjectStore::ClearStructuredCloneBuffer(mCloneBuffer);
|
IDBObjectStore::ClearStructuredCloneBuffer(mCloneWriteInfo.mCloneBuffer);
|
||||||
AsyncConnectionHelper::ReleaseMainThreadObjects();
|
AsyncConnectionHelper::ReleaseMainThreadObjects();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -103,11 +108,10 @@ private:
|
||||||
nsRefPtr<IDBObjectStore> mObjectStore;
|
nsRefPtr<IDBObjectStore> mObjectStore;
|
||||||
|
|
||||||
// These may change in the autoincrement case.
|
// These may change in the autoincrement case.
|
||||||
JSAutoStructuredCloneBuffer mCloneBuffer;
|
StructuredCloneWriteInfo mCloneWriteInfo;
|
||||||
Key mKey;
|
Key mKey;
|
||||||
const bool mOverwrite;
|
const bool mOverwrite;
|
||||||
nsTArray<IndexUpdateInfo> mIndexUpdateInfo;
|
nsTArray<IndexUpdateInfo> mIndexUpdateInfo;
|
||||||
PRUint64 mOffsetToKeyProp;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class GetHelper : public AsyncConnectionHelper
|
class GetHelper : public AsyncConnectionHelper
|
||||||
|
@ -123,7 +127,7 @@ public:
|
||||||
|
|
||||||
~GetHelper()
|
~GetHelper()
|
||||||
{
|
{
|
||||||
IDBObjectStore::ClearStructuredCloneBuffer(mCloneBuffer);
|
IDBObjectStore::ClearStructuredCloneBuffer(mCloneReadInfo.mCloneBuffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
nsresult DoDatabaseWork(mozIStorageConnection* aConnection);
|
nsresult DoDatabaseWork(mozIStorageConnection* aConnection);
|
||||||
|
@ -134,7 +138,7 @@ public:
|
||||||
{
|
{
|
||||||
mObjectStore = nsnull;
|
mObjectStore = nsnull;
|
||||||
mKeyRange = nsnull;
|
mKeyRange = nsnull;
|
||||||
IDBObjectStore::ClearStructuredCloneBuffer(mCloneBuffer);
|
IDBObjectStore::ClearStructuredCloneBuffer(mCloneReadInfo.mCloneBuffer);
|
||||||
AsyncConnectionHelper::ReleaseMainThreadObjects();
|
AsyncConnectionHelper::ReleaseMainThreadObjects();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -145,7 +149,7 @@ protected:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Out-params.
|
// Out-params.
|
||||||
JSAutoStructuredCloneBuffer mCloneBuffer;
|
StructuredCloneReadInfo mCloneReadInfo;
|
||||||
};
|
};
|
||||||
|
|
||||||
class DeleteHelper : public GetHelper
|
class DeleteHelper : public GetHelper
|
||||||
|
@ -199,7 +203,7 @@ public:
|
||||||
|
|
||||||
~OpenCursorHelper()
|
~OpenCursorHelper()
|
||||||
{
|
{
|
||||||
IDBObjectStore::ClearStructuredCloneBuffer(mCloneBuffer);
|
IDBObjectStore::ClearStructuredCloneBuffer(mCloneReadInfo.mCloneBuffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
nsresult DoDatabaseWork(mozIStorageConnection* aConnection);
|
nsresult DoDatabaseWork(mozIStorageConnection* aConnection);
|
||||||
|
@ -210,7 +214,7 @@ public:
|
||||||
{
|
{
|
||||||
mObjectStore = nsnull;
|
mObjectStore = nsnull;
|
||||||
mKeyRange = nsnull;
|
mKeyRange = nsnull;
|
||||||
IDBObjectStore::ClearStructuredCloneBuffer(mCloneBuffer);
|
IDBObjectStore::ClearStructuredCloneBuffer(mCloneReadInfo.mCloneBuffer);
|
||||||
AsyncConnectionHelper::ReleaseMainThreadObjects();
|
AsyncConnectionHelper::ReleaseMainThreadObjects();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -222,7 +226,7 @@ private:
|
||||||
|
|
||||||
// Out-params.
|
// Out-params.
|
||||||
Key mKey;
|
Key mKey;
|
||||||
JSAutoStructuredCloneBuffer mCloneBuffer;
|
StructuredCloneReadInfo mCloneReadInfo;
|
||||||
nsCString mContinueQuery;
|
nsCString mContinueQuery;
|
||||||
nsCString mContinueToQuery;
|
nsCString mContinueToQuery;
|
||||||
Key mRangeKey;
|
Key mRangeKey;
|
||||||
|
@ -312,8 +316,9 @@ public:
|
||||||
|
|
||||||
~GetAllHelper()
|
~GetAllHelper()
|
||||||
{
|
{
|
||||||
for (PRUint32 index = 0; index < mCloneBuffers.Length(); index++) {
|
for (PRUint32 index = 0; index < mCloneReadInfos.Length(); index++) {
|
||||||
IDBObjectStore::ClearStructuredCloneBuffer(mCloneBuffers[index]);
|
IDBObjectStore::ClearStructuredCloneBuffer(
|
||||||
|
mCloneReadInfos[index].mCloneBuffer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -325,8 +330,9 @@ public:
|
||||||
{
|
{
|
||||||
mObjectStore = nsnull;
|
mObjectStore = nsnull;
|
||||||
mKeyRange = nsnull;
|
mKeyRange = nsnull;
|
||||||
for (PRUint32 index = 0; index < mCloneBuffers.Length(); index++) {
|
for (PRUint32 index = 0; index < mCloneReadInfos.Length(); index++) {
|
||||||
IDBObjectStore::ClearStructuredCloneBuffer(mCloneBuffers[index]);
|
IDBObjectStore::ClearStructuredCloneBuffer(
|
||||||
|
mCloneReadInfos[index].mCloneBuffer);
|
||||||
}
|
}
|
||||||
AsyncConnectionHelper::ReleaseMainThreadObjects();
|
AsyncConnectionHelper::ReleaseMainThreadObjects();
|
||||||
}
|
}
|
||||||
|
@ -339,7 +345,7 @@ protected:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Out-params.
|
// Out-params.
|
||||||
nsTArray<JSAutoStructuredCloneBuffer> mCloneBuffers;
|
nsTArray<StructuredCloneReadInfo> mCloneReadInfos;
|
||||||
};
|
};
|
||||||
|
|
||||||
class CountHelper : public AsyncConnectionHelper
|
class CountHelper : public AsyncConnectionHelper
|
||||||
|
@ -492,32 +498,6 @@ JSClass gDummyPropClass = {
|
||||||
JSCLASS_NO_OPTIONAL_MEMBERS
|
JSCLASS_NO_OPTIONAL_MEMBERS
|
||||||
};
|
};
|
||||||
|
|
||||||
JSBool
|
|
||||||
StructuredCloneWriteDummyProp(JSContext* aCx,
|
|
||||||
JSStructuredCloneWriter* aWriter,
|
|
||||||
JSObject* aObj,
|
|
||||||
void* aClosure)
|
|
||||||
{
|
|
||||||
if (JS_GET_CLASS(aCx, aObj) == &gDummyPropClass) {
|
|
||||||
PRUint64* closure = reinterpret_cast<PRUint64*>(aClosure);
|
|
||||||
|
|
||||||
NS_ASSERTION(*closure == 0, "We should not have been here before!");
|
|
||||||
*closure = js_GetSCOffset(aWriter);
|
|
||||||
|
|
||||||
PRUint64 value = 0;
|
|
||||||
return JS_WriteBytes(aWriter, &value, sizeof(value));
|
|
||||||
}
|
|
||||||
|
|
||||||
// try using the runtime callbacks
|
|
||||||
const JSStructuredCloneCallbacks* runtimeCallbacks =
|
|
||||||
aCx->runtime->structuredCloneCallbacks;
|
|
||||||
if (runtimeCallbacks) {
|
|
||||||
return runtimeCallbacks->write(aCx, aWriter, aObj, nsnull);
|
|
||||||
}
|
|
||||||
|
|
||||||
return JS_FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // anonymous namespace
|
} // anonymous namespace
|
||||||
|
|
||||||
// static
|
// static
|
||||||
|
@ -771,23 +751,26 @@ IDBObjectStore::UpdateIndexes(IDBTransaction* aTransaction,
|
||||||
|
|
||||||
// static
|
// static
|
||||||
nsresult
|
nsresult
|
||||||
IDBObjectStore::GetStructuredCloneDataFromStatement(
|
IDBObjectStore::GetStructuredCloneReadInfoFromStatement(
|
||||||
mozIStorageStatement* aStatement,
|
mozIStorageStatement* aStatement,
|
||||||
PRUint32 aIndex,
|
PRUint32 aDataIndex,
|
||||||
JSAutoStructuredCloneBuffer& aBuffer)
|
PRUint32 aFileIdsIndex,
|
||||||
|
FileManager* aFileManager,
|
||||||
|
StructuredCloneReadInfo& aInfo)
|
||||||
{
|
{
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
{
|
{
|
||||||
PRInt32 valueType;
|
PRInt32 type;
|
||||||
NS_ASSERTION(NS_SUCCEEDED(aStatement->GetTypeOfIndex(aIndex, &valueType)) &&
|
NS_ASSERTION(NS_SUCCEEDED(aStatement->GetTypeOfIndex(aDataIndex, &type)) &&
|
||||||
valueType == mozIStorageStatement::VALUE_TYPE_BLOB,
|
type == mozIStorageStatement::VALUE_TYPE_BLOB,
|
||||||
"Bad value type!");
|
"Bad value type!");
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
const PRUint8* blobData;
|
const PRUint8* blobData;
|
||||||
PRUint32 blobDataLength;
|
PRUint32 blobDataLength;
|
||||||
nsresult rv = aStatement->GetSharedBlob(aIndex, &blobDataLength, &blobData);
|
nsresult rv = aStatement->GetSharedBlob(aDataIndex, &blobDataLength,
|
||||||
|
&blobData);
|
||||||
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
|
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
|
||||||
|
|
||||||
const char* compressed = reinterpret_cast<const char*>(blobData);
|
const char* compressed = reinterpret_cast<const char*>(blobData);
|
||||||
|
@ -808,10 +791,38 @@ IDBObjectStore::GetStructuredCloneDataFromStatement(
|
||||||
return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
|
return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
|
||||||
}
|
}
|
||||||
|
|
||||||
return aBuffer.copy(reinterpret_cast<const uint64_t *>(uncompressed.get()),
|
JSAutoStructuredCloneBuffer& buffer = aInfo.mCloneBuffer;
|
||||||
uncompressedLength) ?
|
if (!buffer.copy(reinterpret_cast<const uint64_t *>(uncompressed.get()),
|
||||||
NS_OK :
|
uncompressedLength)) {
|
||||||
NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
|
return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isNull;
|
||||||
|
rv = aStatement->GetIsNull(aFileIdsIndex, &isNull);
|
||||||
|
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
|
||||||
|
|
||||||
|
if (isNull) {
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
nsString ids;
|
||||||
|
rv = aStatement->GetString(aFileIdsIndex, ids);
|
||||||
|
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
|
||||||
|
|
||||||
|
nsAutoTArray<PRInt64, 10> array;
|
||||||
|
rv = ConvertFileIdsToArray(ids, array);
|
||||||
|
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
|
||||||
|
|
||||||
|
for (PRUint32 i = 0; i < array.Length(); i++) {
|
||||||
|
const PRInt64& id = array.ElementAt(i);
|
||||||
|
|
||||||
|
nsRefPtr<FileInfo> fileInfo = aFileManager->GetFileInfo(id);
|
||||||
|
NS_ASSERTION(fileInfo, "Null file info!");
|
||||||
|
|
||||||
|
aInfo.mFileInfos.AppendElement(fileInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
// static
|
// static
|
||||||
|
@ -826,32 +837,36 @@ IDBObjectStore::ClearStructuredCloneBuffer(JSAutoStructuredCloneBuffer& aBuffer)
|
||||||
// static
|
// static
|
||||||
bool
|
bool
|
||||||
IDBObjectStore::DeserializeValue(JSContext* aCx,
|
IDBObjectStore::DeserializeValue(JSContext* aCx,
|
||||||
JSAutoStructuredCloneBuffer& aBuffer,
|
StructuredCloneReadInfo& aCloneReadInfo,
|
||||||
jsval* aValue,
|
jsval* aValue)
|
||||||
JSStructuredCloneCallbacks* aCallbacks,
|
|
||||||
void* aClosure)
|
|
||||||
{
|
{
|
||||||
NS_ASSERTION(NS_IsMainThread(),
|
NS_ASSERTION(NS_IsMainThread(),
|
||||||
"Should only be deserializing on the main thread!");
|
"Should only be deserializing on the main thread!");
|
||||||
NS_ASSERTION(aCx, "A JSContext is required!");
|
NS_ASSERTION(aCx, "A JSContext is required!");
|
||||||
|
|
||||||
if (!aBuffer.data()) {
|
JSAutoStructuredCloneBuffer& buffer = aCloneReadInfo.mCloneBuffer;
|
||||||
|
|
||||||
|
if (!buffer.data()) {
|
||||||
*aValue = JSVAL_VOID;
|
*aValue = JSVAL_VOID;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
JSAutoRequest ar(aCx);
|
JSAutoRequest ar(aCx);
|
||||||
|
|
||||||
return aBuffer.read(aCx, aValue, aCallbacks, aClosure);
|
JSStructuredCloneCallbacks callbacks = {
|
||||||
|
IDBObjectStore::StructuredCloneReadCallback,
|
||||||
|
nsnull,
|
||||||
|
nsnull
|
||||||
|
};
|
||||||
|
|
||||||
|
return buffer.read(aCx, aValue, &callbacks, &aCloneReadInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
// static
|
// static
|
||||||
bool
|
bool
|
||||||
IDBObjectStore::SerializeValue(JSContext* aCx,
|
IDBObjectStore::SerializeValue(JSContext* aCx,
|
||||||
JSAutoStructuredCloneBuffer& aBuffer,
|
StructuredCloneWriteInfo& aCloneWriteInfo,
|
||||||
jsval aValue,
|
jsval aValue)
|
||||||
JSStructuredCloneCallbacks* aCallbacks,
|
|
||||||
void* aClosure)
|
|
||||||
{
|
{
|
||||||
NS_ASSERTION(NS_IsMainThread(),
|
NS_ASSERTION(NS_IsMainThread(),
|
||||||
"Should only be serializing on the main thread!");
|
"Should only be serializing on the main thread!");
|
||||||
|
@ -859,30 +874,264 @@ IDBObjectStore::SerializeValue(JSContext* aCx,
|
||||||
|
|
||||||
JSAutoRequest ar(aCx);
|
JSAutoRequest ar(aCx);
|
||||||
|
|
||||||
return aBuffer.write(aCx, aValue, aCallbacks, aClosure);
|
JSStructuredCloneCallbacks callbacks = {
|
||||||
|
nsnull,
|
||||||
|
StructuredCloneWriteCallback,
|
||||||
|
nsnull
|
||||||
|
};
|
||||||
|
|
||||||
|
JSAutoStructuredCloneBuffer& buffer = aCloneWriteInfo.mCloneBuffer;
|
||||||
|
|
||||||
|
return buffer.write(aCx, aValue, &callbacks, &aCloneWriteInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline PRUint32
|
||||||
|
SwapBytes(PRUint32 u)
|
||||||
|
{
|
||||||
|
#ifdef IS_BIG_ENDIAN
|
||||||
|
return ((u & 0x000000ffU) << 24) |
|
||||||
|
((u & 0x0000ff00U) << 8) |
|
||||||
|
((u & 0x00ff0000U) >> 8) |
|
||||||
|
((u & 0xff000000U) >> 24);
|
||||||
|
#else
|
||||||
|
return u;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline jsdouble
|
static inline jsdouble
|
||||||
SwapBytes(PRUint64 u)
|
SwapBytes(PRUint64 u)
|
||||||
{
|
{
|
||||||
#ifdef IS_BIG_ENDIAN
|
#ifdef IS_BIG_ENDIAN
|
||||||
return ((u & 0x00000000000000ffLLU) << 56) |
|
return ((u & 0x00000000000000ffLLU) << 56) |
|
||||||
((u & 0x000000000000ff00LLU) << 40) |
|
((u & 0x000000000000ff00LLU) << 40) |
|
||||||
((u & 0x0000000000ff0000LLU) << 24) |
|
((u & 0x0000000000ff0000LLU) << 24) |
|
||||||
((u & 0x00000000ff000000LLU) << 8) |
|
((u & 0x00000000ff000000LLU) << 8) |
|
||||||
((u & 0x000000ff00000000LLU) >> 8) |
|
((u & 0x000000ff00000000LLU) >> 8) |
|
||||||
((u & 0x0000ff0000000000LLU) >> 24) |
|
((u & 0x0000ff0000000000LLU) >> 24) |
|
||||||
((u & 0x00ff000000000000LLU) >> 40) |
|
((u & 0x00ff000000000000LLU) >> 40) |
|
||||||
((u & 0xff00000000000000LLU) >> 56);
|
((u & 0xff00000000000000LLU) >> 56);
|
||||||
#else
|
#else
|
||||||
return jsdouble(u);
|
return jsdouble(u);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline bool
|
||||||
|
StructuredCloneReadString(JSStructuredCloneReader* aReader,
|
||||||
|
nsCString& aString)
|
||||||
|
{
|
||||||
|
PRUint32 length;
|
||||||
|
if (!JS_ReadBytes(aReader, &length, sizeof(PRUint32))) {
|
||||||
|
NS_WARNING("Failed to read length!");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
length = SwapBytes(length);
|
||||||
|
|
||||||
|
if (!EnsureStringLength(aString, length)) {
|
||||||
|
NS_WARNING("Out of memory?");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
char* buffer = aString.BeginWriting();
|
||||||
|
|
||||||
|
if (!JS_ReadBytes(aReader, buffer, length)) {
|
||||||
|
NS_WARNING("Failed to read type!");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
JSObject*
|
||||||
|
IDBObjectStore::StructuredCloneReadCallback(JSContext* aCx,
|
||||||
|
JSStructuredCloneReader* aReader,
|
||||||
|
uint32 aTag,
|
||||||
|
uint32 aData,
|
||||||
|
void* aClosure)
|
||||||
|
{
|
||||||
|
if (aTag == SCTAG_DOM_BLOB || aTag == SCTAG_DOM_FILE) {
|
||||||
|
StructuredCloneReadInfo* cloneReadInfo =
|
||||||
|
reinterpret_cast<StructuredCloneReadInfo*>(aClosure);
|
||||||
|
|
||||||
|
NS_ASSERTION(aData < cloneReadInfo->mFileInfos.Length(),
|
||||||
|
"Bad blob index!");
|
||||||
|
|
||||||
|
nsRefPtr<FileInfo> fileInfo = cloneReadInfo->mFileInfos[aData];
|
||||||
|
nsRefPtr<FileManager> fileManager = fileInfo->Manager();
|
||||||
|
nsCOMPtr<nsIFile> directory = fileManager->GetDirectory();
|
||||||
|
if (!directory) {
|
||||||
|
return nsnull;
|
||||||
|
}
|
||||||
|
|
||||||
|
nsCOMPtr<nsIFile> nativeFile =
|
||||||
|
fileManager->GetFileForId(directory, fileInfo->Id());
|
||||||
|
if (!nativeFile) {
|
||||||
|
return nsnull;
|
||||||
|
}
|
||||||
|
|
||||||
|
PRUint64 size;
|
||||||
|
if (!JS_ReadBytes(aReader, &size, sizeof(PRUint64))) {
|
||||||
|
NS_WARNING("Failed to read size!");
|
||||||
|
return nsnull;
|
||||||
|
}
|
||||||
|
size = SwapBytes(size);
|
||||||
|
|
||||||
|
nsCString type;
|
||||||
|
if (!StructuredCloneReadString(aReader, type)) {
|
||||||
|
return nsnull;
|
||||||
|
}
|
||||||
|
NS_ConvertUTF8toUTF16 convType(type);
|
||||||
|
|
||||||
|
if (aTag == SCTAG_DOM_BLOB) {
|
||||||
|
nsCOMPtr<nsIDOMBlob> blob = new nsDOMFileFile(convType, size,
|
||||||
|
nativeFile, fileInfo);
|
||||||
|
|
||||||
|
jsval wrappedBlob;
|
||||||
|
nsresult rv =
|
||||||
|
nsContentUtils::WrapNative(aCx, JS_GetGlobalForScopeChain(aCx), blob,
|
||||||
|
&NS_GET_IID(nsIDOMBlob), &wrappedBlob);
|
||||||
|
if (NS_FAILED(rv)) {
|
||||||
|
NS_WARNING("Failed to wrap native!");
|
||||||
|
return nsnull;
|
||||||
|
}
|
||||||
|
|
||||||
|
return JSVAL_TO_OBJECT(wrappedBlob);
|
||||||
|
}
|
||||||
|
|
||||||
|
nsCString name;
|
||||||
|
if (!StructuredCloneReadString(aReader, name)) {
|
||||||
|
return nsnull;
|
||||||
|
}
|
||||||
|
NS_ConvertUTF8toUTF16 convName(name);
|
||||||
|
|
||||||
|
nsCOMPtr<nsIDOMFile> file = new nsDOMFileFile(convName, convType, size,
|
||||||
|
nativeFile, fileInfo);
|
||||||
|
|
||||||
|
jsval wrappedFile;
|
||||||
|
nsresult rv =
|
||||||
|
nsContentUtils::WrapNative(aCx, JS_GetGlobalForScopeChain(aCx), file,
|
||||||
|
&NS_GET_IID(nsIDOMFile), &wrappedFile);
|
||||||
|
if (NS_FAILED(rv)) {
|
||||||
|
NS_WARNING("Failed to wrap native!");
|
||||||
|
return nsnull;
|
||||||
|
}
|
||||||
|
|
||||||
|
return JSVAL_TO_OBJECT(wrappedFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
const JSStructuredCloneCallbacks* runtimeCallbacks =
|
||||||
|
aCx->runtime->structuredCloneCallbacks;
|
||||||
|
|
||||||
|
if (runtimeCallbacks) {
|
||||||
|
return runtimeCallbacks->read(aCx, aReader, aTag, aData, nsnull);
|
||||||
|
}
|
||||||
|
|
||||||
|
return nsnull;
|
||||||
|
}
|
||||||
|
|
||||||
|
JSBool
|
||||||
|
IDBObjectStore::StructuredCloneWriteCallback(JSContext* aCx,
|
||||||
|
JSStructuredCloneWriter* aWriter,
|
||||||
|
JSObject* aObj,
|
||||||
|
void* aClosure)
|
||||||
|
{
|
||||||
|
StructuredCloneWriteInfo* cloneWriteInfo =
|
||||||
|
reinterpret_cast<StructuredCloneWriteInfo*>(aClosure);
|
||||||
|
|
||||||
|
if (JS_GET_CLASS(aCx, aObj) == &gDummyPropClass) {
|
||||||
|
NS_ASSERTION(cloneWriteInfo->mOffsetToKeyProp == 0,
|
||||||
|
"We should not have been here before!");
|
||||||
|
cloneWriteInfo->mOffsetToKeyProp = js_GetSCOffset(aWriter);
|
||||||
|
|
||||||
|
PRUint64 value = 0;
|
||||||
|
return JS_WriteBytes(aWriter, &value, sizeof(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
nsCOMPtr<nsIXPConnectWrappedNative> wrappedNative;
|
||||||
|
nsContentUtils::XPConnect()->
|
||||||
|
GetWrappedNativeOfJSObject(aCx, aObj, getter_AddRefs(wrappedNative));
|
||||||
|
|
||||||
|
if (wrappedNative) {
|
||||||
|
nsISupports* supports = wrappedNative->Native();
|
||||||
|
|
||||||
|
nsCOMPtr<nsIDOMBlob> blob = do_QueryInterface(supports);
|
||||||
|
if (blob) {
|
||||||
|
nsCOMPtr<nsIDOMFile> file = do_QueryInterface(blob);
|
||||||
|
|
||||||
|
PRUint64 size;
|
||||||
|
if (NS_FAILED(blob->GetSize(&size))) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
size = SwapBytes(size);
|
||||||
|
|
||||||
|
nsString type;
|
||||||
|
if (NS_FAILED(blob->GetType(type))) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
NS_ConvertUTF16toUTF8 convType(type);
|
||||||
|
PRUint32 convTypeLength = SwapBytes(convType.Length());
|
||||||
|
|
||||||
|
if (!JS_WriteUint32Pair(aWriter, file ? SCTAG_DOM_FILE : SCTAG_DOM_BLOB,
|
||||||
|
cloneWriteInfo->mBlobs.Length()) ||
|
||||||
|
!JS_WriteBytes(aWriter, &size, sizeof(PRUint64)) ||
|
||||||
|
!JS_WriteBytes(aWriter, &convTypeLength, sizeof(PRUint32)) ||
|
||||||
|
!JS_WriteBytes(aWriter, convType.get(), convType.Length())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (file) {
|
||||||
|
nsString name;
|
||||||
|
if (NS_FAILED(file->GetName(name))) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
NS_ConvertUTF16toUTF8 convName(name);
|
||||||
|
PRUint32 convNameLength = SwapBytes(convName.Length());
|
||||||
|
|
||||||
|
if (!JS_WriteBytes(aWriter, &convNameLength, sizeof(PRUint32)) ||
|
||||||
|
!JS_WriteBytes(aWriter, convName.get(), convName.Length())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cloneWriteInfo->mBlobs.AppendElement(blob);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// try using the runtime callbacks
|
||||||
|
const JSStructuredCloneCallbacks* runtimeCallbacks =
|
||||||
|
aCx->runtime->structuredCloneCallbacks;
|
||||||
|
if (runtimeCallbacks) {
|
||||||
|
return runtimeCallbacks->write(aCx, aWriter, aObj, nsnull);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
nsresult
|
nsresult
|
||||||
IDBObjectStore::ModifyValueForNewKey(JSAutoStructuredCloneBuffer& aBuffer,
|
IDBObjectStore::ConvertFileIdsToArray(const nsAString& aFileIds,
|
||||||
Key& aKey,
|
nsTArray<PRInt64>& aResult)
|
||||||
PRUint64 aOffsetToKeyProp)
|
{
|
||||||
|
nsCharSeparatedTokenizerTemplate<IgnoreWhitespace> tokenizer(aFileIds, ' ');
|
||||||
|
|
||||||
|
while (tokenizer.hasMoreTokens()) {
|
||||||
|
nsString token(tokenizer.nextToken());
|
||||||
|
|
||||||
|
NS_ASSERTION(!token.IsEmpty(), "Should be a valid id!");
|
||||||
|
|
||||||
|
nsresult rv;
|
||||||
|
PRInt32 id = token.ToInteger(&rv);
|
||||||
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
|
PRInt64* element = aResult.AppendElement();
|
||||||
|
*element = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
nsresult
|
||||||
|
IDBObjectStore::ModifyValueForNewKey(StructuredCloneWriteInfo& aCloneWriteInfo,
|
||||||
|
Key& aKey)
|
||||||
{
|
{
|
||||||
NS_ASSERTION(IsAutoIncrement() && aKey.IsInteger(), "Don't call me!");
|
NS_ASSERTION(IsAutoIncrement() && aKey.IsInteger(), "Don't call me!");
|
||||||
NS_ASSERTION(!NS_IsMainThread(), "Wrong thread");
|
NS_ASSERTION(!NS_IsMainThread(), "Wrong thread");
|
||||||
|
@ -893,9 +1142,12 @@ IDBObjectStore::ModifyValueForNewKey(JSAutoStructuredCloneBuffer& aBuffer,
|
||||||
PRUint64 u;
|
PRUint64 u;
|
||||||
} pun;
|
} pun;
|
||||||
|
|
||||||
pun.d = SwapBytes(aKey.ToInteger());
|
pun.d = SwapBytes(static_cast<PRUint64>(aKey.ToInteger()));
|
||||||
|
|
||||||
memcpy((char*)aBuffer.data() + aOffsetToKeyProp, &pun.u, sizeof(PRUint64));
|
JSAutoStructuredCloneBuffer& buffer = aCloneWriteInfo.mCloneBuffer;
|
||||||
|
PRUint64 offsetToKeyProp = aCloneWriteInfo.mOffsetToKeyProp;
|
||||||
|
|
||||||
|
memcpy((char*)buffer.data() + offsetToKeyProp, &pun.u, sizeof(PRUint64));
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -915,10 +1167,9 @@ nsresult
|
||||||
IDBObjectStore::GetAddInfo(JSContext* aCx,
|
IDBObjectStore::GetAddInfo(JSContext* aCx,
|
||||||
jsval aValue,
|
jsval aValue,
|
||||||
jsval aKeyVal,
|
jsval aKeyVal,
|
||||||
JSAutoStructuredCloneBuffer& aCloneBuffer,
|
StructuredCloneWriteInfo& aCloneWriteInfo,
|
||||||
Key& aKey,
|
Key& aKey,
|
||||||
nsTArray<IndexUpdateInfo>& aUpdateInfoArray,
|
nsTArray<IndexUpdateInfo>& aUpdateInfoArray)
|
||||||
PRUint64* aOffsetToKeyProp)
|
|
||||||
{
|
{
|
||||||
nsresult rv;
|
nsresult rv;
|
||||||
|
|
||||||
|
@ -1066,18 +1317,12 @@ IDBObjectStore::GetAddInfo(JSContext* aCx,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
JSStructuredCloneCallbacks callbacks = {
|
aCloneWriteInfo.mOffsetToKeyProp = 0;
|
||||||
nsnull,
|
|
||||||
StructuredCloneWriteDummyProp,
|
|
||||||
nsnull
|
|
||||||
};
|
|
||||||
*aOffsetToKeyProp = 0;
|
|
||||||
|
|
||||||
// We guard on rv being a success because we need to run the property
|
// We guard on rv being a success because we need to run the property
|
||||||
// deletion code below even if we should not be serializing the value
|
// deletion code below even if we should not be serializing the value
|
||||||
if (NS_SUCCEEDED(rv) &&
|
if (NS_SUCCEEDED(rv) &&
|
||||||
!IDBObjectStore::SerializeValue(aCx, aCloneBuffer, aValue, &callbacks,
|
!IDBObjectStore::SerializeValue(aCx, aCloneWriteInfo, aValue)) {
|
||||||
aOffsetToKeyProp)) {
|
|
||||||
rv = NS_ERROR_DOM_DATA_CLONE_ERR;
|
rv = NS_ERROR_DOM_DATA_CLONE_ERR;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1118,13 +1363,12 @@ IDBObjectStore::AddOrPut(const jsval& aValue,
|
||||||
|
|
||||||
jsval keyval = (aOptionalArgCount >= 1) ? aKey : JSVAL_VOID;
|
jsval keyval = (aOptionalArgCount >= 1) ? aKey : JSVAL_VOID;
|
||||||
|
|
||||||
JSAutoStructuredCloneBuffer cloneBuffer;
|
StructuredCloneWriteInfo cloneWriteInfo;
|
||||||
Key key;
|
Key key;
|
||||||
nsTArray<IndexUpdateInfo> updateInfo;
|
nsTArray<IndexUpdateInfo> updateInfo;
|
||||||
PRUint64 offset;
|
|
||||||
|
|
||||||
nsresult rv =
|
nsresult rv = GetAddInfo(aCx, aValue, keyval, cloneWriteInfo, key,
|
||||||
GetAddInfo(aCx, aValue, keyval, cloneBuffer, key, updateInfo, &offset);
|
updateInfo);
|
||||||
if (NS_FAILED(rv)) {
|
if (NS_FAILED(rv)) {
|
||||||
return rv;
|
return rv;
|
||||||
}
|
}
|
||||||
|
@ -1138,8 +1382,8 @@ IDBObjectStore::AddOrPut(const jsval& aValue,
|
||||||
NS_ENSURE_TRUE(request, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
|
NS_ENSURE_TRUE(request, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
|
||||||
|
|
||||||
nsRefPtr<AddHelper> helper =
|
nsRefPtr<AddHelper> helper =
|
||||||
new AddHelper(mTransaction, request, this, cloneBuffer, key, aOverwrite,
|
new AddHelper(mTransaction, request, this, cloneWriteInfo, key, aOverwrite,
|
||||||
updateInfo, offset);
|
updateInfo);
|
||||||
|
|
||||||
rv = helper->DispatchToTransactionPool();
|
rv = helper->DispatchToTransactionPool();
|
||||||
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
|
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
|
||||||
|
@ -1688,6 +1932,31 @@ IDBObjectStore::Count(const jsval& aKey,
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline nsresult
|
||||||
|
CopyData(nsIInputStream* aStream, quota_FILE* aFile)
|
||||||
|
{
|
||||||
|
do {
|
||||||
|
char copyBuffer[FILE_COPY_BUFFER_SIZE];
|
||||||
|
|
||||||
|
PRUint32 numRead;
|
||||||
|
nsresult rv = aStream->Read(copyBuffer, FILE_COPY_BUFFER_SIZE, &numRead);
|
||||||
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
|
if (numRead <= 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t numWrite = sqlite3_quota_fwrite(copyBuffer, 1, numRead, aFile);
|
||||||
|
NS_ENSURE_TRUE(numWrite == numRead, NS_ERROR_FAILURE);
|
||||||
|
} while (true);
|
||||||
|
|
||||||
|
// Flush and sync
|
||||||
|
NS_ENSURE_TRUE(sqlite3_quota_fflush(aFile, 1) == 0,
|
||||||
|
NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
|
||||||
|
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
nsresult
|
nsresult
|
||||||
AddHelper::DoDatabaseWork(mozIStorageConnection* aConnection)
|
AddHelper::DoDatabaseWork(mozIStorageConnection* aConnection)
|
||||||
{
|
{
|
||||||
|
@ -1755,6 +2024,64 @@ AddHelper::DoDatabaseWork(mozIStorageConnection* aConnection)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
nsRefPtr<FileManager> fileManager = mDatabase->Manager();
|
||||||
|
nsCOMPtr<nsIFile> directory = fileManager->GetDirectory();
|
||||||
|
NS_ENSURE_TRUE(directory, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
|
||||||
|
|
||||||
|
nsAutoString fileIds;
|
||||||
|
|
||||||
|
for (PRUint32 index = 0; index < mCloneWriteInfo.mBlobs.Length(); index++) {
|
||||||
|
nsCOMPtr<nsIDOMBlob>& domBlob = mCloneWriteInfo.mBlobs[index];
|
||||||
|
|
||||||
|
PRInt64 id = -1;
|
||||||
|
|
||||||
|
// Check if it is a blob created from this db or the blob was already
|
||||||
|
// stored in this db
|
||||||
|
nsRefPtr<FileInfo> fileInfo = domBlob->GetFileInfo(fileManager);
|
||||||
|
if (fileInfo) {
|
||||||
|
id = fileInfo->Id();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (id == -1) {
|
||||||
|
fileInfo = fileManager->GetNewFileInfo();
|
||||||
|
NS_ENSURE_TRUE(fileInfo, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
|
||||||
|
|
||||||
|
id = fileInfo->Id();
|
||||||
|
|
||||||
|
mTransaction->OnNewFileInfo(fileInfo);
|
||||||
|
|
||||||
|
// Copy it
|
||||||
|
nsCOMPtr<nsIInputStream> inputStream;
|
||||||
|
rv = domBlob->GetInternalStream(getter_AddRefs(inputStream));
|
||||||
|
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
|
||||||
|
|
||||||
|
nsCOMPtr<nsIFile> nativeFile = fileManager->GetFileForId(directory, id);
|
||||||
|
NS_ENSURE_TRUE(nativeFile, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
|
||||||
|
|
||||||
|
nsString nativeFilePath;
|
||||||
|
rv = nativeFile->GetPath(nativeFilePath);
|
||||||
|
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
|
||||||
|
|
||||||
|
quota_FILE* file =
|
||||||
|
sqlite3_quota_fopen(NS_ConvertUTF16toUTF8(nativeFilePath).get(), "wb");
|
||||||
|
NS_ENSURE_TRUE(file, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
|
||||||
|
|
||||||
|
rv = CopyData(inputStream, file);
|
||||||
|
|
||||||
|
NS_ENSURE_TRUE(sqlite3_quota_fclose(file) == 0,
|
||||||
|
NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
|
||||||
|
|
||||||
|
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
|
||||||
|
|
||||||
|
domBlob->AddFileInfo(fileInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (index) {
|
||||||
|
fileIds.Append(NS_LITERAL_STRING(" "));
|
||||||
|
}
|
||||||
|
fileIds.AppendInt(id);
|
||||||
|
}
|
||||||
|
|
||||||
// Now we add it to the database (or update, depending on our variables).
|
// Now we add it to the database (or update, depending on our variables).
|
||||||
stmt = mTransaction->AddStatement(true, mayOverwrite, autoIncrement);
|
stmt = mTransaction->AddStatement(true, mayOverwrite, autoIncrement);
|
||||||
NS_ENSURE_TRUE(stmt, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
|
NS_ENSURE_TRUE(stmt, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
|
||||||
|
@ -1793,8 +2120,8 @@ AddHelper::DoDatabaseWork(mozIStorageConnection* aConnection)
|
||||||
else {
|
else {
|
||||||
// Compress the bytes before adding into the database.
|
// Compress the bytes before adding into the database.
|
||||||
const char* uncompressed =
|
const char* uncompressed =
|
||||||
reinterpret_cast<const char*>(mCloneBuffer.data());
|
reinterpret_cast<const char*>(mCloneWriteInfo.mCloneBuffer.data());
|
||||||
size_t uncompressedLength = mCloneBuffer.nbytes();
|
size_t uncompressedLength = mCloneWriteInfo.mCloneBuffer.nbytes();
|
||||||
|
|
||||||
size_t compressedLength = snappy::MaxCompressedLength(uncompressedLength);
|
size_t compressedLength = snappy::MaxCompressedLength(uncompressedLength);
|
||||||
compressed = new char[compressedLength];
|
compressed = new char[compressedLength];
|
||||||
|
@ -1809,6 +2136,13 @@ AddHelper::DoDatabaseWork(mozIStorageConnection* aConnection)
|
||||||
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
|
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (fileIds.IsEmpty()) {
|
||||||
|
rv = stmt->BindNullByName(NS_LITERAL_CSTRING("file_ids"));
|
||||||
|
} else {
|
||||||
|
rv = stmt->BindStringByName(NS_LITERAL_CSTRING("file_ids"), fileIds);
|
||||||
|
}
|
||||||
|
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
|
||||||
|
|
||||||
rv = stmt->Execute();
|
rv = stmt->Execute();
|
||||||
if (NS_FAILED(rv)) {
|
if (NS_FAILED(rv)) {
|
||||||
if (mayOverwrite && rv == NS_ERROR_STORAGE_CONSTRAINT) {
|
if (mayOverwrite && rv == NS_ERROR_STORAGE_CONSTRAINT) {
|
||||||
|
@ -1835,6 +2169,13 @@ AddHelper::DoDatabaseWork(mozIStorageConnection* aConnection)
|
||||||
rv = stmt->BindBlobByName(data, dataBuffer, dataBufferLength);
|
rv = stmt->BindBlobByName(data, dataBuffer, dataBufferLength);
|
||||||
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
|
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
|
||||||
|
|
||||||
|
if (fileIds.IsEmpty()) {
|
||||||
|
rv = stmt->BindNullByName(NS_LITERAL_CSTRING("file_ids"));
|
||||||
|
} else {
|
||||||
|
rv = stmt->BindStringByName(NS_LITERAL_CSTRING("file_ids"), fileIds);
|
||||||
|
}
|
||||||
|
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
|
||||||
|
|
||||||
rv = stmt->Execute();
|
rv = stmt->Execute();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1867,8 +2208,7 @@ AddHelper::DoDatabaseWork(mozIStorageConnection* aConnection)
|
||||||
// Special case where someone put an object into an autoIncrement'ing
|
// Special case where someone put an object into an autoIncrement'ing
|
||||||
// objectStore with no key in its keyPath set. We needed to figure out
|
// objectStore with no key in its keyPath set. We needed to figure out
|
||||||
// which row id we would get above before we could set that properly.
|
// which row id we would get above before we could set that properly.
|
||||||
rv = mObjectStore->ModifyValueForNewKey(mCloneBuffer, mKey,
|
rv = mObjectStore->ModifyValueForNewKey(mCloneWriteInfo, mKey);
|
||||||
mOffsetToKeyProp);
|
|
||||||
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
|
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
|
||||||
|
|
||||||
scoper.Abandon();
|
scoper.Abandon();
|
||||||
|
@ -1889,8 +2229,8 @@ AddHelper::DoDatabaseWork(mozIStorageConnection* aConnection)
|
||||||
NS_ASSERTION(!dataBuffer && !dataBufferLength, "These should be unset!");
|
NS_ASSERTION(!dataBuffer && !dataBufferLength, "These should be unset!");
|
||||||
|
|
||||||
const char* uncompressed =
|
const char* uncompressed =
|
||||||
reinterpret_cast<const char*>(mCloneBuffer.data());
|
reinterpret_cast<const char*>(mCloneWriteInfo.mCloneBuffer.data());
|
||||||
size_t uncompressedLength = mCloneBuffer.nbytes();
|
size_t uncompressedLength = mCloneWriteInfo.mCloneBuffer.nbytes();
|
||||||
|
|
||||||
size_t compressedLength =
|
size_t compressedLength =
|
||||||
snappy::MaxCompressedLength(uncompressedLength);
|
snappy::MaxCompressedLength(uncompressedLength);
|
||||||
|
@ -1905,6 +2245,13 @@ AddHelper::DoDatabaseWork(mozIStorageConnection* aConnection)
|
||||||
rv = stmt->BindBlobByName(data, dataBuffer, dataBufferLength);
|
rv = stmt->BindBlobByName(data, dataBuffer, dataBufferLength);
|
||||||
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
|
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
|
||||||
|
|
||||||
|
if (fileIds.IsEmpty()) {
|
||||||
|
rv = stmt->BindNullByName(NS_LITERAL_CSTRING("file_ids"));
|
||||||
|
} else {
|
||||||
|
rv = stmt->BindStringByName(NS_LITERAL_CSTRING("file_ids"), fileIds);
|
||||||
|
}
|
||||||
|
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
|
||||||
|
|
||||||
rv = stmt->Execute();
|
rv = stmt->Execute();
|
||||||
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
|
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
|
||||||
}
|
}
|
||||||
|
@ -1931,7 +2278,7 @@ AddHelper::GetSuccessResult(JSContext* aCx,
|
||||||
{
|
{
|
||||||
NS_ASSERTION(!mKey.IsUnset(), "Badness!");
|
NS_ASSERTION(!mKey.IsUnset(), "Badness!");
|
||||||
|
|
||||||
mCloneBuffer.clear();
|
mCloneWriteInfo.mCloneBuffer.clear();
|
||||||
|
|
||||||
return mKey.ToJSVal(aCx, aVal);
|
return mKey.ToJSVal(aCx, aVal);
|
||||||
}
|
}
|
||||||
|
@ -1959,7 +2306,7 @@ GetHelper::DoDatabaseWork(mozIStorageConnection* /* aConnection */)
|
||||||
|
|
||||||
NS_NAMED_LITERAL_CSTRING(osid, "osid");
|
NS_NAMED_LITERAL_CSTRING(osid, "osid");
|
||||||
|
|
||||||
nsCString query = NS_LITERAL_CSTRING("SELECT data FROM ") + table +
|
nsCString query = NS_LITERAL_CSTRING("SELECT data, file_ids FROM ") + table +
|
||||||
NS_LITERAL_CSTRING(" WHERE object_store_id = :") + osid +
|
NS_LITERAL_CSTRING(" WHERE object_store_id = :") + osid +
|
||||||
keyRangeClause + NS_LITERAL_CSTRING(" LIMIT 1");
|
keyRangeClause + NS_LITERAL_CSTRING(" LIMIT 1");
|
||||||
|
|
||||||
|
@ -1979,8 +2326,8 @@ GetHelper::DoDatabaseWork(mozIStorageConnection* /* aConnection */)
|
||||||
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
|
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
|
||||||
|
|
||||||
if (hasResult) {
|
if (hasResult) {
|
||||||
rv = IDBObjectStore::GetStructuredCloneDataFromStatement(stmt, 0,
|
rv = IDBObjectStore::GetStructuredCloneReadInfoFromStatement(stmt, 0, 1,
|
||||||
mCloneBuffer);
|
mDatabase->Manager(), mCloneReadInfo);
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1991,9 +2338,9 @@ nsresult
|
||||||
GetHelper::GetSuccessResult(JSContext* aCx,
|
GetHelper::GetSuccessResult(JSContext* aCx,
|
||||||
jsval* aVal)
|
jsval* aVal)
|
||||||
{
|
{
|
||||||
bool result = IDBObjectStore::DeserializeValue(aCx, mCloneBuffer, aVal);
|
bool result = IDBObjectStore::DeserializeValue(aCx, mCloneReadInfo, aVal);
|
||||||
|
|
||||||
mCloneBuffer.clear();
|
mCloneReadInfo.mCloneBuffer.clear();
|
||||||
|
|
||||||
NS_ENSURE_TRUE(result, NS_ERROR_FAILURE);
|
NS_ENSURE_TRUE(result, NS_ERROR_FAILURE);
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
|
@ -2121,7 +2468,7 @@ OpenCursorHelper::DoDatabaseWork(mozIStorageConnection* aConnection)
|
||||||
}
|
}
|
||||||
|
|
||||||
nsCString firstQuery = NS_LITERAL_CSTRING("SELECT ") + keyColumn +
|
nsCString firstQuery = NS_LITERAL_CSTRING("SELECT ") + keyColumn +
|
||||||
NS_LITERAL_CSTRING(", data FROM ") + table +
|
NS_LITERAL_CSTRING(", data, file_ids FROM ") + table +
|
||||||
NS_LITERAL_CSTRING(" WHERE object_store_id = :") +
|
NS_LITERAL_CSTRING(" WHERE object_store_id = :") +
|
||||||
id + keyRangeClause + directionClause +
|
id + keyRangeClause + directionClause +
|
||||||
NS_LITERAL_CSTRING(" LIMIT 1");
|
NS_LITERAL_CSTRING(" LIMIT 1");
|
||||||
|
@ -2152,8 +2499,8 @@ OpenCursorHelper::DoDatabaseWork(mozIStorageConnection* aConnection)
|
||||||
rv = mKey.SetFromStatement(stmt, 0);
|
rv = mKey.SetFromStatement(stmt, 0);
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
rv = IDBObjectStore::GetStructuredCloneDataFromStatement(stmt, 1,
|
rv = IDBObjectStore::GetStructuredCloneReadInfoFromStatement(stmt, 1, 2,
|
||||||
mCloneBuffer);
|
mDatabase->Manager(), mCloneReadInfo);
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
// Now we need to make the query to get the next match.
|
// Now we need to make the query to get the next match.
|
||||||
|
@ -2200,13 +2547,13 @@ OpenCursorHelper::DoDatabaseWork(mozIStorageConnection* aConnection)
|
||||||
}
|
}
|
||||||
|
|
||||||
mContinueQuery = NS_LITERAL_CSTRING("SELECT ") + keyColumn +
|
mContinueQuery = NS_LITERAL_CSTRING("SELECT ") + keyColumn +
|
||||||
NS_LITERAL_CSTRING(", data FROM ") + table +
|
NS_LITERAL_CSTRING(", data, file_ids FROM ") + table +
|
||||||
NS_LITERAL_CSTRING(" WHERE object_store_id = :") + id +
|
NS_LITERAL_CSTRING(" WHERE object_store_id = :") + id +
|
||||||
keyRangeClause + directionClause +
|
keyRangeClause + directionClause +
|
||||||
NS_LITERAL_CSTRING(" LIMIT ");
|
NS_LITERAL_CSTRING(" LIMIT ");
|
||||||
|
|
||||||
mContinueToQuery = NS_LITERAL_CSTRING("SELECT ") + keyColumn +
|
mContinueToQuery = NS_LITERAL_CSTRING("SELECT ") + keyColumn +
|
||||||
NS_LITERAL_CSTRING(", data FROM ") + table +
|
NS_LITERAL_CSTRING(", data, file_ids FROM ") + table +
|
||||||
NS_LITERAL_CSTRING(" WHERE object_store_id = :") + id +
|
NS_LITERAL_CSTRING(" WHERE object_store_id = :") + id +
|
||||||
continueToKeyRangeClause + directionClause +
|
continueToKeyRangeClause + directionClause +
|
||||||
NS_LITERAL_CSTRING(" LIMIT ");
|
NS_LITERAL_CSTRING(" LIMIT ");
|
||||||
|
@ -2226,10 +2573,10 @@ OpenCursorHelper::GetSuccessResult(JSContext* aCx,
|
||||||
nsRefPtr<IDBCursor> cursor =
|
nsRefPtr<IDBCursor> cursor =
|
||||||
IDBCursor::Create(mRequest, mTransaction, mObjectStore, mDirection,
|
IDBCursor::Create(mRequest, mTransaction, mObjectStore, mDirection,
|
||||||
mRangeKey, mContinueQuery, mContinueToQuery, mKey,
|
mRangeKey, mContinueQuery, mContinueToQuery, mKey,
|
||||||
mCloneBuffer);
|
mCloneReadInfo);
|
||||||
NS_ENSURE_TRUE(cursor, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
|
NS_ENSURE_TRUE(cursor, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
|
||||||
|
|
||||||
NS_ASSERTION(!mCloneBuffer.data(), "Should have swapped!");
|
NS_ASSERTION(!mCloneReadInfo.mCloneBuffer.data(), "Should have swapped!");
|
||||||
|
|
||||||
return WrapNative(aCx, cursor, aVal);
|
return WrapNative(aCx, cursor, aVal);
|
||||||
}
|
}
|
||||||
|
@ -2391,11 +2738,11 @@ CreateIndexHelper::InsertDataFromObjectStore(mozIStorageConnection* aConnection)
|
||||||
nsCAutoString columns;
|
nsCAutoString columns;
|
||||||
if (mIndex->IsAutoIncrement()) {
|
if (mIndex->IsAutoIncrement()) {
|
||||||
table.AssignLiteral("ai_object_data");
|
table.AssignLiteral("ai_object_data");
|
||||||
columns.AssignLiteral("id, data");
|
columns.AssignLiteral("id, data, file_ids");
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
table.AssignLiteral("object_data");
|
table.AssignLiteral("object_data");
|
||||||
columns.AssignLiteral("id, data, key_value");
|
columns.AssignLiteral("id, data, file_ids, key_value");
|
||||||
}
|
}
|
||||||
|
|
||||||
nsCString query = NS_LITERAL_CSTRING("SELECT ") + columns +
|
nsCString query = NS_LITERAL_CSTRING("SELECT ") + columns +
|
||||||
|
@ -2435,12 +2782,21 @@ CreateIndexHelper::InsertDataFromObjectStore(mozIStorageConnection* aConnection)
|
||||||
JSAutoRequest ar(cx);
|
JSAutoRequest ar(cx);
|
||||||
|
|
||||||
do {
|
do {
|
||||||
JSAutoStructuredCloneBuffer buffer;
|
StructuredCloneReadInfo cloneReadInfo;
|
||||||
rv = IDBObjectStore::GetStructuredCloneDataFromStatement(stmt, 1, buffer);
|
rv = IDBObjectStore::GetStructuredCloneReadInfoFromStatement(stmt, 1, 2,
|
||||||
|
mDatabase->Manager(), cloneReadInfo);
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
|
JSAutoStructuredCloneBuffer& buffer = cloneReadInfo.mCloneBuffer;
|
||||||
|
|
||||||
|
JSStructuredCloneCallbacks callbacks = {
|
||||||
|
IDBObjectStore::StructuredCloneReadCallback,
|
||||||
|
nsnull,
|
||||||
|
nsnull
|
||||||
|
};
|
||||||
|
|
||||||
jsval clone;
|
jsval clone;
|
||||||
if (!buffer.read(cx, &clone)) {
|
if (!buffer.read(cx, &clone, &callbacks, &cloneReadInfo)) {
|
||||||
NS_WARNING("Failed to deserialize structured clone data!");
|
NS_WARNING("Failed to deserialize structured clone data!");
|
||||||
return NS_ERROR_DOM_DATA_CLONE_ERR;
|
return NS_ERROR_DOM_DATA_CLONE_ERR;
|
||||||
}
|
}
|
||||||
|
@ -2458,7 +2814,7 @@ CreateIndexHelper::InsertDataFromObjectStore(mozIStorageConnection* aConnection)
|
||||||
|
|
||||||
Key key;
|
Key key;
|
||||||
if (!mIndex->IsAutoIncrement()) {
|
if (!mIndex->IsAutoIncrement()) {
|
||||||
rv = key.SetFromStatement(stmt, 2);
|
rv = key.SetFromStatement(stmt, 3);
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
@ -2550,12 +2906,12 @@ GetAllHelper::DoDatabaseWork(mozIStorageConnection* aConnection)
|
||||||
limitClause.AppendInt(mLimit);
|
limitClause.AppendInt(mLimit);
|
||||||
}
|
}
|
||||||
|
|
||||||
nsCString query = NS_LITERAL_CSTRING("SELECT data FROM ") + table +
|
nsCString query = NS_LITERAL_CSTRING("SELECT data, file_ids FROM ") + table +
|
||||||
NS_LITERAL_CSTRING(" WHERE object_store_id = :") + osid +
|
NS_LITERAL_CSTRING(" WHERE object_store_id = :") + osid +
|
||||||
keyRangeClause + NS_LITERAL_CSTRING(" ORDER BY ") +
|
keyRangeClause + NS_LITERAL_CSTRING(" ORDER BY ") +
|
||||||
keyColumn + NS_LITERAL_CSTRING(" ASC") + limitClause;
|
keyColumn + NS_LITERAL_CSTRING(" ASC") + limitClause;
|
||||||
|
|
||||||
mCloneBuffers.SetCapacity(50);
|
mCloneReadInfos.SetCapacity(50);
|
||||||
|
|
||||||
nsCOMPtr<mozIStorageStatement> stmt = mTransaction->GetCachedStatement(query);
|
nsCOMPtr<mozIStorageStatement> stmt = mTransaction->GetCachedStatement(query);
|
||||||
NS_ENSURE_TRUE(stmt, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
|
NS_ENSURE_TRUE(stmt, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
|
||||||
|
@ -2578,17 +2934,18 @@ GetAllHelper::DoDatabaseWork(mozIStorageConnection* aConnection)
|
||||||
|
|
||||||
bool hasResult;
|
bool hasResult;
|
||||||
while (NS_SUCCEEDED((rv = stmt->ExecuteStep(&hasResult))) && hasResult) {
|
while (NS_SUCCEEDED((rv = stmt->ExecuteStep(&hasResult))) && hasResult) {
|
||||||
if (mCloneBuffers.Capacity() == mCloneBuffers.Length()) {
|
if (mCloneReadInfos.Capacity() == mCloneReadInfos.Length()) {
|
||||||
if (!mCloneBuffers.SetCapacity(mCloneBuffers.Capacity() * 2)) {
|
if (!mCloneReadInfos.SetCapacity(mCloneReadInfos.Capacity() * 2)) {
|
||||||
NS_ERROR("Out of memory!");
|
NS_ERROR("Out of memory!");
|
||||||
return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
|
return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
JSAutoStructuredCloneBuffer* buffer = mCloneBuffers.AppendElement();
|
StructuredCloneReadInfo* readInfo = mCloneReadInfos.AppendElement();
|
||||||
NS_ASSERTION(buffer, "Shouldn't fail if SetCapacity succeeded!");
|
NS_ASSERTION(readInfo, "Shouldn't fail if SetCapacity succeeded!");
|
||||||
|
|
||||||
rv = IDBObjectStore::GetStructuredCloneDataFromStatement(stmt, 0, *buffer);
|
rv = IDBObjectStore::GetStructuredCloneReadInfoFromStatement(stmt, 0, 1,
|
||||||
|
mDatabase->Manager(), *readInfo);
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
}
|
}
|
||||||
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
|
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
|
||||||
|
@ -2600,12 +2957,12 @@ nsresult
|
||||||
GetAllHelper::GetSuccessResult(JSContext* aCx,
|
GetAllHelper::GetSuccessResult(JSContext* aCx,
|
||||||
jsval* aVal)
|
jsval* aVal)
|
||||||
{
|
{
|
||||||
NS_ASSERTION(mCloneBuffers.Length() <= mLimit, "Too many results!");
|
NS_ASSERTION(mCloneReadInfos.Length() <= mLimit, "Too many results!");
|
||||||
|
|
||||||
nsresult rv = ConvertCloneBuffersToArray(aCx, mCloneBuffers, aVal);
|
nsresult rv = ConvertCloneReadInfosToArray(aCx, mCloneReadInfos, aVal);
|
||||||
|
|
||||||
for (PRUint32 index = 0; index < mCloneBuffers.Length(); index++) {
|
for (PRUint32 index = 0; index < mCloneReadInfos.Length(); index++) {
|
||||||
mCloneBuffers[index].clear();
|
mCloneReadInfos[index].mCloneBuffer.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
|
@ -59,6 +59,8 @@ class Key;
|
||||||
struct ObjectStoreInfo;
|
struct ObjectStoreInfo;
|
||||||
struct IndexInfo;
|
struct IndexInfo;
|
||||||
struct IndexUpdateInfo;
|
struct IndexUpdateInfo;
|
||||||
|
struct StructuredCloneReadInfo;
|
||||||
|
struct StructuredCloneWriteInfo;
|
||||||
|
|
||||||
class IDBObjectStore : public nsIIDBObjectStore
|
class IDBObjectStore : public nsIIDBObjectStore
|
||||||
{
|
{
|
||||||
|
@ -94,26 +96,40 @@ public:
|
||||||
const nsTArray<IndexUpdateInfo>& aUpdateInfoArray);
|
const nsTArray<IndexUpdateInfo>& aUpdateInfoArray);
|
||||||
|
|
||||||
static nsresult
|
static nsresult
|
||||||
GetStructuredCloneDataFromStatement(mozIStorageStatement* aStatement,
|
GetStructuredCloneReadInfoFromStatement(mozIStorageStatement* aStatement,
|
||||||
PRUint32 aIndex,
|
PRUint32 aDataIndex,
|
||||||
JSAutoStructuredCloneBuffer& aBuffer);
|
PRUint32 aFileIdsIndex,
|
||||||
|
FileManager* aFileManager,
|
||||||
|
StructuredCloneReadInfo& aInfo);
|
||||||
|
|
||||||
static void
|
static void
|
||||||
ClearStructuredCloneBuffer(JSAutoStructuredCloneBuffer& aBuffer);
|
ClearStructuredCloneBuffer(JSAutoStructuredCloneBuffer& aBuffer);
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
DeserializeValue(JSContext* aCx,
|
DeserializeValue(JSContext* aCx,
|
||||||
JSAutoStructuredCloneBuffer& aBuffer,
|
StructuredCloneReadInfo& aCloneReadInfo,
|
||||||
jsval* aValue,
|
jsval* aValue);
|
||||||
JSStructuredCloneCallbacks* aCallbacks = nsnull,
|
|
||||||
void* aClosure = nsnull);
|
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
SerializeValue(JSContext* aCx,
|
SerializeValue(JSContext* aCx,
|
||||||
JSAutoStructuredCloneBuffer& aBuffer,
|
StructuredCloneWriteInfo& aCloneWriteInfo,
|
||||||
jsval aValue,
|
jsval aValue);
|
||||||
JSStructuredCloneCallbacks* aCallbacks = nsnull,
|
|
||||||
void* aClosure = nsnull);
|
static JSObject*
|
||||||
|
StructuredCloneReadCallback(JSContext* aCx,
|
||||||
|
JSStructuredCloneReader* aReader,
|
||||||
|
uint32 aTag,
|
||||||
|
uint32 aData,
|
||||||
|
void* aClosure);
|
||||||
|
static JSBool
|
||||||
|
StructuredCloneWriteCallback(JSContext* aCx,
|
||||||
|
JSStructuredCloneWriter* aWriter,
|
||||||
|
JSObject* aObj,
|
||||||
|
void* aClosure);
|
||||||
|
|
||||||
|
static nsresult
|
||||||
|
ConvertFileIdsToArray(const nsAString& aFileIds,
|
||||||
|
nsTArray<PRInt64>& aResult);
|
||||||
|
|
||||||
const nsString& Name() const
|
const nsString& Name() const
|
||||||
{
|
{
|
||||||
|
@ -151,9 +167,8 @@ public:
|
||||||
return mTransaction;
|
return mTransaction;
|
||||||
}
|
}
|
||||||
|
|
||||||
nsresult ModifyValueForNewKey(JSAutoStructuredCloneBuffer& aBuffer,
|
nsresult ModifyValueForNewKey(StructuredCloneWriteInfo& aCloneWriteInfo,
|
||||||
Key& aKey,
|
Key& aKey);
|
||||||
PRUint64 aOffsetToKeyProp);
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
IDBObjectStore();
|
IDBObjectStore();
|
||||||
|
@ -162,10 +177,9 @@ protected:
|
||||||
nsresult GetAddInfo(JSContext* aCx,
|
nsresult GetAddInfo(JSContext* aCx,
|
||||||
jsval aValue,
|
jsval aValue,
|
||||||
jsval aKeyVal,
|
jsval aKeyVal,
|
||||||
JSAutoStructuredCloneBuffer& aCloneBuffer,
|
StructuredCloneWriteInfo& aCloneWriteInfo,
|
||||||
Key& aKey,
|
Key& aKey,
|
||||||
nsTArray<IndexUpdateInfo>& aUpdateInfoArray,
|
nsTArray<IndexUpdateInfo>& aUpdateInfoArray);
|
||||||
PRUint64* aOffsetToKeyProp);
|
|
||||||
|
|
||||||
nsresult AddOrPut(const jsval& aValue,
|
nsresult AddOrPut(const jsval& aValue,
|
||||||
const jsval& aKey,
|
const jsval& aKey,
|
||||||
|
|
|
@ -43,6 +43,7 @@
|
||||||
|
|
||||||
#include "mozilla/storage.h"
|
#include "mozilla/storage.h"
|
||||||
#include "nsDOMClassInfoID.h"
|
#include "nsDOMClassInfoID.h"
|
||||||
|
#include "nsDOMLists.h"
|
||||||
#include "nsEventDispatcher.h"
|
#include "nsEventDispatcher.h"
|
||||||
#include "nsPIDOMWindow.h"
|
#include "nsPIDOMWindow.h"
|
||||||
#include "nsProxyRelease.h"
|
#include "nsProxyRelease.h"
|
||||||
|
@ -325,8 +326,21 @@ IDBTransaction::GetOrCreateConnection(mozIStorageConnection** aResult)
|
||||||
IDBFactory::GetConnection(mDatabase->FilePath());
|
IDBFactory::GetConnection(mDatabase->FilePath());
|
||||||
NS_ENSURE_TRUE(connection, NS_ERROR_FAILURE);
|
NS_ENSURE_TRUE(connection, NS_ERROR_FAILURE);
|
||||||
|
|
||||||
|
nsresult rv;
|
||||||
|
|
||||||
|
nsRefPtr<UpdateRefcountFunction> function;
|
||||||
nsCString beginTransaction;
|
nsCString beginTransaction;
|
||||||
if (mMode != nsIIDBTransaction::READ_ONLY) {
|
if (mMode != nsIIDBTransaction::READ_ONLY) {
|
||||||
|
function = new UpdateRefcountFunction(Database()->Manager());
|
||||||
|
NS_ENSURE_TRUE(function, NS_ERROR_OUT_OF_MEMORY);
|
||||||
|
|
||||||
|
rv = function->Init();
|
||||||
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
|
rv = connection->CreateFunction(
|
||||||
|
NS_LITERAL_CSTRING("update_refcount"), 2, function);
|
||||||
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
beginTransaction.AssignLiteral("BEGIN IMMEDIATE TRANSACTION;");
|
beginTransaction.AssignLiteral("BEGIN IMMEDIATE TRANSACTION;");
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
@ -334,13 +348,13 @@ IDBTransaction::GetOrCreateConnection(mozIStorageConnection** aResult)
|
||||||
}
|
}
|
||||||
|
|
||||||
nsCOMPtr<mozIStorageStatement> stmt;
|
nsCOMPtr<mozIStorageStatement> stmt;
|
||||||
nsresult rv = connection->CreateStatement(beginTransaction,
|
rv = connection->CreateStatement(beginTransaction, getter_AddRefs(stmt));
|
||||||
getter_AddRefs(stmt));
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
NS_ENSURE_SUCCESS(rv, false);
|
|
||||||
|
|
||||||
rv = stmt->Execute();
|
rv = stmt->Execute();
|
||||||
NS_ENSURE_SUCCESS(rv, false);
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
|
function.swap(mUpdateFileRefcountFunction);
|
||||||
connection.swap(mConnection);
|
connection.swap(mConnection);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -364,18 +378,19 @@ IDBTransaction::AddStatement(bool aCreate,
|
||||||
if (aCreate) {
|
if (aCreate) {
|
||||||
if (aOverwrite) {
|
if (aOverwrite) {
|
||||||
return GetCachedStatement(
|
return GetCachedStatement(
|
||||||
"INSERT OR FAIL INTO ai_object_data (object_store_id, id, data) "
|
"INSERT OR FAIL INTO ai_object_data (object_store_id, id, data, "
|
||||||
"VALUES (:osid, :key_value, :data)"
|
"file_ids) "
|
||||||
|
"VALUES (:osid, :key_value, :data, :file_ids)"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return GetCachedStatement(
|
return GetCachedStatement(
|
||||||
"INSERT INTO ai_object_data (object_store_id, data) "
|
"INSERT INTO ai_object_data (object_store_id, data, file_ids) "
|
||||||
"VALUES (:osid, :data)"
|
"VALUES (:osid, :data, :file_ids)"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return GetCachedStatement(
|
return GetCachedStatement(
|
||||||
"UPDATE ai_object_data "
|
"UPDATE ai_object_data "
|
||||||
"SET data = :data "
|
"SET data = :data, file_ids = :file_ids "
|
||||||
"WHERE object_store_id = :osid "
|
"WHERE object_store_id = :osid "
|
||||||
"AND id = :key_value"
|
"AND id = :key_value"
|
||||||
);
|
);
|
||||||
|
@ -383,18 +398,19 @@ IDBTransaction::AddStatement(bool aCreate,
|
||||||
if (aCreate) {
|
if (aCreate) {
|
||||||
if (aOverwrite) {
|
if (aOverwrite) {
|
||||||
return GetCachedStatement(
|
return GetCachedStatement(
|
||||||
"INSERT OR FAIL INTO object_data (object_store_id, key_value, data) "
|
"INSERT OR FAIL INTO object_data (object_store_id, key_value, data, "
|
||||||
"VALUES (:osid, :key_value, :data)"
|
"file_ids) "
|
||||||
|
"VALUES (:osid, :key_value, :data, :file_ids)"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return GetCachedStatement(
|
return GetCachedStatement(
|
||||||
"INSERT INTO object_data (object_store_id, key_value, data) "
|
"INSERT INTO object_data (object_store_id, key_value, data, file_ids) "
|
||||||
"VALUES (:osid, :key_value, :data)"
|
"VALUES (:osid, :key_value, :data, :file_ids)"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return GetCachedStatement(
|
return GetCachedStatement(
|
||||||
"UPDATE object_data "
|
"UPDATE object_data "
|
||||||
"SET data = :data "
|
"SET data = :data, file_ids = :file_ids "
|
||||||
"WHERE object_store_id = :osid "
|
"WHERE object_store_id = :osid "
|
||||||
"AND key_value = :key_value"
|
"AND key_value = :key_value"
|
||||||
);
|
);
|
||||||
|
@ -552,6 +568,18 @@ IDBTransaction::GetOrCreateObjectStore(const nsAString& aName,
|
||||||
return retval.forget();
|
return retval.forget();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
IDBTransaction::OnNewFileInfo(FileInfo* aFileInfo)
|
||||||
|
{
|
||||||
|
mCreatedFileInfos.AppendElement(aFileInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
IDBTransaction::ClearCreatedFileInfos()
|
||||||
|
{
|
||||||
|
mCreatedFileInfos.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
NS_IMPL_CYCLE_COLLECTION_CLASS(IDBTransaction)
|
NS_IMPL_CYCLE_COLLECTION_CLASS(IDBTransaction)
|
||||||
|
|
||||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(IDBTransaction,
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(IDBTransaction,
|
||||||
|
@ -827,6 +855,7 @@ CommitHelper::CommitHelper(IDBTransaction* aTransaction,
|
||||||
mHaveMetadata(false)
|
mHaveMetadata(false)
|
||||||
{
|
{
|
||||||
mConnection.swap(aTransaction->mConnection);
|
mConnection.swap(aTransaction->mConnection);
|
||||||
|
mUpdateFileRefcountFunction.swap(aTransaction->mUpdateFileRefcountFunction);
|
||||||
}
|
}
|
||||||
|
|
||||||
CommitHelper::~CommitHelper()
|
CommitHelper::~CommitHelper()
|
||||||
|
@ -843,6 +872,14 @@ CommitHelper::Run()
|
||||||
|
|
||||||
mTransaction->mReadyState = nsIIDBTransaction::DONE;
|
mTransaction->mReadyState = nsIIDBTransaction::DONE;
|
||||||
|
|
||||||
|
// Release file infos on the main thread, so they will eventually get
|
||||||
|
// destroyed on correct thread.
|
||||||
|
mTransaction->ClearCreatedFileInfos();
|
||||||
|
if (mUpdateFileRefcountFunction) {
|
||||||
|
mUpdateFileRefcountFunction->ClearFileInfoEntries();
|
||||||
|
mUpdateFileRefcountFunction = nsnull;
|
||||||
|
}
|
||||||
|
|
||||||
nsCOMPtr<nsIDOMEvent> event;
|
nsCOMPtr<nsIDOMEvent> event;
|
||||||
if (mAborted) {
|
if (mAborted) {
|
||||||
if (mHaveMetadata) {
|
if (mHaveMetadata) {
|
||||||
|
@ -896,9 +933,19 @@ CommitHelper::Run()
|
||||||
if (mConnection) {
|
if (mConnection) {
|
||||||
IndexedDatabaseManager::SetCurrentWindow(database->Owner());
|
IndexedDatabaseManager::SetCurrentWindow(database->Owner());
|
||||||
|
|
||||||
|
if (!mAborted && mUpdateFileRefcountFunction &&
|
||||||
|
NS_FAILED(mUpdateFileRefcountFunction->UpdateDatabase(mConnection))) {
|
||||||
|
mAborted = true;
|
||||||
|
}
|
||||||
|
|
||||||
if (!mAborted) {
|
if (!mAborted) {
|
||||||
NS_NAMED_LITERAL_CSTRING(release, "COMMIT TRANSACTION");
|
NS_NAMED_LITERAL_CSTRING(release, "COMMIT TRANSACTION");
|
||||||
if (NS_FAILED(mConnection->ExecuteSimpleSQL(release))) {
|
if (NS_SUCCEEDED(mConnection->ExecuteSimpleSQL(release))) {
|
||||||
|
if (mUpdateFileRefcountFunction) {
|
||||||
|
mUpdateFileRefcountFunction->UpdateFileInfos();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
mAborted = true;
|
mAborted = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -927,6 +974,14 @@ CommitHelper::Run()
|
||||||
mDoomedObjects.Clear();
|
mDoomedObjects.Clear();
|
||||||
|
|
||||||
if (mConnection) {
|
if (mConnection) {
|
||||||
|
if (mUpdateFileRefcountFunction) {
|
||||||
|
nsresult rv = mConnection->RemoveFunction(
|
||||||
|
NS_LITERAL_CSTRING("update_refcount"));
|
||||||
|
if (NS_FAILED(rv)) {
|
||||||
|
NS_WARNING("Failed to remove function!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
mConnection->Close();
|
mConnection->Close();
|
||||||
mConnection = nsnull;
|
mConnection = nsnull;
|
||||||
|
|
||||||
|
@ -935,3 +990,191 @@ CommitHelper::Run()
|
||||||
|
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
nsresult
|
||||||
|
UpdateRefcountFunction::Init()
|
||||||
|
{
|
||||||
|
NS_ENSURE_TRUE(mFileInfoEntries.Init(), NS_ERROR_OUT_OF_MEMORY);
|
||||||
|
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
NS_IMPL_THREADSAFE_ISUPPORTS1(UpdateRefcountFunction, mozIStorageFunction)
|
||||||
|
|
||||||
|
NS_IMETHODIMP
|
||||||
|
UpdateRefcountFunction::OnFunctionCall(mozIStorageValueArray* aValues,
|
||||||
|
nsIVariant** _retval)
|
||||||
|
{
|
||||||
|
*_retval = nsnull;
|
||||||
|
|
||||||
|
PRUint32 numEntries;
|
||||||
|
nsresult rv = aValues->GetNumEntries(&numEntries);
|
||||||
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
NS_ASSERTION(numEntries == 2, "unexpected number of arguments");
|
||||||
|
|
||||||
|
#ifdef DEBUG
|
||||||
|
PRInt32 type1 = mozIStorageValueArray::VALUE_TYPE_NULL;
|
||||||
|
aValues->GetTypeOfIndex(0, &type1);
|
||||||
|
|
||||||
|
PRInt32 type2 = mozIStorageValueArray::VALUE_TYPE_NULL;
|
||||||
|
aValues->GetTypeOfIndex(1, &type2);
|
||||||
|
|
||||||
|
NS_ASSERTION(!(type1 == mozIStorageValueArray::VALUE_TYPE_NULL &&
|
||||||
|
type2 == mozIStorageValueArray::VALUE_TYPE_NULL),
|
||||||
|
"Shouldn't be called!");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
rv = ProcessValue(aValues, 0, eDecrement);
|
||||||
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
|
rv = ProcessValue(aValues, 1, eIncrement);
|
||||||
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
nsresult
|
||||||
|
UpdateRefcountFunction::ProcessValue(mozIStorageValueArray* aValues,
|
||||||
|
PRInt32 aIndex,
|
||||||
|
UpdateType aUpdateType)
|
||||||
|
{
|
||||||
|
PRInt32 type;
|
||||||
|
aValues->GetTypeOfIndex(aIndex, &type);
|
||||||
|
if (type == mozIStorageValueArray::VALUE_TYPE_NULL) {
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
nsString ids;
|
||||||
|
aValues->GetString(aIndex, ids);
|
||||||
|
|
||||||
|
nsTArray<PRInt64> fileIds;
|
||||||
|
nsresult rv = IDBObjectStore::ConvertFileIdsToArray(ids, fileIds);
|
||||||
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
|
for (PRUint32 i = 0; i < fileIds.Length(); i++) {
|
||||||
|
PRInt64 id = fileIds.ElementAt(i);
|
||||||
|
|
||||||
|
FileInfoEntry* entry;
|
||||||
|
if (!mFileInfoEntries.Get(id, &entry)) {
|
||||||
|
nsRefPtr<FileInfo> fileInfo = mFileManager->GetFileInfo(id);
|
||||||
|
NS_ASSERTION(fileInfo, "Shouldn't be null!");
|
||||||
|
|
||||||
|
nsAutoPtr<FileInfoEntry> newEntry(new FileInfoEntry(fileInfo));
|
||||||
|
if (!mFileInfoEntries.Put(id, newEntry)) {
|
||||||
|
NS_WARNING("Out of memory?");
|
||||||
|
return NS_ERROR_OUT_OF_MEMORY;
|
||||||
|
}
|
||||||
|
entry = newEntry.forget();
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (aUpdateType) {
|
||||||
|
case eIncrement:
|
||||||
|
entry->mDelta++;
|
||||||
|
break;
|
||||||
|
case eDecrement:
|
||||||
|
entry->mDelta--;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
NS_NOTREACHED("Unknown update type!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
PLDHashOperator
|
||||||
|
UpdateRefcountFunction::DatabaseUpdateCallback(const PRUint64& aKey,
|
||||||
|
FileInfoEntry* aValue,
|
||||||
|
void* aUserArg)
|
||||||
|
{
|
||||||
|
if (!aValue->mDelta) {
|
||||||
|
return PL_DHASH_NEXT;
|
||||||
|
}
|
||||||
|
|
||||||
|
DatabaseUpdateFunction* function =
|
||||||
|
static_cast<DatabaseUpdateFunction*>(aUserArg);
|
||||||
|
|
||||||
|
if (!function->Update(aKey, aValue->mDelta)) {
|
||||||
|
return PL_DHASH_STOP;
|
||||||
|
}
|
||||||
|
|
||||||
|
return PL_DHASH_NEXT;
|
||||||
|
}
|
||||||
|
|
||||||
|
PLDHashOperator
|
||||||
|
UpdateRefcountFunction::FileInfoUpdateCallback(const PRUint64& aKey,
|
||||||
|
FileInfoEntry* aValue,
|
||||||
|
void* aUserArg)
|
||||||
|
{
|
||||||
|
if (aValue->mDelta) {
|
||||||
|
aValue->mFileInfo->UpdateDBRefs(aValue->mDelta);
|
||||||
|
}
|
||||||
|
|
||||||
|
return PL_DHASH_NEXT;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
UpdateRefcountFunction::DatabaseUpdateFunction::Update(PRInt64 aId,
|
||||||
|
PRInt32 aDelta)
|
||||||
|
{
|
||||||
|
nsresult rv = UpdateInternal(aId, aDelta);
|
||||||
|
if (NS_FAILED(rv)) {
|
||||||
|
mErrorCode = rv;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
nsresult
|
||||||
|
UpdateRefcountFunction::DatabaseUpdateFunction::UpdateInternal(PRInt64 aId,
|
||||||
|
PRInt32 aDelta)
|
||||||
|
{
|
||||||
|
nsresult rv;
|
||||||
|
|
||||||
|
if (!mUpdateStatement) {
|
||||||
|
rv = mConnection->CreateStatement(NS_LITERAL_CSTRING(
|
||||||
|
"UPDATE file SET refcount = refcount + :delta WHERE id = :id"
|
||||||
|
), getter_AddRefs(mUpdateStatement));
|
||||||
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
}
|
||||||
|
|
||||||
|
mozStorageStatementScoper updateScoper(mUpdateStatement);
|
||||||
|
|
||||||
|
rv = mUpdateStatement->BindInt32ByName(NS_LITERAL_CSTRING("delta"), aDelta);
|
||||||
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
|
rv = mUpdateStatement->BindInt64ByName(NS_LITERAL_CSTRING("id"), aId);
|
||||||
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
|
rv = mUpdateStatement->Execute();
|
||||||
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
|
PRInt32 rows;
|
||||||
|
rv = mConnection->GetAffectedRows(&rows);
|
||||||
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
|
if (rows > 0) {
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!mInsertStatement) {
|
||||||
|
rv = mConnection->CreateStatement(NS_LITERAL_CSTRING(
|
||||||
|
"INSERT INTO file (id, refcount) VALUES(:id, :delta)"
|
||||||
|
), getter_AddRefs(mInsertStatement));
|
||||||
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
}
|
||||||
|
|
||||||
|
mozStorageStatementScoper insertScoper(mInsertStatement);
|
||||||
|
|
||||||
|
rv = mInsertStatement->BindInt64ByName(NS_LITERAL_CSTRING("id"), aId);
|
||||||
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
|
rv = mInsertStatement->BindInt32ByName(NS_LITERAL_CSTRING("delta"), aDelta);
|
||||||
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
|
rv = mInsertStatement->Execute();
|
||||||
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
|
@ -42,7 +42,11 @@
|
||||||
|
|
||||||
#include "mozilla/dom/indexedDB/IndexedDatabase.h"
|
#include "mozilla/dom/indexedDB/IndexedDatabase.h"
|
||||||
#include "mozilla/dom/indexedDB/IDBDatabase.h"
|
#include "mozilla/dom/indexedDB/IDBDatabase.h"
|
||||||
|
#include "mozilla/dom/indexedDB/FileInfo.h"
|
||||||
|
|
||||||
|
#include "mozIStorageConnection.h"
|
||||||
|
#include "mozIStorageStatement.h"
|
||||||
|
#include "mozIStorageFunction.h"
|
||||||
#include "nsIIDBTransaction.h"
|
#include "nsIIDBTransaction.h"
|
||||||
#include "nsIRunnable.h"
|
#include "nsIRunnable.h"
|
||||||
#include "nsIThreadInternal.h"
|
#include "nsIThreadInternal.h"
|
||||||
|
@ -51,11 +55,10 @@
|
||||||
#include "nsCycleCollectionParticipant.h"
|
#include "nsCycleCollectionParticipant.h"
|
||||||
|
|
||||||
#include "nsAutoPtr.h"
|
#include "nsAutoPtr.h"
|
||||||
|
#include "nsClassHashtable.h"
|
||||||
#include "nsHashKeys.h"
|
#include "nsHashKeys.h"
|
||||||
#include "nsInterfaceHashtable.h"
|
#include "nsInterfaceHashtable.h"
|
||||||
|
|
||||||
class mozIStorageConnection;
|
|
||||||
class mozIStorageStatement;
|
|
||||||
class nsIThread;
|
class nsIThread;
|
||||||
|
|
||||||
BEGIN_INDEXEDDB_NAMESPACE
|
BEGIN_INDEXEDDB_NAMESPACE
|
||||||
|
@ -64,6 +67,7 @@ class AsyncConnectionHelper;
|
||||||
class CommitHelper;
|
class CommitHelper;
|
||||||
struct ObjectStoreInfo;
|
struct ObjectStoreInfo;
|
||||||
class TransactionThreadPool;
|
class TransactionThreadPool;
|
||||||
|
class UpdateRefcountFunction;
|
||||||
|
|
||||||
class IDBTransactionListener
|
class IDBTransactionListener
|
||||||
{
|
{
|
||||||
|
@ -167,6 +171,10 @@ public:
|
||||||
GetOrCreateObjectStore(const nsAString& aName,
|
GetOrCreateObjectStore(const nsAString& aName,
|
||||||
ObjectStoreInfo* aObjectStoreInfo);
|
ObjectStoreInfo* aObjectStoreInfo);
|
||||||
|
|
||||||
|
void OnNewFileInfo(FileInfo* aFileInfo);
|
||||||
|
|
||||||
|
void ClearCreatedFileInfos();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
IDBTransaction();
|
IDBTransaction();
|
||||||
~IDBTransaction();
|
~IDBTransaction();
|
||||||
|
@ -204,6 +212,9 @@ private:
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
bool mFiredCompleteOrAbort;
|
bool mFiredCompleteOrAbort;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
nsRefPtr<UpdateRefcountFunction> mUpdateFileRefcountFunction;
|
||||||
|
nsTArray<nsRefPtr<FileInfo> > mCreatedFileInfos;
|
||||||
};
|
};
|
||||||
|
|
||||||
class CommitHelper : public nsIRunnable
|
class CommitHelper : public nsIRunnable
|
||||||
|
@ -233,6 +244,7 @@ private:
|
||||||
nsRefPtr<IDBTransaction> mTransaction;
|
nsRefPtr<IDBTransaction> mTransaction;
|
||||||
nsRefPtr<IDBTransactionListener> mListener;
|
nsRefPtr<IDBTransactionListener> mListener;
|
||||||
nsCOMPtr<mozIStorageConnection> mConnection;
|
nsCOMPtr<mozIStorageConnection> mConnection;
|
||||||
|
nsRefPtr<UpdateRefcountFunction> mUpdateFileRefcountFunction;
|
||||||
nsAutoTArray<nsCOMPtr<nsISupports>, 10> mDoomedObjects;
|
nsAutoTArray<nsCOMPtr<nsISupports>, 10> mDoomedObjects;
|
||||||
|
|
||||||
PRUint64 mOldVersion;
|
PRUint64 mOldVersion;
|
||||||
|
@ -242,6 +254,101 @@ private:
|
||||||
bool mHaveMetadata;
|
bool mHaveMetadata;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class UpdateRefcountFunction : public mozIStorageFunction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
NS_DECL_ISUPPORTS
|
||||||
|
NS_DECL_MOZISTORAGEFUNCTION
|
||||||
|
|
||||||
|
UpdateRefcountFunction(FileManager* aFileManager)
|
||||||
|
: mFileManager(aFileManager)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
~UpdateRefcountFunction()
|
||||||
|
{ }
|
||||||
|
|
||||||
|
nsresult Init();
|
||||||
|
|
||||||
|
void ClearFileInfoEntries()
|
||||||
|
{
|
||||||
|
mFileInfoEntries.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
nsresult UpdateDatabase(mozIStorageConnection* aConnection)
|
||||||
|
{
|
||||||
|
DatabaseUpdateFunction function(aConnection);
|
||||||
|
|
||||||
|
mFileInfoEntries.EnumerateRead(DatabaseUpdateCallback, &function);
|
||||||
|
|
||||||
|
return function.ErrorCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
void UpdateFileInfos()
|
||||||
|
{
|
||||||
|
mFileInfoEntries.EnumerateRead(FileInfoUpdateCallback, nsnull);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
class FileInfoEntry
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
FileInfoEntry(FileInfo* aFileInfo)
|
||||||
|
: mFileInfo(aFileInfo), mDelta(0)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
~FileInfoEntry()
|
||||||
|
{ }
|
||||||
|
|
||||||
|
nsRefPtr<FileInfo> mFileInfo;
|
||||||
|
PRInt32 mDelta;
|
||||||
|
};
|
||||||
|
|
||||||
|
enum UpdateType {
|
||||||
|
eIncrement,
|
||||||
|
eDecrement
|
||||||
|
};
|
||||||
|
|
||||||
|
class DatabaseUpdateFunction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
DatabaseUpdateFunction(mozIStorageConnection* aConnection)
|
||||||
|
: mConnection(aConnection), mErrorCode(NS_OK)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
bool Update(PRInt64 aId, PRInt32 aDelta);
|
||||||
|
nsresult ErrorCode()
|
||||||
|
{
|
||||||
|
return mErrorCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
nsresult UpdateInternal(PRInt64 aId, PRInt32 aDelta);
|
||||||
|
|
||||||
|
nsCOMPtr<mozIStorageConnection> mConnection;
|
||||||
|
nsCOMPtr<mozIStorageStatement> mUpdateStatement;
|
||||||
|
nsCOMPtr<mozIStorageStatement> mInsertStatement;
|
||||||
|
|
||||||
|
nsresult mErrorCode;
|
||||||
|
};
|
||||||
|
|
||||||
|
nsresult ProcessValue(mozIStorageValueArray* aValues,
|
||||||
|
PRInt32 aIndex,
|
||||||
|
UpdateType aUpdateType);
|
||||||
|
|
||||||
|
static PLDHashOperator
|
||||||
|
DatabaseUpdateCallback(const PRUint64& aKey,
|
||||||
|
FileInfoEntry* aValue,
|
||||||
|
void* aUserArg);
|
||||||
|
|
||||||
|
static PLDHashOperator
|
||||||
|
FileInfoUpdateCallback(const PRUint64& aKey,
|
||||||
|
FileInfoEntry* aValue,
|
||||||
|
void* aUserArg);
|
||||||
|
|
||||||
|
FileManager* mFileManager;
|
||||||
|
nsClassHashtable<nsUint64HashKey, FileInfoEntry> mFileInfoEntries;
|
||||||
|
};
|
||||||
|
|
||||||
END_INDEXEDDB_NAMESPACE
|
END_INDEXEDDB_NAMESPACE
|
||||||
|
|
||||||
#endif // mozilla_dom_indexeddb_idbtransaction_h__
|
#endif // mozilla_dom_indexeddb_idbtransaction_h__
|
||||||
|
|
|
@ -60,8 +60,34 @@
|
||||||
#define USING_INDEXEDDB_NAMESPACE \
|
#define USING_INDEXEDDB_NAMESPACE \
|
||||||
using namespace mozilla::dom::indexedDB;
|
using namespace mozilla::dom::indexedDB;
|
||||||
|
|
||||||
|
class nsIDOMBlob;
|
||||||
|
|
||||||
BEGIN_INDEXEDDB_NAMESPACE
|
BEGIN_INDEXEDDB_NAMESPACE
|
||||||
|
|
||||||
|
class FileInfo;
|
||||||
|
|
||||||
|
struct StructuredCloneReadInfo {
|
||||||
|
void Swap(StructuredCloneReadInfo& aCloneReadInfo) {
|
||||||
|
mCloneBuffer.swap(aCloneReadInfo.mCloneBuffer);
|
||||||
|
mFileInfos.SwapElements(aCloneReadInfo.mFileInfos);
|
||||||
|
}
|
||||||
|
|
||||||
|
JSAutoStructuredCloneBuffer mCloneBuffer;
|
||||||
|
nsTArray<nsRefPtr<FileInfo> > mFileInfos;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct StructuredCloneWriteInfo {
|
||||||
|
void Swap(StructuredCloneWriteInfo& aCloneWriteInfo) {
|
||||||
|
mCloneBuffer.swap(aCloneWriteInfo.mCloneBuffer);
|
||||||
|
mBlobs.SwapElements(aCloneWriteInfo.mBlobs);
|
||||||
|
mOffsetToKeyProp = aCloneWriteInfo.mOffsetToKeyProp;
|
||||||
|
}
|
||||||
|
|
||||||
|
JSAutoStructuredCloneBuffer mCloneBuffer;
|
||||||
|
nsTArray<nsCOMPtr<nsIDOMBlob> > mBlobs;
|
||||||
|
PRUint64 mOffsetToKeyProp;
|
||||||
|
};
|
||||||
|
|
||||||
inline
|
inline
|
||||||
void
|
void
|
||||||
AppendConditionClause(const nsACString& aColumnName,
|
AppendConditionClause(const nsACString& aColumnName,
|
||||||
|
|
|
@ -54,6 +54,7 @@
|
||||||
#include "nsThreadUtils.h"
|
#include "nsThreadUtils.h"
|
||||||
#include "nsXPCOM.h"
|
#include "nsXPCOM.h"
|
||||||
#include "nsXPCOMPrivate.h"
|
#include "nsXPCOMPrivate.h"
|
||||||
|
#include "test_quota.h"
|
||||||
|
|
||||||
#include "AsyncConnectionHelper.h"
|
#include "AsyncConnectionHelper.h"
|
||||||
#include "CheckQuotaHelper.h"
|
#include "CheckQuotaHelper.h"
|
||||||
|
@ -61,6 +62,7 @@
|
||||||
#include "IDBEvents.h"
|
#include "IDBEvents.h"
|
||||||
#include "IDBFactory.h"
|
#include "IDBFactory.h"
|
||||||
#include "LazyIdleThread.h"
|
#include "LazyIdleThread.h"
|
||||||
|
#include "OpenDatabaseHelper.h"
|
||||||
#include "TransactionThreadPool.h"
|
#include "TransactionThreadPool.h"
|
||||||
|
|
||||||
// The amount of time, in milliseconds, that our IO thread will stay alive
|
// The amount of time, in milliseconds, that our IO thread will stay alive
|
||||||
|
@ -87,12 +89,33 @@ using mozilla::Preferences;
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
PRInt32 gShutdown = 0;
|
PRInt32 gShutdown = 0;
|
||||||
|
PRInt32 gClosed = 0;
|
||||||
|
|
||||||
// Does not hold a reference.
|
// Does not hold a reference.
|
||||||
IndexedDatabaseManager* gInstance = nsnull;
|
IndexedDatabaseManager* gInstance = nsnull;
|
||||||
|
|
||||||
PRInt32 gIndexedDBQuotaMB = DEFAULT_QUOTA_MB;
|
PRInt32 gIndexedDBQuotaMB = DEFAULT_QUOTA_MB;
|
||||||
|
|
||||||
|
bool
|
||||||
|
GetBaseFilename(const nsAString& aFilename,
|
||||||
|
nsAString& aBaseFilename)
|
||||||
|
{
|
||||||
|
NS_ASSERTION(!aFilename.IsEmpty(), "Bad argument!");
|
||||||
|
|
||||||
|
NS_NAMED_LITERAL_STRING(sqlite, ".sqlite");
|
||||||
|
nsAString::size_type filenameLen = aFilename.Length();
|
||||||
|
nsAString::size_type sqliteLen = sqlite.Length();
|
||||||
|
|
||||||
|
if (sqliteLen > filenameLen ||
|
||||||
|
Substring(aFilename, filenameLen - sqliteLen, sqliteLen) != sqlite) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
aBaseFilename = Substring(aFilename, 0, filenameLen - sqliteLen);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
class QuotaCallback : public mozIStorageQuotaCallback
|
class QuotaCallback : public mozIStorageQuotaCallback
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
@ -138,11 +161,29 @@ EnumerateToTArray(const nsACString& aKey,
|
||||||
return PL_DHASH_NEXT;
|
return PL_DHASH_NEXT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PLDHashOperator
|
||||||
|
InvalidateAllFileManagers(const nsACString& aKey,
|
||||||
|
nsTArray<nsRefPtr<FileManager> >* aValue,
|
||||||
|
void* aUserArg)
|
||||||
|
{
|
||||||
|
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
|
||||||
|
NS_ASSERTION(!aKey.IsEmpty(), "Empty key!");
|
||||||
|
NS_ASSERTION(aValue, "Null pointer!");
|
||||||
|
|
||||||
|
for (PRUint32 i = 0; i < aValue->Length(); i++) {
|
||||||
|
nsRefPtr<FileManager> fileManager = aValue->ElementAt(i);
|
||||||
|
fileManager->Invalidate();
|
||||||
|
}
|
||||||
|
|
||||||
|
return PL_DHASH_NEXT;
|
||||||
|
}
|
||||||
|
|
||||||
} // anonymous namespace
|
} // anonymous namespace
|
||||||
|
|
||||||
IndexedDatabaseManager::IndexedDatabaseManager()
|
IndexedDatabaseManager::IndexedDatabaseManager()
|
||||||
: mCurrentWindowIndex(BAD_TLS_INDEX),
|
: mCurrentWindowIndex(BAD_TLS_INDEX),
|
||||||
mQuotaHelperMutex("IndexedDatabaseManager.mQuotaHelperMutex")
|
mQuotaHelperMutex("IndexedDatabaseManager.mQuotaHelperMutex"),
|
||||||
|
mFileMutex("IndexedDatabaseManager.mFileMutex")
|
||||||
{
|
{
|
||||||
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
|
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
|
||||||
NS_ASSERTION(!gInstance, "More than one instance!");
|
NS_ASSERTION(!gInstance, "More than one instance!");
|
||||||
|
@ -179,7 +220,8 @@ IndexedDatabaseManager::GetOrCreate()
|
||||||
instance = new IndexedDatabaseManager();
|
instance = new IndexedDatabaseManager();
|
||||||
|
|
||||||
if (!instance->mLiveDatabases.Init() ||
|
if (!instance->mLiveDatabases.Init() ||
|
||||||
!instance->mQuotaHelperHash.Init()) {
|
!instance->mQuotaHelperHash.Init() ||
|
||||||
|
!instance->mFileManagers.Init()) {
|
||||||
NS_WARNING("Out of memory!");
|
NS_WARNING("Out of memory!");
|
||||||
return nsnull;
|
return nsnull;
|
||||||
}
|
}
|
||||||
|
@ -447,6 +489,13 @@ IndexedDatabaseManager::IsShuttingDown()
|
||||||
return !!gShutdown;
|
return !!gShutdown;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// static
|
||||||
|
bool
|
||||||
|
IndexedDatabaseManager::IsClosed()
|
||||||
|
{
|
||||||
|
return !!gClosed;
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
IndexedDatabaseManager::AbortCloseDatabasesForWindow(nsPIDOMWindow* aWindow)
|
IndexedDatabaseManager::AbortCloseDatabasesForWindow(nsPIDOMWindow* aWindow)
|
||||||
{
|
{
|
||||||
|
@ -573,7 +622,8 @@ IndexedDatabaseManager::GetIndexedDBQuotaMB()
|
||||||
}
|
}
|
||||||
|
|
||||||
nsresult
|
nsresult
|
||||||
IndexedDatabaseManager::EnsureQuotaManagementForDirectory(nsIFile* aDirectory)
|
IndexedDatabaseManager::EnsureOriginIsInitialized(const nsACString& aOrigin,
|
||||||
|
nsIFile** aDirectory)
|
||||||
{
|
{
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
{
|
{
|
||||||
|
@ -583,19 +633,35 @@ IndexedDatabaseManager::EnsureQuotaManagementForDirectory(nsIFile* aDirectory)
|
||||||
"Running on the wrong thread!");
|
"Running on the wrong thread!");
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
NS_ASSERTION(aDirectory, "Null pointer!");
|
|
||||||
|
|
||||||
nsCString path;
|
nsCOMPtr<nsIFile> directory;
|
||||||
nsresult rv = aDirectory->GetNativePath(path);
|
nsresult rv = IDBFactory::GetDirectoryForOrigin(aOrigin,
|
||||||
|
getter_AddRefs(directory));
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
if (mTrackedQuotaPaths.Contains(path)) {
|
bool exists;
|
||||||
return true;
|
rv = directory->Exists(&exists);
|
||||||
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
|
if (exists) {
|
||||||
|
bool isDirectory;
|
||||||
|
rv = directory->IsDirectory(&isDirectory);
|
||||||
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
NS_ENSURE_TRUE(isDirectory, NS_ERROR_UNEXPECTED);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
rv = directory->Create(nsIFile::DIRECTORY_TYPE, 0755);
|
||||||
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mFileManagers.Get(aOrigin)) {
|
||||||
|
NS_ADDREF(*aDirectory = directory);
|
||||||
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
// First figure out the filename pattern we'll use.
|
// First figure out the filename pattern we'll use.
|
||||||
nsCOMPtr<nsIFile> patternFile;
|
nsCOMPtr<nsIFile> patternFile;
|
||||||
rv = aDirectory->Clone(getter_AddRefs(patternFile));
|
rv = directory->Clone(getter_AddRefs(patternFile));
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
rv = patternFile->Append(NS_LITERAL_STRING("*"));
|
rv = patternFile->Append(NS_LITERAL_STRING("*"));
|
||||||
|
@ -615,42 +681,141 @@ IndexedDatabaseManager::EnsureQuotaManagementForDirectory(nsIFile* aDirectory)
|
||||||
mQuotaCallbackSingleton, nsnull);
|
mQuotaCallbackSingleton, nsnull);
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
// If the directory exists then we need to see if there are any files in it
|
// We need to see if there are any files in the directory already. If they
|
||||||
// already. We need to tell SQLite about all of them.
|
// are database files then we need to create file managers for them and also
|
||||||
bool exists;
|
// tell SQLite about all of them.
|
||||||
rv = aDirectory->Exists(&exists);
|
|
||||||
|
nsAutoTArray<nsString, 20> subdirectories;
|
||||||
|
nsAutoTArray<nsCOMPtr<nsIFile> , 20> unknownFiles;
|
||||||
|
|
||||||
|
nsAutoPtr<nsTArray<nsRefPtr<FileManager> > > fileManagers(
|
||||||
|
new nsTArray<nsRefPtr<FileManager> >());
|
||||||
|
|
||||||
|
nsCOMPtr<nsISimpleEnumerator> entries;
|
||||||
|
rv = directory->GetDirectoryEntries(getter_AddRefs(entries));
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
if (exists) {
|
bool hasMore;
|
||||||
// Make sure this really is a directory.
|
while (NS_SUCCEEDED((rv = entries->HasMoreElements(&hasMore))) && hasMore) {
|
||||||
|
nsCOMPtr<nsISupports> entry;
|
||||||
|
rv = entries->GetNext(getter_AddRefs(entry));
|
||||||
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
|
nsCOMPtr<nsIFile> file = do_QueryInterface(entry);
|
||||||
|
NS_ENSURE_TRUE(file, NS_NOINTERFACE);
|
||||||
|
|
||||||
|
nsString leafName;
|
||||||
|
rv = file->GetLeafName(leafName);
|
||||||
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
bool isDirectory;
|
bool isDirectory;
|
||||||
rv = aDirectory->IsDirectory(&isDirectory);
|
rv = file->IsDirectory(&isDirectory);
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
|
||||||
NS_ENSURE_TRUE(isDirectory, NS_ERROR_UNEXPECTED);
|
|
||||||
|
|
||||||
nsCOMPtr<nsISimpleEnumerator> entries;
|
|
||||||
rv = aDirectory->GetDirectoryEntries(getter_AddRefs(entries));
|
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
bool hasMore;
|
if (isDirectory) {
|
||||||
while (NS_SUCCEEDED((rv = entries->HasMoreElements(&hasMore))) && hasMore) {
|
subdirectories.AppendElement(leafName);
|
||||||
nsCOMPtr<nsISupports> entry;
|
continue;
|
||||||
rv = entries->GetNext(getter_AddRefs(entry));
|
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
|
||||||
|
|
||||||
nsCOMPtr<nsIFile> file = do_QueryInterface(entry);
|
|
||||||
NS_ENSURE_TRUE(file, NS_NOINTERFACE);
|
|
||||||
|
|
||||||
rv = ss->UpdateQutoaInformationForFile(file);
|
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
nsString dbBaseFilename;
|
||||||
|
if (!GetBaseFilename(leafName, dbBaseFilename)) {
|
||||||
|
unknownFiles.AppendElement(file);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
nsCOMPtr<nsIFile> fileManagerDirectory;
|
||||||
|
rv = directory->Clone(getter_AddRefs(fileManagerDirectory));
|
||||||
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
|
rv = fileManagerDirectory->Append(dbBaseFilename);
|
||||||
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
|
nsString voidString;
|
||||||
|
voidString.SetIsVoid(true);
|
||||||
|
|
||||||
|
nsCOMPtr<mozIStorageConnection> connection;
|
||||||
|
rv = OpenDatabaseHelper::CreateDatabaseConnection(
|
||||||
|
voidString, file, fileManagerDirectory, getter_AddRefs(connection));
|
||||||
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
|
nsCOMPtr<mozIStorageStatement> stmt;
|
||||||
|
rv = connection->CreateStatement(NS_LITERAL_CSTRING(
|
||||||
|
"SELECT name "
|
||||||
|
"FROM database"
|
||||||
|
), getter_AddRefs(stmt));
|
||||||
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
|
bool hasResult;
|
||||||
|
rv = stmt->ExecuteStep(&hasResult);
|
||||||
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
|
if (!hasResult) {
|
||||||
|
NS_ERROR("Database has no name!");
|
||||||
|
return NS_ERROR_UNEXPECTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
nsString databaseName;
|
||||||
|
rv = stmt->GetString(0, databaseName);
|
||||||
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
|
nsRefPtr<FileManager> fileManager = new FileManager(aOrigin, databaseName);
|
||||||
|
|
||||||
|
rv = fileManager->Init();
|
||||||
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
|
rv = fileManager->InitDirectory(fileManagerDirectory, connection);
|
||||||
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
|
fileManagers->AppendElement(fileManager);
|
||||||
|
|
||||||
|
rv = ss->UpdateQuotaInformationForFile(file);
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
}
|
}
|
||||||
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
NS_ASSERTION(!mTrackedQuotaPaths.Contains(path), "What?!");
|
for (PRUint32 i = 0; i < subdirectories.Length(); i++) {
|
||||||
|
const nsString& subdirectory = subdirectories[i];
|
||||||
|
bool unknown = true;
|
||||||
|
for (PRUint32 j = 0; j < fileManagers->Length(); j++) {
|
||||||
|
nsRefPtr<FileManager>& fileManager = fileManagers->ElementAt(j);
|
||||||
|
if (fileManager->DirectoryName().Equals(subdirectory)) {
|
||||||
|
unknown = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (unknown) {
|
||||||
|
NS_WARNING("Unknown subdirectory found!");
|
||||||
|
return NS_ERROR_UNEXPECTED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
mTrackedQuotaPaths.AppendElement(path);
|
for (PRUint32 i = 0; i < unknownFiles.Length(); i++) {
|
||||||
return rv;
|
nsCOMPtr<nsIFile>& unknownFile = unknownFiles[i];
|
||||||
|
|
||||||
|
bool exists;
|
||||||
|
rv = unknownFile->Exists(&exists);
|
||||||
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
|
if (exists) {
|
||||||
|
nsString leafName;
|
||||||
|
unknownFile->GetLeafName(leafName);
|
||||||
|
|
||||||
|
if (!StringEndsWith(leafName, NS_LITERAL_STRING(".sqlite-journal"))) {
|
||||||
|
NS_WARNING("Unknown file found!");
|
||||||
|
return NS_ERROR_UNEXPECTED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!mFileManagers.Put(aOrigin, fileManagers)) {
|
||||||
|
NS_WARNING("Out of memory?");
|
||||||
|
return NS_ERROR_OUT_OF_MEMORY;
|
||||||
|
}
|
||||||
|
|
||||||
|
fileManagers.forget();
|
||||||
|
|
||||||
|
NS_ADDREF(*aDirectory = directory);
|
||||||
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
|
@ -749,6 +914,116 @@ IndexedDatabaseManager::GetASCIIOriginFromWindow(nsPIDOMWindow* aWindow,
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
already_AddRefed<FileManager>
|
||||||
|
IndexedDatabaseManager::GetOrCreateFileManager(const nsACString& aOrigin,
|
||||||
|
const nsAString& aDatabaseName)
|
||||||
|
{
|
||||||
|
nsTArray<nsRefPtr<FileManager> >* array;
|
||||||
|
if (!mFileManagers.Get(aOrigin, &array)) {
|
||||||
|
nsAutoPtr<nsTArray<nsRefPtr<FileManager> > > newArray(
|
||||||
|
new nsTArray<nsRefPtr<FileManager> >());
|
||||||
|
if (!mFileManagers.Put(aOrigin, newArray)) {
|
||||||
|
NS_WARNING("Out of memory?");
|
||||||
|
return nsnull;
|
||||||
|
}
|
||||||
|
array = newArray.forget();
|
||||||
|
}
|
||||||
|
|
||||||
|
nsRefPtr<FileManager> fileManager;
|
||||||
|
for (PRUint32 i = 0; i < array->Length(); i++) {
|
||||||
|
nsRefPtr<FileManager> fm = array->ElementAt(i);
|
||||||
|
|
||||||
|
if (fm->DatabaseName().Equals(aDatabaseName)) {
|
||||||
|
fileManager = fm.forget();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!fileManager) {
|
||||||
|
fileManager = new FileManager(aOrigin, aDatabaseName);
|
||||||
|
|
||||||
|
if (NS_FAILED(fileManager->Init())) {
|
||||||
|
NS_WARNING("Failed to initialize file manager!");
|
||||||
|
return nsnull;
|
||||||
|
}
|
||||||
|
|
||||||
|
array->AppendElement(fileManager);
|
||||||
|
}
|
||||||
|
|
||||||
|
return fileManager.forget();
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
IndexedDatabaseManager::InvalidateFileManagersForOrigin(
|
||||||
|
const nsACString& aOrigin)
|
||||||
|
{
|
||||||
|
nsTArray<nsRefPtr<FileManager> >* array;
|
||||||
|
if (mFileManagers.Get(aOrigin, &array)) {
|
||||||
|
for (PRUint32 i = 0; i < array->Length(); i++) {
|
||||||
|
nsRefPtr<FileManager> fileManager = array->ElementAt(i);
|
||||||
|
fileManager->Invalidate();
|
||||||
|
}
|
||||||
|
mFileManagers.Remove(aOrigin);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
IndexedDatabaseManager::InvalidateFileManager(const nsACString& aOrigin,
|
||||||
|
const nsAString& aDatabaseName)
|
||||||
|
{
|
||||||
|
nsTArray<nsRefPtr<FileManager> >* array;
|
||||||
|
if (!mFileManagers.Get(aOrigin, &array)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (PRUint32 i = 0; i < array->Length(); i++) {
|
||||||
|
nsRefPtr<FileManager> fileManager = array->ElementAt(i);
|
||||||
|
if (fileManager->DatabaseName().Equals(aDatabaseName)) {
|
||||||
|
fileManager->Invalidate();
|
||||||
|
array->RemoveElementAt(i);
|
||||||
|
|
||||||
|
if (array->IsEmpty()) {
|
||||||
|
mFileManagers.Remove(aOrigin);
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
nsresult
|
||||||
|
IndexedDatabaseManager::AsyncDeleteFile(FileManager* aFileManager,
|
||||||
|
PRInt64 aFileId)
|
||||||
|
{
|
||||||
|
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
|
||||||
|
|
||||||
|
NS_ENSURE_ARG_POINTER(aFileManager);
|
||||||
|
|
||||||
|
// See if we're currently clearing the databases for this origin. If so then
|
||||||
|
// we pretend that we've already deleted everything.
|
||||||
|
if (IsClearOriginPending(aFileManager->Origin())) {
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
nsCOMPtr<nsIFile> directory = aFileManager->GetDirectory();
|
||||||
|
NS_ENSURE_TRUE(directory, NS_ERROR_FAILURE);
|
||||||
|
|
||||||
|
nsCOMPtr<nsIFile> file = aFileManager->GetFileForId(directory, aFileId);
|
||||||
|
NS_ENSURE_TRUE(file, NS_ERROR_FAILURE);
|
||||||
|
|
||||||
|
nsString filePath;
|
||||||
|
nsresult rv = file->GetPath(filePath);
|
||||||
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
|
nsRefPtr<AsyncDeleteFileRunnable> runnable =
|
||||||
|
new AsyncDeleteFileRunnable(filePath);
|
||||||
|
|
||||||
|
rv = mIOThread->Dispatch(runnable, NS_DISPATCH_NORMAL);
|
||||||
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
// static
|
// static
|
||||||
nsresult
|
nsresult
|
||||||
IndexedDatabaseManager::DispatchHelper(AsyncConnectionHelper* aHelper)
|
IndexedDatabaseManager::DispatchHelper(AsyncConnectionHelper* aHelper)
|
||||||
|
@ -949,6 +1224,12 @@ IndexedDatabaseManager::Observe(nsISupports* aSubject,
|
||||||
NS_WARNING("Failed to cancel shutdown timer!");
|
NS_WARNING("Failed to cancel shutdown timer!");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mFileManagers.EnumerateRead(InvalidateAllFileManagers, nsnull);
|
||||||
|
|
||||||
|
if (PR_ATOMIC_SET(&gClosed, 1)) {
|
||||||
|
NS_ERROR("Close more than once?!");
|
||||||
|
}
|
||||||
|
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1007,10 +1288,12 @@ IndexedDatabaseManager::OriginClearRunnable::Run()
|
||||||
|
|
||||||
NS_ASSERTION(!mThread, "Should have been cleared already!");
|
NS_ASSERTION(!mThread, "Should have been cleared already!");
|
||||||
|
|
||||||
// Tell the IndexedDatabaseManager that we're done.
|
|
||||||
IndexedDatabaseManager* mgr = IndexedDatabaseManager::Get();
|
IndexedDatabaseManager* mgr = IndexedDatabaseManager::Get();
|
||||||
NS_ASSERTION(mgr, "This should never fail!");
|
NS_ASSERTION(mgr, "This should never fail!");
|
||||||
|
|
||||||
|
mgr->InvalidateFileManagersForOrigin(mOrigin);
|
||||||
|
|
||||||
|
// Tell the IndexedDatabaseManager that we're done.
|
||||||
mgr->AllowNextSynchronizedOp(mOrigin, nsnull);
|
mgr->AllowNextSynchronizedOp(mOrigin, nsnull);
|
||||||
|
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
|
@ -1046,6 +1329,7 @@ IndexedDatabaseManager::AsyncUsageRunnable::AsyncUsageRunnable(
|
||||||
mOrigin(aOrigin),
|
mOrigin(aOrigin),
|
||||||
mCallback(aCallback),
|
mCallback(aCallback),
|
||||||
mUsage(0),
|
mUsage(0),
|
||||||
|
mFileUsage(0),
|
||||||
mCanceled(0)
|
mCanceled(0)
|
||||||
{
|
{
|
||||||
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
|
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
|
||||||
|
@ -1062,13 +1346,27 @@ IndexedDatabaseManager::AsyncUsageRunnable::Cancel()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline void
|
||||||
|
IncrementUsage(PRUint64* aUsage, PRUint64 aDelta)
|
||||||
|
{
|
||||||
|
// Watch for overflow!
|
||||||
|
if ((LL_MAXINT - *aUsage) <= aDelta) {
|
||||||
|
NS_WARNING("Database sizes exceed max we can report!");
|
||||||
|
*aUsage = LL_MAXINT;
|
||||||
|
} else {
|
||||||
|
*aUsage += aDelta;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
nsresult
|
nsresult
|
||||||
IndexedDatabaseManager::AsyncUsageRunnable::RunInternal()
|
IndexedDatabaseManager::AsyncUsageRunnable::RunInternal()
|
||||||
{
|
{
|
||||||
if (NS_IsMainThread()) {
|
if (NS_IsMainThread()) {
|
||||||
// Call the callback unless we were canceled.
|
// Call the callback unless we were canceled.
|
||||||
if (!mCanceled) {
|
if (!mCanceled) {
|
||||||
mCallback->OnUsageResult(mURI, mUsage);
|
PRUint64 usage = mUsage;
|
||||||
|
IncrementUsage(&usage, mFileUsage);
|
||||||
|
mCallback->OnUsageResult(mURI, usage, mFileUsage);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clean up.
|
// Clean up.
|
||||||
|
@ -1101,39 +1399,63 @@ IndexedDatabaseManager::AsyncUsageRunnable::RunInternal()
|
||||||
// If the directory exists then enumerate all the files inside, adding up the
|
// If the directory exists then enumerate all the files inside, adding up the
|
||||||
// sizes to get the final usage statistic.
|
// sizes to get the final usage statistic.
|
||||||
if (exists && !mCanceled) {
|
if (exists && !mCanceled) {
|
||||||
nsCOMPtr<nsISimpleEnumerator> entries;
|
rv = GetUsageForDirectory(directory, &mUsage);
|
||||||
rv = directory->GetDirectoryEntries(getter_AddRefs(entries));
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
}
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
nsresult
|
||||||
|
IndexedDatabaseManager::AsyncUsageRunnable::GetUsageForDirectory(
|
||||||
|
nsIFile* aDirectory,
|
||||||
|
PRUint64* aUsage)
|
||||||
|
{
|
||||||
|
NS_ASSERTION(aDirectory, "Null pointer!");
|
||||||
|
NS_ASSERTION(aUsage, "Null pointer!");
|
||||||
|
|
||||||
|
nsCOMPtr<nsISimpleEnumerator> entries;
|
||||||
|
nsresult rv = aDirectory->GetDirectoryEntries(getter_AddRefs(entries));
|
||||||
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
|
if (!entries) {
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool hasMore;
|
||||||
|
while (NS_SUCCEEDED((rv = entries->HasMoreElements(&hasMore))) &&
|
||||||
|
hasMore && !mCanceled) {
|
||||||
|
nsCOMPtr<nsISupports> entry;
|
||||||
|
rv = entries->GetNext(getter_AddRefs(entry));
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
if (entries) {
|
nsCOMPtr<nsIFile> file(do_QueryInterface(entry));
|
||||||
bool hasMore;
|
NS_ASSERTION(file, "Don't know what this is!");
|
||||||
while (NS_SUCCEEDED((rv = entries->HasMoreElements(&hasMore))) &&
|
|
||||||
hasMore && !mCanceled) {
|
bool isDirectory;
|
||||||
nsCOMPtr<nsISupports> entry;
|
rv = file->IsDirectory(&isDirectory);
|
||||||
rv = entries->GetNext(getter_AddRefs(entry));
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
|
if (isDirectory) {
|
||||||
|
if (aUsage == &mFileUsage) {
|
||||||
|
NS_WARNING("Unknown directory found!");
|
||||||
|
} else {
|
||||||
|
rv = GetUsageForDirectory(file, &mFileUsage);
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
nsCOMPtr<nsIFile> file(do_QueryInterface(entry));
|
|
||||||
NS_ASSERTION(file, "Don't know what this is!");
|
|
||||||
|
|
||||||
PRInt64 fileSize;
|
|
||||||
rv = file->GetFileSize(&fileSize);
|
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
|
||||||
|
|
||||||
NS_ASSERTION(fileSize > 0, "Negative size?!");
|
|
||||||
|
|
||||||
// Watch for overflow!
|
|
||||||
if (NS_UNLIKELY((LL_MAXINT - mUsage) <= PRUint64(fileSize))) {
|
|
||||||
NS_WARNING("Database sizes exceed max we can report!");
|
|
||||||
mUsage = LL_MAXINT;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
mUsage += fileSize;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PRInt64 fileSize;
|
||||||
|
rv = file->GetFileSize(&fileSize);
|
||||||
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
|
NS_ASSERTION(fileSize > 0, "Negative size?!");
|
||||||
|
|
||||||
|
IncrementUsage(aUsage, PRUint64(fileSize));
|
||||||
}
|
}
|
||||||
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1247,3 +1569,20 @@ IndexedDatabaseManager::SynchronizedOp::DispatchDelayedRunnables()
|
||||||
|
|
||||||
mDelayedRunnables.Clear();
|
mDelayedRunnables.Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
NS_IMPL_THREADSAFE_ISUPPORTS1(IndexedDatabaseManager::AsyncDeleteFileRunnable,
|
||||||
|
nsIRunnable)
|
||||||
|
|
||||||
|
NS_IMETHODIMP
|
||||||
|
IndexedDatabaseManager::AsyncDeleteFileRunnable::Run()
|
||||||
|
{
|
||||||
|
NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
|
||||||
|
|
||||||
|
int rc = sqlite3_quota_remove(NS_ConvertUTF16toUTF8(mFilePath).get());
|
||||||
|
if (rc != SQLITE_OK) {
|
||||||
|
NS_WARNING("Failed to delete stored file!");
|
||||||
|
return NS_ERROR_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
|
@ -40,6 +40,7 @@
|
||||||
#ifndef mozilla_dom_indexeddb_indexeddatabasemanager_h__
|
#ifndef mozilla_dom_indexeddb_indexeddatabasemanager_h__
|
||||||
#define mozilla_dom_indexeddb_indexeddatabasemanager_h__
|
#define mozilla_dom_indexeddb_indexeddatabasemanager_h__
|
||||||
|
|
||||||
|
#include "mozilla/dom/indexedDB/FileManager.h"
|
||||||
#include "mozilla/dom/indexedDB/IndexedDatabase.h"
|
#include "mozilla/dom/indexedDB/IndexedDatabase.h"
|
||||||
#include "mozilla/dom/indexedDB/IDBDatabase.h"
|
#include "mozilla/dom/indexedDB/IDBDatabase.h"
|
||||||
#include "mozilla/dom/indexedDB/IDBRequest.h"
|
#include "mozilla/dom/indexedDB/IDBRequest.h"
|
||||||
|
@ -103,6 +104,8 @@ public:
|
||||||
// Returns true if we've begun the shutdown process.
|
// Returns true if we've begun the shutdown process.
|
||||||
static bool IsShuttingDown();
|
static bool IsShuttingDown();
|
||||||
|
|
||||||
|
static bool IsClosed();
|
||||||
|
|
||||||
typedef void (*WaitingOnDatabasesCallback)(nsTArray<nsRefPtr<IDBDatabase> >&, void*);
|
typedef void (*WaitingOnDatabasesCallback)(nsTArray<nsRefPtr<IDBDatabase> >&, void*);
|
||||||
|
|
||||||
// Acquire exclusive access to the database given (waits for all others to
|
// Acquire exclusive access to the database given (waits for all others to
|
||||||
|
@ -148,7 +151,8 @@ public:
|
||||||
static PRUint32
|
static PRUint32
|
||||||
GetIndexedDBQuotaMB();
|
GetIndexedDBQuotaMB();
|
||||||
|
|
||||||
nsresult EnsureQuotaManagementForDirectory(nsIFile* aDirectory);
|
nsresult EnsureOriginIsInitialized(const nsACString& aOrigin,
|
||||||
|
nsIFile** aDirectory);
|
||||||
|
|
||||||
// Determine if the quota is lifted for the Window the current thread is
|
// Determine if the quota is lifted for the Window the current thread is
|
||||||
// using.
|
// using.
|
||||||
|
@ -173,6 +177,26 @@ public:
|
||||||
static nsresult
|
static nsresult
|
||||||
GetASCIIOriginFromWindow(nsPIDOMWindow* aWindow, nsCString& aASCIIOrigin);
|
GetASCIIOriginFromWindow(nsPIDOMWindow* aWindow, nsCString& aASCIIOrigin);
|
||||||
|
|
||||||
|
already_AddRefed<FileManager>
|
||||||
|
GetOrCreateFileManager(const nsACString& aOrigin,
|
||||||
|
const nsAString& aDatabaseName);
|
||||||
|
|
||||||
|
void InvalidateFileManagersForOrigin(const nsACString& aOrigin);
|
||||||
|
|
||||||
|
void InvalidateFileManager(const nsACString& aOrigin,
|
||||||
|
const nsAString& aDatabaseName);
|
||||||
|
|
||||||
|
nsresult AsyncDeleteFile(FileManager* aFileManager,
|
||||||
|
PRInt64 aFileId);
|
||||||
|
|
||||||
|
static mozilla::Mutex& FileMutex()
|
||||||
|
{
|
||||||
|
IndexedDatabaseManager* mgr = Get();
|
||||||
|
NS_ASSERTION(mgr, "Must have a manager here!");
|
||||||
|
|
||||||
|
return mgr->mFileMutex;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
IndexedDatabaseManager();
|
IndexedDatabaseManager();
|
||||||
~IndexedDatabaseManager();
|
~IndexedDatabaseManager();
|
||||||
|
@ -250,10 +274,14 @@ private:
|
||||||
// to the main thread in case of an error.
|
// to the main thread in case of an error.
|
||||||
inline nsresult RunInternal();
|
inline nsresult RunInternal();
|
||||||
|
|
||||||
|
nsresult GetUsageForDirectory(nsIFile* aDirectory,
|
||||||
|
PRUint64* aUsage);
|
||||||
|
|
||||||
nsCOMPtr<nsIURI> mURI;
|
nsCOMPtr<nsIURI> mURI;
|
||||||
nsCString mOrigin;
|
nsCString mOrigin;
|
||||||
nsCOMPtr<nsIIndexedDatabaseUsageCallback> mCallback;
|
nsCOMPtr<nsIIndexedDatabaseUsageCallback> mCallback;
|
||||||
PRUint64 mUsage;
|
PRUint64 mUsage;
|
||||||
|
PRUint64 mFileUsage;
|
||||||
PRInt32 mCanceled;
|
PRInt32 mCanceled;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -302,6 +330,19 @@ private:
|
||||||
SynchronizedOp* mOp;
|
SynchronizedOp* mOp;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class AsyncDeleteFileRunnable : public nsIRunnable
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
NS_DECL_ISUPPORTS
|
||||||
|
NS_DECL_NSIRUNNABLE
|
||||||
|
AsyncDeleteFileRunnable(const nsAString& aFilePath)
|
||||||
|
: mFilePath(aFilePath)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
private:
|
||||||
|
nsString mFilePath;
|
||||||
|
};
|
||||||
|
|
||||||
static nsresult DispatchHelper(AsyncConnectionHelper* aHelper);
|
static nsresult DispatchHelper(AsyncConnectionHelper* aHelper);
|
||||||
|
|
||||||
// Maintains a list of live databases per origin.
|
// Maintains a list of live databases per origin.
|
||||||
|
@ -316,6 +357,12 @@ private:
|
||||||
// A map of Windows to the corresponding quota helper.
|
// A map of Windows to the corresponding quota helper.
|
||||||
nsRefPtrHashtable<nsPtrHashKey<nsPIDOMWindow>, CheckQuotaHelper> mQuotaHelperHash;
|
nsRefPtrHashtable<nsPtrHashKey<nsPIDOMWindow>, CheckQuotaHelper> mQuotaHelperHash;
|
||||||
|
|
||||||
|
// Maintains a list of all file managers per origin. The list is actually also
|
||||||
|
// a list of all origins that were successfully initialized. This list
|
||||||
|
// isn't protected by any mutex but it is only ever touched on the IO thread.
|
||||||
|
nsClassHashtable<nsCStringHashKey,
|
||||||
|
nsTArray<nsRefPtr<FileManager> > > mFileManagers;
|
||||||
|
|
||||||
// Maintains a list of origins that we're currently enumerating to gather
|
// Maintains a list of origins that we're currently enumerating to gather
|
||||||
// usage statistics.
|
// usage statistics.
|
||||||
nsAutoTArray<nsRefPtr<AsyncUsageRunnable>, 1> mUsageRunnables;
|
nsAutoTArray<nsRefPtr<AsyncUsageRunnable>, 1> mUsageRunnables;
|
||||||
|
@ -333,10 +380,10 @@ private:
|
||||||
// thread during GetOrCreate().
|
// thread during GetOrCreate().
|
||||||
nsCOMPtr<mozIStorageQuotaCallback> mQuotaCallbackSingleton;
|
nsCOMPtr<mozIStorageQuotaCallback> mQuotaCallbackSingleton;
|
||||||
|
|
||||||
// A list of all paths that are under SQLite's quota tracking system. This
|
// Lock protecting FileManager.mFileInfos and nsDOMFileBase.mFileInfos
|
||||||
// list isn't protected by any mutex but it is only ever touched on the IO
|
// It's s also used to atomically update FileInfo.mRefCnt, FileInfo.mDBRefCnt
|
||||||
// thread.
|
// and FileInfo.mSliceRefCnt
|
||||||
nsTArray<nsCString> mTrackedQuotaPaths;
|
mozilla::Mutex mFileMutex;
|
||||||
};
|
};
|
||||||
|
|
||||||
class AutoEnterWindow
|
class AutoEnterWindow
|
||||||
|
|
|
@ -56,6 +56,8 @@ CPPSRCS = \
|
||||||
CheckPermissionsHelper.cpp \
|
CheckPermissionsHelper.cpp \
|
||||||
CheckQuotaHelper.cpp \
|
CheckQuotaHelper.cpp \
|
||||||
DatabaseInfo.cpp \
|
DatabaseInfo.cpp \
|
||||||
|
FileInfo.cpp \
|
||||||
|
FileManager.cpp \
|
||||||
IDBCursor.cpp \
|
IDBCursor.cpp \
|
||||||
IDBDatabase.cpp \
|
IDBDatabase.cpp \
|
||||||
IDBEvents.cpp \
|
IDBEvents.cpp \
|
||||||
|
@ -85,9 +87,12 @@ EXPORTS_mozilla/dom/indexedDB = \
|
||||||
IDBFactory.h \
|
IDBFactory.h \
|
||||||
Key.h \
|
Key.h \
|
||||||
LazyIdleThread.h \
|
LazyIdleThread.h \
|
||||||
|
FileManager.h \
|
||||||
|
FileInfo.h \
|
||||||
$(NULL)
|
$(NULL)
|
||||||
|
|
||||||
LOCAL_INCLUDES = \
|
LOCAL_INCLUDES = \
|
||||||
|
-I$(topsrcdir)/db/sqlite3/src \
|
||||||
-I$(topsrcdir)/xpcom/build \
|
-I$(topsrcdir)/xpcom/build \
|
||||||
-I$(topsrcdir)/dom/base \
|
-I$(topsrcdir)/dom/base \
|
||||||
-I$(topsrcdir)/dom/src/storage \
|
-I$(topsrcdir)/dom/src/storage \
|
||||||
|
|
|
@ -45,6 +45,7 @@
|
||||||
#include "nsEscape.h"
|
#include "nsEscape.h"
|
||||||
#include "nsThreadUtils.h"
|
#include "nsThreadUtils.h"
|
||||||
#include "snappy/snappy.h"
|
#include "snappy/snappy.h"
|
||||||
|
#include "test_quota.h"
|
||||||
|
|
||||||
#include "IDBEvents.h"
|
#include "IDBEvents.h"
|
||||||
#include "IDBFactory.h"
|
#include "IDBFactory.h"
|
||||||
|
@ -59,7 +60,7 @@ namespace {
|
||||||
PR_STATIC_ASSERT(JS_STRUCTURED_CLONE_VERSION == 1);
|
PR_STATIC_ASSERT(JS_STRUCTURED_CLONE_VERSION == 1);
|
||||||
|
|
||||||
// Major schema version. Bump for almost everything.
|
// Major schema version. Bump for almost everything.
|
||||||
const PRUint32 kMajorSchemaVersion = 9;
|
const PRUint32 kMajorSchemaVersion = 10;
|
||||||
|
|
||||||
// Minor schema version. Should almost always be 0 (maybe bump on release
|
// Minor schema version. Should almost always be 0 (maybe bump on release
|
||||||
// branches if we have to).
|
// branches if we have to).
|
||||||
|
@ -95,24 +96,10 @@ PRUint32 GetMinorSchemaVersion(PRInt32 aSchemaVersion)
|
||||||
}
|
}
|
||||||
|
|
||||||
nsresult
|
nsresult
|
||||||
GetDatabaseFile(const nsACString& aASCIIOrigin,
|
GetDatabaseFilename(const nsAString& aName,
|
||||||
const nsAString& aName,
|
nsAString& aDatabaseFilename)
|
||||||
nsIFile** aDatabaseFile)
|
|
||||||
{
|
{
|
||||||
NS_ASSERTION(!aASCIIOrigin.IsEmpty() && !aName.IsEmpty(), "Bad arguments!");
|
aDatabaseFilename.AppendInt(HashString(aName));
|
||||||
|
|
||||||
nsCOMPtr<nsIFile> dbFile;
|
|
||||||
nsresult rv = IDBFactory::GetDirectory(getter_AddRefs(dbFile));
|
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
|
||||||
|
|
||||||
NS_ConvertASCIItoUTF16 originSanitized(aASCIIOrigin);
|
|
||||||
originSanitized.ReplaceChar(":/", '+');
|
|
||||||
|
|
||||||
rv = dbFile->Append(originSanitized);
|
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
|
||||||
|
|
||||||
nsAutoString filename;
|
|
||||||
filename.AppendInt(HashString(aName));
|
|
||||||
|
|
||||||
nsCString escapedName;
|
nsCString escapedName;
|
||||||
if (!NS_Escape(NS_ConvertUTF16toUTF8(aName), escapedName, url_XPAlphas)) {
|
if (!NS_Escape(NS_ConvertUTF16toUTF8(aName), escapedName, url_XPAlphas)) {
|
||||||
|
@ -133,13 +120,97 @@ GetDatabaseFile(const nsACString& aASCIIOrigin,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
filename.Append(NS_ConvertASCIItoUTF16(substring));
|
aDatabaseFilename.Append(NS_ConvertASCIItoUTF16(substring));
|
||||||
filename.AppendLiteral(".sqlite");
|
|
||||||
|
|
||||||
rv = dbFile->Append(filename);
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
nsresult
|
||||||
|
CreateFileTables(mozIStorageConnection* aDBConn)
|
||||||
|
{
|
||||||
|
// Table `file`
|
||||||
|
nsresult rv = aDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
|
||||||
|
"CREATE TABLE file ("
|
||||||
|
"id INTEGER PRIMARY KEY, "
|
||||||
|
"refcount INTEGER NOT NULL"
|
||||||
|
");"
|
||||||
|
));
|
||||||
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
|
rv = aDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
|
||||||
|
"CREATE TRIGGER object_data_insert_trigger "
|
||||||
|
"AFTER INSERT ON object_data "
|
||||||
|
"FOR EACH ROW "
|
||||||
|
"WHEN NEW.file_ids IS NOT NULL "
|
||||||
|
"BEGIN "
|
||||||
|
"SELECT update_refcount(NULL, NEW.file_ids); "
|
||||||
|
"END;"
|
||||||
|
));
|
||||||
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
|
rv = aDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
|
||||||
|
"CREATE TRIGGER object_data_update_trigger "
|
||||||
|
"AFTER UPDATE OF file_ids ON object_data "
|
||||||
|
"FOR EACH ROW "
|
||||||
|
"WHEN OLD.file_ids IS NOT NULL OR NEW.file_ids IS NOT NULL "
|
||||||
|
"BEGIN "
|
||||||
|
"SELECT update_refcount(OLD.file_ids, NEW.file_ids); "
|
||||||
|
"END;"
|
||||||
|
));
|
||||||
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
|
rv = aDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
|
||||||
|
"CREATE TRIGGER object_data_delete_trigger "
|
||||||
|
"AFTER DELETE ON object_data "
|
||||||
|
"FOR EACH ROW WHEN OLD.file_ids IS NOT NULL "
|
||||||
|
"BEGIN "
|
||||||
|
"SELECT update_refcount(OLD.file_ids, NULL); "
|
||||||
|
"END;"
|
||||||
|
));
|
||||||
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
|
rv = aDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
|
||||||
|
"CREATE TRIGGER ai_object_data_insert_trigger "
|
||||||
|
"AFTER INSERT ON ai_object_data "
|
||||||
|
"FOR EACH ROW "
|
||||||
|
"WHEN NEW.file_ids IS NOT NULL "
|
||||||
|
"BEGIN "
|
||||||
|
"SELECT update_refcount(NULL, NEW.file_ids); "
|
||||||
|
"END;"
|
||||||
|
));
|
||||||
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
|
rv = aDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
|
||||||
|
"CREATE TRIGGER ai_object_data_update_trigger "
|
||||||
|
"AFTER UPDATE OF file_ids ON ai_object_data "
|
||||||
|
"FOR EACH ROW "
|
||||||
|
"WHEN OLD.file_ids IS NOT NULL OR NEW.file_ids IS NOT NULL "
|
||||||
|
"BEGIN "
|
||||||
|
"SELECT update_refcount(OLD.file_ids, NEW.file_ids); "
|
||||||
|
"END;"
|
||||||
|
));
|
||||||
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
|
rv = aDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
|
||||||
|
"CREATE TRIGGER ai_object_data_delete_trigger "
|
||||||
|
"AFTER DELETE ON ai_object_data "
|
||||||
|
"FOR EACH ROW WHEN OLD.file_ids IS NOT NULL "
|
||||||
|
"BEGIN "
|
||||||
|
"SELECT update_refcount(OLD.file_ids, NULL); "
|
||||||
|
"END;"
|
||||||
|
));
|
||||||
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
|
rv = aDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
|
||||||
|
"CREATE TRIGGER file_update_trigger "
|
||||||
|
"AFTER UPDATE ON file "
|
||||||
|
"FOR EACH ROW WHEN NEW.refcount = 0 "
|
||||||
|
"BEGIN "
|
||||||
|
"DELETE FROM file WHERE id = OLD.id; "
|
||||||
|
"END;"
|
||||||
|
));
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
dbFile.forget(aDatabaseFile);
|
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -178,6 +249,7 @@ CreateTables(mozIStorageConnection* aDBConn)
|
||||||
"object_store_id INTEGER NOT NULL, "
|
"object_store_id INTEGER NOT NULL, "
|
||||||
"key_value DEFAULT NULL, "
|
"key_value DEFAULT NULL, "
|
||||||
"data BLOB NOT NULL, "
|
"data BLOB NOT NULL, "
|
||||||
|
"file_ids TEXT, "
|
||||||
"UNIQUE (object_store_id, key_value), "
|
"UNIQUE (object_store_id, key_value), "
|
||||||
"FOREIGN KEY (object_store_id) REFERENCES object_store(id) ON DELETE "
|
"FOREIGN KEY (object_store_id) REFERENCES object_store(id) ON DELETE "
|
||||||
"CASCADE"
|
"CASCADE"
|
||||||
|
@ -191,6 +263,7 @@ CreateTables(mozIStorageConnection* aDBConn)
|
||||||
"id INTEGER PRIMARY KEY AUTOINCREMENT, "
|
"id INTEGER PRIMARY KEY AUTOINCREMENT, "
|
||||||
"object_store_id INTEGER NOT NULL, "
|
"object_store_id INTEGER NOT NULL, "
|
||||||
"data BLOB NOT NULL, "
|
"data BLOB NOT NULL, "
|
||||||
|
"file_ids TEXT, "
|
||||||
"UNIQUE (object_store_id, id), "
|
"UNIQUE (object_store_id, id), "
|
||||||
"FOREIGN KEY (object_store_id) REFERENCES object_store(id) ON DELETE "
|
"FOREIGN KEY (object_store_id) REFERENCES object_store(id) ON DELETE "
|
||||||
"CASCADE"
|
"CASCADE"
|
||||||
|
@ -310,6 +383,9 @@ CreateTables(mozIStorageConnection* aDBConn)
|
||||||
));
|
));
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
|
rv = CreateFileTables(aDBConn);
|
||||||
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
rv = aDBConn->SetSchemaVersion(kSQLiteSchemaVersion);
|
rv = aDBConn->SetSchemaVersion(kSQLiteSchemaVersion);
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
|
@ -961,127 +1037,24 @@ UpgradeSchemaFrom8To9_0(mozIStorageConnection* aConnection)
|
||||||
}
|
}
|
||||||
|
|
||||||
nsresult
|
nsresult
|
||||||
CreateDatabaseConnection(const nsAString& aName,
|
UpgradeSchemaFrom9_0To10_0(mozIStorageConnection* aConnection)
|
||||||
nsIFile* aDBFile,
|
|
||||||
mozIStorageConnection** aConnection)
|
|
||||||
{
|
{
|
||||||
NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
|
nsresult rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
|
||||||
|
"ALTER TABLE object_data ADD COLUMN file_ids TEXT;"
|
||||||
NS_NAMED_LITERAL_CSTRING(quotaVFSName, "quota");
|
|
||||||
|
|
||||||
nsCOMPtr<mozIStorageServiceQuotaManagement> ss =
|
|
||||||
do_GetService(MOZ_STORAGE_SERVICE_CONTRACTID);
|
|
||||||
NS_ENSURE_TRUE(ss, NS_ERROR_FAILURE);
|
|
||||||
|
|
||||||
nsCOMPtr<mozIStorageConnection> connection;
|
|
||||||
nsresult rv = ss->OpenDatabaseWithVFS(aDBFile, quotaVFSName,
|
|
||||||
getter_AddRefs(connection));
|
|
||||||
if (rv == NS_ERROR_FILE_CORRUPTED) {
|
|
||||||
// Nuke the database file. The web services can recreate their data.
|
|
||||||
rv = aDBFile->Remove(false);
|
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
|
||||||
|
|
||||||
rv = ss->OpenDatabaseWithVFS(aDBFile, quotaVFSName,
|
|
||||||
getter_AddRefs(connection));
|
|
||||||
}
|
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
|
||||||
|
|
||||||
// Check to make sure that the database schema is correct.
|
|
||||||
PRInt32 schemaVersion;
|
|
||||||
rv = connection->GetSchemaVersion(&schemaVersion);
|
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
|
||||||
|
|
||||||
if (schemaVersion > kSQLiteSchemaVersion) {
|
|
||||||
NS_WARNING("Unable to open IndexedDB database, schema is too high!");
|
|
||||||
return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool vacuumNeeded = false;
|
|
||||||
|
|
||||||
if (schemaVersion != kSQLiteSchemaVersion) {
|
|
||||||
mozStorageTransaction transaction(connection, false,
|
|
||||||
mozIStorageConnection::TRANSACTION_IMMEDIATE);
|
|
||||||
|
|
||||||
if (!schemaVersion) {
|
|
||||||
// Brand new file, initialize our tables.
|
|
||||||
rv = CreateTables(connection);
|
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
|
||||||
|
|
||||||
NS_ASSERTION(NS_SUCCEEDED(connection->GetSchemaVersion(&schemaVersion)) &&
|
|
||||||
schemaVersion == kSQLiteSchemaVersion,
|
|
||||||
"CreateTables set a bad schema version!");
|
|
||||||
|
|
||||||
nsCOMPtr<mozIStorageStatement> stmt;
|
|
||||||
nsresult rv = connection->CreateStatement(NS_LITERAL_CSTRING(
|
|
||||||
"INSERT INTO database (name) "
|
|
||||||
"VALUES (:name)"
|
|
||||||
), getter_AddRefs(stmt));
|
|
||||||
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
|
|
||||||
|
|
||||||
rv = stmt->BindStringByName(NS_LITERAL_CSTRING("name"), aName);
|
|
||||||
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
|
|
||||||
|
|
||||||
rv = stmt->Execute();
|
|
||||||
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// This logic needs to change next time we change the schema!
|
|
||||||
PR_STATIC_ASSERT(kSQLiteSchemaVersion == PRInt32((9 << 4) + 0));
|
|
||||||
|
|
||||||
while (schemaVersion != kSQLiteSchemaVersion) {
|
|
||||||
if (schemaVersion == 4) {
|
|
||||||
rv = UpgradeSchemaFrom4To5(connection);
|
|
||||||
}
|
|
||||||
else if (schemaVersion == 5) {
|
|
||||||
rv = UpgradeSchemaFrom5To6(connection);
|
|
||||||
}
|
|
||||||
else if (schemaVersion == 6) {
|
|
||||||
rv = UpgradeSchemaFrom6To7(connection);
|
|
||||||
}
|
|
||||||
else if (schemaVersion == 7) {
|
|
||||||
rv = UpgradeSchemaFrom7To8(connection);
|
|
||||||
}
|
|
||||||
else if (schemaVersion == 8) {
|
|
||||||
rv = UpgradeSchemaFrom8To9_0(connection);
|
|
||||||
vacuumNeeded = true;
|
|
||||||
}
|
|
||||||
#if 0
|
|
||||||
else if (schemaVersion == MakeSchemaVersion(9, 0)) {
|
|
||||||
// Upgrade.
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
else {
|
|
||||||
NS_WARNING("Unable to open IndexedDB database, no upgrade path is "
|
|
||||||
"available!");
|
|
||||||
return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
|
|
||||||
}
|
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
|
||||||
|
|
||||||
rv = connection->GetSchemaVersion(&schemaVersion);
|
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
|
||||||
}
|
|
||||||
|
|
||||||
NS_ASSERTION(schemaVersion == kSQLiteSchemaVersion, "Huh?!");
|
|
||||||
}
|
|
||||||
|
|
||||||
rv = transaction.Commit();
|
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (vacuumNeeded) {
|
|
||||||
rv = connection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
|
|
||||||
"VACUUM;"
|
|
||||||
));
|
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Turn on foreign key constraints.
|
|
||||||
rv = connection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
|
|
||||||
"PRAGMA foreign_keys = ON;"
|
|
||||||
));
|
));
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
connection.forget(aConnection);
|
rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
|
||||||
|
"ALTER TABLE ai_object_data ADD COLUMN file_ids TEXT;"
|
||||||
|
));
|
||||||
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
|
rv = CreateFileTables(aConnection);
|
||||||
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
|
rv = aConnection->SetSchemaVersion(MakeSchemaVersion(10, 0));
|
||||||
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1130,8 +1103,8 @@ protected:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// In-params
|
// In-params
|
||||||
nsRefPtr<OpenDatabaseHelper> mOpenHelper;
|
|
||||||
nsRefPtr<IDBOpenDBRequest> mOpenRequest;
|
nsRefPtr<IDBOpenDBRequest> mOpenRequest;
|
||||||
|
nsRefPtr<OpenDatabaseHelper> mOpenHelper;
|
||||||
PRUint64 mRequestedVersion;
|
PRUint64 mRequestedVersion;
|
||||||
PRUint64 mCurrentVersion;
|
PRUint64 mCurrentVersion;
|
||||||
};
|
};
|
||||||
|
@ -1345,40 +1318,39 @@ OpenDatabaseHelper::DoDatabaseWork()
|
||||||
|
|
||||||
AutoEnterWindow autoWindow(window);
|
AutoEnterWindow autoWindow(window);
|
||||||
|
|
||||||
|
nsCOMPtr<nsIFile> dbDirectory;
|
||||||
|
|
||||||
|
IndexedDatabaseManager* mgr = IndexedDatabaseManager::Get();
|
||||||
|
NS_ASSERTION(mgr, "This should never be null!");
|
||||||
|
|
||||||
|
nsresult rv = mgr->EnsureOriginIsInitialized(mASCIIOrigin,
|
||||||
|
getter_AddRefs(dbDirectory));
|
||||||
|
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
|
||||||
|
|
||||||
|
nsAutoString filename;
|
||||||
|
rv = GetDatabaseFilename(mName, filename);
|
||||||
|
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
|
||||||
|
|
||||||
nsCOMPtr<nsIFile> dbFile;
|
nsCOMPtr<nsIFile> dbFile;
|
||||||
nsresult rv = GetDatabaseFile(mASCIIOrigin, mName, getter_AddRefs(dbFile));
|
rv = dbDirectory->Clone(getter_AddRefs(dbFile));
|
||||||
|
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
|
||||||
|
|
||||||
|
rv = dbFile->Append(filename + NS_LITERAL_STRING(".sqlite"));
|
||||||
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
|
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
|
||||||
|
|
||||||
rv = dbFile->GetPath(mDatabaseFilePath);
|
rv = dbFile->GetPath(mDatabaseFilePath);
|
||||||
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
|
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
|
||||||
|
|
||||||
nsCOMPtr<nsIFile> dbDirectory;
|
nsCOMPtr<nsIFile> fileManagerDirectory;
|
||||||
rv = dbFile->GetParent(getter_AddRefs(dbDirectory));
|
rv = dbDirectory->Clone(getter_AddRefs(fileManagerDirectory));
|
||||||
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
|
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
|
||||||
|
|
||||||
bool exists;
|
rv = fileManagerDirectory->Append(filename);
|
||||||
rv = dbDirectory->Exists(&exists);
|
|
||||||
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
|
|
||||||
|
|
||||||
if (exists) {
|
|
||||||
bool isDirectory;
|
|
||||||
rv = dbDirectory->IsDirectory(&isDirectory);
|
|
||||||
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
|
|
||||||
NS_ENSURE_TRUE(isDirectory, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
rv = dbDirectory->Create(nsIFile::DIRECTORY_TYPE, 0755);
|
|
||||||
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
|
|
||||||
}
|
|
||||||
|
|
||||||
IndexedDatabaseManager* mgr = IndexedDatabaseManager::Get();
|
|
||||||
NS_ASSERTION(mgr, "This should never be null!");
|
|
||||||
|
|
||||||
rv = mgr->EnsureQuotaManagementForDirectory(dbDirectory);
|
|
||||||
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
|
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
|
||||||
|
|
||||||
nsCOMPtr<mozIStorageConnection> connection;
|
nsCOMPtr<mozIStorageConnection> connection;
|
||||||
rv = CreateDatabaseConnection(mName, dbFile, getter_AddRefs(connection));
|
rv = CreateDatabaseConnection(mName, dbFile, fileManagerDirectory,
|
||||||
|
getter_AddRefs(connection));
|
||||||
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
|
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
|
||||||
|
|
||||||
rv = IDBFactory::LoadDatabaseInformation(connection, mDatabaseId,
|
rv = IDBFactory::LoadDatabaseInformation(connection, mDatabaseId,
|
||||||
|
@ -1422,6 +1394,175 @@ OpenDatabaseHelper::DoDatabaseWork()
|
||||||
mState = eSetVersionPending;
|
mState = eSetVersionPending;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mFileManager = mgr->GetOrCreateFileManager(mASCIIOrigin, mName);
|
||||||
|
NS_ENSURE_TRUE(mFileManager, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
|
||||||
|
|
||||||
|
if (!mFileManager->IsDirectoryInited()) {
|
||||||
|
rv = mFileManager->InitDirectory(fileManagerDirectory, connection);
|
||||||
|
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!mFileManager->Loaded()) {
|
||||||
|
rv = mFileManager->Load(connection);
|
||||||
|
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
|
||||||
|
}
|
||||||
|
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
// static
|
||||||
|
nsresult
|
||||||
|
OpenDatabaseHelper::CreateDatabaseConnection(
|
||||||
|
const nsAString& aName,
|
||||||
|
nsIFile* aDBFile,
|
||||||
|
nsIFile* aFileManagerDirectory,
|
||||||
|
mozIStorageConnection** aConnection)
|
||||||
|
{
|
||||||
|
NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
|
||||||
|
|
||||||
|
NS_NAMED_LITERAL_CSTRING(quotaVFSName, "quota");
|
||||||
|
|
||||||
|
nsCOMPtr<mozIStorageServiceQuotaManagement> ss =
|
||||||
|
do_GetService(MOZ_STORAGE_SERVICE_CONTRACTID);
|
||||||
|
NS_ENSURE_TRUE(ss, NS_ERROR_FAILURE);
|
||||||
|
|
||||||
|
nsCOMPtr<mozIStorageConnection> connection;
|
||||||
|
nsresult rv = ss->OpenDatabaseWithVFS(aDBFile, quotaVFSName,
|
||||||
|
getter_AddRefs(connection));
|
||||||
|
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
|
||||||
|
// initialization too.
|
||||||
|
if (aName.IsVoid()) {
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Nuke the database file. The web services can recreate their data.
|
||||||
|
rv = aDBFile->Remove(false);
|
||||||
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
|
bool exists;
|
||||||
|
rv = aFileManagerDirectory->Exists(&exists);
|
||||||
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
|
if (exists) {
|
||||||
|
bool isDirectory;
|
||||||
|
rv = aFileManagerDirectory->IsDirectory(&isDirectory);
|
||||||
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
NS_ENSURE_TRUE(isDirectory, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
|
||||||
|
|
||||||
|
rv = aFileManagerDirectory->Remove(true);
|
||||||
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
}
|
||||||
|
|
||||||
|
rv = ss->OpenDatabaseWithVFS(aDBFile, quotaVFSName,
|
||||||
|
getter_AddRefs(connection));
|
||||||
|
}
|
||||||
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
|
rv = connection->EnableModule(NS_LITERAL_CSTRING("filesystem"));
|
||||||
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
|
// Check to make sure that the database schema is correct.
|
||||||
|
PRInt32 schemaVersion;
|
||||||
|
rv = connection->GetSchemaVersion(&schemaVersion);
|
||||||
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
|
// Unknown schema will fail origin initialization too
|
||||||
|
if (!schemaVersion && aName.IsVoid()) {
|
||||||
|
NS_WARNING("Unable to open IndexedDB database, schema is not set!");
|
||||||
|
return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (schemaVersion > kSQLiteSchemaVersion) {
|
||||||
|
NS_WARNING("Unable to open IndexedDB database, schema is too high!");
|
||||||
|
return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool vacuumNeeded = false;
|
||||||
|
|
||||||
|
if (schemaVersion != kSQLiteSchemaVersion) {
|
||||||
|
mozStorageTransaction transaction(connection, false,
|
||||||
|
mozIStorageConnection::TRANSACTION_IMMEDIATE);
|
||||||
|
|
||||||
|
if (!schemaVersion) {
|
||||||
|
// Brand new file, initialize our tables.
|
||||||
|
rv = CreateTables(connection);
|
||||||
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
|
NS_ASSERTION(NS_SUCCEEDED(connection->GetSchemaVersion(&schemaVersion)) &&
|
||||||
|
schemaVersion == kSQLiteSchemaVersion,
|
||||||
|
"CreateTables set a bad schema version!");
|
||||||
|
|
||||||
|
nsCOMPtr<mozIStorageStatement> stmt;
|
||||||
|
nsresult rv = connection->CreateStatement(NS_LITERAL_CSTRING(
|
||||||
|
"INSERT INTO database (name) "
|
||||||
|
"VALUES (:name)"
|
||||||
|
), getter_AddRefs(stmt));
|
||||||
|
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
|
||||||
|
|
||||||
|
rv = stmt->BindStringByName(NS_LITERAL_CSTRING("name"), aName);
|
||||||
|
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
|
||||||
|
|
||||||
|
rv = stmt->Execute();
|
||||||
|
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// This logic needs to change next time we change the schema!
|
||||||
|
PR_STATIC_ASSERT(kSQLiteSchemaVersion == PRInt32((10 << 4) + 0));
|
||||||
|
|
||||||
|
while (schemaVersion != kSQLiteSchemaVersion) {
|
||||||
|
if (schemaVersion == 4) {
|
||||||
|
rv = UpgradeSchemaFrom4To5(connection);
|
||||||
|
}
|
||||||
|
else if (schemaVersion == 5) {
|
||||||
|
rv = UpgradeSchemaFrom5To6(connection);
|
||||||
|
}
|
||||||
|
else if (schemaVersion == 6) {
|
||||||
|
rv = UpgradeSchemaFrom6To7(connection);
|
||||||
|
}
|
||||||
|
else if (schemaVersion == 7) {
|
||||||
|
rv = UpgradeSchemaFrom7To8(connection);
|
||||||
|
}
|
||||||
|
else if (schemaVersion == 8) {
|
||||||
|
rv = UpgradeSchemaFrom8To9_0(connection);
|
||||||
|
vacuumNeeded = true;
|
||||||
|
}
|
||||||
|
else if (schemaVersion == MakeSchemaVersion(9, 0)) {
|
||||||
|
rv = UpgradeSchemaFrom9_0To10_0(connection);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
NS_WARNING("Unable to open IndexedDB database, no upgrade path is "
|
||||||
|
"available!");
|
||||||
|
return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
|
||||||
|
}
|
||||||
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
|
rv = connection->GetSchemaVersion(&schemaVersion);
|
||||||
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
}
|
||||||
|
|
||||||
|
NS_ASSERTION(schemaVersion == kSQLiteSchemaVersion, "Huh?!");
|
||||||
|
}
|
||||||
|
|
||||||
|
rv = transaction.Commit();
|
||||||
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (vacuumNeeded) {
|
||||||
|
rv = connection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
|
||||||
|
"VACUUM;"
|
||||||
|
));
|
||||||
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Turn on foreign key constraints.
|
||||||
|
rv = connection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
|
||||||
|
"PRAGMA foreign_keys = ON;"
|
||||||
|
));
|
||||||
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
|
connection.forget(aConnection);
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1540,6 +1681,11 @@ OpenDatabaseHelper::Run()
|
||||||
// Destroy the database now (we should have the only ref).
|
// Destroy the database now (we should have the only ref).
|
||||||
mDatabase = nsnull;
|
mDatabase = nsnull;
|
||||||
|
|
||||||
|
IndexedDatabaseManager* mgr = IndexedDatabaseManager::Get();
|
||||||
|
NS_ASSERTION(mgr, "This should never fail!");
|
||||||
|
|
||||||
|
mgr->InvalidateFileManager(mASCIIOrigin, mName);
|
||||||
|
|
||||||
mState = eFiringEvents;
|
mState = eFiringEvents;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -1669,7 +1815,8 @@ OpenDatabaseHelper::EnsureSuccessResult()
|
||||||
IDBDatabase::Create(mOpenDBRequest->ScriptContext(),
|
IDBDatabase::Create(mOpenDBRequest->ScriptContext(),
|
||||||
mOpenDBRequest->Owner(),
|
mOpenDBRequest->Owner(),
|
||||||
dbInfo.forget(),
|
dbInfo.forget(),
|
||||||
mASCIIOrigin);
|
mASCIIOrigin,
|
||||||
|
mFileManager);
|
||||||
if (!database) {
|
if (!database) {
|
||||||
return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
|
return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
|
||||||
}
|
}
|
||||||
|
@ -1888,19 +2035,68 @@ DeleteDatabaseHelper::DoDatabaseWork(mozIStorageConnection* aConnection)
|
||||||
{
|
{
|
||||||
NS_ASSERTION(!aConnection, "How did we get a connection here?");
|
NS_ASSERTION(!aConnection, "How did we get a connection here?");
|
||||||
|
|
||||||
nsCOMPtr<nsIFile> dbFile;
|
nsCOMPtr<nsIFile> directory;
|
||||||
nsresult rv = GetDatabaseFile(mASCIIOrigin, mName, getter_AddRefs(dbFile));
|
nsresult rv = IDBFactory::GetDirectoryForOrigin(mASCIIOrigin,
|
||||||
|
getter_AddRefs(directory));
|
||||||
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
|
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
|
||||||
|
|
||||||
NS_ASSERTION(dbFile, "What?");
|
NS_ASSERTION(directory, "What?");
|
||||||
|
|
||||||
|
nsAutoString filename;
|
||||||
|
rv = GetDatabaseFilename(mName, filename);
|
||||||
|
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
|
||||||
|
|
||||||
|
nsCOMPtr<nsIFile> dbFile;
|
||||||
|
rv = directory->Clone(getter_AddRefs(dbFile));
|
||||||
|
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
|
||||||
|
|
||||||
|
rv = dbFile->Append(filename + NS_LITERAL_STRING(".sqlite"));
|
||||||
|
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
|
||||||
|
|
||||||
bool exists = false;
|
bool exists = false;
|
||||||
rv = dbFile->Exists(&exists);
|
rv = dbFile->Exists(&exists);
|
||||||
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
|
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
|
||||||
|
|
||||||
|
int rc;
|
||||||
|
|
||||||
if (exists) {
|
if (exists) {
|
||||||
rv = dbFile->Remove(false);
|
nsString dbFilePath;
|
||||||
|
rv = dbFile->GetPath(dbFilePath);
|
||||||
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
|
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
nsCOMPtr<nsIFile> fileManagerDirectory;
|
||||||
|
rv = directory->Clone(getter_AddRefs(fileManagerDirectory));
|
||||||
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
|
rv = fileManagerDirectory->Append(filename);
|
||||||
|
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
|
||||||
|
|
||||||
|
rv = fileManagerDirectory->Exists(&exists);
|
||||||
|
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
|
||||||
|
|
||||||
|
if (exists) {
|
||||||
|
bool isDirectory;
|
||||||
|
rv = fileManagerDirectory->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);
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
|
|
|
@ -102,6 +102,12 @@ public:
|
||||||
return mDatabase;
|
return mDatabase;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static
|
||||||
|
nsresult CreateDatabaseConnection(const nsAString& aName,
|
||||||
|
nsIFile* aDBFile,
|
||||||
|
nsIFile* aFileManagerDirectory,
|
||||||
|
mozIStorageConnection** aConnection);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
// Methods only called on the main thread
|
// Methods only called on the main thread
|
||||||
nsresult EnsureSuccessResult();
|
nsresult EnsureSuccessResult();
|
||||||
|
@ -145,6 +151,8 @@ private:
|
||||||
};
|
};
|
||||||
OpenDatabaseState mState;
|
OpenDatabaseState mState;
|
||||||
nsresult mResultCode;
|
nsresult mResultCode;
|
||||||
|
|
||||||
|
nsRefPtr<FileManager> mFileManager;
|
||||||
};
|
};
|
||||||
|
|
||||||
END_INDEXEDDB_NAMESPACE
|
END_INDEXEDDB_NAMESPACE
|
||||||
|
|
|
@ -87,7 +87,7 @@ public:
|
||||||
// committed, for aDatabase.
|
// committed, for aDatabase.
|
||||||
void AbortTransactionsForDatabase(IDBDatabase* aDatabase);
|
void AbortTransactionsForDatabase(IDBDatabase* aDatabase);
|
||||||
|
|
||||||
// Returns true iff there are running or pending transactions for aDatabase.
|
// Returns true if there are running or pending transactions for aDatabase.
|
||||||
bool HasTransactionsForDatabase(IDBDatabase* aDatabase);
|
bool HasTransactionsForDatabase(IDBDatabase* aDatabase);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
|
@ -41,14 +41,15 @@
|
||||||
|
|
||||||
interface nsIURI;
|
interface nsIURI;
|
||||||
|
|
||||||
[scriptable, function, uuid(17675af5-0569-4f5b-987f-ff4bb60f73ee)]
|
[scriptable, function, uuid(ef1795ec-7050-4658-b80f-0e48cbe1d64b)]
|
||||||
interface nsIIndexedDatabaseUsageCallback : nsISupports
|
interface nsIIndexedDatabaseUsageCallback : nsISupports
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
void onUsageResult(in nsIURI aURI,
|
void onUsageResult(in nsIURI aURI,
|
||||||
in unsigned long long aUsage);
|
in unsigned long long aUsage,
|
||||||
|
in unsigned long long aFileUsage);
|
||||||
};
|
};
|
||||||
|
|
||||||
[scriptable, builtinclass, uuid(415f5684-6c84-4a8b-b777-d01f5df778f2)]
|
[scriptable, builtinclass, uuid(415f5684-6c84-4a8b-b777-d01f5df778f2)]
|
||||||
|
|
|
@ -50,6 +50,7 @@ TEST_FILES = \
|
||||||
error_events_abort_transactions_iframe.html \
|
error_events_abort_transactions_iframe.html \
|
||||||
event_propagation_iframe.html \
|
event_propagation_iframe.html \
|
||||||
exceptions_in_success_events_iframe.html \
|
exceptions_in_success_events_iframe.html \
|
||||||
|
file.js \
|
||||||
helpers.js \
|
helpers.js \
|
||||||
leaving_page_iframe.html \
|
leaving_page_iframe.html \
|
||||||
test_add_put.html \
|
test_add_put.html \
|
||||||
|
@ -73,6 +74,15 @@ TEST_FILES = \
|
||||||
test_event_propagation.html \
|
test_event_propagation.html \
|
||||||
test_event_source.html \
|
test_event_source.html \
|
||||||
test_exceptions_in_success_events.html \
|
test_exceptions_in_success_events.html \
|
||||||
|
test_file_array.html \
|
||||||
|
test_file_cross_database_copying.html \
|
||||||
|
test_file_delete.html \
|
||||||
|
test_file_os_delete.html \
|
||||||
|
test_file_put_get_values.html \
|
||||||
|
test_file_resurrection_delete.html \
|
||||||
|
test_file_resurrection_transaction_abort.html \
|
||||||
|
test_file_sharing.html \
|
||||||
|
test_file_transaction_abort.html \
|
||||||
test_getAll.html \
|
test_getAll.html \
|
||||||
test_global_data.html \
|
test_global_data.html \
|
||||||
test_index_empty_keyPath.html \
|
test_index_empty_keyPath.html \
|
||||||
|
|
|
@ -0,0 +1,225 @@
|
||||||
|
/**
|
||||||
|
* Any copyright is dedicated to the Public Domain.
|
||||||
|
* http://creativecommons.org/publicdomain/zero/1.0/
|
||||||
|
*/
|
||||||
|
|
||||||
|
var builder = new MozBlobBuilder();
|
||||||
|
var manager = null;
|
||||||
|
var bufferCache = [];
|
||||||
|
var utils = SpecialPowers.getDOMWindowUtils(window);
|
||||||
|
|
||||||
|
function getBuffer(size)
|
||||||
|
{
|
||||||
|
let buffer = new ArrayBuffer(size);
|
||||||
|
is(buffer.byteLength, size, "Correct byte length");
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getRandomBuffer(size)
|
||||||
|
{
|
||||||
|
let buffer = getBuffer(size);
|
||||||
|
let view = new Uint8Array(buffer);
|
||||||
|
for (let i = 0; i < size; i++) {
|
||||||
|
view[i] = parseInt(Math.random() * 255)
|
||||||
|
}
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
function compareBuffers(buffer1, buffer2)
|
||||||
|
{
|
||||||
|
if (buffer1.byteLength != buffer2.byteLength) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
let view1 = new Uint8Array(buffer1);
|
||||||
|
let view2 = new Uint8Array(buffer2);
|
||||||
|
for (let i = 0; i < buffer1.byteLength; i++) {
|
||||||
|
if (view1[i] != view2[i]) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getBlob(type, buffer)
|
||||||
|
{
|
||||||
|
builder.append(buffer);
|
||||||
|
return builder.getBlob(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getFile(name, type, buffer)
|
||||||
|
{
|
||||||
|
builder.append(buffer);
|
||||||
|
return builder.getFile(name, type);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getRandomBlob(size)
|
||||||
|
{
|
||||||
|
return getBlob("binary/random", getRandomBuffer(size));
|
||||||
|
}
|
||||||
|
|
||||||
|
function getRandomFile(name, size)
|
||||||
|
{
|
||||||
|
return getFile(name, "binary/random", getRandomBuffer(size));
|
||||||
|
}
|
||||||
|
|
||||||
|
function getNullBlob(size)
|
||||||
|
{
|
||||||
|
return getBlob("binary/null", getBuffer(size));
|
||||||
|
}
|
||||||
|
|
||||||
|
function getNullFile(name, size)
|
||||||
|
{
|
||||||
|
return getFile(name, "binary/null", getBuffer(size));
|
||||||
|
}
|
||||||
|
|
||||||
|
function verifyBuffers(buffer1, buffer2)
|
||||||
|
{
|
||||||
|
ok(compareBuffers(buffer1, buffer2), "Correct blob data");
|
||||||
|
}
|
||||||
|
|
||||||
|
function verifyBlob(blob1, blob2, fileId, blobReadHandler)
|
||||||
|
{
|
||||||
|
is(blob1 instanceof Components.interfaces.nsIDOMBlob, true,
|
||||||
|
"Instance of nsIDOMBlob");
|
||||||
|
is(blob1 instanceof Components.interfaces.nsIDOMFile,
|
||||||
|
blob2 instanceof Components.interfaces.nsIDOMFile,
|
||||||
|
"Instance of nsIDOMFile");
|
||||||
|
is(blob1.size, blob2.size, "Correct size");
|
||||||
|
is(blob1.type, blob2.type, "Correct type");
|
||||||
|
if (blob2 instanceof Components.interfaces.nsIDOMFile) {
|
||||||
|
is(blob1.name, blob2.name, "Correct name");
|
||||||
|
}
|
||||||
|
is(utils.getFileId(blob1), fileId, "Correct file id");
|
||||||
|
|
||||||
|
let buffer1;
|
||||||
|
let buffer2;
|
||||||
|
|
||||||
|
for (let i = 0; i < bufferCache.length; i++) {
|
||||||
|
if (bufferCache[i].blob == blob2) {
|
||||||
|
buffer2 = bufferCache[i].buffer;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!buffer2) {
|
||||||
|
let reader = new FileReader();
|
||||||
|
reader.readAsArrayBuffer(blob2);
|
||||||
|
reader.onload = function(event) {
|
||||||
|
buffer2 = event.target.result;
|
||||||
|
bufferCache.push({ blob: blob2, buffer: buffer2 });
|
||||||
|
if (buffer1) {
|
||||||
|
verifyBuffers(buffer1, buffer2);
|
||||||
|
if (blobReadHandler) {
|
||||||
|
blobReadHandler();
|
||||||
|
} else {
|
||||||
|
testGenerator.next();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let reader = new FileReader();
|
||||||
|
reader.readAsArrayBuffer(blob1);
|
||||||
|
reader.onload = function(event) {
|
||||||
|
buffer1 = event.target.result;
|
||||||
|
if (buffer2) {
|
||||||
|
verifyBuffers(buffer1, buffer2);
|
||||||
|
if (blobReadHandler) {
|
||||||
|
blobReadHandler();
|
||||||
|
} else {
|
||||||
|
testGenerator.next();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function verifyBlobArray(blobs1, blobs2, expectedFileIds)
|
||||||
|
{
|
||||||
|
is(blobs1 instanceof Array, true, "Got an array object");
|
||||||
|
is(blobs1.length, blobs2.length, "Correct length");
|
||||||
|
|
||||||
|
if (!blobs1.length) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let verifiedCount = 0;
|
||||||
|
|
||||||
|
function blobReadHandler() {
|
||||||
|
if (++verifiedCount == blobs1.length) {
|
||||||
|
testGenerator.next();
|
||||||
|
} else {
|
||||||
|
verifyBlob(blobs1[verifiedCount], blobs2[verifiedCount],
|
||||||
|
expectedFileIds[verifiedCount], blobReadHandler);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
verifyBlob(blobs1[verifiedCount], blobs2[verifiedCount],
|
||||||
|
expectedFileIds[verifiedCount], blobReadHandler);
|
||||||
|
}
|
||||||
|
|
||||||
|
function grabFileUsageAndContinueHandler(usage, fileUsage)
|
||||||
|
{
|
||||||
|
testGenerator.send(fileUsage);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getUsage(usageHandler)
|
||||||
|
{
|
||||||
|
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
|
||||||
|
|
||||||
|
if (!manager) {
|
||||||
|
manager = Components.classes["@mozilla.org/dom/indexeddb/manager;1"]
|
||||||
|
.getService(Components.interfaces.nsIIndexedDatabaseManager);
|
||||||
|
}
|
||||||
|
|
||||||
|
let uri = SpecialPowers.getDocumentURIObject(window.document);
|
||||||
|
let callback = {
|
||||||
|
onUsageResult: function(uri, usage, fileUsage) {
|
||||||
|
usageHandler(usage, fileUsage);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
manager.getUsageForURI(uri, callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getUsageSync()
|
||||||
|
{
|
||||||
|
let usage;
|
||||||
|
|
||||||
|
getUsage(function(aUsage, aFileUsage) {
|
||||||
|
usage = aUsage;
|
||||||
|
});
|
||||||
|
|
||||||
|
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
|
||||||
|
let thread = Components.classes["@mozilla.org/thread-manager;1"]
|
||||||
|
.getService(Components.interfaces.nsIThreadManager)
|
||||||
|
.currentThread;
|
||||||
|
while (!usage) {
|
||||||
|
thread.processNextEvent(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
return usage;
|
||||||
|
}
|
||||||
|
|
||||||
|
function scheduleGC()
|
||||||
|
{
|
||||||
|
SpecialPowers.exactGC(window, continueToNextStep);
|
||||||
|
}
|
||||||
|
|
||||||
|
function hasFileInfo(name, id)
|
||||||
|
{
|
||||||
|
return utils.getFileReferences(name, id);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getFileRefCount(name, id)
|
||||||
|
{
|
||||||
|
let count = {};
|
||||||
|
utils.getFileReferences(name, id, count);
|
||||||
|
return count.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getFileDBRefCount(name, id)
|
||||||
|
{
|
||||||
|
let count = {};
|
||||||
|
utils.getFileReferences(name, id, {}, count);
|
||||||
|
return count.value;
|
||||||
|
}
|
|
@ -8,6 +8,7 @@ var testGenerator = testSteps();
|
||||||
function runTest()
|
function runTest()
|
||||||
{
|
{
|
||||||
allowIndexedDB();
|
allowIndexedDB();
|
||||||
|
allowUnlimitedQuota();
|
||||||
|
|
||||||
SimpleTest.waitForExplicitFinish();
|
SimpleTest.waitForExplicitFinish();
|
||||||
testGenerator.next();
|
testGenerator.next();
|
||||||
|
@ -15,7 +16,8 @@ function runTest()
|
||||||
|
|
||||||
function finishTest()
|
function finishTest()
|
||||||
{
|
{
|
||||||
disallowIndexedDB();
|
resetUnlimitedQuota();
|
||||||
|
resetIndexedDB();
|
||||||
|
|
||||||
SimpleTest.executeSoon(function() {
|
SimpleTest.executeSoon(function() {
|
||||||
testGenerator.close();
|
testGenerator.close();
|
||||||
|
@ -77,7 +79,7 @@ ExpectError.prototype = {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
function addPermission(permission, url)
|
function addPermission(type, allow, url)
|
||||||
{
|
{
|
||||||
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
|
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
|
||||||
|
|
||||||
|
@ -86,15 +88,20 @@ function addPermission(permission, url)
|
||||||
uri = Components.classes["@mozilla.org/network/io-service;1"]
|
uri = Components.classes["@mozilla.org/network/io-service;1"]
|
||||||
.getService(Components.interfaces.nsIIOService)
|
.getService(Components.interfaces.nsIIOService)
|
||||||
.newURI(url, null, null);
|
.newURI(url, null, null);
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
uri = SpecialPowers.getDocumentURIObject(window.document);
|
uri = SpecialPowers.getDocumentURIObject(window.document);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let permission;
|
||||||
|
if (allow) {
|
||||||
|
permission = Components.interfaces.nsIPermissionManager.ALLOW_ACTION;
|
||||||
|
} else {
|
||||||
|
permission = Components.interfaces.nsIPermissionManager.DENY_ACTION;
|
||||||
|
}
|
||||||
|
|
||||||
Components.classes["@mozilla.org/permissionmanager;1"]
|
Components.classes["@mozilla.org/permissionmanager;1"]
|
||||||
.getService(Components.interfaces.nsIPermissionManager)
|
.getService(Components.interfaces.nsIPermissionManager)
|
||||||
.add(uri, permission,
|
.add(uri, type, permission);
|
||||||
Components.interfaces.nsIPermissionManager.ALLOW_ACTION);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function removePermission(permission, url)
|
function removePermission(permission, url)
|
||||||
|
@ -128,20 +135,25 @@ function setQuota(quota)
|
||||||
|
|
||||||
function allowIndexedDB(url)
|
function allowIndexedDB(url)
|
||||||
{
|
{
|
||||||
addPermission("indexedDB", url);
|
addPermission("indexedDB", true, url);
|
||||||
}
|
}
|
||||||
|
|
||||||
function disallowIndexedDB(url)
|
function resetIndexedDB(url)
|
||||||
{
|
{
|
||||||
removePermission("indexedDB", url);
|
removePermission("indexedDB", url);
|
||||||
}
|
}
|
||||||
|
|
||||||
function allowUnlimitedQuota(url)
|
function allowUnlimitedQuota(url)
|
||||||
{
|
{
|
||||||
addPermission("indexedDB-unlimited", url);
|
addPermission("indexedDB-unlimited", true, url);
|
||||||
}
|
}
|
||||||
|
|
||||||
function disallowUnlimitedQuota(url)
|
function denyUnlimitedQuota(url)
|
||||||
|
{
|
||||||
|
addPermission("indexedDB-unlimited", false, url);
|
||||||
|
}
|
||||||
|
|
||||||
|
function resetUnlimitedQuota(url)
|
||||||
{
|
{
|
||||||
removePermission("indexedDB-unlimited", url);
|
removePermission("indexedDB-unlimited", url);
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,84 @@
|
||||||
|
<!--
|
||||||
|
Any copyright is dedicated to the Public Domain.
|
||||||
|
http://creativecommons.org/publicdomain/zero/1.0/
|
||||||
|
-->
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>Indexed Database Property Test</title>
|
||||||
|
|
||||||
|
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||||
|
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||||
|
|
||||||
|
<script type="text/javascript;version=1.7">
|
||||||
|
function testSteps()
|
||||||
|
{
|
||||||
|
const name = window.location.pathname;
|
||||||
|
const description = "My Test Database";
|
||||||
|
|
||||||
|
const objectStoreName = "Blobs";
|
||||||
|
|
||||||
|
const b1 = getRandomBlob(10000);
|
||||||
|
|
||||||
|
const b2 = [ getRandomBlob(5000), getRandomBlob(3000), getRandomBlob(12000),
|
||||||
|
getRandomBlob(17000), getRandomBlob(16000), getRandomBlob(16000),
|
||||||
|
getRandomBlob(8000)
|
||||||
|
];
|
||||||
|
|
||||||
|
const b3 = [ getRandomBlob(5000), getRandomBlob(3000), getRandomBlob(9000)];
|
||||||
|
|
||||||
|
const objectStoreData = [
|
||||||
|
{ key: 1, blobs: [ b1, b1, b1, b1, b1, b1, b1, b1, b1, b1 ],
|
||||||
|
expectedFileIds: [1, 1, 1, 1, 1, 1, 1, 1, 1, 1] },
|
||||||
|
{ key: 2, blobs: [ b2[0], b2[1], b2[2], b2[3], b2[4], b2[5], b2[6] ],
|
||||||
|
expectedFileIds: [2, 3, 4, 5, 6, 7, 8] },
|
||||||
|
{ key: 3, blobs: [ b3[0], b3[0], b3[1], b3[2], b3[2], b3[0], b3[0] ],
|
||||||
|
expectedFileIds: [9, 9, 10, 11, 11, 9, 9] }
|
||||||
|
];
|
||||||
|
|
||||||
|
let request = mozIndexedDB.open(name, 1, description);
|
||||||
|
request.onerror = errorHandler;
|
||||||
|
request.onupgradeneeded = grabEventAndContinueHandler;
|
||||||
|
request.onsuccess = grabEventAndContinueHandler;
|
||||||
|
let event = yield;
|
||||||
|
|
||||||
|
is(event.type, "upgradeneeded", "Got correct event type");
|
||||||
|
|
||||||
|
let db = event.target.result;
|
||||||
|
db.onerror = errorHandler;
|
||||||
|
|
||||||
|
let objectStore = db.createObjectStore(objectStoreName, { });
|
||||||
|
|
||||||
|
for each (let data in objectStoreData) {
|
||||||
|
objectStore.add(data.blobs, data.key);
|
||||||
|
}
|
||||||
|
|
||||||
|
event = yield;
|
||||||
|
|
||||||
|
is(event.type, "success", "Got correct event type");
|
||||||
|
|
||||||
|
for each (let data in objectStoreData) {
|
||||||
|
objectStore = db.transaction([objectStoreName])
|
||||||
|
.objectStore(objectStoreName);
|
||||||
|
|
||||||
|
request = objectStore.get(data.key);
|
||||||
|
request.onsuccess = grabEventAndContinueHandler;
|
||||||
|
event = yield;
|
||||||
|
|
||||||
|
verifyBlobArray(event.target.result, data.blobs, data.expectedFileIds);
|
||||||
|
yield;
|
||||||
|
}
|
||||||
|
|
||||||
|
is(bufferCache.length, 11, "Correct length");
|
||||||
|
|
||||||
|
finishTest();
|
||||||
|
yield;
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<script type="text/javascript;version=1.7" src="file.js"></script>
|
||||||
|
<script type="text/javascript;version=1.7" src="helpers.js"></script>
|
||||||
|
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body onload="runTest();"></body>
|
||||||
|
|
||||||
|
</html>
|
|
@ -0,0 +1,106 @@
|
||||||
|
<!--
|
||||||
|
Any copyright is dedicated to the Public Domain.
|
||||||
|
http://creativecommons.org/publicdomain/zero/1.0/
|
||||||
|
-->
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>Indexed Database Property Test</title>
|
||||||
|
|
||||||
|
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||||
|
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||||
|
|
||||||
|
<script type="text/javascript;version=1.7">
|
||||||
|
function testSteps()
|
||||||
|
{
|
||||||
|
const READ_WRITE = IDBTransaction.READ_WRITE;
|
||||||
|
|
||||||
|
const databaseInfo = [
|
||||||
|
{ name: window.location.pathname + "1", description: "Test Database 1" },
|
||||||
|
{ name: window.location.pathname + "2", description: "Test Database 2" }
|
||||||
|
];
|
||||||
|
|
||||||
|
const objectStoreName = "Blobs";
|
||||||
|
|
||||||
|
const fileData = { key: 1, file: getRandomFile("random.bin", 100000) };
|
||||||
|
|
||||||
|
let databases = [];
|
||||||
|
for each (let info in databaseInfo) {
|
||||||
|
let request = mozIndexedDB.open(info.name, 1, info.description);
|
||||||
|
request.onerror = errorHandler;
|
||||||
|
request.onupgradeneeded = grabEventAndContinueHandler;
|
||||||
|
request.onsuccess = grabEventAndContinueHandler;
|
||||||
|
let event = yield;
|
||||||
|
|
||||||
|
is(event.type, "upgradeneeded", "Got correct event type");
|
||||||
|
|
||||||
|
let db = event.target.result;
|
||||||
|
db.onerror = errorHandler;
|
||||||
|
|
||||||
|
let objectStore = db.createObjectStore(objectStoreName, { });
|
||||||
|
objectStore.add(fileData.file, fileData.key);
|
||||||
|
|
||||||
|
event = yield;
|
||||||
|
|
||||||
|
is(event.type, "success", "Got correct event type");
|
||||||
|
|
||||||
|
databases.push(db);
|
||||||
|
}
|
||||||
|
|
||||||
|
let refResult;
|
||||||
|
for each (let db in databases) {
|
||||||
|
let request = db.transaction([objectStoreName])
|
||||||
|
.objectStore(objectStoreName).get(fileData.key);
|
||||||
|
request.onsuccess = grabEventAndContinueHandler;
|
||||||
|
event = yield;
|
||||||
|
|
||||||
|
let result = event.target.result;
|
||||||
|
verifyBlob(result, fileData.file, 1);
|
||||||
|
yield;
|
||||||
|
|
||||||
|
if (!refResult) {
|
||||||
|
refResult = result;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
|
||||||
|
isnot(result.mozFullPath, refResult.mozFullPath, "Different os files");
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let i = 1; i < databases.length; i++) {
|
||||||
|
let db = databases[i];
|
||||||
|
|
||||||
|
let objectStore = db.transaction([objectStoreName], READ_WRITE)
|
||||||
|
.objectStore(objectStoreName);
|
||||||
|
|
||||||
|
request = objectStore.add(refResult, 2);
|
||||||
|
request.onsuccess = grabEventAndContinueHandler;
|
||||||
|
event = yield;
|
||||||
|
|
||||||
|
is(event.target.result, 2, "Got correct key");
|
||||||
|
|
||||||
|
request = objectStore.get(2);
|
||||||
|
request.onsuccess = grabEventAndContinueHandler;
|
||||||
|
event = yield;
|
||||||
|
|
||||||
|
let result = event.target.result;
|
||||||
|
verifyBlob(result, refResult, 2);
|
||||||
|
yield;
|
||||||
|
|
||||||
|
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
|
||||||
|
isnot(result.mozFullPath, refResult.mozFullPath, "Different os files");
|
||||||
|
}
|
||||||
|
|
||||||
|
is(bufferCache.length, 2, "Correct length");
|
||||||
|
|
||||||
|
finishTest();
|
||||||
|
yield;
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<script type="text/javascript;version=1.7" src="file.js"></script>
|
||||||
|
<script type="text/javascript;version=1.7" src="helpers.js"></script>
|
||||||
|
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body onload="runTest();"></body>
|
||||||
|
|
||||||
|
</html>
|
|
@ -0,0 +1,134 @@
|
||||||
|
<!--
|
||||||
|
Any copyright is dedicated to the Public Domain.
|
||||||
|
http://creativecommons.org/publicdomain/zero/1.0/
|
||||||
|
-->
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>Indexed Database Property Test</title>
|
||||||
|
|
||||||
|
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||||
|
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||||
|
|
||||||
|
<script type="text/javascript;version=1.7">
|
||||||
|
function testSteps()
|
||||||
|
{
|
||||||
|
const READ_WRITE = IDBTransaction.READ_WRITE;
|
||||||
|
|
||||||
|
const name = window.location.pathname;
|
||||||
|
const description = "My Test Database";
|
||||||
|
|
||||||
|
const objectStoreName = "Blobs";
|
||||||
|
|
||||||
|
const fileData1 = { key: 1, file: getRandomFile("random1.bin", 110000) };
|
||||||
|
const fileData2 = { key: 2, file: getRandomFile("random2.bin", 120000) };
|
||||||
|
const fileData3 = { key: 3, file: getRandomFile("random3.bin", 130000) };
|
||||||
|
|
||||||
|
{
|
||||||
|
let request = mozIndexedDB.open(name, 1, description);
|
||||||
|
request.onerror = errorHandler;
|
||||||
|
request.onupgradeneeded = grabEventAndContinueHandler;
|
||||||
|
request.onsuccess = grabEventAndContinueHandler;
|
||||||
|
let event = yield;
|
||||||
|
|
||||||
|
is(event.type, "upgradeneeded", "Got correct event type");
|
||||||
|
|
||||||
|
let db = event.target.result;
|
||||||
|
db.onerror = errorHandler;
|
||||||
|
|
||||||
|
let objectStore = db.createObjectStore(objectStoreName, { });
|
||||||
|
|
||||||
|
objectStore.add(fileData1.file, fileData1.key);
|
||||||
|
objectStore.add(fileData2.file, fileData2.key);
|
||||||
|
objectStore.add(fileData3.file, fileData3.key);
|
||||||
|
|
||||||
|
event = yield;
|
||||||
|
|
||||||
|
is(event.type, "success", "Got correct event type");
|
||||||
|
|
||||||
|
let trans = db.transaction([objectStoreName], READ_WRITE);
|
||||||
|
trans.objectStore(objectStoreName).delete(fileData1.key);
|
||||||
|
trans.oncomplete = grabEventAndContinueHandler;
|
||||||
|
event = yield;
|
||||||
|
|
||||||
|
is(event.type, "complete", "Got correct event type");
|
||||||
|
|
||||||
|
is(getFileDBRefCount(name, 1), 0, "Correct db ref count");
|
||||||
|
|
||||||
|
fileData1.file = null;
|
||||||
|
fileData2.file = null;
|
||||||
|
fileData3.file = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
scheduleGC();
|
||||||
|
yield;
|
||||||
|
|
||||||
|
ok(!hasFileInfo(name, 1), "Correct ref count");
|
||||||
|
ok(hasFileInfo(name, 2), "Correct ref count");
|
||||||
|
ok(hasFileInfo(name, 3), "Correct ref count");
|
||||||
|
|
||||||
|
{
|
||||||
|
let request = mozIndexedDB.open(name, 1, description);
|
||||||
|
request.onerror = errorHandler;
|
||||||
|
request.onsuccess = grabEventAndContinueHandler;
|
||||||
|
let event = yield;
|
||||||
|
|
||||||
|
is(event.type, "success", "Got correct event type");
|
||||||
|
|
||||||
|
let db = event.target.result;
|
||||||
|
db.onerror = errorHandler;
|
||||||
|
|
||||||
|
trans = db.transaction([objectStoreName], READ_WRITE);
|
||||||
|
objectStore = trans.objectStore(objectStoreName);
|
||||||
|
|
||||||
|
request = objectStore.get(fileData2.key);
|
||||||
|
request.onsuccess = grabEventAndContinueHandler;
|
||||||
|
event = yield;
|
||||||
|
|
||||||
|
let result = event.target.result;
|
||||||
|
ok(result, "Got result");
|
||||||
|
|
||||||
|
objectStore.delete(fileData2.key);
|
||||||
|
|
||||||
|
trans.oncomplete = grabEventAndContinueHandler;
|
||||||
|
event = yield;
|
||||||
|
|
||||||
|
is(event.type, "complete", "Got correct event type");
|
||||||
|
|
||||||
|
is(getFileDBRefCount(name, 2), 0, "Correct db ref count");
|
||||||
|
|
||||||
|
|
||||||
|
trans = db.transaction([objectStoreName], READ_WRITE);
|
||||||
|
objectStore = trans.objectStore(objectStoreName);
|
||||||
|
|
||||||
|
objectStore.delete(fileData3.key);
|
||||||
|
|
||||||
|
trans.oncomplete = grabEventAndContinueHandler;
|
||||||
|
event = yield;
|
||||||
|
|
||||||
|
is(event.type, "complete", "Got correct event type");
|
||||||
|
|
||||||
|
is(getFileDBRefCount(name, 3), -1, "Correct db ref count");
|
||||||
|
|
||||||
|
event = null;
|
||||||
|
result = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
scheduleGC();
|
||||||
|
yield;
|
||||||
|
|
||||||
|
ok(!hasFileInfo(name, 1), "Correct ref count");
|
||||||
|
ok(!hasFileInfo(name, 2), "Correct ref count");
|
||||||
|
ok(!hasFileInfo(name, 3), "Correct ref count");
|
||||||
|
|
||||||
|
finishTest();
|
||||||
|
yield;
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<script type="text/javascript;version=1.7" src="file.js"></script>
|
||||||
|
<script type="text/javascript;version=1.7" src="helpers.js"></script>
|
||||||
|
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body onload="runTest();"></body>
|
||||||
|
|
||||||
|
</html>
|
|
@ -0,0 +1,86 @@
|
||||||
|
<!--
|
||||||
|
Any copyright is dedicated to the Public Domain.
|
||||||
|
http://creativecommons.org/publicdomain/zero/1.0/
|
||||||
|
-->
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>Indexed Database Property Test</title>
|
||||||
|
|
||||||
|
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||||
|
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||||
|
|
||||||
|
<script type="text/javascript;version=1.7">
|
||||||
|
function testSteps()
|
||||||
|
{
|
||||||
|
const READ_WRITE = IDBTransaction.READ_WRITE;
|
||||||
|
|
||||||
|
const name = window.location.pathname;
|
||||||
|
const description = "My Test Database";
|
||||||
|
|
||||||
|
const objectStoreName = "Blobs";
|
||||||
|
|
||||||
|
getUsage(grabFileUsageAndContinueHandler);
|
||||||
|
let startUsage = yield;
|
||||||
|
|
||||||
|
const fileData = { key: 1, file: getRandomFile("random.bin", 100000) };
|
||||||
|
|
||||||
|
{
|
||||||
|
let request = mozIndexedDB.open(name, 1, description);
|
||||||
|
request.onerror = errorHandler;
|
||||||
|
request.onupgradeneeded = grabEventAndContinueHandler;
|
||||||
|
request.onsuccess = grabEventAndContinueHandler;
|
||||||
|
let event = yield;
|
||||||
|
|
||||||
|
is(event.type, "upgradeneeded", "Got correct event type");
|
||||||
|
|
||||||
|
let db = event.target.result;
|
||||||
|
db.onerror = errorHandler;
|
||||||
|
|
||||||
|
let objectStore = db.createObjectStore(objectStoreName, { });
|
||||||
|
|
||||||
|
objectStore.add(fileData.file, fileData.key);
|
||||||
|
|
||||||
|
event = yield;
|
||||||
|
|
||||||
|
is(event.type, "success", "Got correct event type");
|
||||||
|
|
||||||
|
getUsage(grabFileUsageAndContinueHandler);
|
||||||
|
let usage = yield;
|
||||||
|
|
||||||
|
is(usage, startUsage + fileData.file.size, "Correct file usage");
|
||||||
|
|
||||||
|
let trans = db.transaction([objectStoreName], READ_WRITE);
|
||||||
|
trans.objectStore(objectStoreName).delete(fileData.key);
|
||||||
|
trans.oncomplete = grabEventAndContinueHandler;
|
||||||
|
event = yield;
|
||||||
|
|
||||||
|
is(event.type, "complete", "Got correct event type");
|
||||||
|
|
||||||
|
getUsage(grabFileUsageAndContinueHandler);
|
||||||
|
usage = yield;
|
||||||
|
|
||||||
|
is(usage, startUsage + fileData.file.size, "OS file exists");
|
||||||
|
|
||||||
|
fileData.file = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
scheduleGC();
|
||||||
|
yield;
|
||||||
|
|
||||||
|
getUsage(grabFileUsageAndContinueHandler);
|
||||||
|
let endUsage = yield;
|
||||||
|
|
||||||
|
is(endUsage, startUsage, "OS file deleted");
|
||||||
|
|
||||||
|
finishTest();
|
||||||
|
yield;
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<script type="text/javascript;version=1.7" src="file.js"></script>
|
||||||
|
<script type="text/javascript;version=1.7" src="helpers.js"></script>
|
||||||
|
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body onload="runTest();"></body>
|
||||||
|
|
||||||
|
</html>
|
|
@ -0,0 +1,101 @@
|
||||||
|
<!--
|
||||||
|
Any copyright is dedicated to the Public Domain.
|
||||||
|
http://creativecommons.org/publicdomain/zero/1.0/
|
||||||
|
-->
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>Indexed Database Property Test</title>
|
||||||
|
|
||||||
|
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||||
|
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||||
|
|
||||||
|
<script type="text/javascript;version=1.7">
|
||||||
|
function testSteps()
|
||||||
|
{
|
||||||
|
const READ_WRITE = IDBTransaction.READ_WRITE;
|
||||||
|
|
||||||
|
const name = window.location.pathname;
|
||||||
|
const description = "My Test Database";
|
||||||
|
|
||||||
|
const objectStoreName = "Blobs";
|
||||||
|
|
||||||
|
const blobData = { key: 1, blob: getRandomBlob(10000) };
|
||||||
|
const fileData = { key: 2, file: getRandomFile("random.bin", 100000) };
|
||||||
|
|
||||||
|
let request = mozIndexedDB.open(name, 1, description);
|
||||||
|
request.onerror = errorHandler;
|
||||||
|
request.onupgradeneeded = grabEventAndContinueHandler;
|
||||||
|
request.onsuccess = grabEventAndContinueHandler;
|
||||||
|
let event = yield;
|
||||||
|
|
||||||
|
is(event.type, "upgradeneeded", "Got correct event type");
|
||||||
|
|
||||||
|
let db = event.target.result;
|
||||||
|
db.onerror = errorHandler;
|
||||||
|
|
||||||
|
db.createObjectStore(objectStoreName, { });
|
||||||
|
|
||||||
|
event = yield;
|
||||||
|
|
||||||
|
is(event.type, "success", "Got correct event type");
|
||||||
|
|
||||||
|
let objectStore = db.transaction([objectStoreName], READ_WRITE)
|
||||||
|
.objectStore(objectStoreName);
|
||||||
|
request = objectStore.add(blobData.blob, blobData.key);
|
||||||
|
request.onsuccess = grabEventAndContinueHandler;
|
||||||
|
event = yield;
|
||||||
|
|
||||||
|
is(event.target.result, blobData.key, "Got correct key");
|
||||||
|
|
||||||
|
request = objectStore.get(blobData.key);
|
||||||
|
request.onsuccess = grabEventAndContinueHandler;
|
||||||
|
event = yield;
|
||||||
|
|
||||||
|
verifyBlob(event.target.result, blobData.blob, 1);
|
||||||
|
yield;
|
||||||
|
|
||||||
|
request = db.transaction([objectStoreName])
|
||||||
|
.objectStore(objectStoreName).get(blobData.key);
|
||||||
|
request.onsuccess = grabEventAndContinueHandler;
|
||||||
|
event = yield;
|
||||||
|
|
||||||
|
verifyBlob(event.target.result, blobData.blob, 1);
|
||||||
|
yield;
|
||||||
|
|
||||||
|
objectStore = db.transaction([objectStoreName], READ_WRITE)
|
||||||
|
.objectStore(objectStoreName);
|
||||||
|
request = objectStore.add(fileData.file, fileData.key);
|
||||||
|
request.onsuccess = grabEventAndContinueHandler;
|
||||||
|
event = yield;
|
||||||
|
|
||||||
|
is(event.target.result, fileData.key, "Got correct key");
|
||||||
|
|
||||||
|
request = objectStore.get(fileData.key);
|
||||||
|
request.onsuccess = grabEventAndContinueHandler;
|
||||||
|
event = yield;
|
||||||
|
|
||||||
|
verifyBlob(event.target.result, fileData.file, 2);
|
||||||
|
yield;
|
||||||
|
|
||||||
|
request = db.transaction([objectStoreName])
|
||||||
|
.objectStore(objectStoreName).get(fileData.key);
|
||||||
|
request.onsuccess = grabEventAndContinueHandler;
|
||||||
|
event = yield;
|
||||||
|
|
||||||
|
verifyBlob(event.target.result, fileData.file, 2);
|
||||||
|
yield;
|
||||||
|
|
||||||
|
is(bufferCache.length, 2, "Correct length");
|
||||||
|
|
||||||
|
finishTest();
|
||||||
|
yield;
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<script type="text/javascript;version=1.7" src="file.js"></script>
|
||||||
|
<script type="text/javascript;version=1.7" src="helpers.js"></script>
|
||||||
|
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body onload="runTest();"></body>
|
||||||
|
|
||||||
|
</html>
|
|
@ -0,0 +1,75 @@
|
||||||
|
<!--
|
||||||
|
Any copyright is dedicated to the Public Domain.
|
||||||
|
http://creativecommons.org/publicdomain/zero/1.0/
|
||||||
|
-->
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>Indexed Database Property Test</title>
|
||||||
|
|
||||||
|
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||||
|
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||||
|
|
||||||
|
<script type="text/javascript;version=1.7">
|
||||||
|
function testSteps()
|
||||||
|
{
|
||||||
|
denyUnlimitedQuota();
|
||||||
|
|
||||||
|
const READ_WRITE = IDBTransaction.READ_WRITE;
|
||||||
|
const DEFAULT_QUOTA_MB = 50;
|
||||||
|
|
||||||
|
const name = window.location.pathname;
|
||||||
|
const description = "My Test Database";
|
||||||
|
|
||||||
|
const objectStoreName = "Blobs";
|
||||||
|
|
||||||
|
const testData = { key: 0, value: {} };
|
||||||
|
const fileData = { key: 1, file: null };
|
||||||
|
|
||||||
|
let request = mozIndexedDB.open(name, 1, description);
|
||||||
|
request.onerror = errorHandler;
|
||||||
|
request.onupgradeneeded = grabEventAndContinueHandler;
|
||||||
|
request.onsuccess = grabEventAndContinueHandler;
|
||||||
|
let event = yield;
|
||||||
|
|
||||||
|
is(event.type, "upgradeneeded", "Got correct event type");
|
||||||
|
|
||||||
|
let db = event.target.result;
|
||||||
|
|
||||||
|
let objectStore = db.createObjectStore(objectStoreName, { });
|
||||||
|
objectStore.add(testData.value, testData.key);
|
||||||
|
|
||||||
|
let usage = getUsageSync();
|
||||||
|
|
||||||
|
let size = (DEFAULT_QUOTA_MB + 1) * 1024 * 1024 - usage;
|
||||||
|
|
||||||
|
fileData.file = getNullFile("random.bin", size);
|
||||||
|
|
||||||
|
event = yield;
|
||||||
|
|
||||||
|
is(event.type, "success", "Got correct event type");
|
||||||
|
|
||||||
|
trans = db.transaction([objectStoreName], READ_WRITE);
|
||||||
|
objectStore = trans.objectStore(objectStoreName);
|
||||||
|
|
||||||
|
request = objectStore.add(fileData.file, fileData.key);
|
||||||
|
request.onerror = new ExpectError(IDBDatabaseException.UNKNOWN_ERR);
|
||||||
|
request.onsuccess = unexpectedSuccessHandler;
|
||||||
|
event = yield;
|
||||||
|
|
||||||
|
trans.oncomplete = grabEventAndContinueHandler;
|
||||||
|
event = yield;
|
||||||
|
|
||||||
|
is(event.type, "complete", "Got correct event type");
|
||||||
|
|
||||||
|
finishTest();
|
||||||
|
yield;
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<script type="text/javascript;version=1.7" src="file.js"></script>
|
||||||
|
<script type="text/javascript;version=1.7" src="helpers.js"></script>
|
||||||
|
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body onload="runTest();"></body>
|
||||||
|
|
||||||
|
</html>
|
|
@ -0,0 +1,136 @@
|
||||||
|
<!--
|
||||||
|
Any copyright is dedicated to the Public Domain.
|
||||||
|
http://creativecommons.org/publicdomain/zero/1.0/
|
||||||
|
-->
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>Indexed Database Property Test</title>
|
||||||
|
|
||||||
|
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||||
|
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||||
|
|
||||||
|
<script type="text/javascript;version=1.7">
|
||||||
|
function testSteps()
|
||||||
|
{
|
||||||
|
const READ_WRITE = IDBTransaction.READ_WRITE;
|
||||||
|
|
||||||
|
const name = window.location.pathname;
|
||||||
|
const description = "My Test Database";
|
||||||
|
|
||||||
|
const objectStoreName = "Blobs";
|
||||||
|
|
||||||
|
const fileData = { key: 1, file: getRandomFile("random.bin", 100000) };
|
||||||
|
|
||||||
|
{
|
||||||
|
let file = getRandomFile("random1.bin", 100000);
|
||||||
|
|
||||||
|
let request = mozIndexedDB.open(name, 1, description);
|
||||||
|
request.onerror = errorHandler;
|
||||||
|
request.onupgradeneeded = grabEventAndContinueHandler;
|
||||||
|
request.onsuccess = grabEventAndContinueHandler;
|
||||||
|
let event = yield;
|
||||||
|
|
||||||
|
is(event.type, "upgradeneeded", "Got correct event type");
|
||||||
|
|
||||||
|
let db = event.target.result;
|
||||||
|
db.onerror = errorHandler;
|
||||||
|
|
||||||
|
let objectStore = db.createObjectStore(objectStoreName, { });
|
||||||
|
|
||||||
|
objectStore.add(fileData.file, fileData.key);
|
||||||
|
|
||||||
|
event = yield;
|
||||||
|
|
||||||
|
is(event.type, "success", "Got correct event type");
|
||||||
|
|
||||||
|
let trans = db.transaction([objectStoreName], READ_WRITE);
|
||||||
|
objectStore = trans.objectStore(objectStoreName);
|
||||||
|
|
||||||
|
objectStore.delete(fileData.key);
|
||||||
|
|
||||||
|
trans.oncomplete = grabEventAndContinueHandler;
|
||||||
|
event = yield;
|
||||||
|
|
||||||
|
is(getFileDBRefCount(name, 1), 0, "Correct db ref count");
|
||||||
|
|
||||||
|
trans = db.transaction([objectStoreName], READ_WRITE);
|
||||||
|
objectStore = trans.objectStore(objectStoreName);
|
||||||
|
|
||||||
|
request = objectStore.add(fileData.file, fileData.key);
|
||||||
|
request.onsuccess = grabEventAndContinueHandler;
|
||||||
|
event = yield;
|
||||||
|
|
||||||
|
trans.oncomplete = grabEventAndContinueHandler;
|
||||||
|
event = yield;
|
||||||
|
|
||||||
|
is(getFileDBRefCount(name, 1), 1, "Correct db ref count");
|
||||||
|
|
||||||
|
fileData.file = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
scheduleGC();
|
||||||
|
yield;
|
||||||
|
|
||||||
|
is(getFileRefCount(name, 1), 1, "Correct ref count");
|
||||||
|
|
||||||
|
{
|
||||||
|
let request = mozIndexedDB.open(name, 1, description);
|
||||||
|
request.onerror = errorHandler;
|
||||||
|
request.onsuccess = grabEventAndContinueHandler;
|
||||||
|
let event = yield;
|
||||||
|
|
||||||
|
is(event.type, "success", "Got correct event type");
|
||||||
|
|
||||||
|
let db = event.target.result;
|
||||||
|
db.onerror = errorHandler;
|
||||||
|
|
||||||
|
let trans = db.transaction([objectStoreName], READ_WRITE);
|
||||||
|
objectStore = trans.objectStore(objectStoreName);
|
||||||
|
|
||||||
|
request = objectStore.get(fileData.key);
|
||||||
|
request.onsuccess = grabEventAndContinueHandler;
|
||||||
|
event = yield;
|
||||||
|
|
||||||
|
let result = event.target.result;
|
||||||
|
ok(result, "Got result");
|
||||||
|
|
||||||
|
objectStore.delete(fileData.key);
|
||||||
|
|
||||||
|
trans.oncomplete = grabEventAndContinueHandler;
|
||||||
|
event = yield;
|
||||||
|
|
||||||
|
is(getFileDBRefCount(name, 1), 0, "Correct db ref count");
|
||||||
|
|
||||||
|
trans = db.transaction([objectStoreName], READ_WRITE);
|
||||||
|
objectStore = trans.objectStore(objectStoreName);
|
||||||
|
|
||||||
|
request = objectStore.add(result, fileData.key);
|
||||||
|
request.onsuccess = grabEventAndContinueHandler;
|
||||||
|
event = yield;
|
||||||
|
|
||||||
|
trans.oncomplete = grabEventAndContinueHandler;
|
||||||
|
event = yield;
|
||||||
|
|
||||||
|
is(getFileDBRefCount(name, 1), 1, "Correct db ref count");
|
||||||
|
|
||||||
|
event = null;
|
||||||
|
result = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
scheduleGC();
|
||||||
|
yield;
|
||||||
|
|
||||||
|
is(getFileRefCount(name, 1), 1, "Correct ref count");
|
||||||
|
|
||||||
|
finishTest();
|
||||||
|
yield;
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<script type="text/javascript;version=1.7" src="file.js"></script>
|
||||||
|
<script type="text/javascript;version=1.7" src="helpers.js"></script>
|
||||||
|
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body onload="runTest();"></body>
|
||||||
|
|
||||||
|
</html>
|
|
@ -0,0 +1,93 @@
|
||||||
|
<!--
|
||||||
|
Any copyright is dedicated to the Public Domain.
|
||||||
|
http://creativecommons.org/publicdomain/zero/1.0/
|
||||||
|
-->
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>Indexed Database Property Test</title>
|
||||||
|
|
||||||
|
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||||
|
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||||
|
|
||||||
|
<script type="text/javascript;version=1.7">
|
||||||
|
function testSteps()
|
||||||
|
{
|
||||||
|
const READ_WRITE = IDBTransaction.READ_WRITE;
|
||||||
|
|
||||||
|
const name = window.location.pathname;
|
||||||
|
const description = "My Test Database";
|
||||||
|
|
||||||
|
const objectStoreName = "Blobs";
|
||||||
|
|
||||||
|
const fileData = { key: 1, file: getRandomFile("random.bin", 100000) };
|
||||||
|
|
||||||
|
{
|
||||||
|
let request = mozIndexedDB.open(name, 1, description);
|
||||||
|
request.onerror = errorHandler;
|
||||||
|
request.onupgradeneeded = grabEventAndContinueHandler;
|
||||||
|
request.onsuccess = grabEventAndContinueHandler;
|
||||||
|
let event = yield;
|
||||||
|
|
||||||
|
is(event.type, "upgradeneeded", "Got correct event type");
|
||||||
|
|
||||||
|
let db = event.target.result;
|
||||||
|
db.onerror = errorHandler;
|
||||||
|
|
||||||
|
let objectStore = db.createObjectStore(objectStoreName, { });
|
||||||
|
|
||||||
|
event = yield;
|
||||||
|
|
||||||
|
is(event.type, "success", "Got correct event type");
|
||||||
|
|
||||||
|
let trans = db.transaction([objectStoreName], READ_WRITE);
|
||||||
|
objectStore = trans.objectStore(objectStoreName);
|
||||||
|
|
||||||
|
request = objectStore.add(fileData.file, fileData.key);
|
||||||
|
request.onsuccess = grabEventAndContinueHandler;
|
||||||
|
event = yield;
|
||||||
|
|
||||||
|
request = objectStore.get(fileData.key);
|
||||||
|
request.onsuccess = grabEventAndContinueHandler;
|
||||||
|
event = yield;
|
||||||
|
|
||||||
|
let result = event.target.result;
|
||||||
|
ok(result, "Got result");
|
||||||
|
|
||||||
|
trans.onabort = grabEventAndContinueHandler;
|
||||||
|
trans.abort();
|
||||||
|
event = yield;
|
||||||
|
|
||||||
|
is(getFileDBRefCount(name, 1), 0, "Correct db ref count");
|
||||||
|
|
||||||
|
trans = db.transaction([objectStoreName], READ_WRITE);
|
||||||
|
objectStore = trans.objectStore(objectStoreName);
|
||||||
|
|
||||||
|
request = objectStore.add(result, fileData.key);
|
||||||
|
request.onsuccess = grabEventAndContinueHandler;
|
||||||
|
event = yield;
|
||||||
|
|
||||||
|
trans.oncomplete = grabEventAndContinueHandler;
|
||||||
|
event = yield;
|
||||||
|
|
||||||
|
is(getFileDBRefCount(name, 1), 1, "Correct db ref count");
|
||||||
|
|
||||||
|
fileData.file = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
scheduleGC();
|
||||||
|
yield;
|
||||||
|
|
||||||
|
is(getFileRefCount(name, 1), 1, "Correct ref count");
|
||||||
|
|
||||||
|
finishTest();
|
||||||
|
yield;
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<script type="text/javascript;version=1.7" src="file.js"></script>
|
||||||
|
<script type="text/javascript;version=1.7" src="helpers.js"></script>
|
||||||
|
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body onload="runTest();"></body>
|
||||||
|
|
||||||
|
</html>
|
|
@ -0,0 +1,106 @@
|
||||||
|
<!--
|
||||||
|
Any copyright is dedicated to the Public Domain.
|
||||||
|
http://creativecommons.org/publicdomain/zero/1.0/
|
||||||
|
-->
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>Indexed Database Property Test</title>
|
||||||
|
|
||||||
|
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||||
|
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||||
|
|
||||||
|
<script type="text/javascript;version=1.7">
|
||||||
|
function testSteps()
|
||||||
|
{
|
||||||
|
const READ_WRITE = IDBTransaction.READ_WRITE;
|
||||||
|
|
||||||
|
const name = window.location.pathname;
|
||||||
|
const description = "My Test Database";
|
||||||
|
|
||||||
|
const objectStoreInfo = [
|
||||||
|
{ name: "Blobs", options: { } },
|
||||||
|
{ name: "Other Blobs", options: { } }
|
||||||
|
];
|
||||||
|
|
||||||
|
const fileData = { key: 1, file: getRandomFile("random.bin", 100000) };
|
||||||
|
|
||||||
|
let request = mozIndexedDB.open(name, 1, description);
|
||||||
|
request.onerror = errorHandler;
|
||||||
|
request.onupgradeneeded = grabEventAndContinueHandler;
|
||||||
|
request.onsuccess = grabEventAndContinueHandler;
|
||||||
|
let event = yield;
|
||||||
|
|
||||||
|
is(event.type, "upgradeneeded", "Got correct event type");
|
||||||
|
|
||||||
|
let db = event.target.result;
|
||||||
|
db.onerror = errorHandler;
|
||||||
|
|
||||||
|
for each (let info in objectStoreInfo) {
|
||||||
|
let objectStore = db.createObjectStore(info.name, info.options);
|
||||||
|
objectStore.add(fileData.file, fileData.key);
|
||||||
|
}
|
||||||
|
|
||||||
|
event = yield;
|
||||||
|
|
||||||
|
is(event.type, "success", "Got correct event type");
|
||||||
|
|
||||||
|
let refResult;
|
||||||
|
for each (let info in objectStoreInfo) {
|
||||||
|
let objectStore = db.transaction([info.name])
|
||||||
|
.objectStore(info.name);
|
||||||
|
|
||||||
|
request = objectStore.get(fileData.key);
|
||||||
|
request.onsuccess = grabEventAndContinueHandler;
|
||||||
|
event = yield;
|
||||||
|
|
||||||
|
let result = event.target.result;
|
||||||
|
verifyBlob(result, fileData.file, 1);
|
||||||
|
yield;
|
||||||
|
|
||||||
|
if (!refResult) {
|
||||||
|
refResult = result;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
|
||||||
|
is(result.mozFullPath, refResult.mozFullPath, "The same os file");
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let i = 1; i < objectStoreInfo.length; i++) {
|
||||||
|
let info = objectStoreInfo[i];
|
||||||
|
|
||||||
|
let objectStore = db.transaction([info.name], READ_WRITE)
|
||||||
|
.objectStore(info.name);
|
||||||
|
|
||||||
|
request = objectStore.add(refResult, 2);
|
||||||
|
request.onsuccess = grabEventAndContinueHandler;
|
||||||
|
event = yield;
|
||||||
|
|
||||||
|
is(event.target.result, 2, "Got correct key");
|
||||||
|
|
||||||
|
request = objectStore.get(2);
|
||||||
|
request.onsuccess = grabEventAndContinueHandler;
|
||||||
|
event = yield;
|
||||||
|
|
||||||
|
let result = event.target.result;
|
||||||
|
verifyBlob(result, refResult, 1);
|
||||||
|
yield;
|
||||||
|
|
||||||
|
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
|
||||||
|
is(result.mozFullPath, refResult.mozFullPath, "The same os file");
|
||||||
|
}
|
||||||
|
|
||||||
|
is(bufferCache.length, 2, "Correct length");
|
||||||
|
|
||||||
|
finishTest();
|
||||||
|
yield;
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<script type="text/javascript;version=1.7" src="file.js"></script>
|
||||||
|
<script type="text/javascript;version=1.7" src="helpers.js"></script>
|
||||||
|
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body onload="runTest();"></body>
|
||||||
|
|
||||||
|
</html>
|
|
@ -0,0 +1,78 @@
|
||||||
|
<!--
|
||||||
|
Any copyright is dedicated to the Public Domain.
|
||||||
|
http://creativecommons.org/publicdomain/zero/1.0/
|
||||||
|
-->
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>Indexed Database Property Test</title>
|
||||||
|
|
||||||
|
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||||
|
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||||
|
|
||||||
|
<script type="text/javascript;version=1.7">
|
||||||
|
function testSteps()
|
||||||
|
{
|
||||||
|
const READ_WRITE = IDBTransaction.READ_WRITE;
|
||||||
|
|
||||||
|
const name = window.location.pathname;
|
||||||
|
const description = "My Test Database";
|
||||||
|
|
||||||
|
const objectStoreName = "Blobs";
|
||||||
|
|
||||||
|
const fileData = { key: 1, file: getRandomFile("random.bin", 100000) };
|
||||||
|
|
||||||
|
{
|
||||||
|
let request = mozIndexedDB.open(name, 1, description);
|
||||||
|
request.onerror = errorHandler;
|
||||||
|
request.onupgradeneeded = grabEventAndContinueHandler;
|
||||||
|
request.onsuccess = grabEventAndContinueHandler;
|
||||||
|
let event = yield;
|
||||||
|
|
||||||
|
is(event.type, "upgradeneeded", "Got correct event type");
|
||||||
|
|
||||||
|
let db = event.target.result;
|
||||||
|
db.onerror = errorHandler;
|
||||||
|
|
||||||
|
objectStore = db.createObjectStore(objectStoreName, { });
|
||||||
|
|
||||||
|
event = yield;
|
||||||
|
|
||||||
|
is(event.type, "success", "Got correct event type");
|
||||||
|
|
||||||
|
let trans = db.transaction([objectStoreName], READ_WRITE);
|
||||||
|
let objectStore = trans.objectStore(objectStoreName);
|
||||||
|
|
||||||
|
request = objectStore.add(fileData.file, fileData.key);
|
||||||
|
request.onsuccess = grabEventAndContinueHandler;
|
||||||
|
event = yield;
|
||||||
|
|
||||||
|
is(event.target.result, fileData.key, "Got correct key");
|
||||||
|
|
||||||
|
trans.onabort = grabEventAndContinueHandler;
|
||||||
|
trans.abort();
|
||||||
|
event = yield;
|
||||||
|
|
||||||
|
is(event.type, "abort", "Got correct event type");
|
||||||
|
|
||||||
|
is(getFileDBRefCount(name, 1), 0, "Correct db ref count");
|
||||||
|
|
||||||
|
fileData.file = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
scheduleGC();
|
||||||
|
yield;
|
||||||
|
|
||||||
|
ok(!hasFileInfo(name, 1), "Correct ref count");
|
||||||
|
|
||||||
|
finishTest();
|
||||||
|
yield;
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<script type="text/javascript;version=1.7" src="file.js"></script>
|
||||||
|
<script type="text/javascript;version=1.7" src="helpers.js"></script>
|
||||||
|
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body onload="runTest();"></body>
|
||||||
|
|
||||||
|
</html>
|
|
@ -65,10 +65,11 @@ interface nsIDOMEvent;
|
||||||
interface nsITransferable;
|
interface nsITransferable;
|
||||||
interface nsIQueryContentEventResult;
|
interface nsIQueryContentEventResult;
|
||||||
interface nsIDOMWindow;
|
interface nsIDOMWindow;
|
||||||
|
interface nsIDOMBlob;
|
||||||
interface nsIDOMFile;
|
interface nsIDOMFile;
|
||||||
interface nsIFile;
|
interface nsIFile;
|
||||||
|
|
||||||
[scriptable, uuid(bf868921-0288-4799-a806-2fa642590197)]
|
[scriptable, uuid(36adf309-e5c4-4912-9152-7fb151dc754a)]
|
||||||
interface nsIDOMWindowUtils : nsISupports {
|
interface nsIDOMWindowUtils : nsISupports {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -926,4 +927,18 @@ interface nsIDOMWindowUtils : nsISupports {
|
||||||
*/
|
*/
|
||||||
boolean checkAndClearPaintedState(in nsIDOMElement aElement);
|
boolean checkAndClearPaintedState(in nsIDOMElement aElement);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Get internal id of the stored blob.
|
||||||
|
*/
|
||||||
|
long long getFileId(in nsIDOMBlob aBlob);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Get file ref count info for given database and file id.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
boolean getFileReferences(in AString aDatabaseName, in long long aId,
|
||||||
|
[optional] out long aRefCnt,
|
||||||
|
[optional] out long aDBRefCnt,
|
||||||
|
[optional] out long aSliceRefCnt);
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -61,7 +61,7 @@ interface nsIFile;
|
||||||
*
|
*
|
||||||
* @threadsafe
|
* @threadsafe
|
||||||
*/
|
*/
|
||||||
[scriptable, uuid(ad035628-4ffb-42ff-a256-0ed9e410b859)]
|
[scriptable, uuid(b2a4b534-f92e-4387-9bd9-d10408173925)]
|
||||||
interface mozIStorageConnection : nsISupports {
|
interface mozIStorageConnection : nsISupports {
|
||||||
/**
|
/**
|
||||||
* The default size for SQLite database pages used by mozStorage for new
|
* The default size for SQLite database pages used by mozStorage for new
|
||||||
|
@ -143,6 +143,12 @@ interface mozIStorageConnection : nsISupports {
|
||||||
*/
|
*/
|
||||||
readonly attribute long long lastInsertRowID;
|
readonly attribute long long lastInsertRowID;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* affectedRows returns the number of database rows that were changed or
|
||||||
|
* inserted or deleted by last operation.
|
||||||
|
*/
|
||||||
|
readonly attribute long affectedRows;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The last error SQLite error code.
|
* The last error SQLite error code.
|
||||||
*/
|
*/
|
||||||
|
@ -384,4 +390,15 @@ interface mozIStorageConnection : nsISupports {
|
||||||
* If the system is short on storage space.
|
* If the system is short on storage space.
|
||||||
*/
|
*/
|
||||||
void setGrowthIncrement(in PRInt32 aIncrement, in AUTF8String aDatabaseName);
|
void setGrowthIncrement(in PRInt32 aIncrement, in AUTF8String aDatabaseName);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enable a predefined virtual table implementation.
|
||||||
|
*
|
||||||
|
* @param aModuleName
|
||||||
|
* The module to enable. Only "filesystem" is currently supported.
|
||||||
|
*
|
||||||
|
* @throws NS_ERROR_FAILURE
|
||||||
|
* For unknown module names.
|
||||||
|
*/
|
||||||
|
[noscript] void enableModule(in ACString aModuleName);
|
||||||
};
|
};
|
||||||
|
|
|
@ -75,7 +75,7 @@ interface mozIStorageQuotaCallback : nsISupports
|
||||||
* This is a temporary interface that should eventually merge with
|
* This is a temporary interface that should eventually merge with
|
||||||
* mozIStorageService.
|
* mozIStorageService.
|
||||||
*/
|
*/
|
||||||
[scriptable, uuid(11def472-446f-4635-a1d8-8856e85aac50)]
|
[scriptable, uuid(4d81faf5-fe01-428b-99b8-c94cba12fd72)]
|
||||||
interface mozIStorageServiceQuotaManagement : nsISupports
|
interface mozIStorageServiceQuotaManagement : nsISupports
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
|
@ -128,5 +128,5 @@ interface mozIStorageServiceQuotaManagement : nsISupports
|
||||||
* file does not exist then the file will be removed from tracking
|
* file does not exist then the file will be removed from tracking
|
||||||
* under the quota system.
|
* under the quota system.
|
||||||
*/
|
*/
|
||||||
void updateQutoaInformationForFile(in nsIFile aFile);
|
void updateQuotaInformationForFile(in nsIFile aFile);
|
||||||
};
|
};
|
||||||
|
|
|
@ -0,0 +1,336 @@
|
||||||
|
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||||
|
* vim: sw=2 ts=2 et lcs=trail\:.,tab\:>~ :
|
||||||
|
* ***** BEGIN LICENSE BLOCK *****
|
||||||
|
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||||
|
*
|
||||||
|
* The contents of this file are subject to the Mozilla Public License Version
|
||||||
|
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
* http://www.mozilla.org/MPL/
|
||||||
|
*
|
||||||
|
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||||
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||||
|
* for the specific language governing rights and limitations under the
|
||||||
|
* License.
|
||||||
|
*
|
||||||
|
* The Original Code is Mozilla.
|
||||||
|
*
|
||||||
|
* The Initial Developer of the Original Code is Mozilla Foundation.
|
||||||
|
* Portions created by the Initial Developer are Copyright (C) 2011
|
||||||
|
* the Initial Developer. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Contributor(s):
|
||||||
|
*
|
||||||
|
* Alternatively, the contents of this file may be used under the terms of
|
||||||
|
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||||
|
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||||
|
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||||
|
* of those above. If you wish to allow use of your version of this file only
|
||||||
|
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||||
|
* use your version of this file under the terms of the MPL, indicate your
|
||||||
|
* decision by deleting the provisions above and replace them with the notice
|
||||||
|
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||||
|
* the provisions above, a recipient may use your version of this file under
|
||||||
|
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||||
|
*
|
||||||
|
* ***** END LICENSE BLOCK ***** */
|
||||||
|
|
||||||
|
#include "FileSystemModule.h"
|
||||||
|
|
||||||
|
#include "sqlite3.h"
|
||||||
|
#include "nsString.h"
|
||||||
|
#include "nsISimpleEnumerator.h"
|
||||||
|
#include "nsIFile.h"
|
||||||
|
#include "nsILocalFile.h"
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
struct VirtualTableCursorBase
|
||||||
|
{
|
||||||
|
VirtualTableCursorBase()
|
||||||
|
{
|
||||||
|
memset(&mBase, 0, sizeof(mBase));
|
||||||
|
}
|
||||||
|
|
||||||
|
sqlite3_vtab_cursor mBase;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct VirtualTableCursor : public VirtualTableCursorBase
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
VirtualTableCursor()
|
||||||
|
: mRowId(-1)
|
||||||
|
{
|
||||||
|
mCurrentFileName.SetIsVoid(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
const nsString& DirectoryPath() const
|
||||||
|
{
|
||||||
|
return mDirectoryPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
const nsString& CurrentFileName() const
|
||||||
|
{
|
||||||
|
return mCurrentFileName;
|
||||||
|
}
|
||||||
|
|
||||||
|
PRInt64 RowId() const
|
||||||
|
{
|
||||||
|
return mRowId;
|
||||||
|
}
|
||||||
|
|
||||||
|
nsresult Init(const nsAString& aPath);
|
||||||
|
nsresult NextFile();
|
||||||
|
|
||||||
|
private:
|
||||||
|
nsCOMPtr<nsISimpleEnumerator> mEntries;
|
||||||
|
|
||||||
|
nsString mDirectoryPath;
|
||||||
|
nsString mCurrentFileName;
|
||||||
|
|
||||||
|
PRInt64 mRowId;
|
||||||
|
};
|
||||||
|
|
||||||
|
nsresult
|
||||||
|
VirtualTableCursor::Init(const nsAString& aPath)
|
||||||
|
{
|
||||||
|
nsCOMPtr<nsILocalFile> directory =
|
||||||
|
do_CreateInstance(NS_LOCAL_FILE_CONTRACTID);
|
||||||
|
NS_ENSURE_TRUE(directory, NS_ERROR_FAILURE);
|
||||||
|
|
||||||
|
nsresult rv = directory->InitWithPath(aPath);
|
||||||
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
|
rv = directory->GetPath(mDirectoryPath);
|
||||||
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
|
rv = directory->GetDirectoryEntries(getter_AddRefs(mEntries));
|
||||||
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
|
rv = NextFile();
|
||||||
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
nsresult
|
||||||
|
VirtualTableCursor::NextFile()
|
||||||
|
{
|
||||||
|
bool hasMore;
|
||||||
|
nsresult rv = mEntries->HasMoreElements(&hasMore);
|
||||||
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
|
if (!hasMore) {
|
||||||
|
mCurrentFileName.SetIsVoid(true);
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
nsCOMPtr<nsISupports> entry;
|
||||||
|
rv = mEntries->GetNext(getter_AddRefs(entry));
|
||||||
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
|
nsCOMPtr<nsIFile> file = do_QueryInterface(entry);
|
||||||
|
NS_ENSURE_TRUE(file, NS_ERROR_FAILURE);
|
||||||
|
|
||||||
|
rv = file->GetLeafName(mCurrentFileName);
|
||||||
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
|
mRowId++;
|
||||||
|
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Connect(sqlite3* aDB, void* aAux, int aArgc, const char* const* aArgv,
|
||||||
|
sqlite3_vtab** aVtab, char** aErr)
|
||||||
|
{
|
||||||
|
static const char virtualTableSchema[] =
|
||||||
|
"CREATE TABLE fs ("
|
||||||
|
"name TEXT, "
|
||||||
|
"path TEXT"
|
||||||
|
")";
|
||||||
|
|
||||||
|
int rc = sqlite3_declare_vtab(aDB, virtualTableSchema);
|
||||||
|
if (rc != SQLITE_OK) {
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
sqlite3_vtab* vt = new sqlite3_vtab();
|
||||||
|
memset(vt, 0, sizeof(*vt));
|
||||||
|
|
||||||
|
*aVtab = vt;
|
||||||
|
|
||||||
|
return SQLITE_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Disconnect(sqlite3_vtab* aVtab )
|
||||||
|
{
|
||||||
|
delete aVtab;
|
||||||
|
|
||||||
|
return SQLITE_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
int BestIndex(sqlite3_vtab* aVtab, sqlite3_index_info* aInfo)
|
||||||
|
{
|
||||||
|
// Here we specify what index constraints we want to handle. That is, there
|
||||||
|
// might be some columns with particular constraints in which we can help
|
||||||
|
// SQLite narrow down the result set.
|
||||||
|
//
|
||||||
|
// For example, take the "path = x" where x is a directory. In this case,
|
||||||
|
// we can narrow our search to just this directory instead of the entire file
|
||||||
|
// system. This can be a significant optimization. So, we want to handle that
|
||||||
|
// constraint. To do so, we would look for two specific input conditions:
|
||||||
|
//
|
||||||
|
// 1. aInfo->aConstraint[i].iColumn == 1
|
||||||
|
// 2. aInfo->aConstraint[i].op == SQLITE_INDEX_CONSTRAINT_EQ
|
||||||
|
//
|
||||||
|
// The first states that the path column is being used in one of the input
|
||||||
|
// constraints and the second states that the constraint involves the equal
|
||||||
|
// operator.
|
||||||
|
//
|
||||||
|
// An even more specific search would be for name='xxx', in which case we
|
||||||
|
// can limit the search to a single file, if it exists.
|
||||||
|
//
|
||||||
|
// What we have to do here is look for all of our index searches and select
|
||||||
|
// the narrowest. We can only pick one, so obviously we want the one that
|
||||||
|
// is the most specific, which leads to the smallest result set.
|
||||||
|
|
||||||
|
for(int i = 0; i < aInfo->nConstraint; i++) {
|
||||||
|
if (aInfo->aConstraint[i].iColumn == 1 && aInfo->aConstraint[i].usable) {
|
||||||
|
if (aInfo->aConstraint[i].op & SQLITE_INDEX_CONSTRAINT_EQ) {
|
||||||
|
aInfo->aConstraintUsage[i].argvIndex = 1;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: handle single files (constrained also by the name column)
|
||||||
|
}
|
||||||
|
|
||||||
|
return SQLITE_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Open(sqlite3_vtab* aVtab, sqlite3_vtab_cursor** aCursor)
|
||||||
|
{
|
||||||
|
VirtualTableCursor* cursor = new VirtualTableCursor();
|
||||||
|
|
||||||
|
*aCursor = reinterpret_cast<sqlite3_vtab_cursor*>(cursor);
|
||||||
|
|
||||||
|
return SQLITE_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Close(sqlite3_vtab_cursor* aCursor)
|
||||||
|
{
|
||||||
|
VirtualTableCursor* cursor = reinterpret_cast<VirtualTableCursor*>(aCursor);
|
||||||
|
|
||||||
|
delete cursor;
|
||||||
|
|
||||||
|
return SQLITE_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Filter(sqlite3_vtab_cursor* aCursor, int aIdxNum, const char* aIdxStr,
|
||||||
|
int aArgc, sqlite3_value** aArgv)
|
||||||
|
{
|
||||||
|
VirtualTableCursor* cursor = reinterpret_cast<VirtualTableCursor*>(aCursor);
|
||||||
|
|
||||||
|
if(aArgc <= 0) {
|
||||||
|
return SQLITE_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
nsDependentString path(
|
||||||
|
reinterpret_cast<const PRUnichar*>(::sqlite3_value_text16(aArgv[0])));
|
||||||
|
|
||||||
|
nsresult rv = cursor->Init(path);
|
||||||
|
NS_ENSURE_SUCCESS(rv, SQLITE_ERROR);
|
||||||
|
|
||||||
|
return SQLITE_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Next(sqlite3_vtab_cursor* aCursor)
|
||||||
|
{
|
||||||
|
VirtualTableCursor* cursor = reinterpret_cast<VirtualTableCursor*>(aCursor);
|
||||||
|
|
||||||
|
nsresult rv = cursor->NextFile();
|
||||||
|
NS_ENSURE_SUCCESS(rv, SQLITE_ERROR);
|
||||||
|
|
||||||
|
return SQLITE_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Eof(sqlite3_vtab_cursor* aCursor)
|
||||||
|
{
|
||||||
|
VirtualTableCursor* cursor = reinterpret_cast<VirtualTableCursor*>(aCursor);
|
||||||
|
return cursor->CurrentFileName().IsVoid() ? 1 : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Column(sqlite3_vtab_cursor* aCursor, sqlite3_context* aContext,
|
||||||
|
int aColumnIndex)
|
||||||
|
{
|
||||||
|
VirtualTableCursor* cursor = reinterpret_cast<VirtualTableCursor*>(aCursor);
|
||||||
|
|
||||||
|
switch (aColumnIndex) {
|
||||||
|
// name
|
||||||
|
case 0: {
|
||||||
|
const nsString& name = cursor->CurrentFileName();
|
||||||
|
sqlite3_result_text16(aContext, name.get(),
|
||||||
|
name.Length() * sizeof(PRUnichar),
|
||||||
|
SQLITE_TRANSIENT);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// path
|
||||||
|
case 1: {
|
||||||
|
const nsString& path = cursor->DirectoryPath();
|
||||||
|
sqlite3_result_text16(aContext, path.get(),
|
||||||
|
path.Length() * sizeof(PRUnichar),
|
||||||
|
SQLITE_TRANSIENT);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
NS_NOTREACHED("Unsupported column!");
|
||||||
|
}
|
||||||
|
|
||||||
|
return SQLITE_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
int RowId(sqlite3_vtab_cursor* aCursor, sqlite3_int64* aRowid)
|
||||||
|
{
|
||||||
|
VirtualTableCursor* cursor = reinterpret_cast<VirtualTableCursor*>(aCursor);
|
||||||
|
|
||||||
|
*aRowid = cursor->RowId();
|
||||||
|
|
||||||
|
return SQLITE_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // anonymous namespace
|
||||||
|
|
||||||
|
namespace mozilla {
|
||||||
|
namespace storage {
|
||||||
|
|
||||||
|
int RegisterFileSystemModule(sqlite3* aDB, const char* aName)
|
||||||
|
{
|
||||||
|
static sqlite3_module module = {
|
||||||
|
1,
|
||||||
|
Connect,
|
||||||
|
Connect,
|
||||||
|
BestIndex,
|
||||||
|
Disconnect,
|
||||||
|
Disconnect,
|
||||||
|
Open,
|
||||||
|
Close,
|
||||||
|
Filter,
|
||||||
|
Next,
|
||||||
|
Eof,
|
||||||
|
Column,
|
||||||
|
RowId,
|
||||||
|
nsnull,
|
||||||
|
nsnull,
|
||||||
|
nsnull,
|
||||||
|
nsnull,
|
||||||
|
nsnull,
|
||||||
|
nsnull,
|
||||||
|
nsnull
|
||||||
|
};
|
||||||
|
|
||||||
|
return sqlite3_create_module(aDB, aName, &module, nsnull);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace storage
|
||||||
|
} // namespace mozilla
|
|
@ -0,0 +1,53 @@
|
||||||
|
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||||
|
* vim: sw=2 ts=2 et lcs=trail\:.,tab\:>~ :
|
||||||
|
* ***** BEGIN LICENSE BLOCK *****
|
||||||
|
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||||
|
*
|
||||||
|
* The contents of this file are subject to the Mozilla Public License Version
|
||||||
|
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
* http://www.mozilla.org/MPL/
|
||||||
|
*
|
||||||
|
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||||
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||||
|
* for the specific language governing rights and limitations under the
|
||||||
|
* License.
|
||||||
|
*
|
||||||
|
* The Original Code is Mozilla.
|
||||||
|
*
|
||||||
|
* The Initial Developer of the Original Code is Mozilla Foundation.
|
||||||
|
* Portions created by the Initial Developer are Copyright (C) 2011
|
||||||
|
* the Initial Developer. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Contributor(s):
|
||||||
|
*
|
||||||
|
* Alternatively, the contents of this file may be used under the terms of
|
||||||
|
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||||
|
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||||
|
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||||
|
* of those above. If you wish to allow use of your version of this file only
|
||||||
|
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||||
|
* use your version of this file under the terms of the MPL, indicate your
|
||||||
|
* decision by deleting the provisions above and replace them with the notice
|
||||||
|
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||||
|
* the provisions above, a recipient may use your version of this file under
|
||||||
|
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||||
|
*
|
||||||
|
* ***** END LICENSE BLOCK ***** */
|
||||||
|
|
||||||
|
#ifndef mozilla_storage_FileSystemModule_h
|
||||||
|
#define mozilla_storage_FileSystemModule_h
|
||||||
|
|
||||||
|
#include "nscore.h"
|
||||||
|
|
||||||
|
struct sqlite3;
|
||||||
|
|
||||||
|
namespace mozilla {
|
||||||
|
namespace storage {
|
||||||
|
|
||||||
|
NS_HIDDEN_(int) RegisterFileSystemModule(sqlite3* aDB, const char* aName);
|
||||||
|
|
||||||
|
} // namespace storage
|
||||||
|
} // namespace mozilla
|
||||||
|
|
||||||
|
#endif // mozilla_storage_FileSystemModule_h
|
|
@ -86,6 +86,7 @@ CPPSRCS = \
|
||||||
SQLCollations.cpp \
|
SQLCollations.cpp \
|
||||||
VacuumManager.cpp \
|
VacuumManager.cpp \
|
||||||
TelemetryVFS.cpp \
|
TelemetryVFS.cpp \
|
||||||
|
FileSystemModule.cpp \
|
||||||
$(NULL)
|
$(NULL)
|
||||||
|
|
||||||
# For nsDependentJSString
|
# For nsDependentJSString
|
||||||
|
|
|
@ -69,6 +69,7 @@
|
||||||
#include "mozStorageStatementData.h"
|
#include "mozStorageStatementData.h"
|
||||||
#include "StorageBaseStatementInternal.h"
|
#include "StorageBaseStatementInternal.h"
|
||||||
#include "SQLCollations.h"
|
#include "SQLCollations.h"
|
||||||
|
#include "FileSystemModule.h"
|
||||||
|
|
||||||
#include "prlog.h"
|
#include "prlog.h"
|
||||||
#include "prprf.h"
|
#include "prprf.h"
|
||||||
|
@ -158,6 +159,19 @@ sqlite3_T_blob(sqlite3_context *aCtx,
|
||||||
|
|
||||||
#include "variantToSQLiteT_impl.h"
|
#include "variantToSQLiteT_impl.h"
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//// Modules
|
||||||
|
|
||||||
|
struct Module
|
||||||
|
{
|
||||||
|
const char* name;
|
||||||
|
int (*registerFunc)(sqlite3*, const char*);
|
||||||
|
};
|
||||||
|
|
||||||
|
Module gModules[] = {
|
||||||
|
{ "filesystem", RegisterFileSystemModule }
|
||||||
|
};
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
//// Local Functions
|
//// Local Functions
|
||||||
|
|
||||||
|
@ -1129,6 +1143,16 @@ Connection::GetLastInsertRowID(PRInt64 *_id)
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
NS_IMETHODIMP
|
||||||
|
Connection::GetAffectedRows(PRInt32 *_rows)
|
||||||
|
{
|
||||||
|
if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
|
||||||
|
|
||||||
|
*_rows = ::sqlite3_changes(mDBConn);
|
||||||
|
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
NS_IMETHODIMP
|
NS_IMETHODIMP
|
||||||
Connection::GetLastError(PRInt32 *_error)
|
Connection::GetLastError(PRInt32 *_error)
|
||||||
{
|
{
|
||||||
|
@ -1512,5 +1536,24 @@ Connection::SetGrowthIncrement(PRInt32 aChunkSize, const nsACString &aDatabaseNa
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
NS_IMETHODIMP
|
||||||
|
Connection::EnableModule(const nsACString& aModuleName)
|
||||||
|
{
|
||||||
|
if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < ArrayLength(gModules); i++) {
|
||||||
|
struct Module* m = &gModules[i];
|
||||||
|
if (aModuleName.Equals(m->name)) {
|
||||||
|
int srv = m->registerFunc(mDBConn, m->name);
|
||||||
|
if (srv != SQLITE_OK)
|
||||||
|
return convertResultCode(srv);
|
||||||
|
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return NS_ERROR_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace storage
|
} // namespace storage
|
||||||
} // namespace mozilla
|
} // namespace mozilla
|
||||||
|
|
|
@ -57,8 +57,14 @@
|
||||||
#include "mozilla/Preferences.h"
|
#include "mozilla/Preferences.h"
|
||||||
|
|
||||||
#include "sqlite3.h"
|
#include "sqlite3.h"
|
||||||
|
#include "test_quota.h"
|
||||||
#include "test_quota.c"
|
#include "test_quota.c"
|
||||||
|
|
||||||
|
#ifdef SQLITE_OS_WIN
|
||||||
|
// "windows.h" was included and it can #define lots of things we care about...
|
||||||
|
#undef CompareString
|
||||||
|
#endif
|
||||||
|
|
||||||
#include "nsIPromptService.h"
|
#include "nsIPromptService.h"
|
||||||
#include "nsIMemoryReporter.h"
|
#include "nsIMemoryReporter.h"
|
||||||
|
|
||||||
|
@ -745,7 +751,7 @@ Service::SetQuotaForFilenamePattern(const nsACString &aPattern,
|
||||||
}
|
}
|
||||||
|
|
||||||
NS_IMETHODIMP
|
NS_IMETHODIMP
|
||||||
Service::UpdateQutoaInformationForFile(nsIFile *aFile)
|
Service::UpdateQuotaInformationForFile(nsIFile *aFile)
|
||||||
{
|
{
|
||||||
NS_ENSURE_ARG_POINTER(aFile);
|
NS_ENSURE_ARG_POINTER(aFile);
|
||||||
|
|
||||||
|
|
|
@ -527,6 +527,27 @@ SpecialPowersAPI.prototype = {
|
||||||
Components.utils.forceGC();
|
Components.utils.forceGC();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
exactGC: function(win, callback) {
|
||||||
|
var self = this;
|
||||||
|
let count = 0;
|
||||||
|
|
||||||
|
function doPreciseGCandCC() {
|
||||||
|
function scheduledGCCallback() {
|
||||||
|
self.getDOMWindowUtils(win).cycleCollect();
|
||||||
|
|
||||||
|
if (++count < 2) {
|
||||||
|
doPreciseGCandCC();
|
||||||
|
} else {
|
||||||
|
callback();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Components.utils.schedulePreciseGC(scheduledGCCallback);
|
||||||
|
}
|
||||||
|
|
||||||
|
doPreciseGCandCC();
|
||||||
|
},
|
||||||
|
|
||||||
hasContentProcesses: function() {
|
hasContentProcesses: function() {
|
||||||
try {
|
try {
|
||||||
var rt = Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULRuntime);
|
var rt = Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULRuntime);
|
||||||
|
|
Загрузка…
Ссылка в новой задаче