/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=2 et sw=2 tw=80: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #ifndef mozilla_dom_indexeddb_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 nsIFile; class nsITimer; BEGIN_INDEXEDDB_NAMESPACE class AsyncConnectionHelper; class CheckQuotaHelper; class IndexedDatabaseManager MOZ_FINAL : public nsIIndexedDatabaseManager, public nsIObserver { friend class IDBDatabase; public: static already_AddRefed 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 >&, 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 GetOrCreateFileManager(const nsACString& aOrigin, const nsAString& aDatabaseName); already_AddRefed 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); const nsString& GetBaseDirectory() const { return mDatabaseBasePath; } nsresult GetDirectoryForOrigin(const nsACString& aASCIIOrigin, nsIFile** aDirectory) const; 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 MOZ_FINAL : public nsIRunnable { public: NS_DECL_ISUPPORTS NS_DECL_NSIRUNNABLE OriginClearRunnable(const nsACString& aOrigin, nsIThread* aThread) : mOrigin(aOrigin), mThread(aThread), mFirstCallback(true) { } nsCString mOrigin; nsCOMPtr 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 MOZ_FINAL : 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 mURI; nsCString mOrigin; nsCOMPtr 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 mId; nsRefPtr mHelper; nsTArray > mDelayedRunnables; nsTArray > mDatabases; }; // A callback runnable used by the TransactionPool when it's safe to proceed // with a SetVersion/DeleteDatabase/etc. class WaitForTransactionsToFinishRunnable MOZ_FINAL : 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 MOZ_FINAL : 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 > 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, 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 > > mFileManagers; // Maintains a list of origins that we're currently enumerating to gather // usage statistics. nsAutoTArray, 1> mUsageRunnables; // Maintains a list of synchronized operatons that are in progress or queued. nsAutoTArray, 5> mSynchronizedOps; // Thread on which IO is performed. nsCOMPtr mIOThread; // A timer that gets activated at shutdown to ensure we close all databases. nsCOMPtr mShutdownTimer; // A single threadsafe instance of our quota callback. Created on the main // thread during GetOrCreate(). nsCOMPtr mQuotaCallbackSingleton; // 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; nsString mDatabaseBasePath; }; class AutoEnterWindow { public: AutoEnterWindow(nsPIDOMWindow* aWindow) { IndexedDatabaseManager::SetCurrentWindow(aWindow); } ~AutoEnterWindow() { IndexedDatabaseManager::SetCurrentWindow(nsnull); } }; END_INDEXEDDB_NAMESPACE #endif /* mozilla_dom_indexeddb_indexeddatabasemanager_h__ */