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-06-02 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           11271 : IndexedDatabaseManager::Get()
     273                 : {
     274                 :   // Does not return an owning reference.
     275           11271 :   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             752 : IndexedDatabaseManager::IsShuttingDown()
     484                 : {
     485             752 :   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           10550 : IndexedDatabaseManager::SetCurrentWindowInternal(nsPIDOMWindow* aWindow)
     596                 : {
     597           10550 :   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           10550 :     PR_SetThreadPrivate(mCurrentWindowIndex, nsnull);
     610                 :   }
     611           10550 : }
     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