void IndexedDatabaseManager::OnDatabaseClosed(IDBDatabase* aDatabase) { NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); NS_ASSERTION(aDatabase, "Null pointer!"); // Check through the list of SetVersionRunnables we have amassed to see if // this database is part of a SetVersion callback. for (PRUint32 index = 0; index < mSetVersionRunnables.Length(); index++) { nsRefPtr<SetVersionRunnable>& runnable = mSetVersionRunnables[index]; if (runnable->mRequestingDatabase->Id() == aDatabase->Id()) { // This is the SetVersionRunnable for the given database file. Remove the // database from the list of databases that need to be closed. Since we // use this hook for SetVersion requests that don't actually need to wait // for other databases the mDatabases array may be empty. if (!runnable->mDatabases.IsEmpty() && !runnable->mDatabases.RemoveElement(aDatabase)) { NS_ERROR("Didn't have this database in our list!"); } // Now run the helper if there are no more live databases. if (runnable->mHelper && runnable->mDatabases.IsEmpty()) { // Don't hold the callback alive longer than necessary. nsRefPtr<AsyncConnectionHelper> helper; helper.swap(runnable->mHelper); if (NS_FAILED(helper->DispatchToTransactionPool())) { NS_WARNING("Failed to dispatch to thread pool!"); } // Now wait for the transaction to complete. Completing the transaction // will be our cue to remove the SetVersionRunnable from our list and // therefore allow other SetVersion requests to begin. TransactionThreadPool* pool = TransactionThreadPool::Get(); NS_ASSERTION(pool, "This should never be null!"); // All other databases should be closed, so we only need to wait on this // one. nsAutoTArray<nsRefPtr<IDBDatabase>, 1> array; if (!array.AppendElement(aDatabase)) { NS_ERROR("This should never fail!"); } // Use the SetVersionRunnable as the callback. if (!pool->WaitForAllDatabasesToComplete(array, runnable)) { NS_WARNING("Failed to wait for transaction to complete!"); } } break; } } }
void IndexedDatabaseManager::OnDatabaseClosed(IDBDatabase* aDatabase) { NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); NS_ASSERTION(aDatabase, "Null pointer!"); // Check through the list of SynchronizedOps to see if any are waiting for // this database to close before proceeding. PRUint32 count = mSynchronizedOps.Length(); for (PRUint32 index = 0; index < count; index++) { nsAutoPtr<SynchronizedOp>& op = mSynchronizedOps[index]; if (op->mOrigin == aDatabase->Origin() && (op->mId == aDatabase->Id() || !op->mId)) { // This database is in the scope of this SynchronizedOp. Remove it // from the list if necessary. if (op->mDatabases.RemoveElement(aDatabase)) { // Now set up the helper if there are no more live databases. NS_ASSERTION(op->mHelper, "How did we get rid of the helper before " "removing the last database?"); if (op->mDatabases.IsEmpty()) { // At this point, all databases are closed, so no new transactions // can be started. There may, however, still be outstanding // transactions that have not completed. We need to wait for those // before we dispatch the helper. TransactionThreadPool* pool = TransactionThreadPool::GetOrCreate(); if (!pool) { NS_ERROR("IndexedDB is totally broken."); return; } nsRefPtr<WaitForTransactionsToFinishRunnable> waitRunnable = new WaitForTransactionsToFinishRunnable(op); nsAutoTArray<nsRefPtr<IDBDatabase>, 1> array; array.AppendElement(aDatabase); // Use the WaitForTransactionsToFinishRunnable as the callback. if (!pool->WaitForAllDatabasesToComplete(array, waitRunnable)) { NS_WARNING("Failed to wait for transaction to complete!"); } } break; } } } }
NS_IMETHODIMP IndexedDatabaseManager::ClearDatabasesForURI(nsIURI* aURI) { NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); NS_ENSURE_ARG_POINTER(aURI); // Figure out which origin we're dealing with. nsCString origin; nsresult rv = nsContentUtils::GetASCIIOrigin(aURI, origin); NS_ENSURE_SUCCESS(rv, rv); // Non-standard URIs can't create databases anyway, so return immediately. if (origin.EqualsLiteral("null")) { return NS_OK; } // If we're already clearing out files for this origin then return // immediately. PRUint32 clearDataCount = mOriginClearRunnables.Length(); for (PRUint32 index = 0; index < clearDataCount; index++) { if (mOriginClearRunnables[index]->mOrigin == origin) { return NS_OK; } } // We need to grab references to any live databases here to prevent them from // dying while we invalidate them. nsTArray<nsRefPtr<IDBDatabase> > liveDatabases; // Grab all live databases for this origin. nsTArray<IDBDatabase*>* array; if (mLiveDatabases.Get(origin, &array)) { if (!liveDatabases.AppendElements(*array)) { NS_WARNING("Out of memory?"); return NS_ERROR_OUT_OF_MEMORY; } } nsRefPtr<OriginClearRunnable> runnable = new OriginClearRunnable(origin, mIOThread); // Make a new entry for this origin in mOriginClearRunnables. nsRefPtr<OriginClearRunnable>* newRunnable = mOriginClearRunnables.AppendElement(runnable); NS_ENSURE_TRUE(newRunnable, NS_ERROR_OUT_OF_MEMORY); if (liveDatabases.IsEmpty()) { rv = runnable->Run(); NS_ENSURE_SUCCESS(rv, rv); return NS_OK; } // Invalidate all the live databases first. for (PRUint32 index = 0; index < liveDatabases.Length(); index++) { liveDatabases[index]->Invalidate(); } // Now set up our callback so that we know when they have finished. TransactionThreadPool* pool = TransactionThreadPool::GetOrCreate(); NS_ENSURE_TRUE(pool, NS_ERROR_FAILURE); if (!pool->WaitForAllDatabasesToComplete(liveDatabases, runnable)) { NS_WARNING("Can't wait on databases!"); return NS_ERROR_FAILURE; } NS_ASSERTION(liveDatabases.IsEmpty(), "Should have swapped!"); return NS_OK; }