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;
}