mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-30 13:45:27 +00:00
410 lines
14 KiB
C++
410 lines
14 KiB
C++
/* -*- 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) 2010
|
|
* the Initial Developer. All Rights Reserved.
|
|
*
|
|
* Contributor(s):
|
|
* Ben Turner <bent.mozilla@gmail.com>
|
|
*
|
|
* 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_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"
|
|
|
|
#include "mozilla/Mutex.h"
|
|
|
|
#include "nsIIndexedDatabaseManager.h"
|
|
#include "nsIObserver.h"
|
|
#include "nsIRunnable.h"
|
|
#include "nsIThread.h"
|
|
#include "nsIURI.h"
|
|
|
|
#include "nsClassHashtable.h"
|
|
#include "nsRefPtrHashtable.h"
|
|
#include "nsHashKeys.h"
|
|
|
|
#define INDEXEDDB_MANAGER_CONTRACTID "@mozilla.org/dom/indexeddb/manager;1"
|
|
|
|
class mozIStorageQuotaCallback;
|
|
class nsITimer;
|
|
|
|
BEGIN_INDEXEDDB_NAMESPACE
|
|
|
|
class AsyncConnectionHelper;
|
|
|
|
class CheckQuotaHelper;
|
|
|
|
class IndexedDatabaseManager : public nsIIndexedDatabaseManager,
|
|
public nsIObserver
|
|
{
|
|
friend class IDBDatabase;
|
|
|
|
public:
|
|
static already_AddRefed<IndexedDatabaseManager> GetOrCreate();
|
|
|
|
// Returns a non-owning reference.
|
|
static IndexedDatabaseManager* Get();
|
|
|
|
// Returns an owning reference! No one should call this but the factory.
|
|
static IndexedDatabaseManager* FactoryCreate();
|
|
|
|
NS_DECL_ISUPPORTS
|
|
NS_DECL_NSIINDEXEDDATABASEMANAGER
|
|
NS_DECL_NSIOBSERVER
|
|
|
|
// Waits for databases to be cleared and for version change transactions to
|
|
// complete before dispatching the given runnable.
|
|
nsresult WaitForOpenAllowed(const nsACString& aOrigin,
|
|
nsIAtom* aId,
|
|
nsIRunnable* aRunnable);
|
|
|
|
void AllowNextSynchronizedOp(const nsACString& aOrigin,
|
|
nsIAtom* aId);
|
|
|
|
nsIThread* IOThread()
|
|
{
|
|
NS_ASSERTION(mIOThread, "This should never be null!");
|
|
return mIOThread;
|
|
}
|
|
|
|
// 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
|
|
// close). If databases need to close first, the callback will be invoked
|
|
// with an array of said databases.
|
|
nsresult AcquireExclusiveAccess(IDBDatabase* aDatabase,
|
|
AsyncConnectionHelper* aHelper,
|
|
WaitingOnDatabasesCallback aCallback,
|
|
void* aClosure)
|
|
{
|
|
NS_ASSERTION(aDatabase, "Need a DB here!");
|
|
return AcquireExclusiveAccess(aDatabase->Origin(), aDatabase, aHelper,
|
|
aCallback, aClosure);
|
|
}
|
|
nsresult AcquireExclusiveAccess(const nsACString& aOrigin,
|
|
AsyncConnectionHelper* aHelper,
|
|
WaitingOnDatabasesCallback aCallback,
|
|
void* aClosure)
|
|
{
|
|
return AcquireExclusiveAccess(aOrigin, nsnull, aHelper, aCallback,
|
|
aClosure);
|
|
}
|
|
|
|
// Called when a window is being purged from the bfcache or the user leaves
|
|
// a page which isn't going into the bfcache. Forces any live database
|
|
// objects to close themselves and aborts any running transactions.
|
|
void AbortCloseDatabasesForWindow(nsPIDOMWindow* aWindow);
|
|
|
|
// Used to check if there are running transactions in a given window.
|
|
bool HasOpenTransactions(nsPIDOMWindow* aWindow);
|
|
|
|
// Set the Window that the current thread is doing operations for.
|
|
// The caller is responsible for ensuring that aWindow is held alive.
|
|
static inline void
|
|
SetCurrentWindow(nsPIDOMWindow* aWindow)
|
|
{
|
|
IndexedDatabaseManager* mgr = Get();
|
|
NS_ASSERTION(mgr, "Must have a manager here!");
|
|
|
|
return mgr->SetCurrentWindowInternal(aWindow);
|
|
}
|
|
|
|
static PRUint32
|
|
GetIndexedDBQuotaMB();
|
|
|
|
nsresult EnsureOriginIsInitialized(const nsACString& aOrigin,
|
|
nsIFile** aDirectory);
|
|
|
|
// Determine if the quota is lifted for the Window the current thread is
|
|
// using.
|
|
static inline bool
|
|
QuotaIsLifted()
|
|
{
|
|
IndexedDatabaseManager* mgr = Get();
|
|
NS_ASSERTION(mgr, "Must have a manager here!");
|
|
|
|
return mgr->QuotaIsLiftedInternal();
|
|
}
|
|
|
|
static inline void
|
|
CancelPromptsForWindow(nsPIDOMWindow* aWindow)
|
|
{
|
|
IndexedDatabaseManager* mgr = Get();
|
|
NS_ASSERTION(mgr, "Must have a manager here!");
|
|
|
|
mgr->CancelPromptsForWindowInternal(aWindow);
|
|
}
|
|
|
|
static nsresult
|
|
GetASCIIOriginFromWindow(nsPIDOMWindow* aWindow, nsCString& aASCIIOrigin);
|
|
|
|
already_AddRefed<FileManager>
|
|
GetOrCreateFileManager(const nsACString& aOrigin,
|
|
const nsAString& aDatabaseName);
|
|
|
|
already_AddRefed<FileManager>
|
|
GetFileManager(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();
|
|
|
|
nsresult AcquireExclusiveAccess(const nsACString& aOrigin,
|
|
IDBDatabase* aDatabase,
|
|
AsyncConnectionHelper* aHelper,
|
|
WaitingOnDatabasesCallback aCallback,
|
|
void* aClosure);
|
|
|
|
void SetCurrentWindowInternal(nsPIDOMWindow* aWindow);
|
|
bool QuotaIsLiftedInternal();
|
|
void CancelPromptsForWindowInternal(nsPIDOMWindow* aWindow);
|
|
|
|
// Called when a database is created.
|
|
bool RegisterDatabase(IDBDatabase* aDatabase);
|
|
|
|
// Called when a database is being unlinked or destroyed.
|
|
void UnregisterDatabase(IDBDatabase* aDatabase);
|
|
|
|
// Called when a database has been closed.
|
|
void OnDatabaseClosed(IDBDatabase* aDatabase);
|
|
|
|
// Responsible for clearing the database files for a particular origin on the
|
|
// IO thread. Created when nsIIDBIndexedDatabaseManager::ClearDatabasesForURI
|
|
// is called. Runs three times, first on the main thread, next on the IO
|
|
// thread, and then finally again on the main thread. While on the IO thread
|
|
// the runnable will actually remove the origin's database files and the
|
|
// directory that contains them before dispatching itself back to the main
|
|
// thread. When back on the main thread the runnable will notify the
|
|
// IndexedDatabaseManager that the job has been completed.
|
|
class OriginClearRunnable : public nsIRunnable
|
|
{
|
|
public:
|
|
NS_DECL_ISUPPORTS
|
|
NS_DECL_NSIRUNNABLE
|
|
|
|
OriginClearRunnable(const nsACString& aOrigin,
|
|
nsIThread* aThread)
|
|
: mOrigin(aOrigin),
|
|
mThread(aThread),
|
|
mFirstCallback(true)
|
|
{ }
|
|
|
|
nsCString mOrigin;
|
|
nsCOMPtr<nsIThread> mThread;
|
|
bool mFirstCallback;
|
|
};
|
|
|
|
bool IsClearOriginPending(const nsACString& origin);
|
|
|
|
// Responsible for calculating the amount of space taken up by databases of a
|
|
// certain origin. Created when nsIIDBIndexedDatabaseManager::GetUsageForURI
|
|
// is called. May be canceled with
|
|
// nsIIDBIndexedDatabaseManager::CancelGetUsageForURI. Runs twice, first on
|
|
// the IO thread, then again on the main thread. While on the IO thread the
|
|
// runnable will calculate the size of all files in the origin's directory
|
|
// before dispatching itself back to the main thread. When on the main thread
|
|
// the runnable will call the callback and then notify the
|
|
// IndexedDatabaseManager that the job has been completed.
|
|
class AsyncUsageRunnable : public nsIRunnable
|
|
{
|
|
public:
|
|
NS_DECL_ISUPPORTS
|
|
NS_DECL_NSIRUNNABLE
|
|
|
|
AsyncUsageRunnable(nsIURI* aURI,
|
|
const nsACString& aOrigin,
|
|
nsIIndexedDatabaseUsageCallback* aCallback);
|
|
|
|
// Sets the canceled flag so that the callback is never called.
|
|
void Cancel();
|
|
|
|
// Run calls the RunInternal method and makes sure that we always dispatch
|
|
// 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;
|
|
};
|
|
|
|
// Called when AsyncUsageRunnable has finished its Run() method.
|
|
inline void OnUsageCheckComplete(AsyncUsageRunnable* aRunnable);
|
|
|
|
// A struct that contains the information corresponding to a pending or
|
|
// running operation that requires synchronization (e.g. opening a db,
|
|
// clearing dbs for an origin, etc).
|
|
struct SynchronizedOp
|
|
{
|
|
SynchronizedOp(const nsACString& aOrigin, nsIAtom* aId);
|
|
~SynchronizedOp();
|
|
|
|
// Test whether the second SynchronizedOp needs to get behind this one.
|
|
bool MustWaitFor(const SynchronizedOp& aRhs) const;
|
|
|
|
void DelayRunnable(nsIRunnable* aRunnable);
|
|
void DispatchDelayedRunnables();
|
|
|
|
const nsCString mOrigin;
|
|
nsCOMPtr<nsIAtom> mId;
|
|
nsRefPtr<AsyncConnectionHelper> mHelper;
|
|
nsTArray<nsCOMPtr<nsIRunnable> > mDelayedRunnables;
|
|
nsTArray<nsRefPtr<IDBDatabase> > mDatabases;
|
|
};
|
|
|
|
// A callback runnable used by the TransactionPool when it's safe to proceed
|
|
// with a SetVersion/DeleteDatabase/etc.
|
|
class WaitForTransactionsToFinishRunnable : public nsIRunnable
|
|
{
|
|
public:
|
|
WaitForTransactionsToFinishRunnable(SynchronizedOp* aOp)
|
|
: mOp(aOp)
|
|
{
|
|
NS_ASSERTION(mOp, "Why don't we have a runnable?");
|
|
NS_ASSERTION(mOp->mDatabases.IsEmpty(), "We're here too early!");
|
|
NS_ASSERTION(mOp->mHelper, "What are we supposed to do when we're done?");
|
|
}
|
|
|
|
NS_DECL_ISUPPORTS
|
|
NS_DECL_NSIRUNNABLE
|
|
|
|
private:
|
|
// The IndexedDatabaseManager holds this alive.
|
|
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.
|
|
nsClassHashtable<nsCStringHashKey, nsTArray<IDBDatabase*> > mLiveDatabases;
|
|
|
|
// TLS storage index for the current thread's window
|
|
PRUintn mCurrentWindowIndex;
|
|
|
|
// Lock protecting mQuotaHelperHash
|
|
mozilla::Mutex mQuotaHelperMutex;
|
|
|
|
// 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;
|
|
|
|
// Maintains a list of synchronized operatons that are in progress or queued.
|
|
nsAutoTArray<nsAutoPtr<SynchronizedOp>, 5> mSynchronizedOps;
|
|
|
|
// Thread on which IO is performed.
|
|
nsCOMPtr<nsIThread> mIOThread;
|
|
|
|
// A timer that gets activated at shutdown to ensure we close all databases.
|
|
nsCOMPtr<nsITimer> mShutdownTimer;
|
|
|
|
// A single threadsafe instance of our quota callback. Created on the main
|
|
// thread during GetOrCreate().
|
|
nsCOMPtr<mozIStorageQuotaCallback> mQuotaCallbackSingleton;
|
|
|
|
// 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
|
|
{
|
|
public:
|
|
AutoEnterWindow(nsPIDOMWindow* aWindow)
|
|
{
|
|
IndexedDatabaseManager::SetCurrentWindow(aWindow);
|
|
}
|
|
|
|
~AutoEnterWindow()
|
|
{
|
|
IndexedDatabaseManager::SetCurrentWindow(nsnull);
|
|
}
|
|
};
|
|
|
|
END_INDEXEDDB_NAMESPACE
|
|
|
|
#endif /* mozilla_dom_indexeddb_indexeddatabasemanager_h__ */
|