LCOV - app.info - dom/indexedDB/IndexedDatabaseManager.cpp

LCOV - code coverage report
| Current view: | directory - dom/indexedDB - IndexedDatabaseManager.cpp (source / functions) |  | Found | Hit | Coverage |
| Test: | app.info | Lines: | 725 | 347 | 47.9 %
| Date: | 2012-04-21 | Functions: | 71 | 45 | 63.4 %

           1                 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
           2                 : /* vim: set ts=2 et sw=2 tw=80: */
           3                 : /* ***** BEGIN LICENSE BLOCK *****
           4                 :  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
           5                 :  *
           6                 :  * The contents of this file are subject to the Mozilla Public License Version
           7                 :  * 1.1 (the "License"); you may not use this file except in compliance with
           8                 :  * the License. You may obtain a copy of the License at
           9                 :  * http://www.mozilla.org/MPL/
          10                 :  *
          11                 :  * Software distributed under the License is distributed on an "AS IS" basis,
          12                 :  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
          13                 :  * for the specific language governing rights and limitations under the
          14                 :  * License.
          15                 :  *
          16                 :  * The Original Code is Indexed Database.
          17                 :  *
          18                 :  * The Initial Developer of the Original Code is
          19                 :  * The Mozilla Foundation.
          20                 :  * Portions created by the Initial Developer are Copyright (C) 2010
          21                 :  * the Initial Developer. All Rights Reserved.
          22                 :  *
          23                 :  * Contributor(s):
          24                 :  *   Ben Turner <bent.mozilla@gmail.com>
          25                 :  *
          26                 :  * Alternatively, the contents of this file may be used under the terms of
          27                 :  * either the GNU General Public License Version 2 or later (the "GPL"), or
          28                 :  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
          29                 :  * in which case the provisions of the GPL or the LGPL are applicable instead
          30                 :  * of those above. If you wish to allow use of your version of this file only
          31                 :  * under the terms of either the GPL or the LGPL, and not to allow others to
          32                 :  * use your version of this file under the terms of the MPL, indicate your
          33                 :  * decision by deleting the provisions above and replace them with the notice
          34                 :  * and other provisions required by the GPL or the LGPL. If you do not delete
          35                 :  * the provisions above, a recipient may use your version of this file under
          36                 :  * the terms of any one of the MPL, the GPL or the LGPL.
          37                 :  *
          38                 :  * ***** END LICENSE BLOCK ***** */
          39                 :
          40                 : #include "IndexedDatabaseManager.h"
          41                 : #include "DatabaseInfo.h"
          42                 :
          43                 : #include "nsIDOMScriptObjectFactory.h"
          44                 : #include "nsIFile.h"
          45                 : #include "nsIObserverService.h"
          46                 : #include "nsIScriptObjectPrincipal.h"
          47                 : #include "nsIScriptSecurityManager.h"
          48                 : #include "nsISHEntry.h"
          49                 : #include "nsISimpleEnumerator.h"
          50                 : #include "nsITimer.h"
          51                 :
          52                 : #include "mozilla/LazyIdleThread.h"
          53                 : #include "mozilla/Preferences.h"
          54                 : #include "mozilla/Services.h"
          55                 : #include "mozilla/storage.h"
          56                 : #include "nsContentUtils.h"
          57                 : #include "nsThreadUtils.h"
          58                 : #include "nsXPCOM.h"
          59                 : #include "nsXPCOMPrivate.h"
          60                 : #include "test_quota.h"
          61                 : #include "xpcpublic.h"
          62                 :
          63                 : #include "AsyncConnectionHelper.h"
          64                 : #include "CheckQuotaHelper.h"
          65                 : #include "IDBDatabase.h"
          66                 : #include "IDBEvents.h"
          67                 : #include "IDBFactory.h"
          68                 : #include "IDBKeyRange.h"
          69                 : #include "OpenDatabaseHelper.h"
          70                 : #include "TransactionThreadPool.h"
          71                 :
          72                 : // The amount of time, in milliseconds, that our IO thread will stay alive
          73                 : // after the last event it processes.
          74                 : #define DEFAULT_THREAD_TIMEOUT_MS 30000
          75                 :
          76                 : // The amount of time, in milliseconds, that we will wait for active database
          77                 : // transactions on shutdown before aborting them.
          78                 : #define DEFAULT_SHUTDOWN_TIMER_MS 30000
          79                 :
          80                 : // Amount of space that IndexedDB databases may use by default in megabytes.
          81                 : #define DEFAULT_QUOTA_MB 50
          82                 :
          83                 : // Preference that users can set to override DEFAULT_QUOTA_MB
          84                 : #define PREF_INDEXEDDB_QUOTA "dom.indexedDB.warningQuota"
          85                 :
          86                 : USING_INDEXEDDB_NAMESPACE
          87                 : using namespace mozilla::services;
          88                 : using mozilla::Preferences;
          89                 :
          90                 : static NS_DEFINE_CID(kDOMSOF_CID, NS_DOM_SCRIPT_OBJECT_FACTORY_CID);
          91                 :
          92                 : namespace {
          93                 :
          94                 : PRInt32 gShutdown = 0;
          95                 : PRInt32 gClosed = 0;
          96                 :
          97                 : // Does not hold a reference.
          98                 : IndexedDatabaseManager* gInstance = nsnull;
          99                 :
         100                 : PRInt32 gIndexedDBQuotaMB = DEFAULT_QUOTA_MB;
         101                 :
         102                 : bool
         103               0 : GetBaseFilename(const nsAString& aFilename,
         104                 :                 nsAString& aBaseFilename)
         105                 : {
         106               0 :   NS_ASSERTION(!aFilename.IsEmpty(), "Bad argument!");
         107                 :
         108               0 :   NS_NAMED_LITERAL_STRING(sqlite, ".sqlite");
         109               0 :   nsAString::size_type filenameLen = aFilename.Length();
         110               0 :   nsAString::size_type sqliteLen = sqlite.Length();
         111                 :
         112               0 :   if (sqliteLen > filenameLen ||
         113               0 :       Substring(aFilename, filenameLen - sqliteLen, sqliteLen) != sqlite) {
         114               0 :     return false;
         115                 :   }
         116                 :
         117               0 :   aBaseFilename = Substring(aFilename, 0, filenameLen - sqliteLen);
         118                 :
         119               0 :   return true;
         120                 : }
         121                 :
         122                 : class QuotaCallback : public mozIStorageQuotaCallback
         123              54 : {
         124                 : public:
         125                 :   NS_DECL_ISUPPORTS
         126                 :
         127                 :   NS_IMETHOD
         128               0 :   QuotaExceeded(const nsACString& aFilename,
         129                 :                 PRInt64 aCurrentSizeLimit,
         130                 :                 PRInt64 aCurrentTotalSize,
         131                 :                 nsISupports* aUserData,
         132                 :                 PRInt64* _retval)
         133                 :   {
         134               0 :     if (IndexedDatabaseManager::QuotaIsLifted()) {
         135               0 :       *_retval = 0;
         136               0 :       return NS_OK;
         137                 :     }
         138                 :
         139               0 :     return NS_ERROR_FAILURE;
         140                 :   }
         141                 : };
         142                 :
         143             574 : NS_IMPL_THREADSAFE_ISUPPORTS1(QuotaCallback, mozIStorageQuotaCallback)
         144                 :
         145                 : // Adds all databases in the hash to the given array.
         146                 : PLDHashOperator
         147               0 : EnumerateToTArray(const nsACString& aKey,
         148                 :                   nsTArray<IDBDatabase*>* aValue,
         149                 :                   void* aUserArg)
         150                 : {
         151               0 :   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
         152               0 :   NS_ASSERTION(!aKey.IsEmpty(), "Empty key!");
         153               0 :   NS_ASSERTION(aValue, "Null pointer!");
         154               0 :   NS_ASSERTION(aUserArg, "Null pointer!");
         155                 :
         156                 :   nsTArray<IDBDatabase*>* array =
         157               0 :     static_cast<nsTArray<IDBDatabase*>*>(aUserArg);
         158                 :
         159               0 :   if (!array->AppendElements(*aValue)) {
         160               0 :     NS_WARNING("Out of memory!");
         161               0 :     return PL_DHASH_STOP;
         162                 :   }
         163                 :
         164               0 :   return PL_DHASH_NEXT;
         165                 : }
         166                 :
         167                 : PLDHashOperator
         168              50 : InvalidateAllFileManagers(const nsACString& aKey,
         169                 :                           nsTArray<nsRefPtr<FileManager> >* aValue,
         170                 :                           void* aUserArg)
         171                 : {
         172              50 :   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
         173              50 :   NS_ASSERTION(!aKey.IsEmpty(), "Empty key!");
         174              50 :   NS_ASSERTION(aValue, "Null pointer!");
         175                 :
         176             102 :   for (PRUint32 i = 0; i < aValue->Length(); i++) {
         177             104 :     nsRefPtr<FileManager> fileManager = aValue->ElementAt(i);
         178              52 :     fileManager->Invalidate();
         179                 :   }
         180                 :
         181              50 :   return PL_DHASH_NEXT;
         182                 : }
         183                 :
         184                 : } // anonymous namespace
         185                 :
         186              54 : IndexedDatabaseManager::IndexedDatabaseManager()
         187                 : : mCurrentWindowIndex(BAD_TLS_INDEX),
         188                 :   mQuotaHelperMutex("IndexedDatabaseManager.mQuotaHelperMutex"),
         189              54 :   mFileMutex("IndexedDatabaseManager.mFileMutex")
         190                 : {
         191              54 :   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
         192              54 :   NS_ASSERTION(!gInstance, "More than one instance!");
         193              54 : }
         194                 :
         195             108 : IndexedDatabaseManager::~IndexedDatabaseManager()
         196                 : {
         197              54 :   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
         198              54 :   NS_ASSERTION(gInstance == this, "Different instances!");
         199              54 :   gInstance = nsnull;
         200              54 : }
         201                 :
         202                 : // static
         203                 : already_AddRefed<IndexedDatabaseManager>
         204             130 : IndexedDatabaseManager::GetOrCreate()
         205                 : {
         206             130 :   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
         207                 :
         208             130 :   if (IsShuttingDown()) {
         209               0 :     NS_ERROR("Calling GetOrCreateInstance() after shutdown!");
         210               0 :     return nsnull;
         211                 :   }
         212                 :
         213             260 :   nsRefPtr<IndexedDatabaseManager> instance(gInstance);
         214                 :
         215             130 :   if (!instance) {
         216              54 :     if (NS_FAILED(Preferences::AddIntVarCache(&gIndexedDBQuotaMB,
         217                 :                                               PREF_INDEXEDDB_QUOTA,
         218                 :                                               DEFAULT_QUOTA_MB))) {
         219               0 :       NS_WARNING("Unable to respond to quota pref changes!");
         220               0 :       gIndexedDBQuotaMB = DEFAULT_QUOTA_MB;
         221                 :     }
         222                 :
         223              54 :     instance = new IndexedDatabaseManager();
         224                 :
         225             162 :     if (!instance->mLiveDatabases.Init() ||
         226              54 :         !instance->mQuotaHelperHash.Init() ||
         227              54 :         !instance->mFileManagers.Init()) {
         228               0 :       NS_WARNING("Out of memory!");
         229               0 :       return nsnull;
         230                 :     }
         231                 :
         232                 :     // We need a thread-local to hold the current window.
         233              54 :     NS_ASSERTION(instance->mCurrentWindowIndex == BAD_TLS_INDEX, "Huh?");
         234                 :
         235              54 :     if (PR_NewThreadPrivateIndex(&instance->mCurrentWindowIndex, nsnull) !=
         236                 :         PR_SUCCESS) {
         237               0 :       NS_ERROR("PR_NewThreadPrivateIndex failed, IndexedDB disabled");
         238               0 :       instance->mCurrentWindowIndex = BAD_TLS_INDEX;
         239               0 :       return nsnull;
         240                 :     }
         241                 :
         242                 :     // Make a timer here to avoid potential failures later. We don't actually
         243                 :     // initialize the timer until shutdown.
         244              54 :     instance->mShutdownTimer = do_CreateInstance(NS_TIMER_CONTRACTID);
         245              54 :     NS_ENSURE_TRUE(instance->mShutdownTimer, nsnull);
         246                 :
         247             108 :     nsCOMPtr<nsIObserverService> obs = GetObserverService();
         248              54 :     NS_ENSURE_TRUE(obs, nsnull);
         249                 :
         250                 :     // We need this callback to know when to shut down all our threads.
         251              54 :     nsresult rv = obs->AddObserver(instance, NS_XPCOM_SHUTDOWN_OBSERVER_ID,
         252              54 :                                    false);
         253              54 :     NS_ENSURE_SUCCESS(rv, nsnull);
         254                 :
         255                 :     // Make a lazy thread for any IO we need (like clearing or enumerating the
         256                 :     // contents of indexedDB database directories).
         257              54 :     instance->mIOThread = new LazyIdleThread(DEFAULT_THREAD_TIMEOUT_MS,
         258             108 :                                              LazyIdleThread::ManualShutdown);
         259                 :
         260                 :     // We need one quota callback object to hand to SQLite.
         261              54 :     instance->mQuotaCallbackSingleton = new QuotaCallback();
         262                 :
         263                 :     // The observer service will hold our last reference, don't AddRef here.
         264             108 :     gInstance = instance;
         265                 :   }
         266                 :
         267             130 :   return instance.forget();
         268                 : }
         269                 :
         270                 : // static
         271                 : IndexedDatabaseManager*
         272           11989 : IndexedDatabaseManager::Get()
         273                 : {
         274                 :   // Does not return an owning reference.
         275           11989 :   return gInstance;
         276                 : }
         277                 :
         278                 : // static
         279                 : IndexedDatabaseManager*
         280              54 : IndexedDatabaseManager::FactoryCreate()
         281                 : {
         282                 :   // Returns a raw pointer that carries an owning reference! Lame, but the
         283                 :   // singleton factory macros force this.
         284              54 :   return GetOrCreate().get();
         285                 : }
         286                 :
         287                 : bool
         288              75 : IndexedDatabaseManager::RegisterDatabase(IDBDatabase* aDatabase)
         289                 : {
         290              75 :   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
         291              75 :   NS_ASSERTION(aDatabase, "Null pointer!");
         292                 :
         293                 :   // Don't allow any new databases to be created after shutdown.
         294              75 :   if (IsShuttingDown()) {
         295               0 :     return false;
         296                 :   }
         297                 :
         298                 :   // Add this database to its origin array if it exists, create it otherwise.
         299                 :   nsTArray<IDBDatabase*>* array;
         300              75 :   if (!mLiveDatabases.Get(aDatabase->Origin(), &array)) {
         301             100 :     nsAutoPtr<nsTArray<IDBDatabase*> > newArray(new nsTArray<IDBDatabase*>());
         302              50 :     if (!mLiveDatabases.Put(aDatabase->Origin(), newArray)) {
         303               0 :       NS_WARNING("Out of memory?");
         304               0 :       return false;
         305                 :     }
         306             100 :     array = newArray.forget();
         307                 :   }
         308              75 :   if (!array->AppendElement(aDatabase)) {
         309               0 :     NS_WARNING("Out of memory?");
         310               0 :     return false;
         311                 :   }
         312                 :
         313              75 :   aDatabase->mRegistered = true;
         314              75 :   return true;
         315                 : }
         316                 :
         317                 : void
         318              12 : IndexedDatabaseManager::UnregisterDatabase(IDBDatabase* aDatabase)
         319                 : {
         320              12 :   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
         321              12 :   NS_ASSERTION(aDatabase, "Null pointer!");
         322                 :
         323                 :   // Remove this database from its origin array, maybe remove the array if it
         324                 :   // is then empty.
         325                 :   nsTArray<IDBDatabase*>* array;
         326              24 :   if (mLiveDatabases.Get(aDatabase->Origin(), &array) &&
         327              12 :       array->RemoveElement(aDatabase)) {
         328              12 :     if (array->IsEmpty()) {
         329               1 :       mLiveDatabases.Remove(aDatabase->Origin());
         330                 :     }
         331              12 :     return;
         332                 :   }
         333               0 :   NS_ERROR("Didn't know anything about this database!");
         334                 : }
         335                 :
         336                 : void
         337               0 : IndexedDatabaseManager::OnUsageCheckComplete(AsyncUsageRunnable* aRunnable)
         338                 : {
         339               0 :   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
         340               0 :   NS_ASSERTION(aRunnable, "Null pointer!");
         341               0 :   NS_ASSERTION(!aRunnable->mURI, "Should have been cleared!");
         342               0 :   NS_ASSERTION(!aRunnable->mCallback, "Should have been cleared!");
         343                 :
         344               0 :   if (!mUsageRunnables.RemoveElement(aRunnable)) {
         345               0 :     NS_ERROR("Don't know anything about this runnable!");
         346                 :   }
         347               0 : }
         348                 :
         349                 : nsresult
         350              84 : IndexedDatabaseManager::WaitForOpenAllowed(const nsACString& aOrigin,
         351                 :                                            nsIAtom* aId,
         352                 :                                            nsIRunnable* aRunnable)
         353                 : {
         354              84 :   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
         355              84 :   NS_ASSERTION(!aOrigin.IsEmpty(), "Empty origin!");
         356              84 :   NS_ASSERTION(aRunnable, "Null pointer!");
         357                 :
         358             168 :   nsAutoPtr<SynchronizedOp> op(new SynchronizedOp(aOrigin, aId));
         359                 :
         360                 :   // See if this runnable needs to wait.
         361              84 :   bool delayed = false;
         362              90 :   for (PRUint32 index = mSynchronizedOps.Length(); index > 0; index--) {
         363              29 :     nsAutoPtr<SynchronizedOp>& existingOp = mSynchronizedOps[index - 1];
         364              29 :     if (op->MustWaitFor(*existingOp)) {
         365              23 :       existingOp->DelayRunnable(aRunnable);
         366              23 :       delayed = true;
         367              23 :       break;
         368                 :     }
         369                 :   }
         370                 :
         371                 :   // Otherwise, dispatch it immediately.
         372              84 :   if (!delayed) {
         373              61 :     nsresult rv = NS_DispatchToCurrentThread(aRunnable);
         374              61 :     NS_ENSURE_SUCCESS(rv, rv);
         375                 :   }
         376                 :
         377                 :   // Adding this to the synchronized ops list will block any additional
         378                 :   // ops from proceeding until this one is done.
         379              84 :   mSynchronizedOps.AppendElement(op.forget());
         380                 :
         381              84 :   return NS_OK;
         382                 : }
         383                 :
         384                 : void
         385              84 : IndexedDatabaseManager::AllowNextSynchronizedOp(const nsACString& aOrigin,
         386                 :                                                 nsIAtom* aId)
         387                 : {
         388              84 :   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
         389              84 :   NS_ASSERTION(!aOrigin.IsEmpty(), "Empty origin!");
         390                 :
         391              84 :   PRUint32 count = mSynchronizedOps.Length();
         392              84 :   for (PRUint32 index = 0; index < count; index++) {
         393              84 :     nsAutoPtr<SynchronizedOp>& op = mSynchronizedOps[index];
         394              84 :     if (op->mOrigin.Equals(aOrigin)) {
         395              84 :       if (op->mId == aId) {
         396              84 :         NS_ASSERTION(op->mDatabases.IsEmpty(), "How did this happen?");
         397                 :
         398              84 :         op->DispatchDelayedRunnables();
         399                 :
         400              84 :         mSynchronizedOps.RemoveElementAt(index);
         401              84 :         return;
         402                 :       }
         403                 :
         404                 :       // If one or the other is for an origin clear, we should have matched
         405                 :       // solely on origin.
         406               0 :       NS_ASSERTION(op->mId && aId, "Why didn't we match earlier?");
         407                 :     }
         408                 :   }
         409                 :
         410               0 :   NS_NOTREACHED("Why didn't we find a SynchronizedOp?");
         411                 : }
         412                 :
         413                 : nsresult
         414              71 : IndexedDatabaseManager::AcquireExclusiveAccess(const nsACString& aOrigin,
         415                 :                                                IDBDatabase* aDatabase,
         416                 :                                                AsyncConnectionHelper* aHelper,
         417                 :                                                WaitingOnDatabasesCallback aCallback,
         418                 :                                                void* aClosure)
         419                 : {
         420              71 :   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
         421              71 :   NS_ASSERTION(aHelper, "Why are you talking to me?");
         422                 :
         423                 :   // Find the right SynchronizedOp.
         424              71 :   SynchronizedOp* op = nsnull;
         425              71 :   PRUint32 count = mSynchronizedOps.Length();
         426              71 :   for (PRUint32 index = 0; index < count; index++) {
         427              71 :     SynchronizedOp* currentop = mSynchronizedOps[index].get();
         428              71 :     if (currentop->mOrigin.Equals(aOrigin)) {
         429             142 :       if (!currentop->mId ||
         430              71 :           (aDatabase && currentop->mId == aDatabase->Id())) {
         431                 :         // We've found the right one.
         432              71 :         NS_ASSERTION(!currentop->mHelper,
         433                 :                      "SynchronizedOp already has a helper?!?");
         434              71 :         op = currentop;
         435              71 :         break;
         436                 :       }
         437                 :     }
         438                 :   }
         439                 :
         440              71 :   NS_ASSERTION(op, "We didn't find a SynchronizedOp?");
         441                 :
         442                 :   nsTArray<IDBDatabase*>* array;
         443              71 :   mLiveDatabases.Get(aOrigin, &array);
         444                 :
         445                 :   // We need to wait for the databases to go away.
         446                 :   // Hold on to all database objects that represent the same database file
         447                 :   // (except the one that is requesting this version change).
         448             142 :   nsTArray<nsRefPtr<IDBDatabase> > liveDatabases;
         449                 :
         450              71 :   if (array) {
         451              71 :     PRUint32 count = array->Length();
         452             178 :     for (PRUint32 index = 0; index < count; index++) {
         453             107 :       IDBDatabase*& database = array->ElementAt(index);
         454             114 :       if (!database->IsClosed() &&
         455                 :           (!aDatabase ||
         456                 :            (aDatabase &&
         457                 :             database != aDatabase &&
         458               7 :             database->Id() == aDatabase->Id()))) {
         459               4 :         liveDatabases.AppendElement(database);
         460                 :       }
         461                 :     }
         462                 :   }
         463                 :
         464              71 :   if (liveDatabases.IsEmpty()) {
         465              68 :     IndexedDatabaseManager::DispatchHelper(aHelper);
         466              68 :     return NS_OK;
         467                 :   }
         468                 :
         469               3 :   NS_ASSERTION(op->mDatabases.IsEmpty(), "How do we already have databases here?");
         470               3 :   op->mDatabases.AppendElements(liveDatabases);
         471               3 :   op->mHelper = aHelper;
         472                 :
         473                 :   // Give our callback the databases so it can decide what to do with them.
         474               3 :   aCallback(liveDatabases, aClosure);
         475                 :
         476               3 :   NS_ASSERTION(liveDatabases.IsEmpty(),
         477                 :                "Should have done something with the array!");
         478               3 :   return NS_OK;
         479                 : }
         480                 :
         481                 : // static
         482                 : bool
         483             932 : IndexedDatabaseManager::IsShuttingDown()
         484                 : {
         485             932 :   return !!gShutdown;
         486                 : }
         487                 :
         488                 : // static
         489                 : bool
         490              52 : IndexedDatabaseManager::IsClosed()
         491                 : {
         492              52 :   return !!gClosed;
         493                 : }
         494                 :
         495                 : void
         496               0 : IndexedDatabaseManager::AbortCloseDatabasesForWindow(nsPIDOMWindow* aWindow)
         497                 : {
         498               0 :   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
         499               0 :   NS_ASSERTION(aWindow, "Null pointer!");
         500                 :
         501               0 :   nsAutoTArray<IDBDatabase*, 50> liveDatabases;
         502               0 :   mLiveDatabases.EnumerateRead(EnumerateToTArray, &liveDatabases);
         503                 :
         504               0 :   TransactionThreadPool* pool = TransactionThreadPool::Get();
         505                 :
         506               0 :   for (PRUint32 index = 0; index < liveDatabases.Length(); index++) {
         507               0 :     IDBDatabase*& database = liveDatabases[index];
         508               0 :     if (database->GetOwner() == aWindow) {
         509               0 :       if (NS_FAILED(database->Close())) {
         510               0 :         NS_WARNING("Failed to close database for dying window!");
         511                 :       }
         512                 :
         513               0 :       if (pool) {
         514               0 :         pool->AbortTransactionsForDatabase(database);
         515                 :       }
         516                 :     }
         517                 :   }
         518               0 : }
         519                 :
         520                 : bool
         521               0 : IndexedDatabaseManager::HasOpenTransactions(nsPIDOMWindow* aWindow)
         522                 : {
         523               0 :   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
         524               0 :   NS_ASSERTION(aWindow, "Null pointer!");
         525                 :
         526               0 :   nsAutoTArray<IDBDatabase*, 50> liveDatabases;
         527               0 :   mLiveDatabases.EnumerateRead(EnumerateToTArray, &liveDatabases);
         528                 :
         529               0 :   TransactionThreadPool* pool = TransactionThreadPool::Get();
         530               0 :   if (!pool) {
         531               0 :     return false;
         532                 :   }
         533                 :
         534               0 :   for (PRUint32 index = 0; index < liveDatabases.Length(); index++) {
         535               0 :     IDBDatabase*& database = liveDatabases[index];
         536               0 :     if (database->GetOwner() == aWindow &&
         537               0 :         pool->HasTransactionsForDatabase(database)) {
         538               0 :       return true;
         539                 :     }
         540                 :   }
         541                 :
         542               0 :   return false;
         543                 : }
         544                 :
         545                 : void
         546              31 : IndexedDatabaseManager::OnDatabaseClosed(IDBDatabase* aDatabase)
         547                 : {
         548              31 :   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
         549              31 :   NS_ASSERTION(aDatabase, "Null pointer!");
         550                 :
         551                 :   // Check through the list of SynchronizedOps to see if any are waiting for
         552                 :   // this database to close before proceeding.
         553              31 :   PRUint32 count = mSynchronizedOps.Length();
         554              61 :   for (PRUint32 index = 0; index < count; index++) {
         555              34 :     nsAutoPtr<SynchronizedOp>& op = mSynchronizedOps[index];
         556                 :
         557              68 :     if (op->mOrigin == aDatabase->Origin() &&
         558              34 :         (op->mId == aDatabase->Id() || !op->mId)) {
         559                 :       // This database is in the scope of this SynchronizedOp.  Remove it
         560                 :       // from the list if necessary.
         561              34 :       if (op->mDatabases.RemoveElement(aDatabase)) {
         562                 :         // Now set up the helper if there are no more live databases.
         563               4 :         NS_ASSERTION(op->mHelper, "How did we get rid of the helper before "
         564                 :                      "removing the last database?");
         565               4 :         if (op->mDatabases.IsEmpty()) {
         566                 :           // At this point, all databases are closed, so no new transactions
         567                 :           // can be started.  There may, however, still be outstanding
         568                 :           // transactions that have not completed.  We need to wait for those
         569                 :           // before we dispatch the helper.
         570                 :
         571               3 :           TransactionThreadPool* pool = TransactionThreadPool::GetOrCreate();
         572               3 :           if (!pool) {
         573               0 :             NS_ERROR("IndexedDB is totally broken.");
         574               0 :             return;
         575                 :           }
         576                 :
         577                 :           nsRefPtr<WaitForTransactionsToFinishRunnable> waitRunnable =
         578               9 :             new WaitForTransactionsToFinishRunnable(op);
         579                 :
         580               6 :           nsAutoTArray<nsRefPtr<IDBDatabase>, 1> array;
         581               3 :           array.AppendElement(aDatabase);
         582                 :
         583                 :           // Use the WaitForTransactionsToFinishRunnable as the callback.
         584               3 :           if (!pool->WaitForAllDatabasesToComplete(array, waitRunnable)) {
         585               0 :             NS_WARNING("Failed to wait for transaction to complete!");
         586                 :           }
         587                 :         }
         588               4 :         break;
         589                 :       }
         590                 :     }
         591                 :   }
         592                 : }
         593                 :
         594                 : void
         595           11268 : IndexedDatabaseManager::SetCurrentWindowInternal(nsPIDOMWindow* aWindow)
         596                 : {
         597           11268 :   if (aWindow) {
         598                 : #ifdef DEBUG
         599               0 :     NS_ASSERTION(!PR_GetThreadPrivate(mCurrentWindowIndex),
         600                 :                  "Somebody forgot to clear the current window!");
         601                 : #endif
         602               0 :     PR_SetThreadPrivate(mCurrentWindowIndex, aWindow);
         603                 :   }
         604                 :   else {
         605                 :     // We cannot assert PR_GetThreadPrivate(mCurrentWindowIndex) here
         606                 :     // because we cannot distinguish between the thread private became
         607                 :     // null and that it was set to null on the first place,
         608                 :     // because we didn't have a window.
         609           11268 :     PR_SetThreadPrivate(mCurrentWindowIndex, nsnull);
         610                 :   }
         611           11268 : }
         612                 :
         613                 : // static
         614                 : PRUint32
         615              50 : IndexedDatabaseManager::GetIndexedDBQuotaMB()
         616                 : {
         617              50 :   return PRUint32(NS_MAX(gIndexedDBQuotaMB, 0));
         618                 : }
         619                 :
         620                 : nsresult
         621              76 : IndexedDatabaseManager::EnsureOriginIsInitialized(const nsACString& aOrigin,
         622                 :                                                   nsIFile** aDirectory)
         623                 : {
         624                 : #ifdef DEBUG
         625                 :   {
         626                 :     bool correctThread;
         627              76 :     NS_ASSERTION(NS_SUCCEEDED(mIOThread->IsOnCurrentThread(&correctThread)) &&
         628                 :                  correctThread,
         629                 :                  "Running on the wrong thread!");
         630                 :   }
         631                 : #endif
         632                 :
         633             152 :   nsCOMPtr<nsIFile> directory;
         634                 :   nsresult rv = IDBFactory::GetDirectoryForOrigin(aOrigin,
         635              76 :                                                   getter_AddRefs(directory));
         636              76 :   NS_ENSURE_SUCCESS(rv, rv);
         637                 :
         638                 :   bool exists;
         639              76 :   rv = directory->Exists(&exists);
         640              76 :   NS_ENSURE_SUCCESS(rv, rv);
         641                 :
         642              76 :   if (exists) {
         643                 :     bool isDirectory;
         644              26 :     rv = directory->IsDirectory(&isDirectory);
         645              26 :     NS_ENSURE_SUCCESS(rv, rv);
         646              26 :     NS_ENSURE_TRUE(isDirectory, NS_ERROR_UNEXPECTED);
         647                 :   }
         648                 :   else {
         649              50 :     rv = directory->Create(nsIFile::DIRECTORY_TYPE, 0755);
         650              50 :     NS_ENSURE_SUCCESS(rv, rv);
         651                 :   }
         652                 :
         653              76 :   if (mFileManagers.Get(aOrigin)) {
         654              26 :     NS_ADDREF(*aDirectory = directory);
         655              26 :     return NS_OK;
         656                 :   }
         657                 :
         658                 :   // First figure out the filename pattern we'll use.
         659             100 :   nsCOMPtr<nsIFile> patternFile;
         660              50 :   rv = directory->Clone(getter_AddRefs(patternFile));
         661              50 :   NS_ENSURE_SUCCESS(rv, rv);
         662                 :
         663              50 :   rv = patternFile->Append(NS_LITERAL_STRING("*"));
         664              50 :   NS_ENSURE_SUCCESS(rv, rv);
         665                 :
         666             100 :   nsCString pattern;
         667              50 :   rv = patternFile->GetNativePath(pattern);
         668              50 :   NS_ENSURE_SUCCESS(rv, rv);
         669                 :
         670                 :   // Now tell SQLite to start tracking this pattern.
         671                 :   nsCOMPtr<mozIStorageServiceQuotaManagement> ss =
         672             100 :     do_GetService(MOZ_STORAGE_SERVICE_CONTRACTID);
         673              50 :   NS_ENSURE_TRUE(ss, NS_ERROR_FAILURE);
         674                 :
         675              50 :   rv = ss->SetQuotaForFilenamePattern(pattern,
         676              50 :                                       GetIndexedDBQuotaMB() * 1024 * 1024,
         677             100 :                                       mQuotaCallbackSingleton, nsnull);
         678              50 :   NS_ENSURE_SUCCESS(rv, rv);
         679                 :
         680                 :   // We need to see if there are any files in the directory already. If they
         681                 :   // are database files then we need to create file managers for them and also
         682                 :   // tell SQLite about all of them.
         683                 :
         684             100 :   nsAutoTArray<nsString, 20> subdirsToProcess;
         685             100 :   nsAutoTArray<nsCOMPtr<nsIFile> , 20> unknownFiles;
         686                 :
         687                 :   nsAutoPtr<nsTArray<nsRefPtr<FileManager> > > fileManagers(
         688             100 :     new nsTArray<nsRefPtr<FileManager> >());
         689                 :
         690             100 :   nsTHashtable<nsStringHashKey> validSubdirs;
         691              50 :   NS_ENSURE_TRUE(validSubdirs.Init(20), NS_ERROR_OUT_OF_MEMORY);
         692                 :
         693             100 :   nsCOMPtr<nsISimpleEnumerator> entries;
         694              50 :   rv = directory->GetDirectoryEntries(getter_AddRefs(entries));
         695              50 :   NS_ENSURE_SUCCESS(rv, rv);
         696                 :
         697                 :   bool hasMore;
         698             100 :   while (NS_SUCCEEDED((rv = entries->HasMoreElements(&hasMore))) && hasMore) {
         699               0 :     nsCOMPtr<nsISupports> entry;
         700               0 :     rv = entries->GetNext(getter_AddRefs(entry));
         701               0 :     NS_ENSURE_SUCCESS(rv, rv);
         702                 :
         703               0 :     nsCOMPtr<nsIFile> file = do_QueryInterface(entry);
         704               0 :     NS_ENSURE_TRUE(file, NS_NOINTERFACE);
         705                 :
         706               0 :     nsString leafName;
         707               0 :     rv = file->GetLeafName(leafName);
         708               0 :     NS_ENSURE_SUCCESS(rv, rv);
         709                 :
         710                 :     bool isDirectory;
         711               0 :     rv = file->IsDirectory(&isDirectory);
         712               0 :     NS_ENSURE_SUCCESS(rv, rv);
         713                 :
         714               0 :     if (isDirectory) {
         715               0 :       if (!validSubdirs.GetEntry(leafName)) {
         716               0 :         subdirsToProcess.AppendElement(leafName);
         717                 :       }
         718               0 :       continue;
         719                 :     }
         720                 :
         721               0 :     nsString dbBaseFilename;
         722               0 :     if (!GetBaseFilename(leafName, dbBaseFilename)) {
         723               0 :       unknownFiles.AppendElement(file);
         724               0 :       continue;
         725                 :     }
         726                 :
         727               0 :     nsCOMPtr<nsIFile> fileManagerDirectory;
         728               0 :     rv = directory->Clone(getter_AddRefs(fileManagerDirectory));
         729               0 :     NS_ENSURE_SUCCESS(rv, rv);
         730                 :
         731               0 :     rv = fileManagerDirectory->Append(dbBaseFilename);
         732               0 :     NS_ENSURE_SUCCESS(rv, rv);
         733                 :
         734               0 :     nsCOMPtr<mozIStorageConnection> connection;
         735                 :     rv = OpenDatabaseHelper::CreateDatabaseConnection(
         736               0 :       NullString(), file, fileManagerDirectory, getter_AddRefs(connection));
         737               0 :     NS_ENSURE_SUCCESS(rv, rv);
         738                 :
         739               0 :     nsCOMPtr<mozIStorageStatement> stmt;
         740               0 :     rv = connection->CreateStatement(NS_LITERAL_CSTRING(
         741                 :       "SELECT name "
         742                 :       "FROM database"
         743               0 :     ), getter_AddRefs(stmt));
         744               0 :     NS_ENSURE_SUCCESS(rv, rv);
         745                 :
         746                 :     bool hasResult;
         747               0 :     rv = stmt->ExecuteStep(&hasResult);
         748               0 :     NS_ENSURE_SUCCESS(rv, rv);
         749                 :
         750               0 :     if (!hasResult) {
         751               0 :       NS_ERROR("Database has no name!");
         752               0 :       return NS_ERROR_UNEXPECTED;
         753                 :     }
         754                 :
         755               0 :     nsString databaseName;
         756               0 :     rv = stmt->GetString(0, databaseName);
         757               0 :     NS_ENSURE_SUCCESS(rv, rv);
         758                 :
         759               0 :     nsRefPtr<FileManager> fileManager = new FileManager(aOrigin, databaseName);
         760                 :
         761               0 :     rv = fileManager->Init(fileManagerDirectory, connection);
         762               0 :     NS_ENSURE_SUCCESS(rv, rv);
         763                 :
         764               0 :     fileManagers->AppendElement(fileManager);
         765                 :
         766               0 :     rv = ss->UpdateQuotaInformationForFile(file);
         767               0 :     NS_ENSURE_SUCCESS(rv, rv);
         768                 :
         769               0 :     if (!validSubdirs.PutEntry(dbBaseFilename)) {
         770               0 :       NS_WARNING("Out of memory?");
         771               0 :       return NS_ERROR_OUT_OF_MEMORY;
         772                 :     }
         773                 :   }
         774              50 :   NS_ENSURE_SUCCESS(rv, rv);
         775                 :
         776              50 :   for (PRUint32 i = 0; i < subdirsToProcess.Length(); i++) {
         777               0 :     const nsString& subdir = subdirsToProcess[i];
         778               0 :     if (!validSubdirs.GetEntry(subdir)) {
         779               0 :       NS_WARNING("Unknown subdirectory found!");
         780               0 :       return NS_ERROR_UNEXPECTED;
         781                 :     }
         782                 :   }
         783                 :
         784              50 :   for (PRUint32 i = 0; i < unknownFiles.Length(); i++) {
         785               0 :     nsCOMPtr<nsIFile>& unknownFile = unknownFiles[i];
         786                 :
         787                 :     // Some temporary SQLite files could disappear, so we have to check if the
         788                 :     // unknown file still exists.
         789                 :     bool exists;
         790               0 :     rv = unknownFile->Exists(&exists);
         791               0 :     NS_ENSURE_SUCCESS(rv, rv);
         792                 :
         793               0 :     if (exists) {
         794               0 :       nsString leafName;
         795               0 :       unknownFile->GetLeafName(leafName);
         796                 :
         797                 :       // The journal file may exists even after db has been correctly opened.
         798               0 :       if (!StringEndsWith(leafName, NS_LITERAL_STRING(".sqlite-journal"))) {
         799               0 :         NS_WARNING("Unknown file found!");
         800               0 :         return NS_ERROR_UNEXPECTED;
         801                 :       }
         802                 :     }
         803                 :   }
         804                 :
         805              50 :   if (!mFileManagers.Put(aOrigin, fileManagers)) {
         806               0 :     NS_WARNING("Out of memory?");
         807               0 :     return NS_ERROR_OUT_OF_MEMORY;
         808                 :   }
         809                 :
         810              50 :   fileManagers.forget();
         811                 :
         812              50 :   NS_ADDREF(*aDirectory = directory);
         813              50 :   return NS_OK;
         814                 : }
         815                 :
         816                 : bool
         817               0 : IndexedDatabaseManager::QuotaIsLiftedInternal()
         818                 : {
         819               0 :   nsPIDOMWindow* window = nsnull;
         820               0 :   nsRefPtr<CheckQuotaHelper> helper = nsnull;
         821               0 :   bool createdHelper = false;
         822                 :
         823                 :   window =
         824               0 :     static_cast<nsPIDOMWindow*>(PR_GetThreadPrivate(mCurrentWindowIndex));
         825                 :
         826                 :   // Once IDB is supported outside of Windows this should become an early
         827                 :   // return true.
         828               0 :   NS_ASSERTION(window, "Why don't we have a Window here?");
         829                 :
         830                 :   // Hold the lock from here on.
         831               0 :   MutexAutoLock autoLock(mQuotaHelperMutex);
         832                 :
         833               0 :   mQuotaHelperHash.Get(window, getter_AddRefs(helper));
         834                 :
         835               0 :   if (!helper) {
         836               0 :     helper = new CheckQuotaHelper(window, mQuotaHelperMutex);
         837               0 :     createdHelper = true;
         838                 :
         839               0 :     bool result = mQuotaHelperHash.Put(window, helper);
         840               0 :     NS_ENSURE_TRUE(result, result);
         841                 :
         842                 :     // Unlock while calling out to XPCOM
         843                 :     {
         844               0 :       MutexAutoUnlock autoUnlock(mQuotaHelperMutex);
         845                 :
         846               0 :       nsresult rv = NS_DispatchToMainThread(helper);
         847               0 :       NS_ENSURE_SUCCESS(rv, false);
         848                 :     }
         849                 :
         850                 :     // Relocked.  If any other threads hit the quota limit on the same Window,
         851                 :     // they are using the helper we created here and are now blocking in
         852                 :     // PromptAndReturnQuotaDisabled.
         853                 :   }
         854                 :
         855               0 :   bool result = helper->PromptAndReturnQuotaIsDisabled();
         856                 :
         857                 :   // If this thread created the helper and added it to the hash, this thread
         858                 :   // must remove it.
         859               0 :   if (createdHelper) {
         860               0 :     mQuotaHelperHash.Remove(window);
         861                 :   }
         862                 :
         863               0 :   return result;
         864                 : }
         865                 :
         866                 : void
         867               0 : IndexedDatabaseManager::CancelPromptsForWindowInternal(nsPIDOMWindow* aWindow)
         868                 : {
         869               0 :   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
         870                 :
         871               0 :   nsRefPtr<CheckQuotaHelper> helper;
         872                 :
         873               0 :   MutexAutoLock autoLock(mQuotaHelperMutex);
         874                 :
         875               0 :   mQuotaHelperHash.Get(aWindow, getter_AddRefs(helper));
         876                 :
         877               0 :   if (helper) {
         878               0 :     helper->Cancel();
         879                 :   }
         880               0 : }
         881                 :
         882                 : // static
         883                 : nsresult
         884              76 : IndexedDatabaseManager::GetASCIIOriginFromWindow(nsPIDOMWindow* aWindow,
         885                 :                                                  nsCString& aASCIIOrigin)
         886                 : {
         887              76 :   NS_ASSERTION(NS_IsMainThread(),
         888                 :                "We're about to touch a window off the main thread!");
         889                 :
         890              76 :   if (!aWindow) {
         891              76 :     aASCIIOrigin.AssignLiteral("chrome");
         892              76 :     NS_ASSERTION(nsContentUtils::IsCallerChrome(),
         893                 :                  "Null window but not chrome!");
         894              76 :     return NS_OK;
         895                 :   }
         896                 :
         897               0 :   nsCOMPtr<nsIScriptObjectPrincipal> sop = do_QueryInterface(aWindow);
         898               0 :   NS_ENSURE_TRUE(sop, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
         899                 :
         900               0 :   nsCOMPtr<nsIPrincipal> principal = sop->GetPrincipal();
         901               0 :   NS_ENSURE_TRUE(principal, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
         902                 :
         903               0 :   if (nsContentUtils::IsSystemPrincipal(principal)) {
         904               0 :     aASCIIOrigin.AssignLiteral("chrome");
         905                 :   }
         906                 :   else {
         907               0 :     nsresult rv = nsContentUtils::GetASCIIOrigin(principal, aASCIIOrigin);
         908               0 :     NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
         909                 :
         910               0 :     if (aASCIIOrigin.EqualsLiteral("null")) {
         911               0 :       NS_WARNING("IndexedDB databases not allowed for this principal!");
         912               0 :       return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
         913                 :     }
         914                 :   }
         915                 :
         916               0 :   return NS_OK;
         917                 : }
         918                 :
         919                 : already_AddRefed<FileManager>
         920              75 : IndexedDatabaseManager::GetOrCreateFileManager(const nsACString& aOrigin,
         921                 :                                                const nsAString& aDatabaseName)
         922                 : {
         923                 :   nsTArray<nsRefPtr<FileManager> >* array;
         924              75 :   if (!mFileManagers.Get(aOrigin, &array)) {
         925                 :     nsAutoPtr<nsTArray<nsRefPtr<FileManager> > > newArray(
         926               0 :       new nsTArray<nsRefPtr<FileManager> >());
         927               0 :     if (!mFileManagers.Put(aOrigin, newArray)) {
         928               0 :       NS_WARNING("Out of memory?");
         929               0 :       return nsnull;
         930                 :     }
         931               0 :     array = newArray.forget();
         932                 :   }
         933                 :
         934             150 :   nsRefPtr<FileManager> fileManager;
         935              78 :   for (PRUint32 i = 0; i < array->Length(); i++) {
         936              52 :     nsRefPtr<FileManager> fm = array->ElementAt(i);
         937                 :
         938              26 :     if (fm->DatabaseName().Equals(aDatabaseName)) {
         939              23 :       fileManager = fm.forget();
         940                 :       break;
         941                 :     }
         942                 :   }
         943                 :
         944              75 :   if (!fileManager) {
         945              52 :     fileManager = new FileManager(aOrigin, aDatabaseName);
         946                 :
         947              52 :     array->AppendElement(fileManager);
         948                 :   }
         949                 :
         950              75 :   return fileManager.forget();
         951                 : }
         952                 :
         953                 : already_AddRefed<FileManager>
         954               0 : IndexedDatabaseManager::GetFileManager(const nsACString& aOrigin,
         955                 :                                        const nsAString& aDatabaseName)
         956                 : {
         957                 :   nsTArray<nsRefPtr<FileManager> >* array;
         958               0 :   if (!mFileManagers.Get(aOrigin, &array)) {
         959               0 :     return nsnull;
         960                 :   }
         961                 :
         962               0 :   for (PRUint32 i = 0; i < array->Length(); i++) {
         963               0 :     nsRefPtr<FileManager>& fileManager = array->ElementAt(i);
         964                 :
         965               0 :     if (fileManager->DatabaseName().Equals(aDatabaseName)) {
         966               0 :       nsRefPtr<FileManager> result = fileManager;
         967               0 :       return result.forget();
         968                 :     }
         969                 :   }
         970                 :
         971               0 :   return nsnull;
         972                 : }
         973                 :
         974                 : void
         975               8 : IndexedDatabaseManager::InvalidateFileManagersForOrigin(
         976                 :                                                      const nsACString& aOrigin)
         977                 : {
         978                 :   nsTArray<nsRefPtr<FileManager> >* array;
         979               8 :   if (mFileManagers.Get(aOrigin, &array)) {
         980               0 :     for (PRUint32 i = 0; i < array->Length(); i++) {
         981               0 :       nsRefPtr<FileManager> fileManager = array->ElementAt(i);
         982               0 :       fileManager->Invalidate();
         983                 :     }
         984               0 :     mFileManagers.Remove(aOrigin);
         985                 :   }
         986               8 : }
         987                 :
         988                 : void
         989               0 : IndexedDatabaseManager::InvalidateFileManager(const nsACString& aOrigin,
         990                 :                                               const nsAString& aDatabaseName)
         991                 : {
         992                 :   nsTArray<nsRefPtr<FileManager> >* array;
         993               0 :   if (!mFileManagers.Get(aOrigin, &array)) {
         994               0 :     return;
         995                 :   }
         996                 :
         997               0 :   for (PRUint32 i = 0; i < array->Length(); i++) {
         998               0 :     nsRefPtr<FileManager> fileManager = array->ElementAt(i);
         999               0 :     if (fileManager->DatabaseName().Equals(aDatabaseName)) {
        1000               0 :       fileManager->Invalidate();
        1001               0 :       array->RemoveElementAt(i);
        1002                 :
        1003               0 :       if (array->IsEmpty()) {
        1004               0 :         mFileManagers.Remove(aOrigin);
        1005                 :       }
        1006                 :
        1007                 :       break;
        1008                 :     }
        1009                 :   }
        1010                 : }
        1011                 :
        1012                 : nsresult
        1013               0 : IndexedDatabaseManager::AsyncDeleteFile(FileManager* aFileManager,
        1014                 :                                         PRInt64 aFileId)
        1015                 : {
        1016               0 :   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
        1017                 :
        1018               0 :   NS_ENSURE_ARG_POINTER(aFileManager);
        1019                 :
        1020                 :   // See if we're currently clearing the databases for this origin. If so then
        1021                 :   // we pretend that we've already deleted everything.
        1022               0 :   if (IsClearOriginPending(aFileManager->Origin())) {
        1023               0 :     return NS_OK;
        1024                 :   }
        1025                 :
        1026               0 :   nsCOMPtr<nsIFile> directory = aFileManager->GetDirectory();
        1027               0 :   NS_ENSURE_TRUE(directory, NS_ERROR_FAILURE);
        1028                 :
        1029               0 :   nsCOMPtr<nsIFile> file = aFileManager->GetFileForId(directory, aFileId);
        1030               0 :   NS_ENSURE_TRUE(file, NS_ERROR_FAILURE);
        1031                 :
        1032               0 :   nsString filePath;
        1033               0 :   nsresult rv = file->GetPath(filePath);
        1034               0 :   NS_ENSURE_SUCCESS(rv, rv);
        1035                 :
        1036                 :   nsRefPtr<AsyncDeleteFileRunnable> runnable =
        1037               0 :     new AsyncDeleteFileRunnable(filePath);
        1038                 :
        1039               0 :   rv = mIOThread->Dispatch(runnable, NS_DISPATCH_NORMAL);
        1040               0 :   NS_ENSURE_SUCCESS(rv, rv);
        1041                 :
        1042               0 :   return NS_OK;
        1043                 : }
        1044                 :
        1045                 : // static
        1046                 : nsresult
        1047              71 : IndexedDatabaseManager::DispatchHelper(AsyncConnectionHelper* aHelper)
        1048                 : {
        1049              71 :   nsresult rv = NS_OK;
        1050                 :
        1051                 :   // If the helper has a transaction, dispatch it to the transaction
        1052                 :   // threadpool.
        1053              71 :   if (aHelper->HasTransaction()) {
        1054              71 :     rv = aHelper->DispatchToTransactionPool();
        1055                 :   }
        1056                 :   else {
        1057                 :     // Otherwise, dispatch it to the IO thread.
        1058               0 :     IndexedDatabaseManager* manager = IndexedDatabaseManager::Get();
        1059               0 :     NS_ASSERTION(manager, "We should definitely have a manager here");
        1060                 :
        1061               0 :     rv = aHelper->Dispatch(manager->IOThread());
        1062                 :   }
        1063                 :
        1064              71 :   NS_ENSURE_SUCCESS(rv, rv);
        1065              71 :   return rv;
        1066                 : }
        1067                 :
        1068                 : bool
        1069              96 : IndexedDatabaseManager::IsClearOriginPending(const nsACString& origin)
        1070                 : {
        1071                 :   // Iterate through our SynchronizedOps to see if we have an entry that matches
        1072                 :   // this origin and has no id.
        1073              96 :   PRUint32 count = mSynchronizedOps.Length();
        1074             144 :   for (PRUint32 index = 0; index < count; index++) {
        1075             136 :     nsAutoPtr<SynchronizedOp>& op = mSynchronizedOps[index];
        1076             136 :     if (op->mOrigin.Equals(origin) && !op->mId) {
        1077              88 :       return true;
        1078                 :     }
        1079                 :   }
        1080                 :
        1081               8 :   return false;
        1082                 : }
        1083                 :
        1084            2168 : NS_IMPL_ISUPPORTS2(IndexedDatabaseManager, nsIIndexedDatabaseManager,
        1085                 :                                            nsIObserver)
        1086                 :
        1087                 : NS_IMETHODIMP
        1088               0 : IndexedDatabaseManager::GetUsageForURI(
        1089                 :                                      nsIURI* aURI,
        1090                 :                                      nsIIndexedDatabaseUsageCallback* aCallback)
        1091                 : {
        1092               0 :   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
        1093                 :
        1094               0 :   NS_ENSURE_ARG_POINTER(aURI);
        1095               0 :   NS_ENSURE_ARG_POINTER(aCallback);
        1096                 :
        1097                 :   // Figure out which origin we're dealing with.
        1098               0 :   nsCString origin;
        1099               0 :   nsresult rv = nsContentUtils::GetASCIIOrigin(aURI, origin);
        1100               0 :   NS_ENSURE_SUCCESS(rv, rv);
        1101                 :
        1102                 :   nsRefPtr<AsyncUsageRunnable> runnable =
        1103               0 :     new AsyncUsageRunnable(aURI, origin, aCallback);
        1104                 :
        1105                 :   nsRefPtr<AsyncUsageRunnable>* newRunnable =
        1106               0 :     mUsageRunnables.AppendElement(runnable);
        1107               0 :   NS_ENSURE_TRUE(newRunnable, NS_ERROR_OUT_OF_MEMORY);
        1108                 :
        1109                 :   // Non-standard URIs can't create databases anyway so fire the callback
        1110                 :   // immediately.
        1111               0 :   if (origin.EqualsLiteral("null")) {
        1112               0 :     rv = NS_DispatchToCurrentThread(runnable);
        1113               0 :     NS_ENSURE_SUCCESS(rv, rv);
        1114               0 :     return NS_OK;
        1115                 :   }
        1116                 :
        1117                 :   // See if we're currently clearing the databases for this origin. If so then
        1118                 :   // we pretend that we've already deleted everything.
        1119               0 :   if (IsClearOriginPending(origin)) {
        1120               0 :     rv = NS_DispatchToCurrentThread(runnable);
        1121               0 :     NS_ENSURE_SUCCESS(rv, rv);
        1122               0 :     return NS_OK;
        1123                 :   }
        1124                 :
        1125                 :   // Otherwise dispatch to the IO thread to actually compute the usage.
        1126               0 :   rv = mIOThread->Dispatch(runnable, NS_DISPATCH_NORMAL);
        1127               0 :   NS_ENSURE_SUCCESS(rv, rv);
        1128                 :
        1129               0 :   return NS_OK;
        1130                 : }
        1131                 :
        1132                 : NS_IMETHODIMP
        1133               0 : IndexedDatabaseManager::CancelGetUsageForURI(
        1134                 :                                      nsIURI* aURI,
        1135                 :                                      nsIIndexedDatabaseUsageCallback* aCallback)
        1136                 : {
        1137               0 :   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
        1138                 :
        1139               0 :   NS_ENSURE_ARG_POINTER(aURI);
        1140               0 :   NS_ENSURE_ARG_POINTER(aCallback);
        1141                 :
        1142                 :   // See if one of our pending callbacks matches both the URI and the callback
        1143                 :   // given. Cancel an remove it if so.
        1144               0 :   for (PRUint32 index = 0; index < mUsageRunnables.Length(); index++) {
        1145               0 :     nsRefPtr<AsyncUsageRunnable>& runnable = mUsageRunnables[index];
        1146                 :
        1147                 :     bool equals;
        1148               0 :     nsresult rv = runnable->mURI->Equals(aURI, &equals);
        1149               0 :     NS_ENSURE_SUCCESS(rv, rv);
        1150                 :
        1151               0 :     if (equals && SameCOMIdentity(aCallback, runnable->mCallback)) {
        1152               0 :       runnable->Cancel();
        1153               0 :       break;
        1154                 :     }
        1155                 :   }
        1156               0 :   return NS_OK;
        1157                 : }
        1158                 :
        1159                 : NS_IMETHODIMP
        1160              96 : IndexedDatabaseManager::ClearDatabasesForURI(nsIURI* aURI)
        1161                 : {
        1162              96 :   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
        1163                 :
        1164              96 :   NS_ENSURE_ARG_POINTER(aURI);
        1165                 :
        1166                 :   // Figure out which origin we're dealing with.
        1167             192 :   nsCString origin;
        1168              96 :   nsresult rv = nsContentUtils::GetASCIIOrigin(aURI, origin);
        1169              96 :   NS_ENSURE_SUCCESS(rv, rv);
        1170                 :
        1171                 :   // Non-standard URIs can't create databases anyway, so return immediately.
        1172              96 :   if (origin.EqualsLiteral("null")) {
        1173               0 :     return NS_OK;
        1174                 :   }
        1175                 :
        1176                 :   // If there is a pending or running clear operation for this origin, return
        1177                 :   // immediately.
        1178              96 :   if (IsClearOriginPending(origin)) {
        1179              88 :     return NS_OK;
        1180                 :   }
        1181                 :
        1182                 :   // Queue up the origin clear runnable.
        1183                 :   nsRefPtr<OriginClearRunnable> runnable =
        1184              24 :     new OriginClearRunnable(origin, mIOThread);
        1185                 :
        1186               8 :   rv = WaitForOpenAllowed(origin, nsnull, runnable);
        1187               8 :   NS_ENSURE_SUCCESS(rv, rv);
        1188                 :
        1189                 :   // Give the runnable some help by invalidating any databases in the way.
        1190                 :   // We need to grab references to any live databases here to prevent them from
        1191                 :   // dying while we invalidate them.
        1192              16 :   nsTArray<nsRefPtr<IDBDatabase> > liveDatabases;
        1193                 :
        1194                 :   // Grab all live databases for this origin.
        1195                 :   nsTArray<IDBDatabase*>* array;
        1196               8 :   if (mLiveDatabases.Get(origin, &array)) {
        1197               0 :     liveDatabases.AppendElements(*array);
        1198                 :   }
        1199                 :
        1200                 :   // Invalidate all the live databases first.
        1201               8 :   for (PRUint32 index = 0; index < liveDatabases.Length(); index++) {
        1202               0 :     liveDatabases[index]->Invalidate();
        1203                 :   }
        1204                 :
        1205               8 :   DatabaseInfo::RemoveAllForOrigin(origin);
        1206                 :
        1207                 :   // After everything has been invalidated the helper should be dispatched to
        1208                 :   // the end of the event queue.
        1209                 :
        1210               8 :   return NS_OK;
        1211                 : }
        1212                 :
        1213                 : NS_IMETHODIMP
        1214              54 : IndexedDatabaseManager::Observe(nsISupports* aSubject,
        1215                 :                                 const char* aTopic,
        1216                 :                                 const PRUnichar* aData)
        1217                 : {
        1218              54 :   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
        1219                 :
        1220              54 :   if (!strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) {
        1221                 :     // Setting this flag prevents the service from being recreated and prevents
        1222                 :     // further databases from being created.
        1223              54 :     if (PR_ATOMIC_SET(&gShutdown, 1)) {
        1224               0 :       NS_ERROR("Shutdown more than once?!");
        1225                 :     }
        1226                 :
        1227                 :     // Make sure to join with our IO thread.
        1228              54 :     if (NS_FAILED(mIOThread->Shutdown())) {
        1229               0 :       NS_WARNING("Failed to shutdown IO thread!");
        1230                 :     }
        1231                 :
        1232                 :     // Kick off the shutdown timer.
        1233              54 :     if (NS_FAILED(mShutdownTimer->Init(this, DEFAULT_SHUTDOWN_TIMER_MS,
        1234                 :                                        nsITimer::TYPE_ONE_SHOT))) {
        1235               0 :       NS_WARNING("Failed to initialize shutdown timer!");
        1236                 :     }
        1237                 :
        1238                 :     // This will spin the event loop while we wait on all the database threads
        1239                 :     // to close. Our timer may fire during that loop.
        1240              54 :     TransactionThreadPool::Shutdown();
        1241                 :
        1242                 :     // Cancel the timer regardless of whether it actually fired.
        1243              54 :     if (NS_FAILED(mShutdownTimer->Cancel())) {
        1244               0 :       NS_WARNING("Failed to cancel shutdown timer!");
        1245                 :     }
        1246                 :
        1247              54 :     mFileManagers.EnumerateRead(InvalidateAllFileManagers, nsnull);
        1248                 :
        1249              54 :     if (PR_ATOMIC_SET(&gClosed, 1)) {
        1250               0 :       NS_ERROR("Close more than once?!");
        1251                 :     }
        1252                 :
        1253              54 :     return NS_OK;
        1254                 :   }
        1255                 :
        1256               0 :   if (!strcmp(aTopic, NS_TIMER_CALLBACK_TOPIC)) {
        1257                 :     NS_WARNING("Some database operations are taking longer than expected "
        1258               0 :                "during shutdown and will be aborted!");
        1259                 :
        1260                 :     // Grab all live databases, for all origins.
        1261               0 :     nsAutoTArray<IDBDatabase*, 50> liveDatabases;
        1262               0 :     mLiveDatabases.EnumerateRead(EnumerateToTArray, &liveDatabases);
        1263                 :
        1264                 :     // Invalidate them all.
        1265               0 :     if (!liveDatabases.IsEmpty()) {
        1266               0 :       PRUint32 count = liveDatabases.Length();
        1267               0 :       for (PRUint32 index = 0; index < count; index++) {
        1268               0 :         liveDatabases[index]->Invalidate();
        1269                 :       }
        1270                 :     }
        1271                 :
        1272               0 :     return NS_OK;
        1273                 :   }
        1274                 :
        1275               0 :   NS_NOTREACHED("Unknown topic!");
        1276               0 :   return NS_ERROR_UNEXPECTED;
        1277                 : }
        1278                 :
        1279             144 : NS_IMPL_THREADSAFE_ISUPPORTS1(IndexedDatabaseManager::OriginClearRunnable,
        1280                 :                               nsIRunnable)
        1281                 :
        1282                 : NS_IMETHODIMP
        1283              24 : IndexedDatabaseManager::OriginClearRunnable::Run()
        1284                 : {
        1285              24 :   if (NS_IsMainThread()) {
        1286                 :     // On the first time on the main thread we dispatch to the IO thread.
        1287              16 :     if (mFirstCallback) {
        1288               8 :       NS_ASSERTION(mThread, "Should have a thread here!");
        1289                 :
        1290               8 :       mFirstCallback = false;
        1291                 :
        1292              16 :       nsCOMPtr<nsIThread> thread;
        1293               8 :       mThread.swap(thread);
        1294                 :
        1295                 :       // Dispatch to the IO thread.
        1296               8 :       if (NS_FAILED(thread->Dispatch(this, NS_DISPATCH_NORMAL))) {
        1297               0 :         NS_WARNING("Failed to dispatch to IO thread!");
        1298               0 :         return NS_ERROR_FAILURE;
        1299                 :       }
        1300                 :
        1301               8 :       return NS_OK;
        1302                 :     }
        1303                 :
        1304               8 :     NS_ASSERTION(!mThread, "Should have been cleared already!");
        1305                 :
        1306               8 :     IndexedDatabaseManager* mgr = IndexedDatabaseManager::Get();
        1307               8 :     NS_ASSERTION(mgr, "This should never fail!");
        1308                 :
        1309               8 :     mgr->InvalidateFileManagersForOrigin(mOrigin);
        1310                 :
        1311                 :     // Tell the IndexedDatabaseManager that we're done.
        1312               8 :     mgr->AllowNextSynchronizedOp(mOrigin, nsnull);
        1313                 :
        1314               8 :     return NS_OK;
        1315                 :   }
        1316                 :
        1317               8 :   NS_ASSERTION(!mThread, "Should have been cleared already!");
        1318                 :
        1319                 :   // Remove the directory that contains all our databases.
        1320              16 :   nsCOMPtr<nsIFile> directory;
        1321                 :   nsresult rv = IDBFactory::GetDirectoryForOrigin(mOrigin,
        1322               8 :                                                   getter_AddRefs(directory));
        1323               8 :   if (NS_SUCCEEDED(rv)) {
        1324                 :     bool exists;
        1325               8 :     rv = directory->Exists(&exists);
        1326               8 :     if (NS_SUCCEEDED(rv) && exists) {
        1327               0 :       rv = directory->Remove(true);
        1328                 :     }
        1329                 :   }
        1330               8 :   NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "Failed to remove directory!");
        1331                 :
        1332                 :   // Switch back to the main thread to complete the sequence.
        1333               8 :   rv = NS_DispatchToMainThread(this, NS_DISPATCH_NORMAL);
        1334               8 :   NS_ENSURE_SUCCESS(rv, rv);
        1335                 :
        1336               8 :   return NS_OK;
        1337                 : }
        1338                 :
        1339               0 : IndexedDatabaseManager::AsyncUsageRunnable::AsyncUsageRunnable(
        1340                 :                                      nsIURI* aURI,
        1341                 :                                      const nsACString& aOrigin,
        1342                 :                                      nsIIndexedDatabaseUsageCallback* aCallback)
        1343                 : : mURI(aURI),
        1344                 :   mOrigin(aOrigin),
        1345                 :   mCallback(aCallback),
        1346                 :   mUsage(0),
        1347                 :   mFileUsage(0),
        1348               0 :   mCanceled(0)
        1349                 : {
        1350               0 :   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
        1351               0 :   NS_ASSERTION(aURI, "Null pointer!");
        1352               0 :   NS_ASSERTION(!aOrigin.IsEmpty(), "Empty origin!");
        1353               0 :   NS_ASSERTION(aCallback, "Null pointer!");
        1354               0 : }
        1355                 :
        1356                 : void
        1357               0 : IndexedDatabaseManager::AsyncUsageRunnable::Cancel()
        1358                 : {
        1359               0 :   if (PR_ATOMIC_SET(&mCanceled, 1)) {
        1360               0 :     NS_ERROR("Canceled more than once?!");
        1361                 :   }
        1362               0 : }
        1363                 :
        1364                 : inline void
        1365               0 : IncrementUsage(PRUint64* aUsage, PRUint64 aDelta)
        1366                 : {
        1367                 :   // Watch for overflow!
        1368               0 :   if ((LL_MAXINT - *aUsage) <= aDelta) {
        1369               0 :     NS_WARNING("Database sizes exceed max we can report!");
        1370               0 :     *aUsage = LL_MAXINT;
        1371                 :   }
        1372                 :   else {
        1373               0 :     *aUsage += aDelta;
        1374                 :   }
        1375               0 : }
        1376                 :
        1377                 : nsresult
        1378               0 : IndexedDatabaseManager::AsyncUsageRunnable::RunInternal()
        1379                 : {
        1380               0 :   if (NS_IsMainThread()) {
        1381                 :     // Call the callback unless we were canceled.
        1382               0 :     if (!mCanceled) {
        1383               0 :       PRUint64 usage = mUsage;
        1384               0 :       IncrementUsage(&usage, mFileUsage);
        1385               0 :       mCallback->OnUsageResult(mURI, usage, mFileUsage);
        1386                 :     }
        1387                 :
        1388                 :     // Clean up.
        1389               0 :     mURI = nsnull;
        1390               0 :     mCallback = nsnull;
        1391                 :
        1392                 :     // And tell the IndexedDatabaseManager that we're done.
        1393               0 :     IndexedDatabaseManager* mgr = IndexedDatabaseManager::Get();
        1394               0 :     if (mgr) {
        1395               0 :       mgr->OnUsageCheckComplete(this);
        1396                 :     }
        1397                 :
        1398               0 :     return NS_OK;
        1399                 :   }
        1400                 :
        1401               0 :   if (mCanceled) {
        1402               0 :     return NS_OK;
        1403                 :   }
        1404                 :
        1405                 :   // Get the directory that contains all the database files we care about.
        1406               0 :   nsCOMPtr<nsIFile> directory;
        1407                 :   nsresult rv = IDBFactory::GetDirectoryForOrigin(mOrigin,
        1408               0 :                                                   getter_AddRefs(directory));
        1409               0 :   NS_ENSURE_SUCCESS(rv, rv);
        1410                 :
        1411                 :   bool exists;
        1412               0 :   rv = directory->Exists(&exists);
        1413               0 :   NS_ENSURE_SUCCESS(rv, rv);
        1414                 :
        1415                 :   // If the directory exists then enumerate all the files inside, adding up the
        1416                 :   // sizes to get the final usage statistic.
        1417               0 :   if (exists && !mCanceled) {
        1418               0 :     rv = GetUsageForDirectory(directory, &mUsage);
        1419               0 :     NS_ENSURE_SUCCESS(rv, rv);
        1420                 :   }
        1421               0 :   return NS_OK;
        1422                 : }
        1423                 :
        1424                 : nsresult
        1425               0 : IndexedDatabaseManager::AsyncUsageRunnable::GetUsageForDirectory(
        1426                 :                                      nsIFile* aDirectory,
        1427                 :                                      PRUint64* aUsage)
        1428                 : {
        1429               0 :   NS_ASSERTION(aDirectory, "Null pointer!");
        1430               0 :   NS_ASSERTION(aUsage, "Null pointer!");
        1431                 :
        1432               0 :   nsCOMPtr<nsISimpleEnumerator> entries;
        1433               0 :   nsresult rv = aDirectory->GetDirectoryEntries(getter_AddRefs(entries));
        1434               0 :   NS_ENSURE_SUCCESS(rv, rv);
        1435                 :
        1436               0 :   if (!entries) {
        1437               0 :     return NS_OK;
        1438                 :   }
        1439                 :
        1440                 :   bool hasMore;
        1441               0 :   while (NS_SUCCEEDED((rv = entries->HasMoreElements(&hasMore))) &&
        1442               0 :          hasMore && !mCanceled) {
        1443               0 :     nsCOMPtr<nsISupports> entry;
        1444               0 :     rv = entries->GetNext(getter_AddRefs(entry));
        1445               0 :     NS_ENSURE_SUCCESS(rv, rv);
        1446                 :
        1447               0 :     nsCOMPtr<nsIFile> file(do_QueryInterface(entry));
        1448               0 :     NS_ASSERTION(file, "Don't know what this is!");
        1449                 :
        1450                 :     bool isDirectory;
        1451               0 :     rv = file->IsDirectory(&isDirectory);
        1452               0 :     NS_ENSURE_SUCCESS(rv, rv);
        1453                 :
        1454               0 :     if (isDirectory) {
        1455               0 :       if (aUsage == &mFileUsage) {
        1456               0 :         NS_WARNING("Unknown directory found!");
        1457                 :       }
        1458                 :       else {
        1459               0 :         rv = GetUsageForDirectory(file, &mFileUsage);
        1460               0 :         NS_ENSURE_SUCCESS(rv, rv);
        1461                 :       }
        1462                 :
        1463               0 :       continue;
        1464                 :     }
        1465                 :
        1466                 :     PRInt64 fileSize;
        1467               0 :     rv = file->GetFileSize(&fileSize);
        1468               0 :     NS_ENSURE_SUCCESS(rv, rv);
        1469                 :
        1470               0 :     NS_ASSERTION(fileSize > 0, "Negative size?!");
        1471                 :
        1472               0 :     IncrementUsage(aUsage, PRUint64(fileSize));
        1473                 :   }
        1474               0 :   NS_ENSURE_SUCCESS(rv, rv);
        1475                 :
        1476               0 :   return NS_OK;
        1477                 : }
        1478                 :
        1479               0 : NS_IMPL_THREADSAFE_ISUPPORTS1(IndexedDatabaseManager::AsyncUsageRunnable,
        1480                 :                               nsIRunnable)
        1481                 :
        1482                 : NS_IMETHODIMP
        1483               0 : IndexedDatabaseManager::AsyncUsageRunnable::Run()
        1484                 : {
        1485               0 :   nsresult rv = RunInternal();
        1486                 :
        1487               0 :   if (!NS_IsMainThread()) {
        1488               0 :     if (NS_FAILED(rv)) {
        1489               0 :       mUsage = 0;
        1490                 :     }
        1491                 :
        1492               0 :     if (NS_FAILED(NS_DispatchToMainThread(this, NS_DISPATCH_NORMAL))) {
        1493               0 :       NS_WARNING("Failed to dispatch to main thread!");
        1494                 :     }
        1495                 :   }
        1496                 :
        1497               0 :   NS_ENSURE_SUCCESS(rv, rv);
        1498               0 :   return NS_OK;
        1499                 : }
        1500                 :
        1501              24 : NS_IMPL_THREADSAFE_ISUPPORTS1(IndexedDatabaseManager::WaitForTransactionsToFinishRunnable,
        1502                 :                               nsIRunnable)
        1503                 :
        1504                 : NS_IMETHODIMP
        1505               3 : IndexedDatabaseManager::WaitForTransactionsToFinishRunnable::Run()
        1506                 : {
        1507               3 :   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
        1508               3 :   NS_ASSERTION(mOp && mOp->mHelper, "What?");
        1509                 :
        1510                 :   // Don't hold the callback alive longer than necessary.
        1511               6 :   nsRefPtr<AsyncConnectionHelper> helper;
        1512               3 :   helper.swap(mOp->mHelper);
        1513                 :
        1514               3 :   mOp = nsnull;
        1515                 :
        1516               3 :   IndexedDatabaseManager::DispatchHelper(helper);
        1517                 :
        1518                 :   // The helper is responsible for calling
        1519                 :   // IndexedDatabaseManager::AllowNextSynchronizedOp.
        1520                 :
        1521               3 :   return NS_OK;
        1522                 : }
        1523                 :
        1524                 :
        1525              84 : IndexedDatabaseManager::SynchronizedOp::SynchronizedOp(const nsACString& aOrigin,
        1526                 :                                                        nsIAtom* aId)
        1527              84 : : mOrigin(aOrigin), mId(aId)
        1528                 : {
        1529              84 :   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
        1530              84 :   MOZ_COUNT_CTOR(IndexedDatabaseManager::SynchronizedOp);
        1531              84 : }
        1532                 :
        1533             168 : IndexedDatabaseManager::SynchronizedOp::~SynchronizedOp()
        1534                 : {
        1535              84 :   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
        1536              84 :   MOZ_COUNT_DTOR(IndexedDatabaseManager::SynchronizedOp);
        1537              84 : }
        1538                 :
        1539                 : bool
        1540              29 : IndexedDatabaseManager::SynchronizedOp::MustWaitFor(const SynchronizedOp& aRhs)
        1541                 :   const
        1542                 : {
        1543              29 :   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
        1544                 :
        1545                 :   // If the origins don't match, the second can proceed.
        1546              29 :   if (!aRhs.mOrigin.Equals(mOrigin)) {
        1547               4 :     return false;
        1548                 :   }
        1549                 :
        1550                 :   // If the origins and the ids match, the second must wait.
        1551              25 :   if (aRhs.mId == mId) {
        1552              23 :     return true;
        1553                 :   }
        1554                 :
        1555                 :   // Waiting is required if either one corresponds to an origin clearing
        1556                 :   // (a null Id).
        1557               2 :   if (!aRhs.mId || !mId) {
        1558               0 :     return true;
        1559                 :   }
        1560                 :
        1561                 :   // Otherwise, things for the same origin but different databases can proceed
        1562                 :   // independently.
        1563               2 :   return false;
        1564                 : }
        1565                 :
        1566                 : void
        1567              23 : IndexedDatabaseManager::SynchronizedOp::DelayRunnable(nsIRunnable* aRunnable)
        1568                 : {
        1569              23 :   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
        1570              23 :   NS_ASSERTION(mDelayedRunnables.IsEmpty() || !mId,
        1571                 :                "Only ClearOrigin operations can delay multiple runnables!");
        1572                 :
        1573              23 :   mDelayedRunnables.AppendElement(aRunnable);
        1574              23 : }
        1575                 :
        1576                 : void
        1577              84 : IndexedDatabaseManager::SynchronizedOp::DispatchDelayedRunnables()
        1578                 : {
        1579              84 :   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
        1580              84 :   NS_ASSERTION(!mHelper, "Any helper should be gone by now!");
        1581                 :
        1582              84 :   PRUint32 count = mDelayedRunnables.Length();
        1583             107 :   for (PRUint32 index = 0; index < count; index++) {
        1584              23 :     NS_DispatchToCurrentThread(mDelayedRunnables[index]);
        1585                 :   }
        1586                 :
        1587              84 :   mDelayedRunnables.Clear();
        1588              84 : }
        1589                 :
        1590                 : NS_IMETHODIMP
        1591              50 : IndexedDatabaseManager::InitWindowless(const jsval& aObj, JSContext* aCx)
        1592                 : {
        1593              50 :   NS_ENSURE_TRUE(nsContentUtils::IsCallerChrome(), NS_ERROR_NOT_AVAILABLE);
        1594              50 :   NS_ENSURE_ARG(!JSVAL_IS_PRIMITIVE(aObj));
        1595                 :
        1596                 :   // Instantiating this class will register exception providers so even
        1597                 :   // in xpcshell we will get typed (dom) exceptions, instead of general
        1598                 :   // exceptions.
        1599             100 :   nsCOMPtr<nsIDOMScriptObjectFactory> sof(do_GetService(kDOMSOF_CID));
        1600                 :
        1601              50 :   JSObject* obj = JSVAL_TO_OBJECT(aObj);
        1602                 :
        1603              50 :   JSObject* global = JS_GetGlobalForObject(aCx, obj);
        1604                 :
        1605             100 :   nsCOMPtr<nsIIDBFactory> factory = IDBFactory::Create(aCx, global);
        1606              50 :   NS_ENSURE_TRUE(factory, NS_ERROR_FAILURE);
        1607                 :
        1608                 :   jsval mozIndexedDBVal;
        1609              50 :   nsresult rv = nsContentUtils::WrapNative(aCx, obj, factory, &mozIndexedDBVal);
        1610              50 :   NS_ENSURE_SUCCESS(rv, rv);
        1611                 :
        1612              50 :   if (!JS_DefineProperty(aCx, obj, "mozIndexedDB", mozIndexedDBVal, nsnull,
        1613              50 :                          nsnull, JSPROP_ENUMERATE)) {
        1614               0 :     return NS_ERROR_FAILURE;
        1615                 :   }
        1616                 :
        1617              50 :   JSObject* keyrangeObj = JS_NewObject(aCx, nsnull, nsnull, nsnull);
        1618              50 :   NS_ENSURE_TRUE(keyrangeObj, NS_ERROR_OUT_OF_MEMORY);
        1619                 :
        1620              50 :   if (!IDBKeyRange::DefineConstructors(aCx, keyrangeObj)) {
        1621               0 :     return NS_ERROR_FAILURE;
        1622                 :   }
        1623                 :
        1624              50 :   if (!JS_DefineProperty(aCx, obj, "IDBKeyRange", OBJECT_TO_JSVAL(keyrangeObj),
        1625              50 :                          nsnull, nsnull, JSPROP_ENUMERATE)) {
        1626               0 :     return NS_ERROR_FAILURE;
        1627                 :   }
        1628                 :
        1629              50 :   return NS_OK;
        1630                 : }
        1631                 :
        1632               0 : NS_IMPL_THREADSAFE_ISUPPORTS1(IndexedDatabaseManager::AsyncDeleteFileRunnable,
        1633                 :                               nsIRunnable)
        1634                 :
        1635                 : NS_IMETHODIMP
        1636               0 : IndexedDatabaseManager::AsyncDeleteFileRunnable::Run()
        1637                 : {
        1638               0 :   NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
        1639                 :
        1640               0 :   int rc = sqlite3_quota_remove(NS_ConvertUTF16toUTF8(mFilePath).get());
        1641               0 :   if (rc != SQLITE_OK) {
        1642               0 :     NS_WARNING("Failed to delete stored file!");
        1643               0 :     return NS_ERROR_FAILURE;
        1644                 :   }
        1645                 :
        1646               0 :   return NS_OK;
        1647                 : }

Generated by: LCOV version 1.7