зеркало из https://github.com/mozilla/pjs.git
Merge mozilla-central and mozilla-inbound
This commit is contained in:
Коммит
3f360365bf
|
@ -226,7 +226,7 @@ function onIndexedDBClear()
|
|||
initIndexedDBRow();
|
||||
}
|
||||
|
||||
function onIndexedDBUsageCallback(uri, usage)
|
||||
function onIndexedDBUsageCallback(uri, usage, fileUsage)
|
||||
{
|
||||
if (!uri.equals(gPermURI)) {
|
||||
throw new Error("Callback received for bad URI: " + uri);
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
|
|
@ -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)]
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -0,0 +1,209 @@
|
|||
/*
|
||||
** 2011 December 1
|
||||
**
|
||||
** The author disclaims copyright to this source code. In place of
|
||||
** a legal notice, here is a blessing:
|
||||
**
|
||||
** May you do good and not evil.
|
||||
** May you find forgiveness for yourself and forgive others.
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
*************************************************************************
|
||||
**
|
||||
** This file contains the interface definition for the quota a VFS shim.
|
||||
**
|
||||
** This particular shim enforces a quota system on files. One or more
|
||||
** database files are in a "quota group" that is defined by a GLOB
|
||||
** pattern. A quota is set for the combined size of all files in the
|
||||
** the group. A quota of zero means "no limit". If the total size
|
||||
** of all files in the quota group is greater than the limit, then
|
||||
** write requests that attempt to enlarge a file fail with SQLITE_FULL.
|
||||
**
|
||||
** However, before returning SQLITE_FULL, the write requests invoke
|
||||
** a callback function that is configurable for each quota group.
|
||||
** This callback has the opportunity to enlarge the quota. If the
|
||||
** callback does enlarge the quota such that the total size of all
|
||||
** files within the group is less than the new quota, then the write
|
||||
** continues as if nothing had happened.
|
||||
*/
|
||||
#ifndef _QUOTA_H_
|
||||
#include "sqlite3.h"
|
||||
#include <stdio.h>
|
||||
|
||||
/* Make this callable from C++ */
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Initialize the quota VFS shim. Use the VFS named zOrigVfsName
|
||||
** as the VFS that does the actual work. Use the default if
|
||||
** zOrigVfsName==NULL.
|
||||
**
|
||||
** The quota VFS shim is named "quota". It will become the default
|
||||
** VFS if makeDefault is non-zero.
|
||||
**
|
||||
** THIS ROUTINE IS NOT THREADSAFE. Call this routine exactly once
|
||||
** during start-up.
|
||||
*/
|
||||
int sqlite3_quota_initialize(const char *zOrigVfsName, int makeDefault);
|
||||
|
||||
/*
|
||||
** Shutdown the quota system.
|
||||
**
|
||||
** All SQLite database connections must be closed before calling this
|
||||
** routine.
|
||||
**
|
||||
** THIS ROUTINE IS NOT THREADSAFE. Call this routine exactly once while
|
||||
** shutting down in order to free all remaining quota groups.
|
||||
*/
|
||||
int sqlite3_quota_shutdown(void);
|
||||
|
||||
/*
|
||||
** Create or destroy a quota group.
|
||||
**
|
||||
** The quota group is defined by the zPattern. When calling this routine
|
||||
** with a zPattern for a quota group that already exists, this routine
|
||||
** merely updates the iLimit, xCallback, and pArg values for that quota
|
||||
** group. If zPattern is new, then a new quota group is created.
|
||||
**
|
||||
** The zPattern is always compared against the full pathname of the file.
|
||||
** Even if APIs are called with relative pathnames, SQLite converts the
|
||||
** name to a full pathname before comparing it against zPattern. zPattern
|
||||
** is a glob pattern with the following matching rules:
|
||||
**
|
||||
** '*' Matches any sequence of zero or more characters.
|
||||
**
|
||||
** '?' Matches exactly one character.
|
||||
**
|
||||
** [...] Matches one character from the enclosed list of
|
||||
** characters. "]" can be part of the list if it is
|
||||
** the first character. Within the list "X-Y" matches
|
||||
** characters X or Y or any character in between the
|
||||
** two. Ex: "[0-9]" matches any digit.
|
||||
**
|
||||
** [^...] Matches one character not in the enclosed list.
|
||||
**
|
||||
** / Matches either / or \. This allows glob patterns
|
||||
** containing / to work on both unix and windows.
|
||||
**
|
||||
** Note that, unlike unix shell globbing, the directory separator "/"
|
||||
** can match a wildcard. So, for example, the pattern "/abc/xyz/" "*"
|
||||
** matches any files anywhere in the directory hierarchy beneath
|
||||
** /abc/xyz.
|
||||
**
|
||||
** The glob algorithm works on bytes. Multi-byte UTF8 characters are
|
||||
** matched as if each byte were a separate character.
|
||||
**
|
||||
** If the iLimit for a quota group is set to zero, then the quota group
|
||||
** is disabled and will be deleted when the last database connection using
|
||||
** the quota group is closed.
|
||||
**
|
||||
** Calling this routine on a zPattern that does not exist and with a
|
||||
** zero iLimit is a no-op.
|
||||
**
|
||||
** A quota group must exist with a non-zero iLimit prior to opening
|
||||
** database connections if those connections are to participate in the
|
||||
** quota group. Creating a quota group does not affect database connections
|
||||
** that are already open.
|
||||
**
|
||||
** The patterns that define the various quota groups should be distinct.
|
||||
** If the same filename matches more than one quota group pattern, then
|
||||
** the behavior of this package is undefined.
|
||||
*/
|
||||
int sqlite3_quota_set(
|
||||
const char *zPattern, /* The filename pattern */
|
||||
sqlite3_int64 iLimit, /* New quota to set for this quota group */
|
||||
void (*xCallback)( /* Callback invoked when going over quota */
|
||||
const char *zFilename, /* Name of file whose size increases */
|
||||
sqlite3_int64 *piLimit, /* IN/OUT: The current limit */
|
||||
sqlite3_int64 iSize, /* Total size of all files in the group */
|
||||
void *pArg /* Client data */
|
||||
),
|
||||
void *pArg, /* client data passed thru to callback */
|
||||
void (*xDestroy)(void*) /* Optional destructor for pArg */
|
||||
);
|
||||
|
||||
/*
|
||||
** Bring the named file under quota management, assuming its name matches
|
||||
** the glob pattern of some quota group. Or if it is already under
|
||||
** management, update its size. If zFilename does not match the glob
|
||||
** pattern of any quota group, this routine is a no-op.
|
||||
*/
|
||||
int sqlite3_quota_file(const char *zFilename);
|
||||
|
||||
/*
|
||||
** The following object serves the same role as FILE in the standard C
|
||||
** library. It represents an open connection to a file on disk for I/O.
|
||||
**
|
||||
** A single quota_FILE should not be used by two or more threads at the
|
||||
** same time. Multiple threads can be using different quota_FILE objects
|
||||
** simultaneously, but not the same quota_FILE object.
|
||||
*/
|
||||
typedef struct quota_FILE quota_FILE;
|
||||
|
||||
/*
|
||||
** Create a new quota_FILE object used to read and/or write to the
|
||||
** file zFilename. The zMode parameter is as with standard library zMode.
|
||||
*/
|
||||
quota_FILE *sqlite3_quota_fopen(const char *zFilename, const char *zMode);
|
||||
|
||||
/*
|
||||
** Perform I/O against a quota_FILE object. When doing writes, the
|
||||
** quota mechanism may result in a short write, in order to prevent
|
||||
** the sum of sizes of all files from going over quota.
|
||||
*/
|
||||
size_t sqlite3_quota_fread(void*, size_t, size_t, quota_FILE*);
|
||||
size_t sqlite3_quota_fwrite(void*, size_t, size_t, quota_FILE*);
|
||||
|
||||
/*
|
||||
** Flush all written content held in memory buffers out to disk.
|
||||
** This is the equivalent of fflush() in the standard library.
|
||||
**
|
||||
** If the hardSync parameter is true (non-zero) then this routine
|
||||
** also forces OS buffers to disk - the equivalent of fsync().
|
||||
**
|
||||
** This routine return zero on success and non-zero if something goes
|
||||
** wrong.
|
||||
*/
|
||||
int sqlite3_quota_fflush(quota_FILE*, int hardSync);
|
||||
|
||||
/*
|
||||
** Close a quota_FILE object and free all associated resources. The
|
||||
** file remains under quota management.
|
||||
*/
|
||||
int sqlite3_quota_fclose(quota_FILE*);
|
||||
|
||||
/*
|
||||
** Move the read/write pointer for a quota_FILE object. Or tell the
|
||||
** current location of the read/write pointer.
|
||||
*/
|
||||
int sqlite3_quota_fseek(quota_FILE*, long, int);
|
||||
void sqlite3_quota_rewind(quota_FILE*);
|
||||
long sqlite3_quota_ftell(quota_FILE*);
|
||||
|
||||
/*
|
||||
** Delete a file from the disk, if that file is under quota management.
|
||||
** Adjust quotas accordingly.
|
||||
**
|
||||
** If zFilename is the name of a directory that matches one of the
|
||||
** quota glob patterns, then all files under quota management that
|
||||
** are contained within that directory are deleted.
|
||||
**
|
||||
** A standard SQLite result code is returned (SQLITE_OK, SQLITE_NOMEM, etc.)
|
||||
** When deleting a directory of files, if the deletion of any one
|
||||
** file fails (for example due to an I/O error), then this routine
|
||||
** returns immediately, with the error code, and does not try to
|
||||
** delete any of the other files in the specified directory.
|
||||
**
|
||||
** All files are removed from quota management and deleted from disk.
|
||||
** However, no attempt is made to remove empty directories.
|
||||
**
|
||||
** This routine is a no-op for files that are not under quota management.
|
||||
*/
|
||||
int sqlite3_quota_remove(const char *zFilename);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* end of the 'extern "C"' block */
|
||||
#endif
|
||||
#endif /* _QUOTA_H_ */
|
|
@ -45,6 +45,7 @@ namespace dom {
|
|||
enum StructuredCloneTags {
|
||||
SCTAG_BASE = JS_SCTAG_USER_MIN,
|
||||
SCTAG_DOM_BLOB,
|
||||
SCTAG_DOM_FILE,
|
||||
SCTAG_DOM_FILELIST,
|
||||
SCTAG_DOM_MAX
|
||||
};
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -0,0 +1,138 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=2 et sw=2 tw=80: */
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is Indexed Database.
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* The Mozilla Foundation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2011
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
#include "IndexedDatabaseManager.h"
|
||||
#include "FileInfo.h"
|
||||
|
||||
USING_INDEXEDDB_NAMESPACE
|
||||
|
||||
// static
|
||||
FileInfo*
|
||||
FileInfo::Create(FileManager* aFileManager, PRInt64 aId)
|
||||
{
|
||||
NS_ASSERTION(aId > 0, "Wrong id!");
|
||||
|
||||
if (aId <= PR_INT16_MAX) {
|
||||
return new FileInfo16(aFileManager, aId);
|
||||
}
|
||||
|
||||
if (aId <= PR_INT32_MAX) {
|
||||
return new FileInfo32(aFileManager, aId);
|
||||
}
|
||||
|
||||
return new FileInfo64(aFileManager, aId);
|
||||
}
|
||||
|
||||
void
|
||||
FileInfo::GetReferences(PRInt32* aRefCnt, PRInt32* aDBRefCnt,
|
||||
PRInt32* aSliceRefCnt)
|
||||
{
|
||||
if (IndexedDatabaseManager::IsClosed()) {
|
||||
NS_ERROR("Shouldn't be called after shutdown!");
|
||||
|
||||
if (aRefCnt) {
|
||||
*aRefCnt = -1;
|
||||
}
|
||||
|
||||
if (aDBRefCnt) {
|
||||
*aDBRefCnt = -1;
|
||||
}
|
||||
|
||||
if (aSliceRefCnt) {
|
||||
*aSliceRefCnt = -1;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
MutexAutoLock lock(IndexedDatabaseManager::FileMutex());
|
||||
|
||||
if (aRefCnt) {
|
||||
*aRefCnt = mRefCnt;
|
||||
}
|
||||
|
||||
if (aDBRefCnt) {
|
||||
*aDBRefCnt = mDBRefCnt;
|
||||
}
|
||||
|
||||
if (aSliceRefCnt) {
|
||||
*aSliceRefCnt = mSliceRefCnt;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
FileInfo::UpdateReferences(nsAutoRefCnt& aRefCount, PRInt32 aDelta,
|
||||
bool aClear)
|
||||
{
|
||||
if (IndexedDatabaseManager::IsClosed()) {
|
||||
NS_ERROR("Shouldn't be called after shutdown!");
|
||||
return;
|
||||
}
|
||||
|
||||
{
|
||||
MutexAutoLock lock(IndexedDatabaseManager::FileMutex());
|
||||
|
||||
aRefCount = aClear ? 0 : aRefCount + aDelta;
|
||||
|
||||
if (mRefCnt + mDBRefCnt + mSliceRefCnt > 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
mFileManager->mFileInfos.Remove(Id());
|
||||
}
|
||||
|
||||
Cleanup();
|
||||
|
||||
delete this;
|
||||
}
|
||||
|
||||
void
|
||||
FileInfo::Cleanup()
|
||||
{
|
||||
if (IndexedDatabaseManager::IsShuttingDown() ||
|
||||
mFileManager->Invalidated()) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsRefPtr<IndexedDatabaseManager> mgr = IndexedDatabaseManager::Get();
|
||||
NS_ASSERTION(mgr, "Shouldn't be null!");
|
||||
|
||||
if (NS_FAILED(mgr->AsyncDeleteFile(mFileManager, Id()))) {
|
||||
NS_WARNING("Failed to delete file asynchronously!");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,155 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=2 et sw=2 tw=80: */
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is Indexed Database.
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* The Mozilla Foundation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2011
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
#ifndef mozilla_dom_indexeddb_fileinfo_h__
|
||||
#define mozilla_dom_indexeddb_fileinfo_h__
|
||||
|
||||
#include "IndexedDatabase.h"
|
||||
#include "nsAtomicRefcnt.h"
|
||||
#include "nsThreadUtils.h"
|
||||
#include "FileManager.h"
|
||||
#include "IndexedDatabaseManager.h"
|
||||
|
||||
BEGIN_INDEXEDDB_NAMESPACE
|
||||
|
||||
class FileInfo
|
||||
{
|
||||
friend class FileManager;
|
||||
|
||||
public:
|
||||
FileInfo(FileManager* aFileManager)
|
||||
: mFileManager(aFileManager)
|
||||
{ }
|
||||
|
||||
virtual ~FileInfo()
|
||||
{
|
||||
#ifdef DEBUG
|
||||
NS_ASSERTION(NS_IsMainThread(), "File info destroyed on wrong thread!");
|
||||
#endif
|
||||
}
|
||||
|
||||
static
|
||||
FileInfo* Create(FileManager* aFileManager, PRInt64 aId);
|
||||
|
||||
void AddRef()
|
||||
{
|
||||
if (IndexedDatabaseManager::IsClosed()) {
|
||||
NS_AtomicIncrementRefcnt(mRefCnt);
|
||||
}
|
||||
else {
|
||||
UpdateReferences(mRefCnt, 1);
|
||||
}
|
||||
}
|
||||
|
||||
void Release()
|
||||
{
|
||||
if (IndexedDatabaseManager::IsClosed()) {
|
||||
nsrefcnt count = NS_AtomicDecrementRefcnt(mRefCnt);
|
||||
if (count == 0) {
|
||||
mRefCnt = 1;
|
||||
delete this;
|
||||
}
|
||||
}
|
||||
else {
|
||||
UpdateReferences(mRefCnt, -1);
|
||||
}
|
||||
}
|
||||
|
||||
void UpdateDBRefs(PRInt32 aDelta)
|
||||
{
|
||||
UpdateReferences(mDBRefCnt, aDelta);
|
||||
}
|
||||
|
||||
void ClearDBRefs()
|
||||
{
|
||||
UpdateReferences(mDBRefCnt, 0, true);
|
||||
}
|
||||
|
||||
void UpdateSliceRefs(PRInt32 aDelta)
|
||||
{
|
||||
UpdateReferences(mSliceRefCnt, aDelta);
|
||||
}
|
||||
|
||||
void GetReferences(PRInt32* aRefCnt, PRInt32* aDBRefCnt,
|
||||
PRInt32* aSliceRefCnt);
|
||||
|
||||
FileManager* Manager() const
|
||||
{
|
||||
return mFileManager;
|
||||
}
|
||||
|
||||
virtual PRInt64 Id() const = 0;
|
||||
|
||||
private:
|
||||
void UpdateReferences(nsAutoRefCnt& aRefCount, PRInt32 aDelta,
|
||||
bool aClear = false);
|
||||
void Cleanup();
|
||||
|
||||
nsAutoRefCnt mRefCnt;
|
||||
nsAutoRefCnt mDBRefCnt;
|
||||
nsAutoRefCnt mSliceRefCnt;
|
||||
|
||||
nsRefPtr<FileManager> mFileManager;
|
||||
};
|
||||
|
||||
#define FILEINFO_SUBCLASS(_bits) \
|
||||
class FileInfo##_bits : public FileInfo \
|
||||
{ \
|
||||
public: \
|
||||
FileInfo##_bits(FileManager* aFileManager, PRInt64 aId) \
|
||||
: FileInfo(aFileManager), mId(aId) \
|
||||
{ } \
|
||||
\
|
||||
virtual PRInt64 Id() const \
|
||||
{ \
|
||||
return mId; \
|
||||
} \
|
||||
\
|
||||
private: \
|
||||
PRInt##_bits mId; \
|
||||
};
|
||||
|
||||
FILEINFO_SUBCLASS(16);
|
||||
FILEINFO_SUBCLASS(32);
|
||||
FILEINFO_SUBCLASS(64);
|
||||
|
||||
#undef FILEINFO_SUBCLASS
|
||||
|
||||
END_INDEXEDDB_NAMESPACE
|
||||
|
||||
#endif // mozilla_dom_indexeddb_fileinfo_h__
|
|
@ -0,0 +1,309 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=2 et sw=2 tw=80: */
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is Indexed Database.
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* The Mozilla Foundation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2011
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
#include "FileManager.h"
|
||||
|
||||
#include "mozIStorageConnection.h"
|
||||
#include "mozIStorageServiceQuotaManagement.h"
|
||||
#include "mozIStorageStatement.h"
|
||||
#include "nsISimpleEnumerator.h"
|
||||
#include "mozStorageCID.h"
|
||||
#include "nsContentUtils.h"
|
||||
|
||||
#include "FileInfo.h"
|
||||
#include "IndexedDatabaseManager.h"
|
||||
|
||||
USING_INDEXEDDB_NAMESPACE
|
||||
|
||||
namespace {
|
||||
|
||||
PLDHashOperator
|
||||
EnumerateToTArray(const PRUint64& aKey,
|
||||
FileInfo* aValue,
|
||||
void* aUserArg)
|
||||
{
|
||||
NS_ASSERTION(aValue, "Null pointer!");
|
||||
NS_ASSERTION(aUserArg, "Null pointer!");
|
||||
|
||||
nsTArray<FileInfo*>* array =
|
||||
static_cast<nsTArray<FileInfo*>*>(aUserArg);
|
||||
|
||||
array->AppendElement(aValue);
|
||||
|
||||
return PL_DHASH_NEXT;
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
nsresult
|
||||
FileManager::Init()
|
||||
{
|
||||
NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
|
||||
|
||||
NS_ENSURE_TRUE(mFileInfos.Init(), NS_ERROR_OUT_OF_MEMORY);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
FileManager::InitDirectory(nsIFile* aDirectory,
|
||||
mozIStorageConnection* aConnection)
|
||||
{
|
||||
NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
|
||||
|
||||
bool exists;
|
||||
nsresult rv = aDirectory->Exists(&exists);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (exists) {
|
||||
bool isDirectory;
|
||||
rv = aDirectory->IsDirectory(&isDirectory);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
NS_ENSURE_TRUE(isDirectory, NS_ERROR_FAILURE);
|
||||
}
|
||||
else {
|
||||
rv = aDirectory->Create(nsIFile::DIRECTORY_TYPE, 0755);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
|
||||
"CREATE VIRTUAL TABLE fs USING filesystem;"
|
||||
));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsCOMPtr<mozIStorageStatement> stmt;
|
||||
rv = aConnection->CreateStatement(NS_LITERAL_CSTRING(
|
||||
"SELECT name, (name IN (SELECT id FROM file)) FROM fs "
|
||||
"WHERE path = :path"
|
||||
), getter_AddRefs(stmt));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsString path;
|
||||
rv = aDirectory->GetPath(path);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = stmt->BindStringByName(NS_LITERAL_CSTRING("path"), path);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsCOMPtr<mozIStorageServiceQuotaManagement> ss =
|
||||
do_GetService(MOZ_STORAGE_SERVICE_CONTRACTID);
|
||||
NS_ENSURE_TRUE(ss, NS_ERROR_FAILURE);
|
||||
|
||||
bool hasResult;
|
||||
while (NS_SUCCEEDED(stmt->ExecuteStep(&hasResult)) && hasResult) {
|
||||
nsString name;
|
||||
rv = stmt->GetString(0, name);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
PRInt32 flag = stmt->AsInt32(1);
|
||||
|
||||
nsCOMPtr<nsIFile> file;
|
||||
rv = aDirectory->Clone(getter_AddRefs(file));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = file->Append(name);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (flag) {
|
||||
rv = ss->UpdateQuotaInformationForFile(file);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
} else {
|
||||
rv = file->Remove(false);
|
||||
if (NS_FAILED(rv)) {
|
||||
NS_WARNING("Failed to remove orphaned file!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
|
||||
"DROP TABLE fs;"
|
||||
));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = aDirectory->GetPath(mDirectoryPath);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = aDirectory->GetLeafName(mDirectoryName);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
FileManager::Load(mozIStorageConnection* aConnection)
|
||||
{
|
||||
NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
|
||||
|
||||
nsCOMPtr<mozIStorageStatement> stmt;
|
||||
nsresult rv = aConnection->CreateStatement(NS_LITERAL_CSTRING(
|
||||
"SELECT id, refcount "
|
||||
"FROM file"
|
||||
), getter_AddRefs(stmt));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
bool hasResult;
|
||||
while (NS_SUCCEEDED(stmt->ExecuteStep(&hasResult)) && hasResult) {
|
||||
PRInt64 id;
|
||||
rv = stmt->GetInt64(0, &id);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
PRInt32 refcount;
|
||||
rv = stmt->GetInt32(1, &refcount);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
NS_ASSERTION(refcount, "This shouldn't happen!");
|
||||
|
||||
nsRefPtr<FileInfo> fileInfo = FileInfo::Create(this, id);
|
||||
fileInfo->mDBRefCnt = refcount;
|
||||
|
||||
if (!mFileInfos.Put(id, fileInfo)) {
|
||||
NS_WARNING("Out of memory?");
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
mLastFileId = NS_MAX(id, mLastFileId);
|
||||
}
|
||||
|
||||
mLoaded = true;
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
FileManager::Invalidate()
|
||||
{
|
||||
if (IndexedDatabaseManager::IsClosed()) {
|
||||
NS_ERROR("Shouldn't be called after shutdown!");
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
||||
nsTArray<FileInfo*> fileInfos;
|
||||
{
|
||||
MutexAutoLock lock(IndexedDatabaseManager::FileMutex());
|
||||
|
||||
NS_ASSERTION(!mInvalidated, "Invalidate more than once?!");
|
||||
mInvalidated = true;
|
||||
|
||||
fileInfos.SetCapacity(mFileInfos.Count());
|
||||
mFileInfos.EnumerateRead(EnumerateToTArray, &fileInfos);
|
||||
}
|
||||
|
||||
for (PRUint32 i = 0; i < fileInfos.Length(); i++) {
|
||||
FileInfo* fileInfo = fileInfos.ElementAt(i);
|
||||
fileInfo->ClearDBRefs();
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
already_AddRefed<nsIFile>
|
||||
FileManager::GetDirectory()
|
||||
{
|
||||
nsCOMPtr<nsILocalFile> directory =
|
||||
do_CreateInstance(NS_LOCAL_FILE_CONTRACTID);
|
||||
NS_ENSURE_TRUE(directory, nsnull);
|
||||
|
||||
nsresult rv = directory->InitWithPath(mDirectoryPath);
|
||||
NS_ENSURE_SUCCESS(rv, nsnull);
|
||||
|
||||
return directory.forget();
|
||||
}
|
||||
|
||||
already_AddRefed<FileInfo>
|
||||
FileManager::GetFileInfo(PRInt64 aId)
|
||||
{
|
||||
if (IndexedDatabaseManager::IsClosed()) {
|
||||
NS_ERROR("Shouldn't be called after shutdown!");
|
||||
return nsnull;
|
||||
}
|
||||
|
||||
FileInfo* fileInfo = nsnull;
|
||||
{
|
||||
MutexAutoLock lock(IndexedDatabaseManager::FileMutex());
|
||||
fileInfo = mFileInfos.Get(aId);
|
||||
}
|
||||
nsRefPtr<FileInfo> result = fileInfo;
|
||||
return result.forget();
|
||||
}
|
||||
|
||||
already_AddRefed<FileInfo>
|
||||
FileManager::GetNewFileInfo()
|
||||
{
|
||||
if (IndexedDatabaseManager::IsClosed()) {
|
||||
NS_ERROR("Shouldn't be called after shutdown!");
|
||||
return nsnull;
|
||||
}
|
||||
|
||||
nsAutoPtr<FileInfo> fileInfo;
|
||||
|
||||
{
|
||||
MutexAutoLock lock(IndexedDatabaseManager::FileMutex());
|
||||
|
||||
PRInt64 id = mLastFileId + 1;
|
||||
|
||||
fileInfo = FileInfo::Create(this, id);
|
||||
|
||||
if (!mFileInfos.Put(id, fileInfo)) {
|
||||
NS_WARNING("Out of memory?");
|
||||
return nsnull;
|
||||
}
|
||||
|
||||
mLastFileId = id;
|
||||
}
|
||||
|
||||
nsRefPtr<FileInfo> result = fileInfo.forget();
|
||||
return result.forget();
|
||||
}
|
||||
|
||||
already_AddRefed<nsIFile>
|
||||
FileManager::GetFileForId(nsIFile* aDirectory, PRInt64 aId)
|
||||
{
|
||||
NS_ASSERTION(aDirectory, "Null pointer!");
|
||||
|
||||
nsAutoString id;
|
||||
id.AppendInt(aId);
|
||||
|
||||
nsCOMPtr<nsIFile> file;
|
||||
nsresult rv = aDirectory->Clone(getter_AddRefs(file));
|
||||
NS_ENSURE_SUCCESS(rv, nsnull);
|
||||
|
||||
rv = file->Append(id);
|
||||
NS_ENSURE_SUCCESS(rv, nsnull);
|
||||
|
||||
return file.forget();
|
||||
}
|
|
@ -0,0 +1,136 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=2 et sw=2 tw=80: */
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is Indexed Database.
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* The Mozilla Foundation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2011
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
#ifndef mozilla_dom_indexeddb_filemanager_h__
|
||||
#define mozilla_dom_indexeddb_filemanager_h__
|
||||
|
||||
#include "IndexedDatabase.h"
|
||||
#include "nsIFile.h"
|
||||
#include "nsILocalFile.h"
|
||||
#include "nsIDOMFile.h"
|
||||
#include "nsDataHashtable.h"
|
||||
|
||||
class mozIStorageConnection;
|
||||
|
||||
BEGIN_INDEXEDDB_NAMESPACE
|
||||
|
||||
class FileInfo;
|
||||
|
||||
class FileManager
|
||||
{
|
||||
friend class FileInfo;
|
||||
|
||||
public:
|
||||
FileManager(const nsACString& aOrigin,
|
||||
const nsAString& aDatabaseName)
|
||||
: mOrigin(aOrigin), mDatabaseName(aDatabaseName), mLastFileId(0),
|
||||
mLoaded(false), mInvalidated(false)
|
||||
{ }
|
||||
|
||||
~FileManager()
|
||||
{ }
|
||||
|
||||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(FileManager)
|
||||
|
||||
const nsACString& Origin() const
|
||||
{
|
||||
return mOrigin;
|
||||
}
|
||||
|
||||
const nsAString& DatabaseName() const
|
||||
{
|
||||
return mDatabaseName;
|
||||
}
|
||||
|
||||
const nsAString& DirectoryName() const
|
||||
{
|
||||
return mDirectoryName;
|
||||
}
|
||||
|
||||
bool IsDirectoryInited() const
|
||||
{
|
||||
return !mDirectoryPath.IsEmpty();
|
||||
}
|
||||
|
||||
bool Loaded() const
|
||||
{
|
||||
return mLoaded;
|
||||
}
|
||||
|
||||
bool Invalidated() const
|
||||
{
|
||||
return mInvalidated;
|
||||
}
|
||||
|
||||
nsresult Init();
|
||||
|
||||
nsresult InitDirectory(nsIFile* aDirectory,
|
||||
mozIStorageConnection* aConnection);
|
||||
|
||||
nsresult Load(mozIStorageConnection* aConnection);
|
||||
|
||||
nsresult Invalidate();
|
||||
|
||||
already_AddRefed<nsIFile> GetDirectory();
|
||||
|
||||
already_AddRefed<FileInfo> GetFileInfo(PRInt64 aId);
|
||||
|
||||
already_AddRefed<FileInfo> GetNewFileInfo();
|
||||
|
||||
static already_AddRefed<nsIFile> GetFileForId(nsIFile* aDirectory,
|
||||
PRInt64 aId);
|
||||
|
||||
private:
|
||||
nsCString mOrigin;
|
||||
nsString mDatabaseName;
|
||||
|
||||
nsString mDirectoryPath;
|
||||
nsString mDirectoryName;
|
||||
|
||||
PRInt64 mLastFileId;
|
||||
|
||||
// Protected by IndexedDatabaseManager::FileMutex()
|
||||
nsDataHashtable<nsUint64HashKey, FileInfo*> mFileInfos;
|
||||
|
||||
bool mLoaded;
|
||||
bool mInvalidated;
|
||||
};
|
||||
|
||||
END_INDEXEDDB_NAMESPACE
|
||||
|
||||
#endif // mozilla_dom_indexeddb_filemanager_h__
|
|
@ -49,7 +49,6 @@
|
|||
#include "nsThreadUtils.h"
|
||||
|
||||
#include "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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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!");
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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__
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 \
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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)]
|
||||
|
|
|
@ -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 \
|
||||
|
|
|
@ -0,0 +1,225 @@
|
|||
/**
|
||||
* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/
|
||||
*/
|
||||
|
||||
var builder = new MozBlobBuilder();
|
||||
var manager = null;
|
||||
var bufferCache = [];
|
||||
var utils = SpecialPowers.getDOMWindowUtils(window);
|
||||
|
||||
function getBuffer(size)
|
||||
{
|
||||
let buffer = new ArrayBuffer(size);
|
||||
is(buffer.byteLength, size, "Correct byte length");
|
||||
return buffer;
|
||||
}
|
||||
|
||||
function getRandomBuffer(size)
|
||||
{
|
||||
let buffer = getBuffer(size);
|
||||
let view = new Uint8Array(buffer);
|
||||
for (let i = 0; i < size; i++) {
|
||||
view[i] = parseInt(Math.random() * 255)
|
||||
}
|
||||
return buffer;
|
||||
}
|
||||
|
||||
function compareBuffers(buffer1, buffer2)
|
||||
{
|
||||
if (buffer1.byteLength != buffer2.byteLength) {
|
||||
return false;
|
||||
}
|
||||
let view1 = new Uint8Array(buffer1);
|
||||
let view2 = new Uint8Array(buffer2);
|
||||
for (let i = 0; i < buffer1.byteLength; i++) {
|
||||
if (view1[i] != view2[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function getBlob(type, buffer)
|
||||
{
|
||||
builder.append(buffer);
|
||||
return builder.getBlob(type);
|
||||
}
|
||||
|
||||
function getFile(name, type, buffer)
|
||||
{
|
||||
builder.append(buffer);
|
||||
return builder.getFile(name, type);
|
||||
}
|
||||
|
||||
function getRandomBlob(size)
|
||||
{
|
||||
return getBlob("binary/random", getRandomBuffer(size));
|
||||
}
|
||||
|
||||
function getRandomFile(name, size)
|
||||
{
|
||||
return getFile(name, "binary/random", getRandomBuffer(size));
|
||||
}
|
||||
|
||||
function getNullBlob(size)
|
||||
{
|
||||
return getBlob("binary/null", getBuffer(size));
|
||||
}
|
||||
|
||||
function getNullFile(name, size)
|
||||
{
|
||||
return getFile(name, "binary/null", getBuffer(size));
|
||||
}
|
||||
|
||||
function verifyBuffers(buffer1, buffer2)
|
||||
{
|
||||
ok(compareBuffers(buffer1, buffer2), "Correct blob data");
|
||||
}
|
||||
|
||||
function verifyBlob(blob1, blob2, fileId, blobReadHandler)
|
||||
{
|
||||
is(blob1 instanceof Components.interfaces.nsIDOMBlob, true,
|
||||
"Instance of nsIDOMBlob");
|
||||
is(blob1 instanceof Components.interfaces.nsIDOMFile,
|
||||
blob2 instanceof Components.interfaces.nsIDOMFile,
|
||||
"Instance of nsIDOMFile");
|
||||
is(blob1.size, blob2.size, "Correct size");
|
||||
is(blob1.type, blob2.type, "Correct type");
|
||||
if (blob2 instanceof Components.interfaces.nsIDOMFile) {
|
||||
is(blob1.name, blob2.name, "Correct name");
|
||||
}
|
||||
is(utils.getFileId(blob1), fileId, "Correct file id");
|
||||
|
||||
let buffer1;
|
||||
let buffer2;
|
||||
|
||||
for (let i = 0; i < bufferCache.length; i++) {
|
||||
if (bufferCache[i].blob == blob2) {
|
||||
buffer2 = bufferCache[i].buffer;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!buffer2) {
|
||||
let reader = new FileReader();
|
||||
reader.readAsArrayBuffer(blob2);
|
||||
reader.onload = function(event) {
|
||||
buffer2 = event.target.result;
|
||||
bufferCache.push({ blob: blob2, buffer: buffer2 });
|
||||
if (buffer1) {
|
||||
verifyBuffers(buffer1, buffer2);
|
||||
if (blobReadHandler) {
|
||||
blobReadHandler();
|
||||
} else {
|
||||
testGenerator.next();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let reader = new FileReader();
|
||||
reader.readAsArrayBuffer(blob1);
|
||||
reader.onload = function(event) {
|
||||
buffer1 = event.target.result;
|
||||
if (buffer2) {
|
||||
verifyBuffers(buffer1, buffer2);
|
||||
if (blobReadHandler) {
|
||||
blobReadHandler();
|
||||
} else {
|
||||
testGenerator.next();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function verifyBlobArray(blobs1, blobs2, expectedFileIds)
|
||||
{
|
||||
is(blobs1 instanceof Array, true, "Got an array object");
|
||||
is(blobs1.length, blobs2.length, "Correct length");
|
||||
|
||||
if (!blobs1.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
let verifiedCount = 0;
|
||||
|
||||
function blobReadHandler() {
|
||||
if (++verifiedCount == blobs1.length) {
|
||||
testGenerator.next();
|
||||
} else {
|
||||
verifyBlob(blobs1[verifiedCount], blobs2[verifiedCount],
|
||||
expectedFileIds[verifiedCount], blobReadHandler);
|
||||
}
|
||||
}
|
||||
|
||||
verifyBlob(blobs1[verifiedCount], blobs2[verifiedCount],
|
||||
expectedFileIds[verifiedCount], blobReadHandler);
|
||||
}
|
||||
|
||||
function grabFileUsageAndContinueHandler(usage, fileUsage)
|
||||
{
|
||||
testGenerator.send(fileUsage);
|
||||
}
|
||||
|
||||
function getUsage(usageHandler)
|
||||
{
|
||||
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
|
||||
|
||||
if (!manager) {
|
||||
manager = Components.classes["@mozilla.org/dom/indexeddb/manager;1"]
|
||||
.getService(Components.interfaces.nsIIndexedDatabaseManager);
|
||||
}
|
||||
|
||||
let uri = SpecialPowers.getDocumentURIObject(window.document);
|
||||
let callback = {
|
||||
onUsageResult: function(uri, usage, fileUsage) {
|
||||
usageHandler(usage, fileUsage);
|
||||
}
|
||||
};
|
||||
|
||||
manager.getUsageForURI(uri, callback);
|
||||
}
|
||||
|
||||
function getUsageSync()
|
||||
{
|
||||
let usage;
|
||||
|
||||
getUsage(function(aUsage, aFileUsage) {
|
||||
usage = aUsage;
|
||||
});
|
||||
|
||||
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
|
||||
let thread = Components.classes["@mozilla.org/thread-manager;1"]
|
||||
.getService(Components.interfaces.nsIThreadManager)
|
||||
.currentThread;
|
||||
while (!usage) {
|
||||
thread.processNextEvent(true);
|
||||
}
|
||||
|
||||
return usage;
|
||||
}
|
||||
|
||||
function scheduleGC()
|
||||
{
|
||||
SpecialPowers.exactGC(window, continueToNextStep);
|
||||
}
|
||||
|
||||
function hasFileInfo(name, id)
|
||||
{
|
||||
return utils.getFileReferences(name, id);
|
||||
}
|
||||
|
||||
function getFileRefCount(name, id)
|
||||
{
|
||||
let count = {};
|
||||
utils.getFileReferences(name, id, count);
|
||||
return count.value;
|
||||
}
|
||||
|
||||
function getFileDBRefCount(name, id)
|
||||
{
|
||||
let count = {};
|
||||
utils.getFileReferences(name, id, {}, count);
|
||||
return count.value;
|
||||
}
|
|
@ -8,6 +8,7 @@ var testGenerator = testSteps();
|
|||
function runTest()
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,84 @@
|
|||
<!--
|
||||
Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/
|
||||
-->
|
||||
<html>
|
||||
<head>
|
||||
<title>Indexed Database Property Test</title>
|
||||
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
|
||||
<script type="text/javascript;version=1.7">
|
||||
function testSteps()
|
||||
{
|
||||
const name = window.location.pathname;
|
||||
const description = "My Test Database";
|
||||
|
||||
const objectStoreName = "Blobs";
|
||||
|
||||
const b1 = getRandomBlob(10000);
|
||||
|
||||
const b2 = [ getRandomBlob(5000), getRandomBlob(3000), getRandomBlob(12000),
|
||||
getRandomBlob(17000), getRandomBlob(16000), getRandomBlob(16000),
|
||||
getRandomBlob(8000)
|
||||
];
|
||||
|
||||
const b3 = [ getRandomBlob(5000), getRandomBlob(3000), getRandomBlob(9000)];
|
||||
|
||||
const objectStoreData = [
|
||||
{ key: 1, blobs: [ b1, b1, b1, b1, b1, b1, b1, b1, b1, b1 ],
|
||||
expectedFileIds: [1, 1, 1, 1, 1, 1, 1, 1, 1, 1] },
|
||||
{ key: 2, blobs: [ b2[0], b2[1], b2[2], b2[3], b2[4], b2[5], b2[6] ],
|
||||
expectedFileIds: [2, 3, 4, 5, 6, 7, 8] },
|
||||
{ key: 3, blobs: [ b3[0], b3[0], b3[1], b3[2], b3[2], b3[0], b3[0] ],
|
||||
expectedFileIds: [9, 9, 10, 11, 11, 9, 9] }
|
||||
];
|
||||
|
||||
let request = mozIndexedDB.open(name, 1, description);
|
||||
request.onerror = errorHandler;
|
||||
request.onupgradeneeded = grabEventAndContinueHandler;
|
||||
request.onsuccess = grabEventAndContinueHandler;
|
||||
let event = yield;
|
||||
|
||||
is(event.type, "upgradeneeded", "Got correct event type");
|
||||
|
||||
let db = event.target.result;
|
||||
db.onerror = errorHandler;
|
||||
|
||||
let objectStore = db.createObjectStore(objectStoreName, { });
|
||||
|
||||
for each (let data in objectStoreData) {
|
||||
objectStore.add(data.blobs, data.key);
|
||||
}
|
||||
|
||||
event = yield;
|
||||
|
||||
is(event.type, "success", "Got correct event type");
|
||||
|
||||
for each (let data in objectStoreData) {
|
||||
objectStore = db.transaction([objectStoreName])
|
||||
.objectStore(objectStoreName);
|
||||
|
||||
request = objectStore.get(data.key);
|
||||
request.onsuccess = grabEventAndContinueHandler;
|
||||
event = yield;
|
||||
|
||||
verifyBlobArray(event.target.result, data.blobs, data.expectedFileIds);
|
||||
yield;
|
||||
}
|
||||
|
||||
is(bufferCache.length, 11, "Correct length");
|
||||
|
||||
finishTest();
|
||||
yield;
|
||||
}
|
||||
</script>
|
||||
<script type="text/javascript;version=1.7" src="file.js"></script>
|
||||
<script type="text/javascript;version=1.7" src="helpers.js"></script>
|
||||
|
||||
</head>
|
||||
|
||||
<body onload="runTest();"></body>
|
||||
|
||||
</html>
|
|
@ -0,0 +1,106 @@
|
|||
<!--
|
||||
Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/
|
||||
-->
|
||||
<html>
|
||||
<head>
|
||||
<title>Indexed Database Property Test</title>
|
||||
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
|
||||
<script type="text/javascript;version=1.7">
|
||||
function testSteps()
|
||||
{
|
||||
const READ_WRITE = IDBTransaction.READ_WRITE;
|
||||
|
||||
const databaseInfo = [
|
||||
{ name: window.location.pathname + "1", description: "Test Database 1" },
|
||||
{ name: window.location.pathname + "2", description: "Test Database 2" }
|
||||
];
|
||||
|
||||
const objectStoreName = "Blobs";
|
||||
|
||||
const fileData = { key: 1, file: getRandomFile("random.bin", 100000) };
|
||||
|
||||
let databases = [];
|
||||
for each (let info in databaseInfo) {
|
||||
let request = mozIndexedDB.open(info.name, 1, info.description);
|
||||
request.onerror = errorHandler;
|
||||
request.onupgradeneeded = grabEventAndContinueHandler;
|
||||
request.onsuccess = grabEventAndContinueHandler;
|
||||
let event = yield;
|
||||
|
||||
is(event.type, "upgradeneeded", "Got correct event type");
|
||||
|
||||
let db = event.target.result;
|
||||
db.onerror = errorHandler;
|
||||
|
||||
let objectStore = db.createObjectStore(objectStoreName, { });
|
||||
objectStore.add(fileData.file, fileData.key);
|
||||
|
||||
event = yield;
|
||||
|
||||
is(event.type, "success", "Got correct event type");
|
||||
|
||||
databases.push(db);
|
||||
}
|
||||
|
||||
let refResult;
|
||||
for each (let db in databases) {
|
||||
let request = db.transaction([objectStoreName])
|
||||
.objectStore(objectStoreName).get(fileData.key);
|
||||
request.onsuccess = grabEventAndContinueHandler;
|
||||
event = yield;
|
||||
|
||||
let result = event.target.result;
|
||||
verifyBlob(result, fileData.file, 1);
|
||||
yield;
|
||||
|
||||
if (!refResult) {
|
||||
refResult = result;
|
||||
continue;
|
||||
}
|
||||
|
||||
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
|
||||
isnot(result.mozFullPath, refResult.mozFullPath, "Different os files");
|
||||
}
|
||||
|
||||
for (let i = 1; i < databases.length; i++) {
|
||||
let db = databases[i];
|
||||
|
||||
let objectStore = db.transaction([objectStoreName], READ_WRITE)
|
||||
.objectStore(objectStoreName);
|
||||
|
||||
request = objectStore.add(refResult, 2);
|
||||
request.onsuccess = grabEventAndContinueHandler;
|
||||
event = yield;
|
||||
|
||||
is(event.target.result, 2, "Got correct key");
|
||||
|
||||
request = objectStore.get(2);
|
||||
request.onsuccess = grabEventAndContinueHandler;
|
||||
event = yield;
|
||||
|
||||
let result = event.target.result;
|
||||
verifyBlob(result, refResult, 2);
|
||||
yield;
|
||||
|
||||
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
|
||||
isnot(result.mozFullPath, refResult.mozFullPath, "Different os files");
|
||||
}
|
||||
|
||||
is(bufferCache.length, 2, "Correct length");
|
||||
|
||||
finishTest();
|
||||
yield;
|
||||
}
|
||||
</script>
|
||||
<script type="text/javascript;version=1.7" src="file.js"></script>
|
||||
<script type="text/javascript;version=1.7" src="helpers.js"></script>
|
||||
|
||||
</head>
|
||||
|
||||
<body onload="runTest();"></body>
|
||||
|
||||
</html>
|
|
@ -0,0 +1,134 @@
|
|||
<!--
|
||||
Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/
|
||||
-->
|
||||
<html>
|
||||
<head>
|
||||
<title>Indexed Database Property Test</title>
|
||||
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
|
||||
<script type="text/javascript;version=1.7">
|
||||
function testSteps()
|
||||
{
|
||||
const READ_WRITE = IDBTransaction.READ_WRITE;
|
||||
|
||||
const name = window.location.pathname;
|
||||
const description = "My Test Database";
|
||||
|
||||
const objectStoreName = "Blobs";
|
||||
|
||||
const fileData1 = { key: 1, file: getRandomFile("random1.bin", 110000) };
|
||||
const fileData2 = { key: 2, file: getRandomFile("random2.bin", 120000) };
|
||||
const fileData3 = { key: 3, file: getRandomFile("random3.bin", 130000) };
|
||||
|
||||
{
|
||||
let request = mozIndexedDB.open(name, 1, description);
|
||||
request.onerror = errorHandler;
|
||||
request.onupgradeneeded = grabEventAndContinueHandler;
|
||||
request.onsuccess = grabEventAndContinueHandler;
|
||||
let event = yield;
|
||||
|
||||
is(event.type, "upgradeneeded", "Got correct event type");
|
||||
|
||||
let db = event.target.result;
|
||||
db.onerror = errorHandler;
|
||||
|
||||
let objectStore = db.createObjectStore(objectStoreName, { });
|
||||
|
||||
objectStore.add(fileData1.file, fileData1.key);
|
||||
objectStore.add(fileData2.file, fileData2.key);
|
||||
objectStore.add(fileData3.file, fileData3.key);
|
||||
|
||||
event = yield;
|
||||
|
||||
is(event.type, "success", "Got correct event type");
|
||||
|
||||
let trans = db.transaction([objectStoreName], READ_WRITE);
|
||||
trans.objectStore(objectStoreName).delete(fileData1.key);
|
||||
trans.oncomplete = grabEventAndContinueHandler;
|
||||
event = yield;
|
||||
|
||||
is(event.type, "complete", "Got correct event type");
|
||||
|
||||
is(getFileDBRefCount(name, 1), 0, "Correct db ref count");
|
||||
|
||||
fileData1.file = null;
|
||||
fileData2.file = null;
|
||||
fileData3.file = null;
|
||||
}
|
||||
|
||||
scheduleGC();
|
||||
yield;
|
||||
|
||||
ok(!hasFileInfo(name, 1), "Correct ref count");
|
||||
ok(hasFileInfo(name, 2), "Correct ref count");
|
||||
ok(hasFileInfo(name, 3), "Correct ref count");
|
||||
|
||||
{
|
||||
let request = mozIndexedDB.open(name, 1, description);
|
||||
request.onerror = errorHandler;
|
||||
request.onsuccess = grabEventAndContinueHandler;
|
||||
let event = yield;
|
||||
|
||||
is(event.type, "success", "Got correct event type");
|
||||
|
||||
let db = event.target.result;
|
||||
db.onerror = errorHandler;
|
||||
|
||||
trans = db.transaction([objectStoreName], READ_WRITE);
|
||||
objectStore = trans.objectStore(objectStoreName);
|
||||
|
||||
request = objectStore.get(fileData2.key);
|
||||
request.onsuccess = grabEventAndContinueHandler;
|
||||
event = yield;
|
||||
|
||||
let result = event.target.result;
|
||||
ok(result, "Got result");
|
||||
|
||||
objectStore.delete(fileData2.key);
|
||||
|
||||
trans.oncomplete = grabEventAndContinueHandler;
|
||||
event = yield;
|
||||
|
||||
is(event.type, "complete", "Got correct event type");
|
||||
|
||||
is(getFileDBRefCount(name, 2), 0, "Correct db ref count");
|
||||
|
||||
|
||||
trans = db.transaction([objectStoreName], READ_WRITE);
|
||||
objectStore = trans.objectStore(objectStoreName);
|
||||
|
||||
objectStore.delete(fileData3.key);
|
||||
|
||||
trans.oncomplete = grabEventAndContinueHandler;
|
||||
event = yield;
|
||||
|
||||
is(event.type, "complete", "Got correct event type");
|
||||
|
||||
is(getFileDBRefCount(name, 3), -1, "Correct db ref count");
|
||||
|
||||
event = null;
|
||||
result = null;
|
||||
}
|
||||
|
||||
scheduleGC();
|
||||
yield;
|
||||
|
||||
ok(!hasFileInfo(name, 1), "Correct ref count");
|
||||
ok(!hasFileInfo(name, 2), "Correct ref count");
|
||||
ok(!hasFileInfo(name, 3), "Correct ref count");
|
||||
|
||||
finishTest();
|
||||
yield;
|
||||
}
|
||||
</script>
|
||||
<script type="text/javascript;version=1.7" src="file.js"></script>
|
||||
<script type="text/javascript;version=1.7" src="helpers.js"></script>
|
||||
|
||||
</head>
|
||||
|
||||
<body onload="runTest();"></body>
|
||||
|
||||
</html>
|
|
@ -0,0 +1,86 @@
|
|||
<!--
|
||||
Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/
|
||||
-->
|
||||
<html>
|
||||
<head>
|
||||
<title>Indexed Database Property Test</title>
|
||||
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
|
||||
<script type="text/javascript;version=1.7">
|
||||
function testSteps()
|
||||
{
|
||||
const READ_WRITE = IDBTransaction.READ_WRITE;
|
||||
|
||||
const name = window.location.pathname;
|
||||
const description = "My Test Database";
|
||||
|
||||
const objectStoreName = "Blobs";
|
||||
|
||||
getUsage(grabFileUsageAndContinueHandler);
|
||||
let startUsage = yield;
|
||||
|
||||
const fileData = { key: 1, file: getRandomFile("random.bin", 100000) };
|
||||
|
||||
{
|
||||
let request = mozIndexedDB.open(name, 1, description);
|
||||
request.onerror = errorHandler;
|
||||
request.onupgradeneeded = grabEventAndContinueHandler;
|
||||
request.onsuccess = grabEventAndContinueHandler;
|
||||
let event = yield;
|
||||
|
||||
is(event.type, "upgradeneeded", "Got correct event type");
|
||||
|
||||
let db = event.target.result;
|
||||
db.onerror = errorHandler;
|
||||
|
||||
let objectStore = db.createObjectStore(objectStoreName, { });
|
||||
|
||||
objectStore.add(fileData.file, fileData.key);
|
||||
|
||||
event = yield;
|
||||
|
||||
is(event.type, "success", "Got correct event type");
|
||||
|
||||
getUsage(grabFileUsageAndContinueHandler);
|
||||
let usage = yield;
|
||||
|
||||
is(usage, startUsage + fileData.file.size, "Correct file usage");
|
||||
|
||||
let trans = db.transaction([objectStoreName], READ_WRITE);
|
||||
trans.objectStore(objectStoreName).delete(fileData.key);
|
||||
trans.oncomplete = grabEventAndContinueHandler;
|
||||
event = yield;
|
||||
|
||||
is(event.type, "complete", "Got correct event type");
|
||||
|
||||
getUsage(grabFileUsageAndContinueHandler);
|
||||
usage = yield;
|
||||
|
||||
is(usage, startUsage + fileData.file.size, "OS file exists");
|
||||
|
||||
fileData.file = null;
|
||||
}
|
||||
|
||||
scheduleGC();
|
||||
yield;
|
||||
|
||||
getUsage(grabFileUsageAndContinueHandler);
|
||||
let endUsage = yield;
|
||||
|
||||
is(endUsage, startUsage, "OS file deleted");
|
||||
|
||||
finishTest();
|
||||
yield;
|
||||
}
|
||||
</script>
|
||||
<script type="text/javascript;version=1.7" src="file.js"></script>
|
||||
<script type="text/javascript;version=1.7" src="helpers.js"></script>
|
||||
|
||||
</head>
|
||||
|
||||
<body onload="runTest();"></body>
|
||||
|
||||
</html>
|
|
@ -0,0 +1,101 @@
|
|||
<!--
|
||||
Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/
|
||||
-->
|
||||
<html>
|
||||
<head>
|
||||
<title>Indexed Database Property Test</title>
|
||||
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
|
||||
<script type="text/javascript;version=1.7">
|
||||
function testSteps()
|
||||
{
|
||||
const READ_WRITE = IDBTransaction.READ_WRITE;
|
||||
|
||||
const name = window.location.pathname;
|
||||
const description = "My Test Database";
|
||||
|
||||
const objectStoreName = "Blobs";
|
||||
|
||||
const blobData = { key: 1, blob: getRandomBlob(10000) };
|
||||
const fileData = { key: 2, file: getRandomFile("random.bin", 100000) };
|
||||
|
||||
let request = mozIndexedDB.open(name, 1, description);
|
||||
request.onerror = errorHandler;
|
||||
request.onupgradeneeded = grabEventAndContinueHandler;
|
||||
request.onsuccess = grabEventAndContinueHandler;
|
||||
let event = yield;
|
||||
|
||||
is(event.type, "upgradeneeded", "Got correct event type");
|
||||
|
||||
let db = event.target.result;
|
||||
db.onerror = errorHandler;
|
||||
|
||||
db.createObjectStore(objectStoreName, { });
|
||||
|
||||
event = yield;
|
||||
|
||||
is(event.type, "success", "Got correct event type");
|
||||
|
||||
let objectStore = db.transaction([objectStoreName], READ_WRITE)
|
||||
.objectStore(objectStoreName);
|
||||
request = objectStore.add(blobData.blob, blobData.key);
|
||||
request.onsuccess = grabEventAndContinueHandler;
|
||||
event = yield;
|
||||
|
||||
is(event.target.result, blobData.key, "Got correct key");
|
||||
|
||||
request = objectStore.get(blobData.key);
|
||||
request.onsuccess = grabEventAndContinueHandler;
|
||||
event = yield;
|
||||
|
||||
verifyBlob(event.target.result, blobData.blob, 1);
|
||||
yield;
|
||||
|
||||
request = db.transaction([objectStoreName])
|
||||
.objectStore(objectStoreName).get(blobData.key);
|
||||
request.onsuccess = grabEventAndContinueHandler;
|
||||
event = yield;
|
||||
|
||||
verifyBlob(event.target.result, blobData.blob, 1);
|
||||
yield;
|
||||
|
||||
objectStore = db.transaction([objectStoreName], READ_WRITE)
|
||||
.objectStore(objectStoreName);
|
||||
request = objectStore.add(fileData.file, fileData.key);
|
||||
request.onsuccess = grabEventAndContinueHandler;
|
||||
event = yield;
|
||||
|
||||
is(event.target.result, fileData.key, "Got correct key");
|
||||
|
||||
request = objectStore.get(fileData.key);
|
||||
request.onsuccess = grabEventAndContinueHandler;
|
||||
event = yield;
|
||||
|
||||
verifyBlob(event.target.result, fileData.file, 2);
|
||||
yield;
|
||||
|
||||
request = db.transaction([objectStoreName])
|
||||
.objectStore(objectStoreName).get(fileData.key);
|
||||
request.onsuccess = grabEventAndContinueHandler;
|
||||
event = yield;
|
||||
|
||||
verifyBlob(event.target.result, fileData.file, 2);
|
||||
yield;
|
||||
|
||||
is(bufferCache.length, 2, "Correct length");
|
||||
|
||||
finishTest();
|
||||
yield;
|
||||
}
|
||||
</script>
|
||||
<script type="text/javascript;version=1.7" src="file.js"></script>
|
||||
<script type="text/javascript;version=1.7" src="helpers.js"></script>
|
||||
|
||||
</head>
|
||||
|
||||
<body onload="runTest();"></body>
|
||||
|
||||
</html>
|
|
@ -0,0 +1,75 @@
|
|||
<!--
|
||||
Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/
|
||||
-->
|
||||
<html>
|
||||
<head>
|
||||
<title>Indexed Database Property Test</title>
|
||||
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
|
||||
<script type="text/javascript;version=1.7">
|
||||
function testSteps()
|
||||
{
|
||||
denyUnlimitedQuota();
|
||||
|
||||
const READ_WRITE = IDBTransaction.READ_WRITE;
|
||||
const DEFAULT_QUOTA_MB = 50;
|
||||
|
||||
const name = window.location.pathname;
|
||||
const description = "My Test Database";
|
||||
|
||||
const objectStoreName = "Blobs";
|
||||
|
||||
const testData = { key: 0, value: {} };
|
||||
const fileData = { key: 1, file: null };
|
||||
|
||||
let request = mozIndexedDB.open(name, 1, description);
|
||||
request.onerror = errorHandler;
|
||||
request.onupgradeneeded = grabEventAndContinueHandler;
|
||||
request.onsuccess = grabEventAndContinueHandler;
|
||||
let event = yield;
|
||||
|
||||
is(event.type, "upgradeneeded", "Got correct event type");
|
||||
|
||||
let db = event.target.result;
|
||||
|
||||
let objectStore = db.createObjectStore(objectStoreName, { });
|
||||
objectStore.add(testData.value, testData.key);
|
||||
|
||||
let usage = getUsageSync();
|
||||
|
||||
let size = (DEFAULT_QUOTA_MB + 1) * 1024 * 1024 - usage;
|
||||
|
||||
fileData.file = getNullFile("random.bin", size);
|
||||
|
||||
event = yield;
|
||||
|
||||
is(event.type, "success", "Got correct event type");
|
||||
|
||||
trans = db.transaction([objectStoreName], READ_WRITE);
|
||||
objectStore = trans.objectStore(objectStoreName);
|
||||
|
||||
request = objectStore.add(fileData.file, fileData.key);
|
||||
request.onerror = new ExpectError(IDBDatabaseException.UNKNOWN_ERR);
|
||||
request.onsuccess = unexpectedSuccessHandler;
|
||||
event = yield;
|
||||
|
||||
trans.oncomplete = grabEventAndContinueHandler;
|
||||
event = yield;
|
||||
|
||||
is(event.type, "complete", "Got correct event type");
|
||||
|
||||
finishTest();
|
||||
yield;
|
||||
}
|
||||
</script>
|
||||
<script type="text/javascript;version=1.7" src="file.js"></script>
|
||||
<script type="text/javascript;version=1.7" src="helpers.js"></script>
|
||||
|
||||
</head>
|
||||
|
||||
<body onload="runTest();"></body>
|
||||
|
||||
</html>
|
|
@ -0,0 +1,136 @@
|
|||
<!--
|
||||
Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/
|
||||
-->
|
||||
<html>
|
||||
<head>
|
||||
<title>Indexed Database Property Test</title>
|
||||
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
|
||||
<script type="text/javascript;version=1.7">
|
||||
function testSteps()
|
||||
{
|
||||
const READ_WRITE = IDBTransaction.READ_WRITE;
|
||||
|
||||
const name = window.location.pathname;
|
||||
const description = "My Test Database";
|
||||
|
||||
const objectStoreName = "Blobs";
|
||||
|
||||
const fileData = { key: 1, file: getRandomFile("random.bin", 100000) };
|
||||
|
||||
{
|
||||
let file = getRandomFile("random1.bin", 100000);
|
||||
|
||||
let request = mozIndexedDB.open(name, 1, description);
|
||||
request.onerror = errorHandler;
|
||||
request.onupgradeneeded = grabEventAndContinueHandler;
|
||||
request.onsuccess = grabEventAndContinueHandler;
|
||||
let event = yield;
|
||||
|
||||
is(event.type, "upgradeneeded", "Got correct event type");
|
||||
|
||||
let db = event.target.result;
|
||||
db.onerror = errorHandler;
|
||||
|
||||
let objectStore = db.createObjectStore(objectStoreName, { });
|
||||
|
||||
objectStore.add(fileData.file, fileData.key);
|
||||
|
||||
event = yield;
|
||||
|
||||
is(event.type, "success", "Got correct event type");
|
||||
|
||||
let trans = db.transaction([objectStoreName], READ_WRITE);
|
||||
objectStore = trans.objectStore(objectStoreName);
|
||||
|
||||
objectStore.delete(fileData.key);
|
||||
|
||||
trans.oncomplete = grabEventAndContinueHandler;
|
||||
event = yield;
|
||||
|
||||
is(getFileDBRefCount(name, 1), 0, "Correct db ref count");
|
||||
|
||||
trans = db.transaction([objectStoreName], READ_WRITE);
|
||||
objectStore = trans.objectStore(objectStoreName);
|
||||
|
||||
request = objectStore.add(fileData.file, fileData.key);
|
||||
request.onsuccess = grabEventAndContinueHandler;
|
||||
event = yield;
|
||||
|
||||
trans.oncomplete = grabEventAndContinueHandler;
|
||||
event = yield;
|
||||
|
||||
is(getFileDBRefCount(name, 1), 1, "Correct db ref count");
|
||||
|
||||
fileData.file = null;
|
||||
}
|
||||
|
||||
scheduleGC();
|
||||
yield;
|
||||
|
||||
is(getFileRefCount(name, 1), 1, "Correct ref count");
|
||||
|
||||
{
|
||||
let request = mozIndexedDB.open(name, 1, description);
|
||||
request.onerror = errorHandler;
|
||||
request.onsuccess = grabEventAndContinueHandler;
|
||||
let event = yield;
|
||||
|
||||
is(event.type, "success", "Got correct event type");
|
||||
|
||||
let db = event.target.result;
|
||||
db.onerror = errorHandler;
|
||||
|
||||
let trans = db.transaction([objectStoreName], READ_WRITE);
|
||||
objectStore = trans.objectStore(objectStoreName);
|
||||
|
||||
request = objectStore.get(fileData.key);
|
||||
request.onsuccess = grabEventAndContinueHandler;
|
||||
event = yield;
|
||||
|
||||
let result = event.target.result;
|
||||
ok(result, "Got result");
|
||||
|
||||
objectStore.delete(fileData.key);
|
||||
|
||||
trans.oncomplete = grabEventAndContinueHandler;
|
||||
event = yield;
|
||||
|
||||
is(getFileDBRefCount(name, 1), 0, "Correct db ref count");
|
||||
|
||||
trans = db.transaction([objectStoreName], READ_WRITE);
|
||||
objectStore = trans.objectStore(objectStoreName);
|
||||
|
||||
request = objectStore.add(result, fileData.key);
|
||||
request.onsuccess = grabEventAndContinueHandler;
|
||||
event = yield;
|
||||
|
||||
trans.oncomplete = grabEventAndContinueHandler;
|
||||
event = yield;
|
||||
|
||||
is(getFileDBRefCount(name, 1), 1, "Correct db ref count");
|
||||
|
||||
event = null;
|
||||
result = null;
|
||||
}
|
||||
|
||||
scheduleGC();
|
||||
yield;
|
||||
|
||||
is(getFileRefCount(name, 1), 1, "Correct ref count");
|
||||
|
||||
finishTest();
|
||||
yield;
|
||||
}
|
||||
</script>
|
||||
<script type="text/javascript;version=1.7" src="file.js"></script>
|
||||
<script type="text/javascript;version=1.7" src="helpers.js"></script>
|
||||
|
||||
</head>
|
||||
|
||||
<body onload="runTest();"></body>
|
||||
|
||||
</html>
|
|
@ -0,0 +1,93 @@
|
|||
<!--
|
||||
Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/
|
||||
-->
|
||||
<html>
|
||||
<head>
|
||||
<title>Indexed Database Property Test</title>
|
||||
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
|
||||
<script type="text/javascript;version=1.7">
|
||||
function testSteps()
|
||||
{
|
||||
const READ_WRITE = IDBTransaction.READ_WRITE;
|
||||
|
||||
const name = window.location.pathname;
|
||||
const description = "My Test Database";
|
||||
|
||||
const objectStoreName = "Blobs";
|
||||
|
||||
const fileData = { key: 1, file: getRandomFile("random.bin", 100000) };
|
||||
|
||||
{
|
||||
let request = mozIndexedDB.open(name, 1, description);
|
||||
request.onerror = errorHandler;
|
||||
request.onupgradeneeded = grabEventAndContinueHandler;
|
||||
request.onsuccess = grabEventAndContinueHandler;
|
||||
let event = yield;
|
||||
|
||||
is(event.type, "upgradeneeded", "Got correct event type");
|
||||
|
||||
let db = event.target.result;
|
||||
db.onerror = errorHandler;
|
||||
|
||||
let objectStore = db.createObjectStore(objectStoreName, { });
|
||||
|
||||
event = yield;
|
||||
|
||||
is(event.type, "success", "Got correct event type");
|
||||
|
||||
let trans = db.transaction([objectStoreName], READ_WRITE);
|
||||
objectStore = trans.objectStore(objectStoreName);
|
||||
|
||||
request = objectStore.add(fileData.file, fileData.key);
|
||||
request.onsuccess = grabEventAndContinueHandler;
|
||||
event = yield;
|
||||
|
||||
request = objectStore.get(fileData.key);
|
||||
request.onsuccess = grabEventAndContinueHandler;
|
||||
event = yield;
|
||||
|
||||
let result = event.target.result;
|
||||
ok(result, "Got result");
|
||||
|
||||
trans.onabort = grabEventAndContinueHandler;
|
||||
trans.abort();
|
||||
event = yield;
|
||||
|
||||
is(getFileDBRefCount(name, 1), 0, "Correct db ref count");
|
||||
|
||||
trans = db.transaction([objectStoreName], READ_WRITE);
|
||||
objectStore = trans.objectStore(objectStoreName);
|
||||
|
||||
request = objectStore.add(result, fileData.key);
|
||||
request.onsuccess = grabEventAndContinueHandler;
|
||||
event = yield;
|
||||
|
||||
trans.oncomplete = grabEventAndContinueHandler;
|
||||
event = yield;
|
||||
|
||||
is(getFileDBRefCount(name, 1), 1, "Correct db ref count");
|
||||
|
||||
fileData.file = null;
|
||||
}
|
||||
|
||||
scheduleGC();
|
||||
yield;
|
||||
|
||||
is(getFileRefCount(name, 1), 1, "Correct ref count");
|
||||
|
||||
finishTest();
|
||||
yield;
|
||||
}
|
||||
</script>
|
||||
<script type="text/javascript;version=1.7" src="file.js"></script>
|
||||
<script type="text/javascript;version=1.7" src="helpers.js"></script>
|
||||
|
||||
</head>
|
||||
|
||||
<body onload="runTest();"></body>
|
||||
|
||||
</html>
|
|
@ -0,0 +1,106 @@
|
|||
<!--
|
||||
Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/
|
||||
-->
|
||||
<html>
|
||||
<head>
|
||||
<title>Indexed Database Property Test</title>
|
||||
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
|
||||
<script type="text/javascript;version=1.7">
|
||||
function testSteps()
|
||||
{
|
||||
const READ_WRITE = IDBTransaction.READ_WRITE;
|
||||
|
||||
const name = window.location.pathname;
|
||||
const description = "My Test Database";
|
||||
|
||||
const objectStoreInfo = [
|
||||
{ name: "Blobs", options: { } },
|
||||
{ name: "Other Blobs", options: { } }
|
||||
];
|
||||
|
||||
const fileData = { key: 1, file: getRandomFile("random.bin", 100000) };
|
||||
|
||||
let request = mozIndexedDB.open(name, 1, description);
|
||||
request.onerror = errorHandler;
|
||||
request.onupgradeneeded = grabEventAndContinueHandler;
|
||||
request.onsuccess = grabEventAndContinueHandler;
|
||||
let event = yield;
|
||||
|
||||
is(event.type, "upgradeneeded", "Got correct event type");
|
||||
|
||||
let db = event.target.result;
|
||||
db.onerror = errorHandler;
|
||||
|
||||
for each (let info in objectStoreInfo) {
|
||||
let objectStore = db.createObjectStore(info.name, info.options);
|
||||
objectStore.add(fileData.file, fileData.key);
|
||||
}
|
||||
|
||||
event = yield;
|
||||
|
||||
is(event.type, "success", "Got correct event type");
|
||||
|
||||
let refResult;
|
||||
for each (let info in objectStoreInfo) {
|
||||
let objectStore = db.transaction([info.name])
|
||||
.objectStore(info.name);
|
||||
|
||||
request = objectStore.get(fileData.key);
|
||||
request.onsuccess = grabEventAndContinueHandler;
|
||||
event = yield;
|
||||
|
||||
let result = event.target.result;
|
||||
verifyBlob(result, fileData.file, 1);
|
||||
yield;
|
||||
|
||||
if (!refResult) {
|
||||
refResult = result;
|
||||
continue;
|
||||
}
|
||||
|
||||
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
|
||||
is(result.mozFullPath, refResult.mozFullPath, "The same os file");
|
||||
}
|
||||
|
||||
for (let i = 1; i < objectStoreInfo.length; i++) {
|
||||
let info = objectStoreInfo[i];
|
||||
|
||||
let objectStore = db.transaction([info.name], READ_WRITE)
|
||||
.objectStore(info.name);
|
||||
|
||||
request = objectStore.add(refResult, 2);
|
||||
request.onsuccess = grabEventAndContinueHandler;
|
||||
event = yield;
|
||||
|
||||
is(event.target.result, 2, "Got correct key");
|
||||
|
||||
request = objectStore.get(2);
|
||||
request.onsuccess = grabEventAndContinueHandler;
|
||||
event = yield;
|
||||
|
||||
let result = event.target.result;
|
||||
verifyBlob(result, refResult, 1);
|
||||
yield;
|
||||
|
||||
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
|
||||
is(result.mozFullPath, refResult.mozFullPath, "The same os file");
|
||||
}
|
||||
|
||||
is(bufferCache.length, 2, "Correct length");
|
||||
|
||||
finishTest();
|
||||
yield;
|
||||
}
|
||||
</script>
|
||||
<script type="text/javascript;version=1.7" src="file.js"></script>
|
||||
<script type="text/javascript;version=1.7" src="helpers.js"></script>
|
||||
|
||||
</head>
|
||||
|
||||
<body onload="runTest();"></body>
|
||||
|
||||
</html>
|
|
@ -0,0 +1,78 @@
|
|||
<!--
|
||||
Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/
|
||||
-->
|
||||
<html>
|
||||
<head>
|
||||
<title>Indexed Database Property Test</title>
|
||||
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
|
||||
<script type="text/javascript;version=1.7">
|
||||
function testSteps()
|
||||
{
|
||||
const READ_WRITE = IDBTransaction.READ_WRITE;
|
||||
|
||||
const name = window.location.pathname;
|
||||
const description = "My Test Database";
|
||||
|
||||
const objectStoreName = "Blobs";
|
||||
|
||||
const fileData = { key: 1, file: getRandomFile("random.bin", 100000) };
|
||||
|
||||
{
|
||||
let request = mozIndexedDB.open(name, 1, description);
|
||||
request.onerror = errorHandler;
|
||||
request.onupgradeneeded = grabEventAndContinueHandler;
|
||||
request.onsuccess = grabEventAndContinueHandler;
|
||||
let event = yield;
|
||||
|
||||
is(event.type, "upgradeneeded", "Got correct event type");
|
||||
|
||||
let db = event.target.result;
|
||||
db.onerror = errorHandler;
|
||||
|
||||
objectStore = db.createObjectStore(objectStoreName, { });
|
||||
|
||||
event = yield;
|
||||
|
||||
is(event.type, "success", "Got correct event type");
|
||||
|
||||
let trans = db.transaction([objectStoreName], READ_WRITE);
|
||||
let objectStore = trans.objectStore(objectStoreName);
|
||||
|
||||
request = objectStore.add(fileData.file, fileData.key);
|
||||
request.onsuccess = grabEventAndContinueHandler;
|
||||
event = yield;
|
||||
|
||||
is(event.target.result, fileData.key, "Got correct key");
|
||||
|
||||
trans.onabort = grabEventAndContinueHandler;
|
||||
trans.abort();
|
||||
event = yield;
|
||||
|
||||
is(event.type, "abort", "Got correct event type");
|
||||
|
||||
is(getFileDBRefCount(name, 1), 0, "Correct db ref count");
|
||||
|
||||
fileData.file = null;
|
||||
}
|
||||
|
||||
scheduleGC();
|
||||
yield;
|
||||
|
||||
ok(!hasFileInfo(name, 1), "Correct ref count");
|
||||
|
||||
finishTest();
|
||||
yield;
|
||||
}
|
||||
</script>
|
||||
<script type="text/javascript;version=1.7" src="file.js"></script>
|
||||
<script type="text/javascript;version=1.7" src="helpers.js"></script>
|
||||
|
||||
</head>
|
||||
|
||||
<body onload="runTest();"></body>
|
||||
|
||||
</html>
|
|
@ -65,10 +65,11 @@ interface nsIDOMEvent;
|
|||
interface nsITransferable;
|
||||
interface 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);
|
||||
|
||||
};
|
||||
|
|
|
@ -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);
|
||||
};
|
||||
|
|
|
@ -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);
|
||||
};
|
||||
|
|
|
@ -0,0 +1,336 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||
* vim: sw=2 ts=2 et lcs=trail\:.,tab\:>~ :
|
||||
* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is Mozilla.
|
||||
*
|
||||
* The Initial Developer of the Original Code is Mozilla Foundation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2011
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
#include "FileSystemModule.h"
|
||||
|
||||
#include "sqlite3.h"
|
||||
#include "nsString.h"
|
||||
#include "nsISimpleEnumerator.h"
|
||||
#include "nsIFile.h"
|
||||
#include "nsILocalFile.h"
|
||||
|
||||
namespace {
|
||||
|
||||
struct VirtualTableCursorBase
|
||||
{
|
||||
VirtualTableCursorBase()
|
||||
{
|
||||
memset(&mBase, 0, sizeof(mBase));
|
||||
}
|
||||
|
||||
sqlite3_vtab_cursor mBase;
|
||||
};
|
||||
|
||||
struct VirtualTableCursor : public VirtualTableCursorBase
|
||||
{
|
||||
public:
|
||||
VirtualTableCursor()
|
||||
: mRowId(-1)
|
||||
{
|
||||
mCurrentFileName.SetIsVoid(true);
|
||||
}
|
||||
|
||||
const nsString& DirectoryPath() const
|
||||
{
|
||||
return mDirectoryPath;
|
||||
}
|
||||
|
||||
const nsString& CurrentFileName() const
|
||||
{
|
||||
return mCurrentFileName;
|
||||
}
|
||||
|
||||
PRInt64 RowId() const
|
||||
{
|
||||
return mRowId;
|
||||
}
|
||||
|
||||
nsresult Init(const nsAString& aPath);
|
||||
nsresult NextFile();
|
||||
|
||||
private:
|
||||
nsCOMPtr<nsISimpleEnumerator> mEntries;
|
||||
|
||||
nsString mDirectoryPath;
|
||||
nsString mCurrentFileName;
|
||||
|
||||
PRInt64 mRowId;
|
||||
};
|
||||
|
||||
nsresult
|
||||
VirtualTableCursor::Init(const nsAString& aPath)
|
||||
{
|
||||
nsCOMPtr<nsILocalFile> directory =
|
||||
do_CreateInstance(NS_LOCAL_FILE_CONTRACTID);
|
||||
NS_ENSURE_TRUE(directory, NS_ERROR_FAILURE);
|
||||
|
||||
nsresult rv = directory->InitWithPath(aPath);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = directory->GetPath(mDirectoryPath);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = directory->GetDirectoryEntries(getter_AddRefs(mEntries));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = NextFile();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
VirtualTableCursor::NextFile()
|
||||
{
|
||||
bool hasMore;
|
||||
nsresult rv = mEntries->HasMoreElements(&hasMore);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (!hasMore) {
|
||||
mCurrentFileName.SetIsVoid(true);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsISupports> entry;
|
||||
rv = mEntries->GetNext(getter_AddRefs(entry));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsCOMPtr<nsIFile> file = do_QueryInterface(entry);
|
||||
NS_ENSURE_TRUE(file, NS_ERROR_FAILURE);
|
||||
|
||||
rv = file->GetLeafName(mCurrentFileName);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
mRowId++;
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
int Connect(sqlite3* aDB, void* aAux, int aArgc, const char* const* aArgv,
|
||||
sqlite3_vtab** aVtab, char** aErr)
|
||||
{
|
||||
static const char virtualTableSchema[] =
|
||||
"CREATE TABLE fs ("
|
||||
"name TEXT, "
|
||||
"path TEXT"
|
||||
")";
|
||||
|
||||
int rc = sqlite3_declare_vtab(aDB, virtualTableSchema);
|
||||
if (rc != SQLITE_OK) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
sqlite3_vtab* vt = new sqlite3_vtab();
|
||||
memset(vt, 0, sizeof(*vt));
|
||||
|
||||
*aVtab = vt;
|
||||
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
int Disconnect(sqlite3_vtab* aVtab )
|
||||
{
|
||||
delete aVtab;
|
||||
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
int BestIndex(sqlite3_vtab* aVtab, sqlite3_index_info* aInfo)
|
||||
{
|
||||
// Here we specify what index constraints we want to handle. That is, there
|
||||
// might be some columns with particular constraints in which we can help
|
||||
// SQLite narrow down the result set.
|
||||
//
|
||||
// For example, take the "path = x" where x is a directory. In this case,
|
||||
// we can narrow our search to just this directory instead of the entire file
|
||||
// system. This can be a significant optimization. So, we want to handle that
|
||||
// constraint. To do so, we would look for two specific input conditions:
|
||||
//
|
||||
// 1. aInfo->aConstraint[i].iColumn == 1
|
||||
// 2. aInfo->aConstraint[i].op == SQLITE_INDEX_CONSTRAINT_EQ
|
||||
//
|
||||
// The first states that the path column is being used in one of the input
|
||||
// constraints and the second states that the constraint involves the equal
|
||||
// operator.
|
||||
//
|
||||
// An even more specific search would be for name='xxx', in which case we
|
||||
// can limit the search to a single file, if it exists.
|
||||
//
|
||||
// What we have to do here is look for all of our index searches and select
|
||||
// the narrowest. We can only pick one, so obviously we want the one that
|
||||
// is the most specific, which leads to the smallest result set.
|
||||
|
||||
for(int i = 0; i < aInfo->nConstraint; i++) {
|
||||
if (aInfo->aConstraint[i].iColumn == 1 && aInfo->aConstraint[i].usable) {
|
||||
if (aInfo->aConstraint[i].op & SQLITE_INDEX_CONSTRAINT_EQ) {
|
||||
aInfo->aConstraintUsage[i].argvIndex = 1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// TODO: handle single files (constrained also by the name column)
|
||||
}
|
||||
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
int Open(sqlite3_vtab* aVtab, sqlite3_vtab_cursor** aCursor)
|
||||
{
|
||||
VirtualTableCursor* cursor = new VirtualTableCursor();
|
||||
|
||||
*aCursor = reinterpret_cast<sqlite3_vtab_cursor*>(cursor);
|
||||
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
int Close(sqlite3_vtab_cursor* aCursor)
|
||||
{
|
||||
VirtualTableCursor* cursor = reinterpret_cast<VirtualTableCursor*>(aCursor);
|
||||
|
||||
delete cursor;
|
||||
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
int Filter(sqlite3_vtab_cursor* aCursor, int aIdxNum, const char* aIdxStr,
|
||||
int aArgc, sqlite3_value** aArgv)
|
||||
{
|
||||
VirtualTableCursor* cursor = reinterpret_cast<VirtualTableCursor*>(aCursor);
|
||||
|
||||
if(aArgc <= 0) {
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
nsDependentString path(
|
||||
reinterpret_cast<const PRUnichar*>(::sqlite3_value_text16(aArgv[0])));
|
||||
|
||||
nsresult rv = cursor->Init(path);
|
||||
NS_ENSURE_SUCCESS(rv, SQLITE_ERROR);
|
||||
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
int Next(sqlite3_vtab_cursor* aCursor)
|
||||
{
|
||||
VirtualTableCursor* cursor = reinterpret_cast<VirtualTableCursor*>(aCursor);
|
||||
|
||||
nsresult rv = cursor->NextFile();
|
||||
NS_ENSURE_SUCCESS(rv, SQLITE_ERROR);
|
||||
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
int Eof(sqlite3_vtab_cursor* aCursor)
|
||||
{
|
||||
VirtualTableCursor* cursor = reinterpret_cast<VirtualTableCursor*>(aCursor);
|
||||
return cursor->CurrentFileName().IsVoid() ? 1 : 0;
|
||||
}
|
||||
|
||||
int Column(sqlite3_vtab_cursor* aCursor, sqlite3_context* aContext,
|
||||
int aColumnIndex)
|
||||
{
|
||||
VirtualTableCursor* cursor = reinterpret_cast<VirtualTableCursor*>(aCursor);
|
||||
|
||||
switch (aColumnIndex) {
|
||||
// name
|
||||
case 0: {
|
||||
const nsString& name = cursor->CurrentFileName();
|
||||
sqlite3_result_text16(aContext, name.get(),
|
||||
name.Length() * sizeof(PRUnichar),
|
||||
SQLITE_TRANSIENT);
|
||||
break;
|
||||
}
|
||||
|
||||
// path
|
||||
case 1: {
|
||||
const nsString& path = cursor->DirectoryPath();
|
||||
sqlite3_result_text16(aContext, path.get(),
|
||||
path.Length() * sizeof(PRUnichar),
|
||||
SQLITE_TRANSIENT);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
NS_NOTREACHED("Unsupported column!");
|
||||
}
|
||||
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
int RowId(sqlite3_vtab_cursor* aCursor, sqlite3_int64* aRowid)
|
||||
{
|
||||
VirtualTableCursor* cursor = reinterpret_cast<VirtualTableCursor*>(aCursor);
|
||||
|
||||
*aRowid = cursor->RowId();
|
||||
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
namespace mozilla {
|
||||
namespace storage {
|
||||
|
||||
int RegisterFileSystemModule(sqlite3* aDB, const char* aName)
|
||||
{
|
||||
static sqlite3_module module = {
|
||||
1,
|
||||
Connect,
|
||||
Connect,
|
||||
BestIndex,
|
||||
Disconnect,
|
||||
Disconnect,
|
||||
Open,
|
||||
Close,
|
||||
Filter,
|
||||
Next,
|
||||
Eof,
|
||||
Column,
|
||||
RowId,
|
||||
nsnull,
|
||||
nsnull,
|
||||
nsnull,
|
||||
nsnull,
|
||||
nsnull,
|
||||
nsnull,
|
||||
nsnull
|
||||
};
|
||||
|
||||
return sqlite3_create_module(aDB, aName, &module, nsnull);
|
||||
}
|
||||
|
||||
} // namespace storage
|
||||
} // namespace mozilla
|
|
@ -0,0 +1,53 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||
* vim: sw=2 ts=2 et lcs=trail\:.,tab\:>~ :
|
||||
* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is Mozilla.
|
||||
*
|
||||
* The Initial Developer of the Original Code is Mozilla Foundation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2011
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
#ifndef mozilla_storage_FileSystemModule_h
|
||||
#define mozilla_storage_FileSystemModule_h
|
||||
|
||||
#include "nscore.h"
|
||||
|
||||
struct sqlite3;
|
||||
|
||||
namespace mozilla {
|
||||
namespace storage {
|
||||
|
||||
NS_HIDDEN_(int) RegisterFileSystemModule(sqlite3* aDB, const char* aName);
|
||||
|
||||
} // namespace storage
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_storage_FileSystemModule_h
|
|
@ -86,6 +86,7 @@ CPPSRCS = \
|
|||
SQLCollations.cpp \
|
||||
VacuumManager.cpp \
|
||||
TelemetryVFS.cpp \
|
||||
FileSystemModule.cpp \
|
||||
$(NULL)
|
||||
|
||||
# For nsDependentJSString
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
Загрузка…
Ссылка в новой задаче