Merge mozilla-central and mozilla-inbound

This commit is contained in:
Ed Morley 2011-12-16 10:19:52 +00:00
commit 58f24579d9
56 changed files with 5423 additions and 549 deletions

View File

@ -226,7 +226,7 @@ function onIndexedDBClear()
initIndexedDBRow();
}
function onIndexedDBUsageCallback(uri, usage)
function onIndexedDBUsageCallback(uri, usage, fileUsage)
{
if (!uri.equals(gPermURI)) {
throw new Error("Callback received for bad URI: " + uri);

View File

@ -53,6 +53,9 @@
#include "nsIXMLHttpRequest.h"
#include "prmem.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"
@ -67,12 +70,13 @@ class nsIBlobBuilder;
nsresult NS_NewBlobBuilder(nsISupports* *aSupports);
using namespace mozilla::dom;
class nsDOMFileBase : public nsIDOMFile,
public nsIXHRSendable,
public nsIMutable
{
public:
nsDOMFileBase(const nsAString& aName, const nsAString& aContentType,
PRUint64 aLength)
: mIsFile(true), mImmutable(false), mContentType(aContentType),
@ -119,6 +123,21 @@ protected:
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 mImmutable;
nsString mContentType;
@ -126,6 +145,9 @@ protected:
PRUint64 mStart;
PRUint64 mLength;
// Protected by IndexedDatabaseManager::FileMutex()
nsTArray<nsRefPtr<indexedDB::FileInfo> > mFileInfos;
};
class nsDOMFileFile : public nsDOMFileBase,
@ -135,7 +157,7 @@ public:
// Create as a file
nsDOMFileFile(nsIFile *aFile)
: nsDOMFileBase(EmptyString(), EmptyString(), PR_UINT64_MAX),
mFile(aFile), mWholeFile(true)
mFile(aFile), mWholeFile(true), mStoredFile(false)
{
NS_ASSERTION(mFile, "must have file");
// Lazily get the content type and size
@ -147,16 +169,37 @@ public:
nsDOMFileFile(nsIFile *aFile, const nsAString& aContentType,
nsISupports *aCacheToken = nsnull)
: nsDOMFileBase(aContentType, PR_UINT64_MAX),
mFile(aFile), mWholeFile(true),
mFile(aFile), mWholeFile(true), mStoredFile(false),
mCacheToken(aCacheToken)
{
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
nsDOMFileFile()
: nsDOMFileBase(EmptyString(), EmptyString(), PR_UINT64_MAX),
mWholeFile(true)
mWholeFile(true), mStoredFile(false)
{
// Lazily get the content type and size
mContentType.SetIsVoid(true);
@ -188,17 +231,47 @@ protected:
const nsAString& aContentType)
: nsDOMFileBase(aContentType, aOther->mStart + aStart, aLength),
mFile(aOther->mFile), mWholeFile(false),
mCacheToken(aOther->mCacheToken)
mStoredFile(aOther->mStoredFile), mCacheToken(aOther->mCacheToken)
{
NS_ASSERTION(mFile, "must have file");
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>
CreateSlice(PRUint64 aStart, PRUint64 aLength,
const nsAString& aContentType);
virtual bool IsStoredFile()
{
return mStoredFile;
}
virtual bool IsWholeFile()
{
return mWholeFile;
}
nsCOMPtr<nsIFile> mFile;
bool mWholeFile;
bool mStoredFile;
nsCOMPtr<nsISupports> mCacheToken;
};

View File

@ -39,15 +39,28 @@
%{C++
#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 nsIInputStream;
interface nsIURI;
interface nsIPrincipal;
interface nsIDOMBlob;
[scriptable, builtinclass, uuid(d5237f31-443a-460b-9e42-449a135346f0)]
[scriptable, builtinclass, uuid(f62c6887-e3bc-495a-802c-287e12e969a0)]
interface nsIDOMBlob : nsISupports
{
readonly attribute unsigned long long size;
@ -61,6 +74,18 @@ interface nsIDOMBlob : nsISupports
[optional_argc] nsIDOMBlob mozSlice([optional] in long long start,
[optional] in long long end,
[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)]

View File

@ -298,6 +298,76 @@ nsDOMFileBase::GetInternalUrl(nsIPrincipal* aPrincipal, nsAString& aURL)
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
nsDOMFileBase::GetSendInfo(nsIInputStream** aBody,
nsACString& aContentType,

View File

@ -11,7 +11,7 @@ To move to a new version:
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
well as the version number at the top of this file.

View File

@ -181,6 +181,7 @@ EXPORTS
sqlite3_vfs_unregister
sqlite3_vfs_register
sqlite3_vmprintf
sqlite3_win32_utf8_to_mbcs
#ifdef SQLITE_DEBUG
sqlite3_mutex_held
sqlite3_mutex_notheld

View File

@ -27,7 +27,7 @@
** files within the group is less than the new quota, then the write
** continues as if nothing had happened.
*/
#include "sqlite3.h"
#include "test_quota.h"
#include <string.h>
#include <assert.h>
@ -45,20 +45,6 @@
#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 ******************************/
/* Forward declaration of all object types */
@ -125,6 +111,18 @@ struct quotaConn {
/* 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 **********************************/
/*
** 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 "/" or "\\"
**
*/
static int quotaStrglob(const char *zGlob, const char *z){
int c, c2;
int c, c2, cx;
int invert;
int seen;
@ -258,8 +258,9 @@ static int quotaStrglob(const char *zGlob, const char *z){
}
return (*z)!=0;
}
cx = (c=='/') ? '\\' : c;
while( (c2 = (*(z++)))!=0 ){
while( c2!=c ){
while( c2!=c && c2!=cx ){
c2 = *(z++);
if( c2==0 ) return 0;
}
@ -297,6 +298,9 @@ static int quotaStrglob(const char *zGlob, const char *z){
c2 = *(zGlob++);
}
if( c2==0 || (seen ^ invert)==0 ) return 0;
}else if( c=='/' ){
if( z[0]!='/' && z[0]!='\\' ) return 0;
z++;
}else{
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.
** 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;
while( pFile && strcmp(pFile->zFilename, zName)!=0 ){
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;
}
/*
** 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 *****************************/
/*
** This is the xOpen method used for the "quota" VFS.
@ -378,25 +499,13 @@ static int quotaOpen(
pSubOpen = quotaSubOpen(pConn);
rc = pOrigVfs->xOpen(pOrigVfs, zName, pSubOpen, flags, pOutFlags);
if( rc==SQLITE_OK ){
pFile = quotaFindFile(pGroup, zName);
pFile = quotaFindFile(pGroup, zName, 1);
if( pFile==0 ){
int nName = strlen(zName);
pFile = (quotaFile *)sqlite3_malloc( sizeof(*pFile) + nName + 1 );
if( pFile==0 ){
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;
quotaLeave();
pSubOpen->pMethods->xClose(pSubOpen);
return SQLITE_NOMEM;
}
pFile->deleteOnClose = (flags & SQLITE_OPEN_DELETEONCLOSE)!=0;
pFile->nRef++;
pQuotaOpen->pFile = pFile;
if( pSubOpen->pMethods->iVersion==1 ){
@ -437,7 +546,7 @@ static int quotaDelete(
quotaEnter();
pGroup = quotaGroupFind(zName);
if( pGroup ){
pFile = quotaFindFile(pGroup, zName);
pFile = quotaFindFile(pGroup, zName, 0);
if( pFile ){
if( pFile->nRef ){
pFile->deleteOnClose = 1;
@ -469,7 +578,10 @@ static int quotaClose(sqlite3_file *pConn){
pFile->nRef--;
if( pFile->nRef==0 ){
quotaGroup *pGroup = pFile->pGroup;
if( pFile->deleteOnClose ) quotaRemoveFile(pFile);
if( pFile->deleteOnClose ){
gQuota.pOrigVfs->xDelete(gQuota.pOrigVfs, pFile->zFilename, 0);
quotaRemoveFile(pFile);
}
quotaGroupDeref(pGroup);
}
quotaLeave();
@ -603,7 +715,13 @@ static int quotaCheckReservedLock(sqlite3_file *pConn, int *pResOut){
*/
static int quotaFileControl(sqlite3_file *pConn, int op, void *pArg){
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.
@ -819,8 +937,8 @@ int sqlite3_quota_file(const char *zFilename){
int rc;
int outFlags = 0;
sqlite3_int64 iSize;
fd = (sqlite3_file*)
sqlite3_malloc(gQuota.sThisVfs.szOsFile + gQuota.sThisVfs.mxPathname+1);
fd = (sqlite3_file*)sqlite3_malloc(gQuota.sThisVfs.szOsFile +
gQuota.sThisVfs.mxPathname+1);
if( fd==0 ) return SQLITE_NOMEM;
zFull = gQuota.sThisVfs.szOsFile + (char*)fd;
rc = gQuota.pOrigVfs->xFullPathname(gQuota.pOrigVfs, zFilename,
@ -838,7 +956,7 @@ int sqlite3_quota_file(const char *zFilename){
quotaEnter();
pGroup = quotaGroupFind(zFull);
if( pGroup ){
pFile = quotaFindFile(pGroup, zFull);
pFile = quotaFindFile(pGroup, zFull, 0);
if( pFile ) quotaRemoveFile(pFile);
}
quotaLeave();
@ -847,6 +965,220 @@ int sqlite3_quota_file(const char *zFilename){
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 ***********************************/
#ifdef SQLITE_TEST
@ -1075,9 +1407,13 @@ static int test_quota_dump(
Tcl_ListObjAppendElement(interp, pGroupTerm,
Tcl_NewWideIntObj(pGroup->iSize));
for(pFile=pGroup->pFiles; pFile; pFile=pFile->pNext){
int i;
char zTemp[1000];
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_NewStringObj(pFile->zFilename, -1));
Tcl_NewStringObj(zTemp, -1));
Tcl_ListObjAppendElement(interp, pFileTerm,
Tcl_NewWideIntObj(pFile->iSize));
Tcl_ListObjAppendElement(interp, pFileTerm,
@ -1093,6 +1429,272 @@ static int test_quota_dump(
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
** module. This should be the only procedure visible from outside
@ -1104,10 +1706,20 @@ int Sqlitequota_Init(Tcl_Interp *interp){
Tcl_ObjCmdProc *xProc;
} aCmd[] = {
{ "sqlite3_quota_initialize", test_quota_initialize },
{ "sqlite3_quota_shutdown", test_quota_shutdown },
{ "sqlite3_quota_set", test_quota_set },
{ "sqlite3_quota_file", test_quota_file },
{ "sqlite3_quota_dump", test_quota_dump },
{ "sqlite3_quota_shutdown", test_quota_shutdown },
{ "sqlite3_quota_set", test_quota_set },
{ "sqlite3_quota_file", test_quota_file },
{ "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;

209
db/sqlite3/src/test_quota.h Normal file
View File

@ -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_ */

View File

@ -45,6 +45,7 @@ namespace dom {
enum StructuredCloneTags {
SCTAG_BASE = JS_SCTAG_USER_MIN,
SCTAG_DOM_BLOB,
SCTAG_DOM_FILE,
SCTAG_DOM_FILELIST,
SCTAG_DOM_MAX
};

View File

@ -83,6 +83,8 @@
#include "nsIIOService.h"
#include "mozilla/dom/Element.h"
#include "mozilla/dom/indexedDB/FileInfo.h"
#include "mozilla/dom/indexedDB/IndexedDatabaseManager.h"
#include "sampler.h"
using namespace mozilla::dom;
@ -1922,3 +1924,51 @@ nsDOMWindowUtils::CheckAndClearPaintedState(nsIDOMElement* aElement, bool* aResu
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;
}

View File

@ -77,9 +77,9 @@ private:
// something fails.
inline
nsresult
ConvertCloneBuffersToArrayInternal(
ConvertCloneReadInfosToArrayInternal(
JSContext* aCx,
nsTArray<JSAutoStructuredCloneBuffer>& aBuffers,
nsTArray<StructuredCloneReadInfo>& aReadInfos,
jsval* aResult)
{
JSObject* array = JS_NewArrayObject(aCx, 0, nsnull);
@ -88,17 +88,18 @@ ConvertCloneBuffersToArrayInternal(
return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
}
if (!aBuffers.IsEmpty()) {
if (!JS_SetArrayLength(aCx, array, jsuint(aBuffers.Length()))) {
if (!aReadInfos.IsEmpty()) {
if (!JS_SetArrayLength(aCx, array, jsuint(aReadInfos.Length()))) {
NS_WARNING("Failed to set array length!");
return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
}
for (uint32 index = 0, count = aBuffers.Length(); index < count; index++) {
JSAutoStructuredCloneBuffer& buffer = aBuffers[index];
for (uint32 index = 0, count = aReadInfos.Length(); index < count;
index++) {
StructuredCloneReadInfo& readInfo = aReadInfos[index];
jsval val;
if (!IDBObjectStore::DeserializeValue(aCx, buffer, &val)) {
if (!IDBObjectStore::DeserializeValue(aCx, readInfo, &val)) {
NS_WARNING("Failed to decode!");
return NS_ERROR_DOM_DATA_CLONE_ERR;
}
@ -504,9 +505,9 @@ AsyncConnectionHelper::ReleaseMainThreadObjects()
// static
nsresult
AsyncConnectionHelper::ConvertCloneBuffersToArray(
AsyncConnectionHelper::ConvertCloneReadInfosToArray(
JSContext* aCx,
nsTArray<JSAutoStructuredCloneBuffer>& aBuffers,
nsTArray<StructuredCloneReadInfo>& aReadInfos,
jsval* aResult)
{
NS_ASSERTION(aCx, "Null context!");
@ -514,12 +515,12 @@ AsyncConnectionHelper::ConvertCloneBuffersToArray(
JSAutoRequest ar(aCx);
nsresult rv = ConvertCloneBuffersToArrayInternal(aCx, aBuffers, aResult);
nsresult rv = ConvertCloneReadInfosToArrayInternal(aCx, aReadInfos, aResult);
for (PRUint32 index = 0; index < aBuffers.Length(); index++) {
aBuffers[index].clear();
for (PRUint32 index = 0; index < aReadInfos.Length(); index++) {
aReadInfos[index].mCloneBuffer.clear();
}
aBuffers.Clear();
aReadInfos.Clear();
return rv;
}

View File

@ -41,6 +41,7 @@
#define mozilla_dom_indexeddb_asyncconnectionhelper_h__
// Only meant to be included in IndexedDB source files, not exported.
#include "DatabaseInfo.h"
#include "IndexedDatabase.h"
#include "IDBDatabase.h"
#include "IDBRequest.h"
@ -198,9 +199,9 @@ protected:
/**
* Helper to make a JS array object out of an array of clone buffers.
*/
static nsresult ConvertCloneBuffersToArray(
static nsresult ConvertCloneReadInfosToArray(
JSContext* aCx,
nsTArray<JSAutoStructuredCloneBuffer>& aBuffers,
nsTArray<StructuredCloneReadInfo>& aReadInfos,
jsval* aResult);
protected:

View File

@ -205,9 +205,6 @@ CheckPermissionsHelper::Observe(nsISupports* aSubject,
mPromptResult = nsDependentString(aData).ToInteger(&rv);
NS_ENSURE_SUCCESS(rv, rv);
IndexedDatabaseManager* mgr = IndexedDatabaseManager::Get();
NS_ASSERTION(mgr, "This should never be null!");
rv = NS_DispatchToCurrentThread(this);
NS_ENSURE_SUCCESS(rv, rv);

138
dom/indexedDB/FileInfo.cpp Normal file
View File

@ -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!");
}
}

155
dom/indexedDB/FileInfo.h Normal file
View File

@ -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__

View File

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

136
dom/indexedDB/FileManager.h Normal file
View File

@ -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__

View File

@ -49,7 +49,6 @@
#include "nsThreadUtils.h"
#include "AsyncConnectionHelper.h"
#include "DatabaseInfo.h"
#include "IDBEvents.h"
#include "IDBIndex.h"
#include "IDBObjectStore.h"
@ -87,7 +86,7 @@ public:
~ContinueHelper()
{
IDBObjectStore::ClearStructuredCloneBuffer(mCloneBuffer);
IDBObjectStore::ClearStructuredCloneBuffer(mCloneReadInfo.mCloneBuffer);
}
nsresult DoDatabaseWork(mozIStorageConnection* aConnection);
@ -112,7 +111,7 @@ protected:
PRInt32 mCount;
Key mKey;
Key mObjectKey;
JSAutoStructuredCloneBuffer mCloneBuffer;
StructuredCloneReadInfo mCloneReadInfo;
};
class ContinueObjectStoreHelper : public ContinueHelper
@ -165,7 +164,7 @@ IDBCursor::Create(IDBRequest* aRequest,
const nsACString& aContinueQuery,
const nsACString& aContinueToQuery,
const Key& aKey,
JSAutoStructuredCloneBuffer& aCloneBuffer)
StructuredCloneReadInfo& aCloneReadInfo)
{
NS_ASSERTION(aObjectStore, "Null pointer!");
NS_ASSERTION(!aKey.IsUnset(), "Bad key!");
@ -178,7 +177,7 @@ IDBCursor::Create(IDBRequest* aRequest,
cursor->mObjectStore = aObjectStore;
cursor->mType = OBJECTSTORE;
cursor->mKey = aKey;
cursor->mCloneBuffer.swap(aCloneBuffer);
cursor->mCloneReadInfo.Swap(aCloneReadInfo);
return cursor.forget();
}
@ -224,7 +223,7 @@ IDBCursor::Create(IDBRequest* aRequest,
const nsACString& aContinueToQuery,
const Key& aKey,
const Key& aObjectKey,
JSAutoStructuredCloneBuffer& aCloneBuffer)
StructuredCloneReadInfo& aCloneReadInfo)
{
NS_ASSERTION(aIndex, "Null pointer!");
NS_ASSERTION(!aKey.IsUnset(), "Bad key!");
@ -240,7 +239,7 @@ IDBCursor::Create(IDBRequest* aRequest,
cursor->mType = INDEXOBJECT;
cursor->mKey = aKey;
cursor->mObjectKey = aObjectKey;
cursor->mCloneBuffer.swap(aCloneBuffer);
cursor->mCloneReadInfo.Swap(aCloneReadInfo);
return cursor.forget();
}
@ -300,7 +299,7 @@ IDBCursor::~IDBCursor()
if (mRooted) {
NS_DROP_JS_OBJECTS(this, IDBCursor);
}
IDBObjectStore::ClearStructuredCloneBuffer(mCloneBuffer);
IDBObjectStore::ClearStructuredCloneBuffer(mCloneReadInfo.mCloneBuffer);
}
nsresult
@ -529,12 +528,12 @@ IDBCursor::GetValue(JSContext* aCx,
mRooted = true;
}
if (!IDBObjectStore::DeserializeValue(aCx, mCloneBuffer, &mCachedValue)) {
if (!IDBObjectStore::DeserializeValue(aCx, mCloneReadInfo, &mCachedValue)) {
mCachedValue = JSVAL_VOID;
return NS_ERROR_DOM_DATA_CLONE_ERR;
}
mCloneBuffer.clear();
mCloneReadInfo.mCloneBuffer.clear();
mHaveCachedValue = true;
}
@ -762,8 +761,8 @@ ContinueHelper::GetSuccessResult(JSContext* aCx,
mCursor->mObjectKey = mObjectKey;
mCursor->mContinueToKey.Unset();
mCursor->mCloneBuffer.swap(mCloneBuffer);
mCloneBuffer.clear();
mCursor->mCloneReadInfo.Swap(mCloneReadInfo);
mCloneReadInfo.mCloneBuffer.clear();
nsresult rv = WrapNative(aCx, mCursor, aVal);
NS_ENSURE_SUCCESS(rv, rv);
@ -811,8 +810,8 @@ ContinueObjectStoreHelper::GatherResultsFromStatement(
nsresult rv = mKey.SetFromStatement(aStatement, 0);
NS_ENSURE_SUCCESS(rv, rv);
rv = IDBObjectStore::GetStructuredCloneDataFromStatement(aStatement, 1,
mCloneBuffer);
rv = IDBObjectStore::GetStructuredCloneReadInfoFromStatement(aStatement, 1, 2,
mDatabase->Manager(), mCloneReadInfo);
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
@ -881,8 +880,8 @@ ContinueIndexObjectHelper::GatherResultsFromStatement(
rv = mObjectKey.SetFromStatement(aStatement, 1);
NS_ENSURE_SUCCESS(rv, rv);
rv = IDBObjectStore::GetStructuredCloneDataFromStatement(aStatement, 2,
mCloneBuffer);
rv = IDBObjectStore::GetStructuredCloneReadInfoFromStatement(aStatement, 2, 3,
mDatabase->Manager(), mCloneReadInfo);
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;

View File

@ -88,7 +88,7 @@ public:
const nsACString& aContinueQuery,
const nsACString& aContinueToQuery,
const Key& aKey,
JSAutoStructuredCloneBuffer& aCloneBuffer);
StructuredCloneReadInfo& aCloneReadInfo);
// For INDEXKEY cursors.
static
@ -115,7 +115,7 @@ public:
const nsACString& aContinueToQuery,
const Key& aKey,
const Key& aObjectKey,
JSAutoStructuredCloneBuffer& aCloneBuffer);
StructuredCloneReadInfo& aCloneReadInfo);
enum Type
{
@ -169,7 +169,7 @@ protected:
Key mKey;
Key mObjectKey;
JSAutoStructuredCloneBuffer mCloneBuffer;
StructuredCloneReadInfo mCloneReadInfo;
Key mContinueToKey;
bool mHaveCachedKey;

View File

@ -43,6 +43,7 @@
#include "mozilla/Mutex.h"
#include "mozilla/storage.h"
#include "nsDOMClassInfo.h"
#include "nsDOMLists.h"
#include "nsEventDispatcher.h"
#include "nsJSUtils.h"
#include "nsProxyRelease.h"
@ -151,7 +152,8 @@ already_AddRefed<IDBDatabase>
IDBDatabase::Create(nsIScriptContext* aScriptContext,
nsPIDOMWindow* aOwner,
already_AddRefed<DatabaseInfo> aDatabaseInfo,
const nsACString& aASCIIOrigin)
const nsACString& aASCIIOrigin,
FileManager* aFileManager)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
NS_ASSERTION(!aASCIIOrigin.IsEmpty(), "Empty origin!");
@ -169,6 +171,7 @@ IDBDatabase::Create(nsIScriptContext* aScriptContext,
db->mFilePath = databaseInfo->filePath;
databaseInfo.swap(db->mDatabaseInfo);
db->mASCIIOrigin = aASCIIOrigin;
db->mFileManager = aFileManager;
IndexedDatabaseManager* mgr = IndexedDatabaseManager::Get();
NS_ASSERTION(mgr, "This should never be null!");

View File

@ -41,12 +41,12 @@
#define mozilla_dom_indexeddb_idbdatabase_h__
#include "mozilla/dom/indexedDB/IndexedDatabase.h"
#include "mozilla/dom/indexedDB/FileManager.h"
#include "nsIIDBDatabase.h"
#include "nsCycleCollectionParticipant.h"
#include "nsDOMEventTargetHelper.h"
#include "nsDOMLists.h"
#include "nsIDocument.h"
class nsIScriptContext;
@ -78,7 +78,8 @@ public:
Create(nsIScriptContext* aScriptContext,
nsPIDOMWindow* aOwner,
already_AddRefed<DatabaseInfo> aDatabaseInfo,
const nsACString& aASCIIOrigin);
const nsACString& aASCIIOrigin,
FileManager* aFileManager);
// nsIDOMEventTarget
virtual nsresult PostHandleEvent(nsEventChainPostVisitor& aVisitor);
@ -141,6 +142,11 @@ public:
void EnterSetVersionTransaction();
void ExitSetVersionTransaction();
FileManager* Manager() const
{
return mFileManager;
}
private:
IDBDatabase();
~IDBDatabase();
@ -158,6 +164,8 @@ private:
bool mClosed;
bool mRunningVersionChange;
nsRefPtr<FileManager> mFileManager;
// Only touched on the main thread.
nsRefPtr<nsDOMEventListenerWrapper> mOnAbortListener;
nsRefPtr<nsDOMEventListenerWrapper> mOnErrorListener;

View File

@ -104,7 +104,7 @@ public:
~GetHelper()
{
IDBObjectStore::ClearStructuredCloneBuffer(mCloneBuffer);
IDBObjectStore::ClearStructuredCloneBuffer(mCloneReadInfo.mCloneBuffer);
}
nsresult DoDatabaseWork(mozIStorageConnection* aConnection);
@ -113,12 +113,12 @@ public:
void ReleaseMainThreadObjects()
{
IDBObjectStore::ClearStructuredCloneBuffer(mCloneBuffer);
IDBObjectStore::ClearStructuredCloneBuffer(mCloneReadInfo.mCloneBuffer);
GetKeyHelper::ReleaseMainThreadObjects();
}
protected:
JSAutoStructuredCloneBuffer mCloneBuffer;
StructuredCloneReadInfo mCloneReadInfo;
};
class GetAllKeysHelper : public GetKeyHelper
@ -154,8 +154,9 @@ public:
~GetAllHelper()
{
for (PRUint32 index = 0; index < mCloneBuffers.Length(); index++) {
IDBObjectStore::ClearStructuredCloneBuffer(mCloneBuffers[index]);
for (PRUint32 index = 0; index < mCloneReadInfos.Length(); index++) {
IDBObjectStore::ClearStructuredCloneBuffer(
mCloneReadInfos[index].mCloneBuffer);
}
}
@ -165,15 +166,16 @@ public:
void ReleaseMainThreadObjects()
{
for (PRUint32 index = 0; index < mCloneBuffers.Length(); index++) {
IDBObjectStore::ClearStructuredCloneBuffer(mCloneBuffers[index]);
for (PRUint32 index = 0; index < mCloneReadInfos.Length(); index++) {
IDBObjectStore::ClearStructuredCloneBuffer(
mCloneReadInfos[index].mCloneBuffer);
}
GetKeyHelper::ReleaseMainThreadObjects();
}
protected:
const PRUint32 mLimit;
nsTArray<JSAutoStructuredCloneBuffer> mCloneBuffers;
nsTArray<StructuredCloneReadInfo> mCloneReadInfos;
};
class OpenKeyCursorHelper : public AsyncConnectionHelper
@ -227,7 +229,7 @@ public:
~OpenCursorHelper()
{
IDBObjectStore::ClearStructuredCloneBuffer(mCloneBuffer);
IDBObjectStore::ClearStructuredCloneBuffer(mCloneReadInfo.mCloneBuffer);
}
nsresult DoDatabaseWork(mozIStorageConnection* aConnection);
@ -250,7 +252,7 @@ private:
// Out-params.
Key mKey;
Key mObjectKey;
JSAutoStructuredCloneBuffer mCloneBuffer;
StructuredCloneReadInfo mCloneReadInfo;
nsCString mContinueQuery;
nsCString mContinueToQuery;
Key mRangeKey;
@ -793,7 +795,7 @@ GetHelper::DoDatabaseWork(mozIStorageConnection* /* aConnection */)
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(" ON ") + objectTable +
NS_LITERAL_CSTRING(".id = ") + joinTable +
@ -818,8 +820,8 @@ GetHelper::DoDatabaseWork(mozIStorageConnection* /* aConnection */)
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
if (hasResult) {
rv = IDBObjectStore::GetStructuredCloneDataFromStatement(stmt, 0,
mCloneBuffer);
rv = IDBObjectStore::GetStructuredCloneReadInfoFromStatement(stmt, 0, 1,
mDatabase->Manager(), mCloneReadInfo);
NS_ENSURE_SUCCESS(rv, rv);
}
@ -830,9 +832,9 @@ nsresult
GetHelper::GetSuccessResult(JSContext* aCx,
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);
return NS_OK;
@ -1003,7 +1005,7 @@ GetAllHelper::DoDatabaseWork(mozIStorageConnection* /* aConnection */)
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(" ON ") + dataTableName +
NS_LITERAL_CSTRING(".id = ") + indexTableName +
@ -1025,18 +1027,19 @@ GetAllHelper::DoDatabaseWork(mozIStorageConnection* /* aConnection */)
NS_ENSURE_SUCCESS(rv, rv);
}
mCloneBuffers.SetCapacity(50);
mCloneReadInfos.SetCapacity(50);
bool hasResult;
while(NS_SUCCEEDED((rv = stmt->ExecuteStep(&hasResult))) && hasResult) {
if (mCloneBuffers.Capacity() == mCloneBuffers.Length()) {
mCloneBuffers.SetCapacity(mCloneBuffers.Capacity() * 2);
if (mCloneReadInfos.Capacity() == mCloneReadInfos.Length()) {
mCloneReadInfos.SetCapacity(mCloneReadInfos.Capacity() * 2);
}
JSAutoStructuredCloneBuffer* buffer = mCloneBuffers.AppendElement();
NS_ASSERTION(buffer, "This shouldn't fail!");
StructuredCloneReadInfo* readInfo = mCloneReadInfos.AppendElement();
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, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
@ -1048,12 +1051,12 @@ nsresult
GetAllHelper::GetSuccessResult(JSContext* aCx,
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++) {
mCloneBuffers[index].clear();
for (PRUint32 index = 0; index < mCloneReadInfos.Length(); index++) {
mCloneReadInfos[index].mCloneBuffer.clear();
}
NS_ENSURE_SUCCESS(rv, rv);
@ -1317,9 +1320,10 @@ OpenCursorHelper::DoDatabaseWork(mozIStorageConnection* aConnection)
NS_NAMED_LITERAL_CSTRING(commaspace, ", ");
nsCString data = objectTable + NS_LITERAL_CSTRING(".data");
nsCString fileIds = objectTable + NS_LITERAL_CSTRING(".file_ids");
nsCString firstQuery = NS_LITERAL_CSTRING("SELECT ") + value + commaspace +
keyValue + commaspace + data +
keyValue + commaspace + data + commaspace + fileIds +
NS_LITERAL_CSTRING(" FROM ") + indexTable +
NS_LITERAL_CSTRING(" INNER JOIN ") + objectTable +
NS_LITERAL_CSTRING(" ON ") + indexTable + dot +
@ -1359,13 +1363,14 @@ OpenCursorHelper::DoDatabaseWork(mozIStorageConnection* aConnection)
rv = mObjectKey.SetFromStatement(stmt, 1);
NS_ENSURE_SUCCESS(rv, rv);
rv = IDBObjectStore::GetStructuredCloneDataFromStatement(stmt, 2,
mCloneBuffer);
rv = IDBObjectStore::GetStructuredCloneReadInfoFromStatement(stmt, 2, 3,
mDatabase->Manager(), mCloneReadInfo);
NS_ENSURE_SUCCESS(rv, rv);
// Now we need to make the query to get the next match.
nsCAutoString queryStart = NS_LITERAL_CSTRING("SELECT ") + value +
commaspace + keyValue + commaspace + data +
commaspace + fileIds +
NS_LITERAL_CSTRING(" FROM ") + indexTable +
NS_LITERAL_CSTRING(" INNER JOIN ") + objectTable +
NS_LITERAL_CSTRING(" ON ") + indexTable + dot +
@ -1458,10 +1463,10 @@ OpenCursorHelper::GetSuccessResult(JSContext* aCx,
nsRefPtr<IDBCursor> cursor =
IDBCursor::Create(mRequest, mTransaction, mIndex, mDirection, mRangeKey,
mContinueQuery, mContinueToQuery, mKey, mObjectKey,
mCloneBuffer);
mCloneReadInfo);
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);
}

View File

@ -42,15 +42,19 @@
#include "nsIJSContextStack.h"
#include "jsclone.h"
#include "mozilla/dom/StructuredCloneTags.h"
#include "mozilla/storage.h"
#include "nsCharSeparatedTokenizer.h"
#include "nsContentUtils.h"
#include "nsDOMClassInfo.h"
#include "nsDOMFile.h"
#include "nsDOMLists.h"
#include "nsEventDispatcher.h"
#include "nsJSUtils.h"
#include "nsServiceManagerUtils.h"
#include "nsThreadUtils.h"
#include "snappy/snappy.h"
#include "test_quota.h"
#include "AsyncConnectionHelper.h"
#include "IDBCursor.h"
@ -60,6 +64,8 @@
#include "IDBTransaction.h"
#include "DatabaseInfo.h"
#define FILE_COPY_BUFFER_SIZE 32768
USING_INDEXEDDB_NAMESPACE
namespace {
@ -70,21 +76,20 @@ public:
AddHelper(IDBTransaction* aTransaction,
IDBRequest* aRequest,
IDBObjectStore* aObjectStore,
JSAutoStructuredCloneBuffer& aCloneBuffer,
StructuredCloneWriteInfo& aCloneWriteInfo,
const Key& aKey,
bool aOverwrite,
nsTArray<IndexUpdateInfo>& aIndexUpdateInfo,
PRUint64 aOffsetToKeyProp)
nsTArray<IndexUpdateInfo>& aIndexUpdateInfo)
: AsyncConnectionHelper(aTransaction, aRequest), mObjectStore(aObjectStore),
mKey(aKey), mOverwrite(aOverwrite), mOffsetToKeyProp(aOffsetToKeyProp)
mKey(aKey), mOverwrite(aOverwrite)
{
mCloneBuffer.swap(aCloneBuffer);
mCloneWriteInfo.Swap(aCloneWriteInfo);
mIndexUpdateInfo.SwapElements(aIndexUpdateInfo);
}
~AddHelper()
{
IDBObjectStore::ClearStructuredCloneBuffer(mCloneBuffer);
IDBObjectStore::ClearStructuredCloneBuffer(mCloneWriteInfo.mCloneBuffer);
}
nsresult DoDatabaseWork(mozIStorageConnection* aConnection);
@ -94,7 +99,7 @@ public:
void ReleaseMainThreadObjects()
{
mObjectStore = nsnull;
IDBObjectStore::ClearStructuredCloneBuffer(mCloneBuffer);
IDBObjectStore::ClearStructuredCloneBuffer(mCloneWriteInfo.mCloneBuffer);
AsyncConnectionHelper::ReleaseMainThreadObjects();
}
@ -103,11 +108,10 @@ private:
nsRefPtr<IDBObjectStore> mObjectStore;
// These may change in the autoincrement case.
JSAutoStructuredCloneBuffer mCloneBuffer;
StructuredCloneWriteInfo mCloneWriteInfo;
Key mKey;
const bool mOverwrite;
nsTArray<IndexUpdateInfo> mIndexUpdateInfo;
PRUint64 mOffsetToKeyProp;
};
class GetHelper : public AsyncConnectionHelper
@ -123,7 +127,7 @@ public:
~GetHelper()
{
IDBObjectStore::ClearStructuredCloneBuffer(mCloneBuffer);
IDBObjectStore::ClearStructuredCloneBuffer(mCloneReadInfo.mCloneBuffer);
}
nsresult DoDatabaseWork(mozIStorageConnection* aConnection);
@ -134,7 +138,7 @@ public:
{
mObjectStore = nsnull;
mKeyRange = nsnull;
IDBObjectStore::ClearStructuredCloneBuffer(mCloneBuffer);
IDBObjectStore::ClearStructuredCloneBuffer(mCloneReadInfo.mCloneBuffer);
AsyncConnectionHelper::ReleaseMainThreadObjects();
}
@ -145,7 +149,7 @@ protected:
private:
// Out-params.
JSAutoStructuredCloneBuffer mCloneBuffer;
StructuredCloneReadInfo mCloneReadInfo;
};
class DeleteHelper : public GetHelper
@ -199,7 +203,7 @@ public:
~OpenCursorHelper()
{
IDBObjectStore::ClearStructuredCloneBuffer(mCloneBuffer);
IDBObjectStore::ClearStructuredCloneBuffer(mCloneReadInfo.mCloneBuffer);
}
nsresult DoDatabaseWork(mozIStorageConnection* aConnection);
@ -210,7 +214,7 @@ public:
{
mObjectStore = nsnull;
mKeyRange = nsnull;
IDBObjectStore::ClearStructuredCloneBuffer(mCloneBuffer);
IDBObjectStore::ClearStructuredCloneBuffer(mCloneReadInfo.mCloneBuffer);
AsyncConnectionHelper::ReleaseMainThreadObjects();
}
@ -222,7 +226,7 @@ private:
// Out-params.
Key mKey;
JSAutoStructuredCloneBuffer mCloneBuffer;
StructuredCloneReadInfo mCloneReadInfo;
nsCString mContinueQuery;
nsCString mContinueToQuery;
Key mRangeKey;
@ -312,8 +316,9 @@ public:
~GetAllHelper()
{
for (PRUint32 index = 0; index < mCloneBuffers.Length(); index++) {
IDBObjectStore::ClearStructuredCloneBuffer(mCloneBuffers[index]);
for (PRUint32 index = 0; index < mCloneReadInfos.Length(); index++) {
IDBObjectStore::ClearStructuredCloneBuffer(
mCloneReadInfos[index].mCloneBuffer);
}
}
@ -325,8 +330,9 @@ public:
{
mObjectStore = nsnull;
mKeyRange = nsnull;
for (PRUint32 index = 0; index < mCloneBuffers.Length(); index++) {
IDBObjectStore::ClearStructuredCloneBuffer(mCloneBuffers[index]);
for (PRUint32 index = 0; index < mCloneReadInfos.Length(); index++) {
IDBObjectStore::ClearStructuredCloneBuffer(
mCloneReadInfos[index].mCloneBuffer);
}
AsyncConnectionHelper::ReleaseMainThreadObjects();
}
@ -339,7 +345,7 @@ protected:
private:
// Out-params.
nsTArray<JSAutoStructuredCloneBuffer> mCloneBuffers;
nsTArray<StructuredCloneReadInfo> mCloneReadInfos;
};
class CountHelper : public AsyncConnectionHelper
@ -492,32 +498,6 @@ JSClass gDummyPropClass = {
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
// static
@ -771,23 +751,26 @@ IDBObjectStore::UpdateIndexes(IDBTransaction* aTransaction,
// static
nsresult
IDBObjectStore::GetStructuredCloneDataFromStatement(
IDBObjectStore::GetStructuredCloneReadInfoFromStatement(
mozIStorageStatement* aStatement,
PRUint32 aIndex,
JSAutoStructuredCloneBuffer& aBuffer)
PRUint32 aDataIndex,
PRUint32 aFileIdsIndex,
FileManager* aFileManager,
StructuredCloneReadInfo& aInfo)
{
#ifdef DEBUG
{
PRInt32 valueType;
NS_ASSERTION(NS_SUCCEEDED(aStatement->GetTypeOfIndex(aIndex, &valueType)) &&
valueType == mozIStorageStatement::VALUE_TYPE_BLOB,
PRInt32 type;
NS_ASSERTION(NS_SUCCEEDED(aStatement->GetTypeOfIndex(aDataIndex, &type)) &&
type == mozIStorageStatement::VALUE_TYPE_BLOB,
"Bad value type!");
}
#endif
const PRUint8* blobData;
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);
const char* compressed = reinterpret_cast<const char*>(blobData);
@ -808,10 +791,38 @@ IDBObjectStore::GetStructuredCloneDataFromStatement(
return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
}
return aBuffer.copy(reinterpret_cast<const uint64_t *>(uncompressed.get()),
uncompressedLength) ?
NS_OK :
NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
JSAutoStructuredCloneBuffer& buffer = aInfo.mCloneBuffer;
if (!buffer.copy(reinterpret_cast<const uint64_t *>(uncompressed.get()),
uncompressedLength)) {
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
@ -826,32 +837,36 @@ IDBObjectStore::ClearStructuredCloneBuffer(JSAutoStructuredCloneBuffer& aBuffer)
// static
bool
IDBObjectStore::DeserializeValue(JSContext* aCx,
JSAutoStructuredCloneBuffer& aBuffer,
jsval* aValue,
JSStructuredCloneCallbacks* aCallbacks,
void* aClosure)
StructuredCloneReadInfo& aCloneReadInfo,
jsval* aValue)
{
NS_ASSERTION(NS_IsMainThread(),
"Should only be deserializing on the main thread!");
NS_ASSERTION(aCx, "A JSContext is required!");
if (!aBuffer.data()) {
JSAutoStructuredCloneBuffer& buffer = aCloneReadInfo.mCloneBuffer;
if (!buffer.data()) {
*aValue = JSVAL_VOID;
return true;
}
JSAutoRequest ar(aCx);
return aBuffer.read(aCx, aValue, aCallbacks, aClosure);
JSStructuredCloneCallbacks callbacks = {
IDBObjectStore::StructuredCloneReadCallback,
nsnull,
nsnull
};
return buffer.read(aCx, aValue, &callbacks, &aCloneReadInfo);
}
// static
bool
IDBObjectStore::SerializeValue(JSContext* aCx,
JSAutoStructuredCloneBuffer& aBuffer,
jsval aValue,
JSStructuredCloneCallbacks* aCallbacks,
void* aClosure)
StructuredCloneWriteInfo& aCloneWriteInfo,
jsval aValue)
{
NS_ASSERTION(NS_IsMainThread(),
"Should only be serializing on the main thread!");
@ -859,30 +874,264 @@ IDBObjectStore::SerializeValue(JSContext* 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
SwapBytes(PRUint64 u)
{
#ifdef IS_BIG_ENDIAN
return ((u & 0x00000000000000ffLLU) << 56) |
((u & 0x000000000000ff00LLU) << 40) |
((u & 0x0000000000ff0000LLU) << 24) |
((u & 0x00000000ff000000LLU) << 8) |
((u & 0x000000ff00000000LLU) >> 8) |
((u & 0x0000ff0000000000LLU) >> 24) |
((u & 0x00ff000000000000LLU) >> 40) |
((u & 0xff00000000000000LLU) >> 56);
return ((u & 0x00000000000000ffLLU) << 56) |
((u & 0x000000000000ff00LLU) << 40) |
((u & 0x0000000000ff0000LLU) << 24) |
((u & 0x00000000ff000000LLU) << 8) |
((u & 0x000000ff00000000LLU) >> 8) |
((u & 0x0000ff0000000000LLU) >> 24) |
((u & 0x00ff000000000000LLU) >> 40) |
((u & 0xff00000000000000LLU) >> 56);
#else
return jsdouble(u);
return jsdouble(u);
#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
IDBObjectStore::ModifyValueForNewKey(JSAutoStructuredCloneBuffer& aBuffer,
Key& aKey,
PRUint64 aOffsetToKeyProp)
IDBObjectStore::ConvertFileIdsToArray(const nsAString& aFileIds,
nsTArray<PRInt64>& aResult)
{
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(!NS_IsMainThread(), "Wrong thread");
@ -893,9 +1142,12 @@ IDBObjectStore::ModifyValueForNewKey(JSAutoStructuredCloneBuffer& aBuffer,
PRUint64 u;
} 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;
}
@ -915,10 +1167,9 @@ nsresult
IDBObjectStore::GetAddInfo(JSContext* aCx,
jsval aValue,
jsval aKeyVal,
JSAutoStructuredCloneBuffer& aCloneBuffer,
StructuredCloneWriteInfo& aCloneWriteInfo,
Key& aKey,
nsTArray<IndexUpdateInfo>& aUpdateInfoArray,
PRUint64* aOffsetToKeyProp)
nsTArray<IndexUpdateInfo>& aUpdateInfoArray)
{
nsresult rv;
@ -1066,18 +1317,12 @@ IDBObjectStore::GetAddInfo(JSContext* aCx,
}
}
JSStructuredCloneCallbacks callbacks = {
nsnull,
StructuredCloneWriteDummyProp,
nsnull
};
*aOffsetToKeyProp = 0;
aCloneWriteInfo.mOffsetToKeyProp = 0;
// 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
if (NS_SUCCEEDED(rv) &&
!IDBObjectStore::SerializeValue(aCx, aCloneBuffer, aValue, &callbacks,
aOffsetToKeyProp)) {
!IDBObjectStore::SerializeValue(aCx, aCloneWriteInfo, aValue)) {
rv = NS_ERROR_DOM_DATA_CLONE_ERR;
}
@ -1118,13 +1363,12 @@ IDBObjectStore::AddOrPut(const jsval& aValue,
jsval keyval = (aOptionalArgCount >= 1) ? aKey : JSVAL_VOID;
JSAutoStructuredCloneBuffer cloneBuffer;
StructuredCloneWriteInfo cloneWriteInfo;
Key key;
nsTArray<IndexUpdateInfo> updateInfo;
PRUint64 offset;
nsresult rv =
GetAddInfo(aCx, aValue, keyval, cloneBuffer, key, updateInfo, &offset);
nsresult rv = GetAddInfo(aCx, aValue, keyval, cloneWriteInfo, key,
updateInfo);
if (NS_FAILED(rv)) {
return rv;
}
@ -1138,8 +1382,8 @@ IDBObjectStore::AddOrPut(const jsval& aValue,
NS_ENSURE_TRUE(request, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
nsRefPtr<AddHelper> helper =
new AddHelper(mTransaction, request, this, cloneBuffer, key, aOverwrite,
updateInfo, offset);
new AddHelper(mTransaction, request, this, cloneWriteInfo, key, aOverwrite,
updateInfo);
rv = helper->DispatchToTransactionPool();
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
@ -1688,6 +1932,31 @@ IDBObjectStore::Count(const jsval& aKey,
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
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).
stmt = mTransaction->AddStatement(true, mayOverwrite, autoIncrement);
NS_ENSURE_TRUE(stmt, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
@ -1793,8 +2120,8 @@ AddHelper::DoDatabaseWork(mozIStorageConnection* aConnection)
else {
// Compress the bytes before adding into the database.
const char* uncompressed =
reinterpret_cast<const char*>(mCloneBuffer.data());
size_t uncompressedLength = mCloneBuffer.nbytes();
reinterpret_cast<const char*>(mCloneWriteInfo.mCloneBuffer.data());
size_t uncompressedLength = mCloneWriteInfo.mCloneBuffer.nbytes();
size_t compressedLength = snappy::MaxCompressedLength(uncompressedLength);
compressed = new char[compressedLength];
@ -1809,6 +2136,13 @@ AddHelper::DoDatabaseWork(mozIStorageConnection* aConnection)
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();
if (NS_FAILED(rv)) {
if (mayOverwrite && rv == NS_ERROR_STORAGE_CONSTRAINT) {
@ -1835,6 +2169,13 @@ AddHelper::DoDatabaseWork(mozIStorageConnection* aConnection)
rv = stmt->BindBlobByName(data, dataBuffer, dataBufferLength);
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();
}
@ -1867,8 +2208,7 @@ AddHelper::DoDatabaseWork(mozIStorageConnection* aConnection)
// Special case where someone put an object into an autoIncrement'ing
// 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.
rv = mObjectStore->ModifyValueForNewKey(mCloneBuffer, mKey,
mOffsetToKeyProp);
rv = mObjectStore->ModifyValueForNewKey(mCloneWriteInfo, mKey);
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
scoper.Abandon();
@ -1889,8 +2229,8 @@ AddHelper::DoDatabaseWork(mozIStorageConnection* aConnection)
NS_ASSERTION(!dataBuffer && !dataBufferLength, "These should be unset!");
const char* uncompressed =
reinterpret_cast<const char*>(mCloneBuffer.data());
size_t uncompressedLength = mCloneBuffer.nbytes();
reinterpret_cast<const char*>(mCloneWriteInfo.mCloneBuffer.data());
size_t uncompressedLength = mCloneWriteInfo.mCloneBuffer.nbytes();
size_t compressedLength =
snappy::MaxCompressedLength(uncompressedLength);
@ -1905,6 +2245,13 @@ AddHelper::DoDatabaseWork(mozIStorageConnection* aConnection)
rv = stmt->BindBlobByName(data, dataBuffer, dataBufferLength);
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();
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
}
@ -1931,7 +2278,7 @@ AddHelper::GetSuccessResult(JSContext* aCx,
{
NS_ASSERTION(!mKey.IsUnset(), "Badness!");
mCloneBuffer.clear();
mCloneWriteInfo.mCloneBuffer.clear();
return mKey.ToJSVal(aCx, aVal);
}
@ -1959,7 +2306,7 @@ GetHelper::DoDatabaseWork(mozIStorageConnection* /* aConnection */)
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 +
keyRangeClause + NS_LITERAL_CSTRING(" LIMIT 1");
@ -1979,8 +2326,8 @@ GetHelper::DoDatabaseWork(mozIStorageConnection* /* aConnection */)
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
if (hasResult) {
rv = IDBObjectStore::GetStructuredCloneDataFromStatement(stmt, 0,
mCloneBuffer);
rv = IDBObjectStore::GetStructuredCloneReadInfoFromStatement(stmt, 0, 1,
mDatabase->Manager(), mCloneReadInfo);
NS_ENSURE_SUCCESS(rv, rv);
}
@ -1991,9 +2338,9 @@ nsresult
GetHelper::GetSuccessResult(JSContext* aCx,
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);
return NS_OK;
@ -2121,7 +2468,7 @@ OpenCursorHelper::DoDatabaseWork(mozIStorageConnection* aConnection)
}
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 = :") +
id + keyRangeClause + directionClause +
NS_LITERAL_CSTRING(" LIMIT 1");
@ -2152,8 +2499,8 @@ OpenCursorHelper::DoDatabaseWork(mozIStorageConnection* aConnection)
rv = mKey.SetFromStatement(stmt, 0);
NS_ENSURE_SUCCESS(rv, rv);
rv = IDBObjectStore::GetStructuredCloneDataFromStatement(stmt, 1,
mCloneBuffer);
rv = IDBObjectStore::GetStructuredCloneReadInfoFromStatement(stmt, 1, 2,
mDatabase->Manager(), mCloneReadInfo);
NS_ENSURE_SUCCESS(rv, rv);
// 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 +
NS_LITERAL_CSTRING(", data FROM ") + table +
NS_LITERAL_CSTRING(", data, file_ids FROM ") + table +
NS_LITERAL_CSTRING(" WHERE object_store_id = :") + id +
keyRangeClause + directionClause +
NS_LITERAL_CSTRING(" LIMIT ");
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 +
continueToKeyRangeClause + directionClause +
NS_LITERAL_CSTRING(" LIMIT ");
@ -2226,10 +2573,10 @@ OpenCursorHelper::GetSuccessResult(JSContext* aCx,
nsRefPtr<IDBCursor> cursor =
IDBCursor::Create(mRequest, mTransaction, mObjectStore, mDirection,
mRangeKey, mContinueQuery, mContinueToQuery, mKey,
mCloneBuffer);
mCloneReadInfo);
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);
}
@ -2391,11 +2738,11 @@ CreateIndexHelper::InsertDataFromObjectStore(mozIStorageConnection* aConnection)
nsCAutoString columns;
if (mIndex->IsAutoIncrement()) {
table.AssignLiteral("ai_object_data");
columns.AssignLiteral("id, data");
columns.AssignLiteral("id, data, file_ids");
}
else {
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 +
@ -2435,12 +2782,21 @@ CreateIndexHelper::InsertDataFromObjectStore(mozIStorageConnection* aConnection)
JSAutoRequest ar(cx);
do {
JSAutoStructuredCloneBuffer buffer;
rv = IDBObjectStore::GetStructuredCloneDataFromStatement(stmt, 1, buffer);
StructuredCloneReadInfo cloneReadInfo;
rv = IDBObjectStore::GetStructuredCloneReadInfoFromStatement(stmt, 1, 2,
mDatabase->Manager(), cloneReadInfo);
NS_ENSURE_SUCCESS(rv, rv);
JSAutoStructuredCloneBuffer& buffer = cloneReadInfo.mCloneBuffer;
JSStructuredCloneCallbacks callbacks = {
IDBObjectStore::StructuredCloneReadCallback,
nsnull,
nsnull
};
jsval clone;
if (!buffer.read(cx, &clone)) {
if (!buffer.read(cx, &clone, &callbacks, &cloneReadInfo)) {
NS_WARNING("Failed to deserialize structured clone data!");
return NS_ERROR_DOM_DATA_CLONE_ERR;
}
@ -2458,7 +2814,7 @@ CreateIndexHelper::InsertDataFromObjectStore(mozIStorageConnection* aConnection)
Key key;
if (!mIndex->IsAutoIncrement()) {
rv = key.SetFromStatement(stmt, 2);
rv = key.SetFromStatement(stmt, 3);
NS_ENSURE_SUCCESS(rv, rv);
}
else {
@ -2550,12 +2906,12 @@ GetAllHelper::DoDatabaseWork(mozIStorageConnection* aConnection)
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 +
keyRangeClause + NS_LITERAL_CSTRING(" ORDER BY ") +
keyColumn + NS_LITERAL_CSTRING(" ASC") + limitClause;
mCloneBuffers.SetCapacity(50);
mCloneReadInfos.SetCapacity(50);
nsCOMPtr<mozIStorageStatement> stmt = mTransaction->GetCachedStatement(query);
NS_ENSURE_TRUE(stmt, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
@ -2578,17 +2934,18 @@ GetAllHelper::DoDatabaseWork(mozIStorageConnection* aConnection)
bool hasResult;
while (NS_SUCCEEDED((rv = stmt->ExecuteStep(&hasResult))) && hasResult) {
if (mCloneBuffers.Capacity() == mCloneBuffers.Length()) {
if (!mCloneBuffers.SetCapacity(mCloneBuffers.Capacity() * 2)) {
if (mCloneReadInfos.Capacity() == mCloneReadInfos.Length()) {
if (!mCloneReadInfos.SetCapacity(mCloneReadInfos.Capacity() * 2)) {
NS_ERROR("Out of memory!");
return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
}
}
JSAutoStructuredCloneBuffer* buffer = mCloneBuffers.AppendElement();
NS_ASSERTION(buffer, "Shouldn't fail if SetCapacity succeeded!");
StructuredCloneReadInfo* readInfo = mCloneReadInfos.AppendElement();
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, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
@ -2600,12 +2957,12 @@ nsresult
GetAllHelper::GetSuccessResult(JSContext* aCx,
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++) {
mCloneBuffers[index].clear();
for (PRUint32 index = 0; index < mCloneReadInfos.Length(); index++) {
mCloneReadInfos[index].mCloneBuffer.clear();
}
NS_ENSURE_SUCCESS(rv, rv);

View File

@ -59,6 +59,8 @@ class Key;
struct ObjectStoreInfo;
struct IndexInfo;
struct IndexUpdateInfo;
struct StructuredCloneReadInfo;
struct StructuredCloneWriteInfo;
class IDBObjectStore : public nsIIDBObjectStore
{
@ -94,26 +96,40 @@ public:
const nsTArray<IndexUpdateInfo>& aUpdateInfoArray);
static nsresult
GetStructuredCloneDataFromStatement(mozIStorageStatement* aStatement,
PRUint32 aIndex,
JSAutoStructuredCloneBuffer& aBuffer);
GetStructuredCloneReadInfoFromStatement(mozIStorageStatement* aStatement,
PRUint32 aDataIndex,
PRUint32 aFileIdsIndex,
FileManager* aFileManager,
StructuredCloneReadInfo& aInfo);
static void
ClearStructuredCloneBuffer(JSAutoStructuredCloneBuffer& aBuffer);
static bool
DeserializeValue(JSContext* aCx,
JSAutoStructuredCloneBuffer& aBuffer,
jsval* aValue,
JSStructuredCloneCallbacks* aCallbacks = nsnull,
void* aClosure = nsnull);
StructuredCloneReadInfo& aCloneReadInfo,
jsval* aValue);
static bool
SerializeValue(JSContext* aCx,
JSAutoStructuredCloneBuffer& aBuffer,
jsval aValue,
JSStructuredCloneCallbacks* aCallbacks = nsnull,
void* aClosure = nsnull);
StructuredCloneWriteInfo& aCloneWriteInfo,
jsval aValue);
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
{
@ -151,9 +167,8 @@ public:
return mTransaction;
}
nsresult ModifyValueForNewKey(JSAutoStructuredCloneBuffer& aBuffer,
Key& aKey,
PRUint64 aOffsetToKeyProp);
nsresult ModifyValueForNewKey(StructuredCloneWriteInfo& aCloneWriteInfo,
Key& aKey);
protected:
IDBObjectStore();
@ -162,10 +177,9 @@ protected:
nsresult GetAddInfo(JSContext* aCx,
jsval aValue,
jsval aKeyVal,
JSAutoStructuredCloneBuffer& aCloneBuffer,
StructuredCloneWriteInfo& aCloneWriteInfo,
Key& aKey,
nsTArray<IndexUpdateInfo>& aUpdateInfoArray,
PRUint64* aOffsetToKeyProp);
nsTArray<IndexUpdateInfo>& aUpdateInfoArray);
nsresult AddOrPut(const jsval& aValue,
const jsval& aKey,

View File

@ -43,6 +43,7 @@
#include "mozilla/storage.h"
#include "nsDOMClassInfoID.h"
#include "nsDOMLists.h"
#include "nsEventDispatcher.h"
#include "nsPIDOMWindow.h"
#include "nsProxyRelease.h"
@ -325,8 +326,21 @@ IDBTransaction::GetOrCreateConnection(mozIStorageConnection** aResult)
IDBFactory::GetConnection(mDatabase->FilePath());
NS_ENSURE_TRUE(connection, NS_ERROR_FAILURE);
nsresult rv;
nsRefPtr<UpdateRefcountFunction> function;
nsCString beginTransaction;
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;");
}
else {
@ -334,13 +348,13 @@ IDBTransaction::GetOrCreateConnection(mozIStorageConnection** aResult)
}
nsCOMPtr<mozIStorageStatement> stmt;
nsresult rv = connection->CreateStatement(beginTransaction,
getter_AddRefs(stmt));
NS_ENSURE_SUCCESS(rv, false);
rv = connection->CreateStatement(beginTransaction, getter_AddRefs(stmt));
NS_ENSURE_SUCCESS(rv, rv);
rv = stmt->Execute();
NS_ENSURE_SUCCESS(rv, false);
NS_ENSURE_SUCCESS(rv, rv);
function.swap(mUpdateFileRefcountFunction);
connection.swap(mConnection);
}
@ -364,18 +378,19 @@ IDBTransaction::AddStatement(bool aCreate,
if (aCreate) {
if (aOverwrite) {
return GetCachedStatement(
"INSERT OR FAIL INTO ai_object_data (object_store_id, id, data) "
"VALUES (:osid, :key_value, :data)"
"INSERT OR FAIL INTO ai_object_data (object_store_id, id, data, "
"file_ids) "
"VALUES (:osid, :key_value, :data, :file_ids)"
);
}
return GetCachedStatement(
"INSERT INTO ai_object_data (object_store_id, data) "
"VALUES (:osid, :data)"
"INSERT INTO ai_object_data (object_store_id, data, file_ids) "
"VALUES (:osid, :data, :file_ids)"
);
}
return GetCachedStatement(
"UPDATE ai_object_data "
"SET data = :data "
"SET data = :data, file_ids = :file_ids "
"WHERE object_store_id = :osid "
"AND id = :key_value"
);
@ -383,18 +398,19 @@ IDBTransaction::AddStatement(bool aCreate,
if (aCreate) {
if (aOverwrite) {
return GetCachedStatement(
"INSERT OR FAIL INTO object_data (object_store_id, key_value, data) "
"VALUES (:osid, :key_value, :data)"
"INSERT OR FAIL INTO object_data (object_store_id, key_value, data, "
"file_ids) "
"VALUES (:osid, :key_value, :data, :file_ids)"
);
}
return GetCachedStatement(
"INSERT INTO object_data (object_store_id, key_value, data) "
"VALUES (:osid, :key_value, :data)"
"INSERT INTO object_data (object_store_id, key_value, data, file_ids) "
"VALUES (:osid, :key_value, :data, :file_ids)"
);
}
return GetCachedStatement(
"UPDATE object_data "
"SET data = :data "
"SET data = :data, file_ids = :file_ids "
"WHERE object_store_id = :osid "
"AND key_value = :key_value"
);
@ -552,6 +568,18 @@ IDBTransaction::GetOrCreateObjectStore(const nsAString& aName,
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_TRAVERSE_BEGIN_INHERITED(IDBTransaction,
@ -827,6 +855,7 @@ CommitHelper::CommitHelper(IDBTransaction* aTransaction,
mHaveMetadata(false)
{
mConnection.swap(aTransaction->mConnection);
mUpdateFileRefcountFunction.swap(aTransaction->mUpdateFileRefcountFunction);
}
CommitHelper::~CommitHelper()
@ -843,6 +872,14 @@ CommitHelper::Run()
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;
if (mAborted) {
if (mHaveMetadata) {
@ -896,9 +933,19 @@ CommitHelper::Run()
if (mConnection) {
IndexedDatabaseManager::SetCurrentWindow(database->Owner());
if (!mAborted && mUpdateFileRefcountFunction &&
NS_FAILED(mUpdateFileRefcountFunction->UpdateDatabase(mConnection))) {
mAborted = true;
}
if (!mAborted) {
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;
}
}
@ -927,6 +974,14 @@ CommitHelper::Run()
mDoomedObjects.Clear();
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 = nsnull;
@ -935,3 +990,191 @@ CommitHelper::Run()
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;
}

View File

@ -42,7 +42,11 @@
#include "mozilla/dom/indexedDB/IndexedDatabase.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 "nsIRunnable.h"
#include "nsIThreadInternal.h"
@ -51,11 +55,10 @@
#include "nsCycleCollectionParticipant.h"
#include "nsAutoPtr.h"
#include "nsClassHashtable.h"
#include "nsHashKeys.h"
#include "nsInterfaceHashtable.h"
class mozIStorageConnection;
class mozIStorageStatement;
class nsIThread;
BEGIN_INDEXEDDB_NAMESPACE
@ -64,6 +67,7 @@ class AsyncConnectionHelper;
class CommitHelper;
struct ObjectStoreInfo;
class TransactionThreadPool;
class UpdateRefcountFunction;
class IDBTransactionListener
{
@ -167,6 +171,10 @@ public:
GetOrCreateObjectStore(const nsAString& aName,
ObjectStoreInfo* aObjectStoreInfo);
void OnNewFileInfo(FileInfo* aFileInfo);
void ClearCreatedFileInfos();
private:
IDBTransaction();
~IDBTransaction();
@ -204,6 +212,9 @@ private:
#ifdef DEBUG
bool mFiredCompleteOrAbort;
#endif
nsRefPtr<UpdateRefcountFunction> mUpdateFileRefcountFunction;
nsTArray<nsRefPtr<FileInfo> > mCreatedFileInfos;
};
class CommitHelper : public nsIRunnable
@ -233,6 +244,7 @@ private:
nsRefPtr<IDBTransaction> mTransaction;
nsRefPtr<IDBTransactionListener> mListener;
nsCOMPtr<mozIStorageConnection> mConnection;
nsRefPtr<UpdateRefcountFunction> mUpdateFileRefcountFunction;
nsAutoTArray<nsCOMPtr<nsISupports>, 10> mDoomedObjects;
PRUint64 mOldVersion;
@ -242,6 +254,101 @@ private:
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
#endif // mozilla_dom_indexeddb_idbtransaction_h__

View File

@ -60,8 +60,34 @@
#define USING_INDEXEDDB_NAMESPACE \
using namespace mozilla::dom::indexedDB;
class nsIDOMBlob;
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
void
AppendConditionClause(const nsACString& aColumnName,

View File

@ -54,6 +54,7 @@
#include "nsThreadUtils.h"
#include "nsXPCOM.h"
#include "nsXPCOMPrivate.h"
#include "test_quota.h"
#include "AsyncConnectionHelper.h"
#include "CheckQuotaHelper.h"
@ -61,6 +62,7 @@
#include "IDBEvents.h"
#include "IDBFactory.h"
#include "LazyIdleThread.h"
#include "OpenDatabaseHelper.h"
#include "TransactionThreadPool.h"
// The amount of time, in milliseconds, that our IO thread will stay alive
@ -87,12 +89,33 @@ using mozilla::Preferences;
namespace {
PRInt32 gShutdown = 0;
PRInt32 gClosed = 0;
// Does not hold a reference.
IndexedDatabaseManager* gInstance = nsnull;
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
{
public:
@ -138,11 +161,29 @@ EnumerateToTArray(const nsACString& aKey,
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
IndexedDatabaseManager::IndexedDatabaseManager()
: mCurrentWindowIndex(BAD_TLS_INDEX),
mQuotaHelperMutex("IndexedDatabaseManager.mQuotaHelperMutex")
mQuotaHelperMutex("IndexedDatabaseManager.mQuotaHelperMutex"),
mFileMutex("IndexedDatabaseManager.mFileMutex")
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
NS_ASSERTION(!gInstance, "More than one instance!");
@ -179,7 +220,8 @@ IndexedDatabaseManager::GetOrCreate()
instance = new IndexedDatabaseManager();
if (!instance->mLiveDatabases.Init() ||
!instance->mQuotaHelperHash.Init()) {
!instance->mQuotaHelperHash.Init() ||
!instance->mFileManagers.Init()) {
NS_WARNING("Out of memory!");
return nsnull;
}
@ -447,6 +489,13 @@ IndexedDatabaseManager::IsShuttingDown()
return !!gShutdown;
}
// static
bool
IndexedDatabaseManager::IsClosed()
{
return !!gClosed;
}
void
IndexedDatabaseManager::AbortCloseDatabasesForWindow(nsPIDOMWindow* aWindow)
{
@ -573,7 +622,8 @@ IndexedDatabaseManager::GetIndexedDBQuotaMB()
}
nsresult
IndexedDatabaseManager::EnsureQuotaManagementForDirectory(nsIFile* aDirectory)
IndexedDatabaseManager::EnsureOriginIsInitialized(const nsACString& aOrigin,
nsIFile** aDirectory)
{
#ifdef DEBUG
{
@ -583,19 +633,35 @@ IndexedDatabaseManager::EnsureQuotaManagementForDirectory(nsIFile* aDirectory)
"Running on the wrong thread!");
}
#endif
NS_ASSERTION(aDirectory, "Null pointer!");
nsCString path;
nsresult rv = aDirectory->GetNativePath(path);
nsCOMPtr<nsIFile> directory;
nsresult rv = IDBFactory::GetDirectoryForOrigin(aOrigin,
getter_AddRefs(directory));
NS_ENSURE_SUCCESS(rv, rv);
if (mTrackedQuotaPaths.Contains(path)) {
return true;
bool exists;
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.
nsCOMPtr<nsIFile> patternFile;
rv = aDirectory->Clone(getter_AddRefs(patternFile));
rv = directory->Clone(getter_AddRefs(patternFile));
NS_ENSURE_SUCCESS(rv, rv);
rv = patternFile->Append(NS_LITERAL_STRING("*"));
@ -615,42 +681,141 @@ IndexedDatabaseManager::EnsureQuotaManagementForDirectory(nsIFile* aDirectory)
mQuotaCallbackSingleton, nsnull);
NS_ENSURE_SUCCESS(rv, rv);
// If the directory exists then we need to see if there are any files in it
// already. We need to tell SQLite about all of them.
bool exists;
rv = aDirectory->Exists(&exists);
// We need to see if there are any files in the directory already. If they
// are database files then we need to create file managers for them and also
// tell SQLite about all of them.
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);
if (exists) {
// Make sure this really is a directory.
bool hasMore;
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;
rv = aDirectory->IsDirectory(&isDirectory);
NS_ENSURE_SUCCESS(rv, rv);
NS_ENSURE_TRUE(isDirectory, NS_ERROR_UNEXPECTED);
nsCOMPtr<nsISimpleEnumerator> entries;
rv = aDirectory->GetDirectoryEntries(getter_AddRefs(entries));
rv = file->IsDirectory(&isDirectory);
NS_ENSURE_SUCCESS(rv, rv);
bool hasMore;
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);
rv = ss->UpdateQutoaInformationForFile(file);
NS_ENSURE_SUCCESS(rv, rv);
if (isDirectory) {
subdirectories.AppendElement(leafName);
continue;
}
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_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);
return rv;
for (PRUint32 i = 0; i < unknownFiles.Length(); i++) {
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
@ -749,6 +914,116 @@ IndexedDatabaseManager::GetASCIIOriginFromWindow(nsPIDOMWindow* aWindow,
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
nsresult
IndexedDatabaseManager::DispatchHelper(AsyncConnectionHelper* aHelper)
@ -949,6 +1224,12 @@ IndexedDatabaseManager::Observe(nsISupports* aSubject,
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;
}
@ -1007,10 +1288,12 @@ IndexedDatabaseManager::OriginClearRunnable::Run()
NS_ASSERTION(!mThread, "Should have been cleared already!");
// Tell the IndexedDatabaseManager that we're done.
IndexedDatabaseManager* mgr = IndexedDatabaseManager::Get();
NS_ASSERTION(mgr, "This should never fail!");
mgr->InvalidateFileManagersForOrigin(mOrigin);
// Tell the IndexedDatabaseManager that we're done.
mgr->AllowNextSynchronizedOp(mOrigin, nsnull);
return NS_OK;
@ -1046,6 +1329,7 @@ IndexedDatabaseManager::AsyncUsageRunnable::AsyncUsageRunnable(
mOrigin(aOrigin),
mCallback(aCallback),
mUsage(0),
mFileUsage(0),
mCanceled(0)
{
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
IndexedDatabaseManager::AsyncUsageRunnable::RunInternal()
{
if (NS_IsMainThread()) {
// Call the callback unless we were canceled.
if (!mCanceled) {
mCallback->OnUsageResult(mURI, mUsage);
PRUint64 usage = mUsage;
IncrementUsage(&usage, mFileUsage);
mCallback->OnUsageResult(mURI, usage, mFileUsage);
}
// Clean up.
@ -1101,39 +1399,63 @@ IndexedDatabaseManager::AsyncUsageRunnable::RunInternal()
// If the directory exists then enumerate all the files inside, adding up the
// sizes to get the final usage statistic.
if (exists && !mCanceled) {
nsCOMPtr<nsISimpleEnumerator> entries;
rv = directory->GetDirectoryEntries(getter_AddRefs(entries));
rv = GetUsageForDirectory(directory, &mUsage);
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);
if (entries) {
bool hasMore;
while (NS_SUCCEEDED((rv = entries->HasMoreElements(&hasMore))) &&
hasMore && !mCanceled) {
nsCOMPtr<nsISupports> entry;
rv = entries->GetNext(getter_AddRefs(entry));
nsCOMPtr<nsIFile> file(do_QueryInterface(entry));
NS_ASSERTION(file, "Don't know what this is!");
bool isDirectory;
rv = file->IsDirectory(&isDirectory);
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);
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;
}
@ -1247,3 +1569,20 @@ IndexedDatabaseManager::SynchronizedOp::DispatchDelayedRunnables()
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;
}

View File

@ -40,6 +40,7 @@
#ifndef 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/IDBDatabase.h"
#include "mozilla/dom/indexedDB/IDBRequest.h"
@ -103,6 +104,8 @@ public:
// Returns true if we've begun the shutdown process.
static bool IsShuttingDown();
static bool IsClosed();
typedef void (*WaitingOnDatabasesCallback)(nsTArray<nsRefPtr<IDBDatabase> >&, void*);
// Acquire exclusive access to the database given (waits for all others to
@ -148,7 +151,8 @@ public:
static PRUint32
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
// using.
@ -173,6 +177,26 @@ public:
static nsresult
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:
IndexedDatabaseManager();
~IndexedDatabaseManager();
@ -250,10 +274,14 @@ private:
// to the main thread in case of an error.
inline nsresult RunInternal();
nsresult GetUsageForDirectory(nsIFile* aDirectory,
PRUint64* aUsage);
nsCOMPtr<nsIURI> mURI;
nsCString mOrigin;
nsCOMPtr<nsIIndexedDatabaseUsageCallback> mCallback;
PRUint64 mUsage;
PRUint64 mFileUsage;
PRInt32 mCanceled;
};
@ -302,6 +330,19 @@ private:
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);
// Maintains a list of live databases per origin.
@ -316,6 +357,12 @@ private:
// A map of Windows to the corresponding quota helper.
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
// usage statistics.
nsAutoTArray<nsRefPtr<AsyncUsageRunnable>, 1> mUsageRunnables;
@ -333,10 +380,10 @@ private:
// thread during GetOrCreate().
nsCOMPtr<mozIStorageQuotaCallback> mQuotaCallbackSingleton;
// A list of all paths that are under SQLite's quota tracking system. This
// list isn't protected by any mutex but it is only ever touched on the IO
// thread.
nsTArray<nsCString> mTrackedQuotaPaths;
// Lock protecting FileManager.mFileInfos and nsDOMFileBase.mFileInfos
// It's s also used to atomically update FileInfo.mRefCnt, FileInfo.mDBRefCnt
// and FileInfo.mSliceRefCnt
mozilla::Mutex mFileMutex;
};
class AutoEnterWindow

View File

@ -56,6 +56,8 @@ CPPSRCS = \
CheckPermissionsHelper.cpp \
CheckQuotaHelper.cpp \
DatabaseInfo.cpp \
FileInfo.cpp \
FileManager.cpp \
IDBCursor.cpp \
IDBDatabase.cpp \
IDBEvents.cpp \
@ -85,9 +87,12 @@ EXPORTS_mozilla/dom/indexedDB = \
IDBFactory.h \
Key.h \
LazyIdleThread.h \
FileManager.h \
FileInfo.h \
$(NULL)
LOCAL_INCLUDES = \
-I$(topsrcdir)/db/sqlite3/src \
-I$(topsrcdir)/xpcom/build \
-I$(topsrcdir)/dom/base \
-I$(topsrcdir)/dom/src/storage \

View File

@ -45,6 +45,7 @@
#include "nsEscape.h"
#include "nsThreadUtils.h"
#include "snappy/snappy.h"
#include "test_quota.h"
#include "IDBEvents.h"
#include "IDBFactory.h"
@ -59,7 +60,7 @@ namespace {
PR_STATIC_ASSERT(JS_STRUCTURED_CLONE_VERSION == 1);
// 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
// branches if we have to).
@ -95,24 +96,10 @@ PRUint32 GetMinorSchemaVersion(PRInt32 aSchemaVersion)
}
nsresult
GetDatabaseFile(const nsACString& aASCIIOrigin,
const nsAString& aName,
nsIFile** aDatabaseFile)
GetDatabaseFilename(const nsAString& aName,
nsAString& aDatabaseFilename)
{
NS_ASSERTION(!aASCIIOrigin.IsEmpty() && !aName.IsEmpty(), "Bad arguments!");
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));
aDatabaseFilename.AppendInt(HashString(aName));
nsCString escapedName;
if (!NS_Escape(NS_ConvertUTF16toUTF8(aName), escapedName, url_XPAlphas)) {
@ -133,13 +120,97 @@ GetDatabaseFile(const nsACString& aASCIIOrigin,
}
}
filename.Append(NS_ConvertASCIItoUTF16(substring));
filename.AppendLiteral(".sqlite");
aDatabaseFilename.Append(NS_ConvertASCIItoUTF16(substring));
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);
dbFile.forget(aDatabaseFile);
return NS_OK;
}
@ -178,6 +249,7 @@ CreateTables(mozIStorageConnection* aDBConn)
"object_store_id INTEGER NOT NULL, "
"key_value DEFAULT NULL, "
"data BLOB NOT NULL, "
"file_ids TEXT, "
"UNIQUE (object_store_id, key_value), "
"FOREIGN KEY (object_store_id) REFERENCES object_store(id) ON DELETE "
"CASCADE"
@ -191,6 +263,7 @@ CreateTables(mozIStorageConnection* aDBConn)
"id INTEGER PRIMARY KEY AUTOINCREMENT, "
"object_store_id INTEGER NOT NULL, "
"data BLOB NOT NULL, "
"file_ids TEXT, "
"UNIQUE (object_store_id, id), "
"FOREIGN KEY (object_store_id) REFERENCES object_store(id) ON DELETE "
"CASCADE"
@ -310,6 +383,9 @@ CreateTables(mozIStorageConnection* aDBConn)
));
NS_ENSURE_SUCCESS(rv, rv);
rv = CreateFileTables(aDBConn);
NS_ENSURE_SUCCESS(rv, rv);
rv = aDBConn->SetSchemaVersion(kSQLiteSchemaVersion);
NS_ENSURE_SUCCESS(rv, rv);
@ -961,127 +1037,24 @@ UpgradeSchemaFrom8To9_0(mozIStorageConnection* aConnection)
}
nsresult
CreateDatabaseConnection(const nsAString& aName,
nsIFile* aDBFile,
mozIStorageConnection** aConnection)
UpgradeSchemaFrom9_0To10_0(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) {
// 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;"
nsresult rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
"ALTER TABLE object_data ADD COLUMN file_ids TEXT;"
));
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;
}
@ -1130,8 +1103,8 @@ protected:
private:
// In-params
nsRefPtr<OpenDatabaseHelper> mOpenHelper;
nsRefPtr<IDBOpenDBRequest> mOpenRequest;
nsRefPtr<OpenDatabaseHelper> mOpenHelper;
PRUint64 mRequestedVersion;
PRUint64 mCurrentVersion;
};
@ -1345,40 +1318,39 @@ OpenDatabaseHelper::DoDatabaseWork()
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;
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);
rv = dbFile->GetPath(mDatabaseFilePath);
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
nsCOMPtr<nsIFile> dbDirectory;
rv = dbFile->GetParent(getter_AddRefs(dbDirectory));
nsCOMPtr<nsIFile> fileManagerDirectory;
rv = dbDirectory->Clone(getter_AddRefs(fileManagerDirectory));
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
bool exists;
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);
rv = fileManagerDirectory->Append(filename);
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
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);
rv = IDBFactory::LoadDatabaseInformation(connection, mDatabaseId,
@ -1422,6 +1394,175 @@ OpenDatabaseHelper::DoDatabaseWork()
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;
}
@ -1540,6 +1681,11 @@ OpenDatabaseHelper::Run()
// Destroy the database now (we should have the only ref).
mDatabase = nsnull;
IndexedDatabaseManager* mgr = IndexedDatabaseManager::Get();
NS_ASSERTION(mgr, "This should never fail!");
mgr->InvalidateFileManager(mASCIIOrigin, mName);
mState = eFiringEvents;
break;
}
@ -1669,7 +1815,8 @@ OpenDatabaseHelper::EnsureSuccessResult()
IDBDatabase::Create(mOpenDBRequest->ScriptContext(),
mOpenDBRequest->Owner(),
dbInfo.forget(),
mASCIIOrigin);
mASCIIOrigin,
mFileManager);
if (!database) {
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?");
nsCOMPtr<nsIFile> dbFile;
nsresult rv = GetDatabaseFile(mASCIIOrigin, mName, getter_AddRefs(dbFile));
nsCOMPtr<nsIFile> directory;
nsresult rv = IDBFactory::GetDirectoryForOrigin(mASCIIOrigin,
getter_AddRefs(directory));
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;
rv = dbFile->Exists(&exists);
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
int rc;
if (exists) {
rv = dbFile->Remove(false);
nsString dbFilePath;
rv = dbFile->GetPath(dbFilePath);
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;

View File

@ -102,6 +102,12 @@ public:
return mDatabase;
}
static
nsresult CreateDatabaseConnection(const nsAString& aName,
nsIFile* aDBFile,
nsIFile* aFileManagerDirectory,
mozIStorageConnection** aConnection);
protected:
// Methods only called on the main thread
nsresult EnsureSuccessResult();
@ -145,6 +151,8 @@ private:
};
OpenDatabaseState mState;
nsresult mResultCode;
nsRefPtr<FileManager> mFileManager;
};
END_INDEXEDDB_NAMESPACE

View File

@ -87,7 +87,7 @@ public:
// committed, for 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);
protected:

View File

@ -41,14 +41,15 @@
interface nsIURI;
[scriptable, function, uuid(17675af5-0569-4f5b-987f-ff4bb60f73ee)]
[scriptable, function, uuid(ef1795ec-7050-4658-b80f-0e48cbe1d64b)]
interface nsIIndexedDatabaseUsageCallback : nsISupports
{
/**
*
*/
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)]

View File

@ -50,6 +50,7 @@ TEST_FILES = \
error_events_abort_transactions_iframe.html \
event_propagation_iframe.html \
exceptions_in_success_events_iframe.html \
file.js \
helpers.js \
leaving_page_iframe.html \
test_add_put.html \
@ -73,6 +74,15 @@ TEST_FILES = \
test_event_propagation.html \
test_event_source.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_global_data.html \
test_index_empty_keyPath.html \

225
dom/indexedDB/test/file.js Normal file
View File

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

View File

@ -8,6 +8,7 @@ var testGenerator = testSteps();
function runTest()
{
allowIndexedDB();
allowUnlimitedQuota();
SimpleTest.waitForExplicitFinish();
testGenerator.next();
@ -15,7 +16,8 @@ function runTest()
function finishTest()
{
disallowIndexedDB();
resetUnlimitedQuota();
resetIndexedDB();
SimpleTest.executeSoon(function() {
testGenerator.close();
@ -77,7 +79,7 @@ ExpectError.prototype = {
}
};
function addPermission(permission, url)
function addPermission(type, allow, url)
{
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
@ -86,15 +88,20 @@ function addPermission(permission, url)
uri = Components.classes["@mozilla.org/network/io-service;1"]
.getService(Components.interfaces.nsIIOService)
.newURI(url, null, null);
}
else {
} else {
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"]
.getService(Components.interfaces.nsIPermissionManager)
.add(uri, permission,
Components.interfaces.nsIPermissionManager.ALLOW_ACTION);
.add(uri, type, permission);
}
function removePermission(permission, url)
@ -128,20 +135,25 @@ function setQuota(quota)
function allowIndexedDB(url)
{
addPermission("indexedDB", url);
addPermission("indexedDB", true, url);
}
function disallowIndexedDB(url)
function resetIndexedDB(url)
{
removePermission("indexedDB", 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);
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -65,10 +65,11 @@ interface nsIDOMEvent;
interface nsITransferable;
interface nsIQueryContentEventResult;
interface nsIDOMWindow;
interface nsIDOMBlob;
interface nsIDOMFile;
interface nsIFile;
[scriptable, uuid(bf868921-0288-4799-a806-2fa642590197)]
[scriptable, uuid(36adf309-e5c4-4912-9152-7fb151dc754a)]
interface nsIDOMWindowUtils : nsISupports {
/**
@ -926,4 +927,18 @@ interface nsIDOMWindowUtils : nsISupports {
*/
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);
};

View File

@ -61,7 +61,7 @@ interface nsIFile;
*
* @threadsafe
*/
[scriptable, uuid(ad035628-4ffb-42ff-a256-0ed9e410b859)]
[scriptable, uuid(b2a4b534-f92e-4387-9bd9-d10408173925)]
interface mozIStorageConnection : nsISupports {
/**
* The default size for SQLite database pages used by mozStorage for new
@ -143,6 +143,12 @@ interface mozIStorageConnection : nsISupports {
*/
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.
*/
@ -384,4 +390,15 @@ interface mozIStorageConnection : nsISupports {
* If the system is short on storage space.
*/
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);
};

View File

@ -75,7 +75,7 @@ interface mozIStorageQuotaCallback : nsISupports
* This is a temporary interface that should eventually merge with
* mozIStorageService.
*/
[scriptable, uuid(11def472-446f-4635-a1d8-8856e85aac50)]
[scriptable, uuid(4d81faf5-fe01-428b-99b8-c94cba12fd72)]
interface mozIStorageServiceQuotaManagement : nsISupports
{
/**
@ -128,5 +128,5 @@ interface mozIStorageServiceQuotaManagement : nsISupports
* file does not exist then the file will be removed from tracking
* under the quota system.
*/
void updateQutoaInformationForFile(in nsIFile aFile);
void updateQuotaInformationForFile(in nsIFile aFile);
};

View File

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

View File

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

View File

@ -86,6 +86,7 @@ CPPSRCS = \
SQLCollations.cpp \
VacuumManager.cpp \
TelemetryVFS.cpp \
FileSystemModule.cpp \
$(NULL)
# For nsDependentJSString

View File

@ -69,6 +69,7 @@
#include "mozStorageStatementData.h"
#include "StorageBaseStatementInternal.h"
#include "SQLCollations.h"
#include "FileSystemModule.h"
#include "prlog.h"
#include "prprf.h"
@ -158,6 +159,19 @@ sqlite3_T_blob(sqlite3_context *aCtx,
#include "variantToSQLiteT_impl.h"
////////////////////////////////////////////////////////////////////////////////
//// Modules
struct Module
{
const char* name;
int (*registerFunc)(sqlite3*, const char*);
};
Module gModules[] = {
{ "filesystem", RegisterFileSystemModule }
};
////////////////////////////////////////////////////////////////////////////////
//// Local Functions
@ -1129,6 +1143,16 @@ Connection::GetLastInsertRowID(PRInt64 *_id)
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
Connection::GetLastError(PRInt32 *_error)
{
@ -1512,5 +1536,24 @@ Connection::SetGrowthIncrement(PRInt32 aChunkSize, const nsACString &aDatabaseNa
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 mozilla

View File

@ -57,8 +57,14 @@
#include "mozilla/Preferences.h"
#include "sqlite3.h"
#include "test_quota.h"
#include "test_quota.c"
#ifdef SQLITE_OS_WIN
// "windows.h" was included and it can #define lots of things we care about...
#undef CompareString
#endif
#include "nsIPromptService.h"
#include "nsIMemoryReporter.h"
@ -745,7 +751,7 @@ Service::SetQuotaForFilenamePattern(const nsACString &aPattern,
}
NS_IMETHODIMP
Service::UpdateQutoaInformationForFile(nsIFile *aFile)
Service::UpdateQuotaInformationForFile(nsIFile *aFile)
{
NS_ENSURE_ARG_POINTER(aFile);

View File

@ -527,6 +527,27 @@ SpecialPowersAPI.prototype = {
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() {
try {
var rt = Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULRuntime);