Move to global mainthread metadata storage for databases and object stores
authorBen Turner <bent.mozilla@gmail.com>
Sun, 23 May 2010 23:54:39 -0700
changeset 44018 4e86c9f33706a40d2818943ddc5771de2a305c74
parent 44017 9c5cc89881a73d87d13f1cd56abba1f3e0357a63
child 44019 56aee677be8b98b8fa5825f7880b7aa8232d9c76
push id1
push userroot
push dateTue, 26 Apr 2011 22:38:44 +0000
treeherdermozilla-beta@bfdb6e623a36 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone1.9.3a5pre
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
Move to global mainthread metadata storage for databases and object stores
dom/indexedDB/DatabaseInfo.cpp
dom/indexedDB/DatabaseInfo.h
dom/indexedDB/IDBDatabaseRequest.cpp
dom/indexedDB/IDBDatabaseRequest.h
dom/indexedDB/IDBObjectStoreRequest.cpp
dom/indexedDB/IDBObjectStoreRequest.h
dom/indexedDB/IDBTransactionRequest.cpp
dom/indexedDB/IDBTransactionRequest.h
dom/indexedDB/IndexedDatabase.h
dom/indexedDB/IndexedDatabaseRequest.cpp
dom/indexedDB/Makefile.in
dom/indexedDB/test/Makefile.in
dom/indexedDB/test/test_global_data.html
layout/build/nsLayoutStatics.cpp
new file mode 100644
--- /dev/null
+++ b/dom/indexedDB/DatabaseInfo.cpp
@@ -0,0 +1,233 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Indexed Database.
+ *
+ * The Initial Developer of the Original Code is
+ * The Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Ben Turner <bent.mozilla@gmail.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include "DatabaseInfo.h"
+
+#include "nsClassHashtable.h"
+#include "nsHashKeys.h"
+#include "nsThreadUtils.h"
+
+USING_INDEXEDDB_NAMESPACE
+
+namespace {
+
+typedef nsClassHashtable<nsStringHashKey, ObjectStoreInfo>
+        ObjectStoreInfoHash;
+
+struct DatabaseInfoHash
+{
+  DatabaseInfoHash(DatabaseInfo* aInfo) {
+    NS_ASSERTION(aInfo, "Null pointer!");
+    info = aInfo;
+  }
+
+  nsAutoPtr<DatabaseInfo> info;
+  nsAutoPtr<ObjectStoreInfoHash> objectStoreHash;
+};
+
+typedef nsClassHashtable<nsUint32HashKey, DatabaseInfoHash>
+        DatabaseHash;
+
+DatabaseHash* gDatabaseHash = nsnull;
+bool gShutdown = false;
+
+}
+
+// static
+bool
+DatabaseInfo::Get(PRUint32 aId,
+                  DatabaseInfo** aInfo)
+{
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+  NS_ASSERTION(aId, "Bad id!");
+
+  if (gDatabaseHash) {
+    DatabaseInfoHash* hash;
+    if (gDatabaseHash->Get(aId, &hash)) {
+      if (aInfo) {
+        *aInfo = hash->info;
+      }
+      return true;
+    }
+  }
+  return false;
+}
+
+// static
+bool
+DatabaseInfo::Put(DatabaseInfo* aInfo)
+{
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+  NS_ASSERTION(aInfo, "Null pointer!");
+
+  NS_ENSURE_FALSE(gShutdown, false);
+
+  if (!gDatabaseHash) {
+    gDatabaseHash = new DatabaseHash();
+    if (!gDatabaseHash->Init()) {
+      NS_ERROR("Failed to initialize hashtable!");
+      return false;
+    }
+  }
+
+  if (gDatabaseHash->Get(aInfo->id, nsnull)) {
+    NS_ERROR("Already know about this database!");
+    return false;
+  }
+
+  nsAutoPtr<DatabaseInfoHash> hash(new DatabaseInfoHash(aInfo));
+  if (!gDatabaseHash->Put(aInfo->id, hash)) {
+    NS_ERROR("Put failed!");
+    return false;
+  }
+
+  hash.forget();
+  return true;
+}
+
+// static
+void
+DatabaseInfo::Remove(PRUint32 aId)
+{
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+  NS_ASSERTION(Get(aId, nsnull), "Don't know anything about this one!");
+
+  if (gDatabaseHash) {
+    gDatabaseHash->Remove(aId);
+  }
+}
+
+// static
+bool
+ObjectStoreInfo::Get(PRUint32 aDatabaseId,
+                     const nsAString& aName,
+                     ObjectStoreInfo** aInfo)
+{
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+  NS_ASSERTION(!aName.IsEmpty(), "Empty object store name!");
+
+  if (gDatabaseHash) {
+    DatabaseInfoHash* hash;
+    if (gDatabaseHash->Get(aDatabaseId, &hash)) {
+      if (hash->objectStoreHash) {
+        return !!hash->objectStoreHash->Get(aName, aInfo);
+      }
+    }
+  }
+
+  return false;
+}
+
+// static
+bool
+ObjectStoreInfo::Put(ObjectStoreInfo* aInfo)
+{
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+  NS_ASSERTION(aInfo, "Null pointer!");
+
+  NS_ENSURE_FALSE(gShutdown, false);
+
+  if (!gDatabaseHash) {
+    NS_ERROR("No databases known!");
+    return false;
+  }
+
+  DatabaseInfoHash* hash;
+  if (!gDatabaseHash->Get(aInfo->databaseId, &hash)) {
+    NS_ERROR("Don't know about this database!");
+    return false;
+  }
+
+  if (!hash->objectStoreHash) {
+    hash->objectStoreHash = new ObjectStoreInfoHash();
+    if (!hash->objectStoreHash->Init()) {
+      NS_ERROR("Failed to initialize hashtable!");
+      return false;
+    }
+  }
+
+  if (hash->objectStoreHash->Get(aInfo->name, nsnull)) {
+    NS_ERROR("Already have an entry for this objectstore!");
+    return false;
+  }
+
+  bool ok = !!hash->objectStoreHash->Put(aInfo->name, aInfo);
+  if (ok && !hash->info->objectStoreNames.AppendElement(aInfo->name)) {
+    NS_ERROR("Out of memory!");
+  }
+  return ok;
+}
+
+// static
+void
+ObjectStoreInfo::Remove(PRUint32 aDatabaseId,
+                        const nsAString& aName)
+{
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+  NS_ASSERTION(Get(aDatabaseId, aName, nsnull), "Don't know about this one!");
+
+  if (gDatabaseHash) {
+    DatabaseInfoHash* hash;
+    if (gDatabaseHash->Get(aDatabaseId, &hash)) {
+      if (hash->objectStoreHash) {
+        hash->objectStoreHash->Remove(aName);
+      }
+      hash->info->objectStoreNames.RemoveElement(aName);
+    }
+  }
+}
+
+// static
+void
+ObjectStoreInfo::RemoveAllForDatabase(PRUint32 aDatabaseId)
+{
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+
+  DatabaseInfo::Remove(aDatabaseId);
+}
+
+void
+mozilla::dom::indexedDB::Shutdown()
+{
+  NS_ASSERTION(!gShutdown, "Shutdown called twice!");
+  gShutdown = true;
+
+  // Kill the hash
+  delete gDatabaseHash;
+}
new file mode 100644
--- /dev/null
+++ b/dom/indexedDB/DatabaseInfo.h
@@ -0,0 +1,95 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Indexed Database.
+ *
+ * The Initial Developer of the Original Code is
+ * The Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Ben Turner <bent.mozilla@gmail.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef mozilla_dom_indexeddb_databaseinfo_h__
+#define mozilla_dom_indexeddb_databaseinfo_h__
+
+// Only meant to be included in IndexedDB source files, not exported.
+#include "IndexedDatabase.h"
+
+BEGIN_INDEXEDDB_NAMESPACE
+
+struct DatabaseInfo
+{
+  nsString name;
+  nsString description;
+  nsString version;
+  PRUint32 id;
+  nsString filePath;
+  nsTArray<nsString> objectStoreNames;
+  nsAutoRefCnt referenceCount;
+
+  DatabaseInfo()
+  : id(0) { }
+
+  static bool Get(PRUint32 aId,
+                  DatabaseInfo** aInfo);
+
+  static bool Put(DatabaseInfo* aInfo);
+
+  static void Remove(PRUint32 aId);
+};
+
+struct ObjectStoreInfo
+{
+  nsString name;
+  PRInt64 id;
+  nsString keyPath;
+  bool autoIncrement;
+  PRUint32 databaseId;
+
+  ObjectStoreInfo()
+  : id(0), autoIncrement(false), databaseId(0) { }
+
+  static bool Get(PRUint32 aDatabaseId,
+                  const nsAString& aName,
+                  ObjectStoreInfo** aInfo);
+
+  static bool Put(ObjectStoreInfo* aInfo);
+
+  static void Remove(PRUint32 aDatabaseId,
+                     const nsAString& aName);
+
+  static void RemoveAllForDatabase(PRUint32 aDatabaseId);
+};
+
+END_INDEXEDDB_NAMESPACE
+
+#endif // mozilla_dom_indexeddb_databaseinfo_h__
+
--- a/dom/indexedDB/IDBDatabaseRequest.cpp
+++ b/dom/indexedDB/IDBDatabaseRequest.cpp
@@ -42,16 +42,17 @@
 #include "nsIIDBDatabaseException.h"
 #include "nsIIDBTransactionRequest.h"
 
 #include "mozilla/Storage.h"
 #include "nsDOMClassInfo.h"
 #include "nsThreadUtils.h"
 
 #include "AsyncConnectionHelper.h"
+#include "DatabaseInfo.h"
 #include "IDBEvents.h"
 #include "IDBObjectStoreRequest.h"
 #include "IDBTransactionRequest.h"
 #include "IndexedDatabaseRequest.h"
 #include "LazyIdleThread.h"
 
 USING_INDEXEDDB_NAMESPACE
 
@@ -116,69 +117,48 @@ class CreateObjectStoreHelper : public A
 {
 public:
   CreateObjectStoreHelper(IDBDatabaseRequest* aDatabase,
                           IDBRequest* aRequest,
                           const nsAString& aName,
                           const nsAString& aKeyPath,
                           bool aAutoIncrement)
   : AsyncConnectionHelper(aDatabase, aRequest), mName(aName),
-    mKeyPath(aKeyPath), mAutoIncrement(aAutoIncrement), mId(LL_MININT)
+    mKeyPath(aKeyPath), mAutoIncrement(aAutoIncrement),
+    mId(LL_MININT)
   { }
 
   PRUint16 DoDatabaseWork(mozIStorageConnection* aConnection);
   PRUint16 OnSuccess(nsIDOMEventTarget* aTarget);
 
 protected:
   // In-params.
   nsString mName;
   nsString mKeyPath;
   bool mAutoIncrement;
 
   // Out-params.
   PRInt64 mId;
 };
 
-#if 0
-class OpenObjectStoreHelper : public CreateObjectStoreHelper
-{
-public:
-  OpenObjectStoreHelper(IDBDatabaseRequest* aDatabase,
-                        IDBRequest* aRequest,
-                        const nsAString& aName,
-                        PRUint16 aMode)
-  : CreateObjectStoreHelper(aDatabase, aRequest, aName, EmptyString(), false),
-    mMode(aMode)
-  { }
-
-  PRUint16 DoDatabaseWork(mozIStorageConnection* aConnection);
-  PRUint16 OnSuccess(nsIDOMEventTarget* aTarget);
-  PRUint16 GetSuccessResult(nsIWritableVariant* aResult);
-
-protected:
-  // In-params.
-  PRUint16 mMode;
-};
-#endif
-
 class RemoveObjectStoreHelper : public AsyncConnectionHelper
 {
 public:
   RemoveObjectStoreHelper(IDBDatabaseRequest* aDatabase,
                           IDBRequest* aRequest,
-                          const ObjectStoreInfo& aStore)
-  : AsyncConnectionHelper(aDatabase, aRequest), mStore(aStore)
+                          const nsAString& aName)
+  : AsyncConnectionHelper(aDatabase, aRequest), mName(aName)
   { }
 
   PRUint16 DoDatabaseWork(mozIStorageConnection* aConnection);
   PRUint16 GetSuccessResult(nsIWritableVariant* aResult);
 
 private:
   // In-params.
-  ObjectStoreInfo mStore;
+  nsString mName;
 };
 
 class AutoFree
 {
 public:
   AutoFree(void* aPtr) : mPtr(aPtr) { }
   ~AutoFree() { NS_Free(mPtr); }
 private:
@@ -253,67 +233,69 @@ ConvertVariantToStringArray(nsIVariant* 
 
   return NS_OK;
 }
 
 } // anonymous namespace
 
 // static
 already_AddRefed<IDBDatabaseRequest>
-IDBDatabaseRequest::Create(const nsAString& aName,
-                           const nsAString& aDescription,
-                           nsTArray<ObjectStoreInfo>& aObjectStores,
-                           const nsAString& aVersion,
+IDBDatabaseRequest::Create(DatabaseInfo* aDatabaseInfo,
                            LazyIdleThread* aThread,
-                           const nsAString& aDatabaseFilePath,
                            nsCOMPtr<mozIStorageConnection>& aConnection)
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+  NS_ASSERTION(aDatabaseInfo, "Null pointer!");
+  NS_ASSERTION(aThread, "Null pointer!");
+  NS_ASSERTION(aConnection, "Null pointer!");
 
   nsRefPtr<IDBDatabaseRequest> db(new IDBDatabaseRequest());
 
-  db->mName.Assign(aName);
-  db->mDescription.Assign(aDescription);
-  db->mObjectStores.SwapElements(aObjectStores);
-  db->mVersion.Assign(aVersion);
-  db->mDatabaseFilePath.Assign(aDatabaseFilePath);
+  db->mDatabaseInfo = aDatabaseInfo;
 
   aThread->SetWeakIdleObserver(db);
   db->mConnectionThread = aThread;
 
   db->mConnection.swap(aConnection);
 
   return db.forget();
 }
 
 IDBDatabaseRequest::IDBDatabaseRequest()
+: mDatabaseInfo(nsnull)
 {
 
 }
 
 IDBDatabaseRequest::~IDBDatabaseRequest()
 {
   mConnectionThread->SetWeakIdleObserver(nsnull);
   FireCloseConnectionRunnable();
+
+  if (mDatabaseInfo &&
+      --mDatabaseInfo->referenceCount == 0) {
+    DatabaseInfo::Remove(mDatabaseInfo->id);
+  }
 }
 
 nsCOMPtr<mozIStorageConnection>&
 IDBDatabaseRequest::Connection()
 {
   NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
   return mConnection;
 }
 
 nsresult
 IDBDatabaseRequest::EnsureConnection()
 {
   NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
 
   if (!mConnection) {
-    mConnection = IndexedDatabaseRequest::GetConnection(mDatabaseFilePath);
+    mConnection =
+      IndexedDatabaseRequest::GetConnection(mDatabaseInfo->filePath);
     NS_ENSURE_TRUE(mConnection, NS_ERROR_FAILURE);
   }
 
   return NS_OK;
 }
 
 already_AddRefed<mozIStorageStatement>
 IDBDatabaseRequest::AddStatement(bool aCreate,
@@ -495,43 +477,20 @@ IDBDatabaseRequest::FireCloseConnectionR
   doomedObjects.AppendElement(do_QIAndNull(mGetAutoIncrementStmt));
 
   nsCOMPtr<nsIRunnable> runnable(new CloseConnectionRunnable(doomedObjects));
   mConnectionThread->Dispatch(runnable, NS_DISPATCH_NORMAL);
 
   NS_ASSERTION(doomedObjects.Length() == 0, "Should have swapped!");
 }
 
-void
-IDBDatabaseRequest::OnVersionSet(const nsString& aVersion)
-{
-  mVersion = aVersion;
-}
-
-void
-IDBDatabaseRequest::OnObjectStoreCreated(const ObjectStoreInfo& aInfo)
+PRUint32
+IDBDatabaseRequest::Id()
 {
-  NS_ASSERTION(!mObjectStores.Contains(aInfo), "Already know about this one!");
-  if (!mObjectStores.AppendElement(aInfo)) {
-    NS_ERROR("Failed to add object store name! OOM?");
-  }
-}
-
-void
-IDBDatabaseRequest::OnObjectStoreRemoved(const ObjectStoreInfo& aInfo)
-{
-  NS_ASSERTION(mObjectStores.Contains(aInfo), "Didn't know about this one!");
-  mObjectStores.RemoveElement(aInfo);
-}
-
-nsresult
-IDBDatabaseRequest::QueueDatabaseWork(nsIRunnable* aRunnable)
-{
-  return mPendingDatabaseWork.AppendElement(aRunnable) ? NS_OK :
-                                                         NS_ERROR_OUT_OF_MEMORY;
+  return mDatabaseInfo->id;
 }
 
 NS_IMPL_ADDREF(IDBDatabaseRequest)
 NS_IMPL_RELEASE(IDBDatabaseRequest)
 
 NS_INTERFACE_MAP_BEGIN(IDBDatabaseRequest)
   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIIDBDatabaseRequest)
   NS_INTERFACE_MAP_ENTRY(nsIIDBDatabaseRequest)
@@ -541,49 +500,48 @@ NS_INTERFACE_MAP_BEGIN(IDBDatabaseReques
 NS_INTERFACE_MAP_END
 
 DOMCI_DATA(IDBDatabaseRequest, IDBDatabaseRequest)
 
 NS_IMETHODIMP
 IDBDatabaseRequest::GetName(nsAString& aName)
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
-
-  aName.Assign(mName);
+  aName.Assign(mDatabaseInfo->name);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 IDBDatabaseRequest::GetDescription(nsAString& aDescription)
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
-  aDescription.Assign(mDescription);
+  aDescription.Assign(mDatabaseInfo->description);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 IDBDatabaseRequest::GetVersion(nsAString& aVersion)
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
-
-  aVersion.Assign(mVersion);
+  aVersion.Assign(mDatabaseInfo->version);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 IDBDatabaseRequest::GetObjectStoreNames(nsIDOMDOMStringList** aObjectStores)
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
+  nsTArray<nsString>& objectStoreNames = mDatabaseInfo->objectStoreNames;
+
   nsRefPtr<nsDOMStringList> list(new nsDOMStringList());
-  PRUint32 count = mObjectStores.Length();
+  PRUint32 count = objectStoreNames.Length();
   for (PRUint32 index = 0; index < count; index++) {
-    NS_ENSURE_TRUE(list->Add(mObjectStores[index].name),
-                   NS_ERROR_OUT_OF_MEMORY);
+    NS_ENSURE_TRUE(list->Add(objectStoreNames[index]), NS_ERROR_OUT_OF_MEMORY);
   }
 
   list.forget(aObjectStores);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 IDBDatabaseRequest::CreateObjectStore(const nsAString& aName,
@@ -601,73 +559,71 @@ IDBDatabaseRequest::CreateObjectStore(co
   nsString keyPath(aKeyPath);
   if (keyPath.IsVoid()) {
     keyPath.Truncate();
   }
 
   nsRefPtr<IDBRequest> request = GenerateRequest();
   NS_ENSURE_TRUE(request, NS_ERROR_FAILURE);
 
+  nsTArray<nsString>& objectStoreNames = mDatabaseInfo->objectStoreNames;
+
   bool exists = false;
-  PRUint32 count = mObjectStores.Length();
+  PRUint32 count = objectStoreNames.Length();
   for (PRUint32 index = 0; index < count; index++) {
-    if (mObjectStores[index].name.Equals(aName)) {
+    if (objectStoreNames[index] == aName) {
       exists = true;
       break;
     }
   }
 
-  nsresult rv;
-  if (NS_UNLIKELY(exists)) {
-    nsCOMPtr<nsIRunnable> runnable =
-      IDBErrorEvent::CreateRunnable(request,
-                                    nsIIDBDatabaseException::CONSTRAINT_ERR);
-    NS_ENSURE_TRUE(runnable, NS_ERROR_UNEXPECTED);
-    rv = NS_DispatchToCurrentThread(runnable);
+  if (exists) {
+    return NS_ERROR_ALREADY_INITIALIZED;
   }
-  else {
-    nsRefPtr<CreateObjectStoreHelper> helper =
-      new CreateObjectStoreHelper(this, request, aName, keyPath,
-                                  !!aAutoIncrement);
-    rv = helper->Dispatch(mConnectionThread);
-  }
+
+  nsRefPtr<CreateObjectStoreHelper> helper =
+    new CreateObjectStoreHelper(this, request, aName, keyPath,
+                                !!aAutoIncrement);
+  nsresult rv = helper->Dispatch(mConnectionThread);
   NS_ENSURE_SUCCESS(rv, rv);
 
   request.forget(_retval);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 IDBDatabaseRequest::RemoveObjectStore(const nsAString& aName,
                                       nsIIDBRequest** _retval)
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
   if (aName.IsEmpty()) {
     return NS_ERROR_INVALID_ARG;
   }
 
+  nsTArray<nsString>& objectStoreNames = mDatabaseInfo->objectStoreNames;
+
+  bool exists = false;
+  PRUint32 count = objectStoreNames.Length();
+  for (PRUint32 index = 0; index < count; index++) {
+    if (objectStoreNames[index] == aName) {
+      exists = true;
+      break;
+    }
+  }
+
+  if (!exists) {
+    return NS_ERROR_NOT_AVAILABLE;
+  }
+
   nsRefPtr<IDBRequest> request = GenerateRequest();
 
-  ObjectStoreInfo* info;
-  bool exists = ObjectStoreInfoForName(aName, &info);
-
-  nsresult rv;
-  if (NS_UNLIKELY(!exists)) {
-    nsCOMPtr<nsIRunnable> runnable =
-      IDBErrorEvent::CreateRunnable(request,
-                                    nsIIDBDatabaseException::NOT_FOUND_ERR);
-    NS_ENSURE_TRUE(runnable, NS_ERROR_UNEXPECTED);
-    rv = NS_DispatchToCurrentThread(runnable);
-  }
-  else {
-    nsRefPtr<RemoveObjectStoreHelper> helper =
-      new RemoveObjectStoreHelper(this, request, *info);
-    rv = helper->Dispatch(mConnectionThread);
-  }
+  nsRefPtr<RemoveObjectStoreHelper> helper =
+    new RemoveObjectStoreHelper(this, request, aName);
+  nsresult rv = helper->Dispatch(mConnectionThread);
   NS_ENSURE_SUCCESS(rv, rv);
 
   request.forget(_retval);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 IDBDatabaseRequest::SetVersion(const nsAString& aVersion,
@@ -709,40 +665,41 @@ IDBDatabaseRequest::Transaction(nsIVaria
   if (aOptionalArgCount <= 1) {
     aTimeout = kDefaultDatabaseTimeoutSeconds;
   }
 
   PRUint16 type;
   nsresult rv = aStoreNames->GetDataType(&type);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  PRUint32 storeCount = mObjectStores.Length();
-  nsTArray<ObjectStoreInfo> storesToOpen;
+  nsTArray<nsString>& objectStoreNames = mDatabaseInfo->objectStoreNames;
+  PRUint32 storeCount = objectStoreNames.Length();
+
+  nsTArray<nsString> storesToOpen;
 
   switch (type) {
     case nsIDataType::VTYPE_VOID:
     case nsIDataType::VTYPE_EMPTY:
     case nsIDataType::VTYPE_EMPTY_ARRAY: {
       // Empty, request all object stores
-      if (!storesToOpen.AppendElements(mObjectStores)) {
+      if (!storesToOpen.AppendElements(objectStoreNames)) {
         NS_ERROR("Out of memory?");
         return NS_ERROR_OUT_OF_MEMORY;
       }
     } break;
 
     case nsIDataType::VTYPE_WSTRING_SIZE_IS: {
       // Single name
       nsString name;
       rv = aStoreNames->GetAsAString(name);
       NS_ENSURE_SUCCESS(rv, rv);
 
       for (PRUint32 index = 0; index < storeCount; index++) {
-        ObjectStoreInfo& info = mObjectStores[index];
-        if (info.name == name) {
-          if (!storesToOpen.AppendElement(info)) {
+        if (objectStoreNames[index] == name) {
+          if (!storesToOpen.AppendElement(name)) {
             NS_ERROR("Out of memory?");
             return NS_ERROR_OUT_OF_MEMORY;
           }
           break;
         }
       }
       if (storesToOpen.IsEmpty()) {
         return NS_ERROR_NOT_AVAILABLE;
@@ -754,28 +711,27 @@ IDBDatabaseRequest::Transaction(nsIVaria
       rv = ConvertVariantToStringArray(aStoreNames, names);
       NS_ENSURE_SUCCESS(rv, rv);
 
       PRUint32 nameCount = names.Length();
       for (PRUint32 nameIndex = 0; nameIndex < nameCount; nameIndex++) {
         nsString& name = names[nameIndex];
         bool found = false;
         for (PRUint32 storeIndex = 0; storeIndex < storeCount; storeIndex++) {
-          ObjectStoreInfo& info = mObjectStores[storeIndex];
-          if (info.name == name) {
-            if (!storesToOpen.AppendElement(info)) {
+          if (objectStoreNames[storeIndex] == name) {
+            if (!storesToOpen.AppendElement(name)) {
               NS_ERROR("Out of memory?");
               return NS_ERROR_OUT_OF_MEMORY;
             }
             found = true;
             break;
           }
         }
         if (!found) {
-          return NS_ERROR_ILLEGAL_VALUE;
+          return NS_ERROR_NOT_AVAILABLE;
         }
       }
       NS_ASSERTION(nameCount == storesToOpen.Length(), "Should have bailed!");
     } break;
 
     case nsIDataType::VTYPE_INTERFACE:
     case nsIDataType::VTYPE_INTERFACE_IS: {
       nsCOMPtr<nsISupports> supports;
@@ -797,28 +753,27 @@ IDBDatabaseRequest::Transaction(nsIVaria
 
       for (PRUint32 stringIndex = 0; stringIndex < stringCount; stringIndex++) {
         nsString string;
         rv = stringList->Item(stringIndex, string);
         NS_ENSURE_SUCCESS(rv, rv);
 
         bool found = false;
         for (PRUint32 storeIndex = 0; storeIndex < storeCount; storeIndex++) {
-          ObjectStoreInfo& info = mObjectStores[storeIndex];
-          if (info.name == string) {
-            if (!storesToOpen.AppendElement(info)) {
+          if (objectStoreNames[storeIndex] == string) {
+            if (!storesToOpen.AppendElement(string)) {
               NS_ERROR("Out of memory?");
               return NS_ERROR_OUT_OF_MEMORY;
             }
             found = true;
             break;
           }
         }
         if (!found) {
-          return NS_ERROR_ILLEGAL_VALUE;
+          return NS_ERROR_NOT_AVAILABLE;
         }
       }
     } break;
 
     default:
       return NS_ERROR_ILLEGAL_VALUE;
   }
 
@@ -849,30 +804,39 @@ IDBDatabaseRequest::ObjectStore(const ns
         aMode != nsIIDBTransaction::SNAPSHOT_READ) {
       return NS_ERROR_INVALID_ARG;
     }
   }
   else {
     aMode = nsIIDBTransaction::READ_ONLY;
   }
 
-  ObjectStoreInfo* info;
-  if (!ObjectStoreInfoForName(aName, &info)) {
-    NS_NOTYETIMPLEMENTED("Need right return code");
-    return NS_ERROR_INVALID_ARG;
+  nsTArray<nsString>& objectStoreNames = mDatabaseInfo->objectStoreNames;
+  PRUint32 count = objectStoreNames.Length();
+
+  PRUint32 foundIndex = PR_UINT32_MAX;
+  for (PRUint32 index = 0; index < count; index++) {
+    if (objectStoreNames[index] == aName) {
+      foundIndex = index;
+      break;
+    }
   }
 
-  nsTArray<ObjectStoreInfo> objectStores;
-  if (!objectStores.AppendElement(*info)) {
+  if (foundIndex == PR_UINT32_MAX) {
+    return NS_ERROR_NOT_AVAILABLE;
+  }
+
+  nsTArray<nsString> name;
+  if (!name.AppendElement(objectStoreNames[foundIndex])) {
     NS_ERROR("Out of memory");
     return NS_ERROR_OUT_OF_MEMORY;
   }
 
   nsRefPtr<IDBTransactionRequest> transaction =
-    IDBTransactionRequest::Create(this, objectStores, aMode,
+    IDBTransactionRequest::Create(this, name, aMode,
                                   kDefaultDatabaseTimeoutSeconds);
   NS_ENSURE_TRUE(transaction, NS_ERROR_FAILURE);
 
   nsresult rv = transaction->ObjectStore(aName, _retval);
   NS_ENSURE_SUCCESS(rv, rv);
 
   return NS_OK;
 }
@@ -910,17 +874,22 @@ SetVersionHelper::DoDatabaseWork(mozISto
   }
 
   return OK;
 }
 
 PRUint16
 SetVersionHelper::OnSuccess(nsIDOMEventTarget* aTarget)
 {
-  mDatabase->OnVersionSet(mVersion);
+  DatabaseInfo* info;
+  if (!DatabaseInfo::Get(mDatabase->Id(), &info)) {
+    NS_ERROR("This should never fail!");
+    return nsIIDBDatabaseException::UNKNOWN_ERR;
+  }
+  info->version = mVersion;
 
   return AsyncConnectionHelper::OnSuccess(aTarget);
 }
 
 PRUint16
 CreateObjectStoreHelper::DoDatabaseWork(mozIStorageConnection* aConnection)
 {
   // Insert the data into the database.
@@ -949,35 +918,41 @@ CreateObjectStoreHelper::DoDatabaseWork(
   (void)aConnection->GetLastInsertRowID(&mId);
 
   return OK;
 }
 
 PRUint16
 CreateObjectStoreHelper::OnSuccess(nsIDOMEventTarget* aTarget)
 {
-  nsTArray<ObjectStoreInfo> objectStores;
-  ObjectStoreInfo* info = objectStores.AppendElement();
-  if (!info) {
-    NS_ERROR("Out of memory?!");
-    return nsIIDBDatabaseException::UNKNOWN_ERR;
-  }
+  nsAutoPtr<ObjectStoreInfo> info(new ObjectStoreInfo());
 
   info->name = mName;
   info->id = mId;
   info->keyPath = mKeyPath;
   info->autoIncrement = mAutoIncrement;
+  info->databaseId = mDatabase->Id();
 
-  mDatabase->OnObjectStoreCreated(*info);
+  if (!ObjectStoreInfo::Put(info)) {
+    NS_ERROR("Put failed!");
+    return nsIIDBDatabaseException::UNKNOWN_ERR;
+  }
+  info.forget();
+
+  nsTArray<nsString> objectStores;
+  nsString* name = objectStores.AppendElement(mName);
+  if (!name) {
+    NS_ERROR("Out of memory?");
+    return nsIIDBDatabaseException::UNKNOWN_ERR;
+  }
 
   nsRefPtr<IDBTransactionRequest> transaction =
     IDBTransactionRequest::Create(mDatabase, objectStores,
                                   nsIIDBTransaction::READ_WRITE,
                                   kDefaultDatabaseTimeoutSeconds);
-  NS_ASSERTION(objectStores.IsEmpty(), "Should have swapped!");
 
   nsCOMPtr<nsIIDBObjectStoreRequest> result;
   nsresult rv = transaction->ObjectStore(mName, getter_AddRefs(result));
   NS_ENSURE_SUCCESS(rv, nsIIDBDatabaseException::UNKNOWN_ERR);
 
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
   nsCOMPtr<nsIWritableVariant> variant =
@@ -1003,86 +978,34 @@ CreateObjectStoreHelper::OnSuccess(nsIDO
 
   AutoTransactionRequestNotifier notifier(transaction);
 
   PRBool dummy;
   aTarget->DispatchEvent(event, &dummy);
   return OK;
 }
 
-#if 0
-PRUint16
-OpenObjectStoreHelper::DoDatabaseWork(mozIStorageConnection* aConnection)
-{
-  // TODO pull this up to the connection and cache it so opening these is
-  // cheaper.
-  nsCOMPtr<mozIStorageStatement> stmt;
-  nsresult rv = aConnection->CreateStatement(NS_LITERAL_CSTRING(
-    "SELECT id, key_path, auto_increment "
-    "FROM object_store "
-    "WHERE name = :name"
-  ), getter_AddRefs(stmt));
-  NS_ENSURE_SUCCESS(rv, nsIIDBDatabaseException::UNKNOWN_ERR);
-
-  rv = stmt->BindStringByName(NS_LITERAL_CSTRING("name"), mName);
-  NS_ENSURE_SUCCESS(rv, nsIIDBDatabaseException::UNKNOWN_ERR);
-
-  PRBool hasResult;
-  rv = stmt->ExecuteStep(&hasResult);
-  NS_ENSURE_SUCCESS(rv, nsIIDBDatabaseException::UNKNOWN_ERR);
-  NS_ENSURE_TRUE(hasResult, nsIIDBDatabaseException::NOT_FOUND_ERR);
-
-  mId = stmt->AsInt64(0);
-  (void)stmt->GetString(1, mKeyPath);
-  mAutoIncrement = !!stmt->AsInt32(2);
-
-  return OK;
-}
-
-PRUint16
-OpenObjectStoreHelper::OnSuccess(nsIDOMEventTarget* aTarget)
-{
-  ObjectStoreInfo info(mName, mId);
-  mObjectStore =
-    IDBObjectStoreRequest::Create(mDatabase, info, mKeyPath, mAutoIncrement,
-                                  mMode);
-  NS_ENSURE_TRUE(mObjectStore, nsIIDBDatabaseException::UNKNOWN_ERR);
-  return AsyncConnectionHelper::OnSuccess(aTarget);
-}
-
-PRUint16
-OpenObjectStoreHelper::GetSuccessResult(nsIWritableVariant* aResult)
-{
-  nsCOMPtr<nsISupports> result =
-    do_QueryInterface(static_cast<nsIIDBObjectStoreRequest*>(mObjectStore));
-  NS_ASSERTION(result, "Failed to QI!");
-
-  aResult->SetAsISupports(result);
-  return OK;
-}
-#endif
-
 PRUint16
 RemoveObjectStoreHelper::DoDatabaseWork(mozIStorageConnection* aConnection)
 {
   nsCOMPtr<mozIStorageStatement> stmt;
   nsresult rv = aConnection->CreateStatement(NS_LITERAL_CSTRING(
     "DELETE FROM object_store "
     "WHERE name = :name "
   ), getter_AddRefs(stmt));
   NS_ENSURE_SUCCESS(rv, nsIIDBDatabaseException::UNKNOWN_ERR);
 
-  rv = stmt->BindStringByName(NS_LITERAL_CSTRING("name"), mStore.name);
+  rv = stmt->BindStringByName(NS_LITERAL_CSTRING("name"), mName);
   NS_ENSURE_SUCCESS(rv, nsIIDBDatabaseException::UNKNOWN_ERR);
 
   if (NS_FAILED(stmt->Execute())) {
     return nsIIDBDatabaseException::NOT_FOUND_ERR;
   }
 
   return OK;
 }
 
 PRUint16
 RemoveObjectStoreHelper::GetSuccessResult(nsIWritableVariant* /* aResult */)
 {
-  mDatabase->OnObjectStoreRemoved(mStore);
+  ObjectStoreInfo::Remove(mDatabase->Id(), mName);
   return OK;
 }
--- a/dom/indexedDB/IDBDatabaseRequest.h
+++ b/dom/indexedDB/IDBDatabaseRequest.h
@@ -47,80 +47,33 @@
 #include "nsIIDBDatabaseRequest.h"
 #include "nsIObserver.h"
 
 #include "nsDOMLists.h"
 
 BEGIN_INDEXEDDB_NAMESPACE
 
 class AsyncConnectionHelper;
-class IDBTransactionRequest;
-
-class ObjectStoreInfo
-{
-public:
-  nsString name;
-  PRInt64 id;
-  nsString keyPath;
-  bool autoIncrement;
-
-  ObjectStoreInfo()
-  : id(0), autoIncrement(false)
-  { }
-
-  ObjectStoreInfo(const nsAString& aName,
-                  PRInt64 aId)
-  : name(aName),
-    id(aId),
-    autoIncrement(false)
-  { }
-
-  ObjectStoreInfo(const nsAString& aName,
-                  PRInt64 aId,
-                  const nsAString& aKeyPath,
-                  bool aAutoIncrement)
-  : name(aName),
-    id(aId),
-    keyPath(aKeyPath),
-    autoIncrement(false)
-  { }
-
-  bool operator==(const ObjectStoreInfo& aOther) const {
-    if (id == aOther.id) {
-      NS_ASSERTION(name == aOther.name, "Huh?!");
-      return true;
-    }
-    return false;
-  }
-
-  bool operator<(const ObjectStoreInfo& aOther) const {
-    return id < aOther.id;
-  }
-
-};
+class DatabaseInfo;
 
 class IDBDatabaseRequest : public IDBRequest::Generator,
                            public nsIIDBDatabaseRequest,
                            public nsIObserver
 {
   friend class AsyncConnectionHelper;
 
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSIIDBDATABASE
   NS_DECL_NSIIDBDATABASEREQUEST
   NS_DECL_NSIOBSERVER
 
   static already_AddRefed<IDBDatabaseRequest>
-  Create(const nsAString& aName,
-         const nsAString& aDescription,
-         nsTArray<ObjectStoreInfo>& aObjectStores,
-         const nsAString& aVersion,
+  Create(DatabaseInfo* aDatabaseInfo,
          LazyIdleThread* aThread,
-         const nsAString& aDatabaseFilePath,
          nsCOMPtr<mozIStorageConnection>& aConnection);
 
   /**
    * Obtains a cached statement for the add operation on object stores.
    *
    * @pre Called from mStorageThread.
    *
    * @param aOverwrite
@@ -159,82 +112,51 @@ public:
   already_AddRefed<mozIStorageStatement> GetStatement(bool aAutoIncrement);
 
   nsIThread* ConnectionThread() {
     return mConnectionThread;
   }
 
   void FireCloseConnectionRunnable();
 
-  void OnVersionSet(const nsString& aVersion);
-  void OnObjectStoreCreated(const ObjectStoreInfo& aInfo);
-  void OnObjectStoreRemoved(const ObjectStoreInfo& aInfo);
-
   void DisableConnectionThreadTimeout() {
     mConnectionThread->DisableIdleTimeout();
   }
 
   void EnableConnectionThreadTimeout() {
     mConnectionThread->EnableIdleTimeout();
   }
 
+  PRUint32 Id();
+
 protected:
   IDBDatabaseRequest();
   ~IDBDatabaseRequest();
 
   // Only meant to be called on mStorageThread!
   nsCOMPtr<mozIStorageConnection>& Connection();
 
   // Only meant to be called on mStorageThread!
   nsresult EnsureConnection();
 
-  nsresult QueueDatabaseWork(nsIRunnable* aRunnable);
-
-  bool ObjectStoreInfoForName(const nsAString& aName,
-                              ObjectStoreInfo** aInfo) {
-    NS_ASSERTION(aInfo, "Null pointer!");
-    PRUint32 count = mObjectStores.Length();
-    for (PRUint32 index = 0; index < count; index++) {
-      ObjectStoreInfo& store = mObjectStores[index];
-      if (store.name == aName) {
-        if (aInfo) {
-          *aInfo = &store;
-        }
-        return true;
-      }
-    }
-    if (aInfo) {
-      *aInfo = nsnull;
-    }
-    return false;
-  }
-
 private:
-  nsString mName;
-  nsString mDescription;
-  nsString mVersion;
-  nsString mDatabaseFilePath;
-
-  nsTArray<ObjectStoreInfo> mObjectStores;
+  DatabaseInfo* mDatabaseInfo;
 
   nsRefPtr<LazyIdleThread> mConnectionThread;
 
   // Only touched on mStorageThread! These must be destroyed in the
   // FireCloseConnectionRunnable method.
   nsCOMPtr<mozIStorageConnection> mConnection;
   nsCOMPtr<mozIStorageStatement> mAddStmt;
   nsCOMPtr<mozIStorageStatement> mAddAutoIncrementStmt;
   nsCOMPtr<mozIStorageStatement> mModifyStmt;
   nsCOMPtr<mozIStorageStatement> mModifyAutoIncrementStmt;
   nsCOMPtr<mozIStorageStatement> mAddOrModifyStmt;
   nsCOMPtr<mozIStorageStatement> mAddOrModifyAutoIncrementStmt;
   nsCOMPtr<mozIStorageStatement> mRemoveStmt;
   nsCOMPtr<mozIStorageStatement> mRemoveAutoIncrementStmt;
   nsCOMPtr<mozIStorageStatement> mGetStmt;
   nsCOMPtr<mozIStorageStatement> mGetAutoIncrementStmt;
-
-  nsTArray<nsCOMPtr<nsIRunnable> > mPendingDatabaseWork;
-  nsTArray<nsRefPtr<IDBTransactionRequest> > mTransactions;
 };
 
 END_INDEXEDDB_NAMESPACE
 
 #endif // mozilla_dom_indexeddb_idbdatabaserequest_h__
--- a/dom/indexedDB/IDBObjectStoreRequest.cpp
+++ b/dom/indexedDB/IDBObjectStoreRequest.cpp
@@ -50,16 +50,17 @@
 #include "nsIJSContextStack.h"
 
 #include "nsDOMClassInfo.h"
 #include "nsThreadUtils.h"
 #include "mozilla/Storage.h"
 
 #include "AsyncConnectionHelper.h"
 #include "IDBTransactionRequest.h"
+#include "DatabaseInfo.h"
 
 USING_INDEXEDDB_NAMESPACE
 
 namespace {
 
 class AddHelper : public AsyncConnectionHelper
 {
 public:
@@ -259,29 +260,29 @@ GetKeyFromObject(JSContext* aCx,
 }
 
 } // anonymous namespace
 
 // static
 already_AddRefed<IDBObjectStoreRequest>
 IDBObjectStoreRequest::Create(IDBDatabaseRequest* aDatabase,
                               IDBTransactionRequest* aTransaction,
-                              const ObjectStoreInfo& aStoreInfo,
+                              const ObjectStoreInfo* aStoreInfo,
                               PRUint16 aMode)
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
   nsRefPtr<IDBObjectStoreRequest> objectStore = new IDBObjectStoreRequest();
 
   objectStore->mDatabase = aDatabase;
   objectStore->mTransaction = aTransaction;
-  objectStore->mName = aStoreInfo.name;
-  objectStore->mId = aStoreInfo.id;
-  objectStore->mKeyPath = aStoreInfo.keyPath;
-  objectStore->mAutoIncrement = aStoreInfo.autoIncrement;
+  objectStore->mName = aStoreInfo->name;
+  objectStore->mId = aStoreInfo->id;
+  objectStore->mKeyPath = aStoreInfo->keyPath;
+  objectStore->mAutoIncrement = aStoreInfo->autoIncrement;
   objectStore->mMode = aMode;
 
   return objectStore.forget();
 }
 
 IDBObjectStoreRequest::IDBObjectStoreRequest()
 : mId(LL_MININT),
   mAutoIncrement(PR_FALSE),
@@ -397,21 +398,22 @@ IDBObjectStoreRequest::GetKeyPath(nsAStr
 }
 
 NS_IMETHODIMP
 IDBObjectStoreRequest::GetIndexNames(nsIDOMDOMStringList** aIndexNames)
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
   nsRefPtr<nsDOMStringList> list(new nsDOMStringList());
+#if 0
   PRUint32 count = mIndexes.Length();
   for (PRUint32 index = 0; index < count; index++) {
     NS_ENSURE_TRUE(list->Add(mIndexes[index]), NS_ERROR_OUT_OF_MEMORY);
   }
-
+#endif
   list.forget(aIndexNames);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 IDBObjectStoreRequest::Get(nsIVariant* aKey,
                            nsIIDBRequest** _retval)
 {
--- a/dom/indexedDB/IDBObjectStoreRequest.h
+++ b/dom/indexedDB/IDBObjectStoreRequest.h
@@ -43,16 +43,17 @@
 #include "mozilla/dom/indexedDB/IDBRequest.h"
 #include "mozilla/dom/indexedDB/IDBDatabaseRequest.h"
 
 #include "nsIIDBObjectStoreRequest.h"
 
 BEGIN_INDEXEDDB_NAMESPACE
 
 class IDBTransactionRequest;
+class ObjectStoreInfo;
 
 class Key
 {
 public:
   enum Type { UNSETKEY, NULLKEY, STRINGKEY, INTKEY };
 
   Key()
   : mType(UNSETKEY), mInt(0)
@@ -138,36 +139,34 @@ class IDBObjectStoreRequest : public IDB
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSIIDBOBJECTSTORE
   NS_DECL_NSIIDBOBJECTSTOREREQUEST
 
   static already_AddRefed<IDBObjectStoreRequest>
   Create(IDBDatabaseRequest* aDatabase,
          IDBTransactionRequest* aTransaction,
-         const ObjectStoreInfo& aStoreInfo,
+         const ObjectStoreInfo* aInfo,
          PRUint16 aMode);
 
 protected:
   IDBObjectStoreRequest();
   ~IDBObjectStoreRequest();
 
   nsresult GetJSONAndKeyForAdd(/* jsval aValue, */
                                nsIVariant* aKeyVariant,
                                nsString& aJSON,
                                Key& aKey);
 
 private:
   nsRefPtr<IDBDatabaseRequest> mDatabase;
   nsRefPtr<IDBTransactionRequest> mTransaction;
 
+  PRInt64 mId;
   nsString mName;
-  PRInt64 mId;
   nsString mKeyPath;
   PRBool mAutoIncrement;
   PRUint16 mMode;
-
-  nsTArray<nsString> mIndexes;
 };
 
 END_INDEXEDDB_NAMESPACE
 
 #endif // mozilla_dom_indexeddb_idbobjectstorerequest_h__
--- a/dom/indexedDB/IDBTransactionRequest.cpp
+++ b/dom/indexedDB/IDBTransactionRequest.cpp
@@ -39,35 +39,40 @@
 
 #include "IDBTransactionRequest.h"
 
 #include "nsDOMClassInfo.h"
 #include "nsThreadUtils.h"
 
 #include "IDBEvents.h"
 #include "IDBObjectStoreRequest.h"
+#include "DatabaseInfo.h"
 
 USING_INDEXEDDB_NAMESPACE
 
 // static
 already_AddRefed<IDBTransactionRequest>
 IDBTransactionRequest::Create(IDBDatabaseRequest* aDatabase,
-                              nsTArray<ObjectStoreInfo>& aObjectStores,
+                              nsTArray<nsString>& aObjectStoreNames,
                               PRUint16 aMode,
                               PRUint32 aTimeout)
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
   nsRefPtr<IDBTransactionRequest> transaction = new IDBTransactionRequest();
 
   transaction->mDatabase = aDatabase;
-  transaction->mObjectStores.SwapElements(aObjectStores);
   transaction->mMode = aMode;
   transaction->mTimeout = aTimeout;
 
+  if (!transaction->mObjectStoreNames.AppendElements(aObjectStoreNames)) {
+    NS_ERROR("Out of memory!");
+    return nsnull;
+  }
+
   return transaction.forget();
 }
 
 IDBTransactionRequest::IDBTransactionRequest()
 : mReadyState(nsIIDBTransaction::INITIAL),
   mMode(nsIIDBTransaction::READ_ONLY),
   mTimeout(0),
   mPendingRequests(0),
@@ -141,32 +146,30 @@ IDBTransactionRequest::Commit()
 }
 
 bool
 IDBTransactionRequest::StartSavepoint(const nsCString& aName)
 {
   NS_PRECONDITION(!NS_IsMainThread(), "Wrong thread!");
 
   // TODO try to cache this statement
-  nsCAutoString sql;
-  sql.AppendLiteral("SAVEPOINT ");
+  nsCAutoString sql("SAVEPOINT ");
   sql.Append(aName);
   nsresult rv = mConnection->ExecuteSimpleSQL(sql);
   NS_ENSURE_SUCCESS(rv, false);
   return true;
 }
 
 void
 IDBTransactionRequest::RevertToSavepoint(const nsCString& aName)
 {
   NS_PRECONDITION(!NS_IsMainThread(), "Wrong thread!");
 
   // TODO try to cache this statement
-  nsCAutoString sql;
-  sql.AppendLiteral("SAVEPOINT ");
+  nsCAutoString sql("SAVEPOINT ");
   sql.Append(aName);
   nsresult rv = mConnection->ExecuteSimpleSQL(sql);
   NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "Rollback failed");
 }
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(IDBTransactionRequest)
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(IDBTransactionRequest,
@@ -219,49 +222,49 @@ IDBTransactionRequest::GetMode(PRUint16*
 }
 
 NS_IMETHODIMP
 IDBTransactionRequest::GetObjectStoreNames(nsIDOMDOMStringList** aObjectStores)
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
   nsRefPtr<nsDOMStringList> list(new nsDOMStringList());
-  PRUint32 count = mObjectStores.Length();
+  PRUint32 count = mObjectStoreNames.Length();
   for (PRUint32 index = 0; index < count; index++) {
-    NS_ENSURE_TRUE(list->Add(mObjectStores[index].name),
-                   NS_ERROR_OUT_OF_MEMORY);
+    NS_ENSURE_TRUE(list->Add(mObjectStoreNames[index]), NS_ERROR_OUT_OF_MEMORY);
   }
-
   list.forget(aObjectStores);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 IDBTransactionRequest::ObjectStore(const nsAString& aName,
                                    nsIIDBObjectStoreRequest** _retval)
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
-  ObjectStoreInfo* objectStoreInfo = nsnull;
+  ObjectStoreInfo* info = nsnull;
 
-  PRUint32 count = mObjectStores.Length();
+  PRUint32 count = mObjectStoreNames.Length();
   for (PRUint32 index = 0; index < count; index++) {
-    ObjectStoreInfo& info = mObjectStores[index];
-    if (info.name == aName) {
-      objectStoreInfo = &info;
+    nsString& name = mObjectStoreNames[index];
+    if (name == aName) {
+      if (!ObjectStoreInfo::Get(mDatabase->Id(), aName, &info)) {
+        NS_ERROR("Don't know about this one?!");
+      }
       break;
     }
   }
 
-  if (!objectStoreInfo) {
+  if (!info) {
     return NS_ERROR_NOT_AVAILABLE;
   }
 
   nsRefPtr<IDBObjectStoreRequest> objectStore =
-    IDBObjectStoreRequest::Create(mDatabase, this, *objectStoreInfo, mMode);
+    IDBObjectStoreRequest::Create(mDatabase, this, info, mMode);
   NS_ENSURE_TRUE(objectStore, NS_ERROR_FAILURE);
 
   objectStore.forget(_retval);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 IDBTransactionRequest::Abort()
--- a/dom/indexedDB/IDBTransactionRequest.h
+++ b/dom/indexedDB/IDBTransactionRequest.h
@@ -49,16 +49,17 @@
 #include "nsCycleCollectionParticipant.h"
 
 #include "nsAutoPtr.h"
 #include "mozilla/Storage.h"
 
 BEGIN_INDEXEDDB_NAMESPACE
 
 class AsyncConnectionHelper;
+class ObjectStoreInfo;
 
 class IDBTransactionRequest : public nsDOMEventTargetHelper,
                               public IDBRequest::Generator,
                               public nsIIDBTransactionRequest
 {
   friend class AsyncConnectionHelper;
 
 public:
@@ -66,17 +67,17 @@ public:
   NS_DECL_NSIIDBTRANSACTION
   NS_DECL_NSIIDBTRANSACTIONREQUEST
 
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(IDBTransactionRequest,
                                            nsDOMEventTargetHelper)
 
   static already_AddRefed<IDBTransactionRequest>
   Create(IDBDatabaseRequest* aDatabase,
-         nsTArray<ObjectStoreInfo>& aObjectStores,
+         nsTArray<nsString>& aObjectStoreNames,
          PRUint16 aMode,
          PRUint32 aTimeout);
 
   void OnNewRequest();
   void OnRequestFinished();
 
   nsresult Commit();
 
@@ -85,17 +86,17 @@ public:
   void RevertToSavepoint(const nsCString& aName);
 
 private:
   IDBTransactionRequest();
   ~IDBTransactionRequest();
 
   nsRefPtr<IDBDatabaseRequest> mDatabase;
   nsCOMPtr<mozIStorageConnection> mConnection;
-  nsTArray<ObjectStoreInfo> mObjectStores;
+  nsTArray<nsString> mObjectStoreNames;
   PRUint16 mReadyState;
   PRUint16 mMode;
   PRUint32 mTimeout;
   PRUint32 mPendingRequests;
 
   PRInt64 mLastUniqueNumber;
 
   nsAutoPtr<mozStorageTransaction> mDBTransaction;
--- a/dom/indexedDB/IndexedDatabase.h
+++ b/dom/indexedDB/IndexedDatabase.h
@@ -53,9 +53,16 @@
   namespace mozilla { namespace dom { namespace indexedDB {
 
 #define END_INDEXEDDB_NAMESPACE \
   } /* namespace indexedDB */ } /* namepsace dom */ } /* namespace mozilla */
 
 #define USING_INDEXEDDB_NAMESPACE \
   using namespace mozilla::dom::indexedDB;
 
+BEGIN_INDEXEDDB_NAMESPACE
+
+// Defined in DatabaseInfo.cpp
+extern void Shutdown();
+
+END_INDEXEDDB_NAMESPACE
+
 #endif // mozilla_dom_indexeddb_indexeddatabase_h__
--- a/dom/indexedDB/IndexedDatabaseRequest.cpp
+++ b/dom/indexedDB/IndexedDatabaseRequest.cpp
@@ -49,16 +49,17 @@
 #include "nsDirectoryServiceUtils.h"
 #include "nsDOMClassInfo.h"
 #include "nsHashKeys.h"
 #include "nsServiceManagerUtils.h"
 #include "nsThreadUtils.h"
 #include "nsXPCOMCID.h"
 
 #include "AsyncConnectionHelper.h"
+#include "DatabaseInfo.h"
 #include "IDBDatabaseRequest.h"
 #include "LazyIdleThread.h"
 
 #define DB_SCHEMA_VERSION 1
 
 USING_INDEXEDDB_NAMESPACE
 
 namespace {
@@ -70,35 +71,36 @@ class OpenDatabaseHelper : public AsyncC
 public:
   OpenDatabaseHelper(IDBRequest* aRequest,
                      const nsAString& aName,
                      const nsAString& aDescription,
                      const nsACString& aASCIIOrigin,
                      LazyIdleThread* aThread)
   : AsyncConnectionHelper(static_cast<IDBDatabaseRequest*>(nsnull), aRequest),
     mName(aName), mDescription(aDescription), mASCIIOrigin(aASCIIOrigin),
-    mThread(aThread)
+    mThread(aThread), mDatabaseId(0)
   { }
 
   PRUint16 DoDatabaseWork(mozIStorageConnection* aConnection);
   PRUint16 GetSuccessResult(nsIWritableVariant* aResult);
 
 private:
   // In-params.
   nsString mName;
   nsString mDescription;
   nsCString mASCIIOrigin;
   nsRefPtr<LazyIdleThread> mThread;
 
   // Out-params.
-  nsTArray<ObjectStoreInfo> mObjectStores;
+  nsTArray<nsAutoPtr<ObjectStoreInfo> > mObjectStores;
   nsString mVersion;
 
   nsCOMPtr<mozIStorageConnection> mConnection;
   nsString mDatabaseFilePath;
+  PRUint32 mDatabaseId;
 };
 
 nsresult
 CreateTables(mozIStorageConnection* aDBConn)
 {
   NS_PRECONDITION(!NS_IsMainThread(),
                   "Creating tables on the main thread!");
   NS_PRECONDITION(aDBConn, "Passing a null database connection!");
@@ -531,33 +533,40 @@ OpenDatabaseHelper::DoDatabaseWork(mozIS
 #endif
   NS_ASSERTION(!aConnection, "Huh?!");
 
   nsresult rv = CreateDatabaseConnection(mASCIIOrigin, mName, mDescription,
                                          mDatabaseFilePath,
                                          getter_AddRefs(mConnection));
   NS_ENSURE_SUCCESS(rv, nsIIDBDatabaseException::UNKNOWN_ERR);
 
+  mDatabaseId = HashString(mDatabaseFilePath);
+  NS_ASSERTION(mDatabaseId, "HashString gave us 0?!");
+
   { // Load object store names and ids.
     nsCOMPtr<mozIStorageStatement> stmt;
     rv = mConnection->CreateStatement(NS_LITERAL_CSTRING(
       "SELECT name, id, key_path, auto_increment "
       "FROM object_store"
     ), getter_AddRefs(stmt));
     NS_ENSURE_SUCCESS(rv, nsIIDBDatabaseException::UNKNOWN_ERR);
 
     PRBool hasResult;
     while (NS_SUCCEEDED(stmt->ExecuteStep(&hasResult)) && hasResult) {
-      ObjectStoreInfo* info = mObjectStores.AppendElement();
-      NS_ENSURE_TRUE(info, nsIIDBDatabaseException::UNKNOWN_ERR);
+      nsAutoPtr<ObjectStoreInfo>* element =
+        mObjectStores.AppendElement(new ObjectStoreInfo());
+      NS_ENSURE_TRUE(element, nsIIDBDatabaseException::UNKNOWN_ERR);
+
+      ObjectStoreInfo* const info = element->get();
 
       stmt->GetString(0, info->name);
       info->id = stmt->AsInt64(1);
       rv = stmt->GetString(2, info->keyPath);
       info->autoIncrement = !!stmt->AsInt32(3);
+      info->databaseId = mDatabaseId;
     }
   }
 
   { // Load version information.
     nsCOMPtr<mozIStorageStatement> stmt;
     rv = mConnection->CreateStatement(NS_LITERAL_CSTRING(
       "SELECT version "
       "FROM database"
@@ -579,18 +588,81 @@ OpenDatabaseHelper::DoDatabaseWork(mozIS
   return OK;
 }
 
 PRUint16
 OpenDatabaseHelper::GetSuccessResult(nsIWritableVariant* aResult)
 {
   NS_ASSERTION(mConnection, "Should have a connection!");
 
+  DatabaseInfo* dbInfo;
+  if (DatabaseInfo::Get(mDatabaseId, &dbInfo)) {
+    ++dbInfo->referenceCount;
+#ifdef DEBUG
+    {
+      NS_ASSERTION(dbInfo->name == mName &&
+                   dbInfo->description == mDescription &&
+                   dbInfo->version == mVersion &&
+                   dbInfo->id == mDatabaseId &&
+                   dbInfo->filePath == mDatabaseFilePath,
+                   "Metadata mismatch!");
+
+      PRUint32 objectStoreCount = mObjectStores.Length();
+      for (PRUint32 index = 0; index < objectStoreCount; index++) {
+        nsAutoPtr<ObjectStoreInfo>& info = mObjectStores[index];
+        NS_ASSERTION(info->databaseId == mDatabaseId, "Huh?!");
+
+        ObjectStoreInfo* otherInfo;
+        NS_ASSERTION(ObjectStoreInfo::Get(mDatabaseId, info->name, &otherInfo),
+                     "ObjectStore not known!");
+
+        NS_ASSERTION(info->name == otherInfo->name &&
+                     info->id == otherInfo->id &&
+                     info->keyPath == otherInfo->keyPath &&
+                     info->autoIncrement == otherInfo->autoIncrement &&
+                     info->databaseId == otherInfo->databaseId,
+                     "Metadata mismatch!");
+        NS_ASSERTION(dbInfo->objectStoreNames.Contains(info->name),
+                     "Object store names out of date!");
+      }
+    }
+#endif
+  }
+  else {
+    nsAutoPtr<DatabaseInfo> newInfo(new DatabaseInfo());
+
+    newInfo->name = mName;
+    newInfo->description = mDescription;
+    newInfo->version = mVersion;
+    newInfo->id = mDatabaseId;
+    newInfo->filePath = mDatabaseFilePath;
+    newInfo->referenceCount = 1;
+
+    if (!DatabaseInfo::Put(newInfo)) {
+      NS_ERROR("Failed to add to hash!");
+      return nsIIDBDatabaseException::UNKNOWN_ERR;
+    }
+
+    dbInfo = newInfo.forget();
+
+    PRUint32 objectStoreCount = mObjectStores.Length();
+    for (PRUint32 index = 0; index < objectStoreCount; index++) {
+      nsAutoPtr<ObjectStoreInfo>& info = mObjectStores[index];
+      NS_ASSERTION(info->databaseId == mDatabaseId, "Huh?!");
+  
+      if (!ObjectStoreInfo::Put(info)) {
+        NS_ERROR("Failed to add to hash!");
+        return nsIIDBDatabaseException::UNKNOWN_ERR;
+      }
+  
+      info.forget();
+    }
+  }
+
   nsRefPtr<IDBDatabaseRequest> db =
-    IDBDatabaseRequest::Create(mName, mDescription, mObjectStores, mVersion,
-                               mThread, mDatabaseFilePath, mConnection);
+    IDBDatabaseRequest::Create(dbInfo, mThread, mConnection);
   NS_ASSERTION(db, "This can't fail!");
 
   NS_ASSERTION(!mConnection, "Should have swapped out!");
 
   aResult->SetAsISupports(static_cast<IDBRequest::Generator*>(db));
   return OK;
 }
--- a/dom/indexedDB/Makefile.in
+++ b/dom/indexedDB/Makefile.in
@@ -48,16 +48,17 @@ LIBRARY_NAME = domindexedDB_s
 XPIDL_MODULE = domindexedDB
 LIBXUL_LIBRARY = 1
 FORCE_STATIC_LIB = 1
 
 EXPORTS_NAMESPACES = mozilla/dom/indexedDB
 
 CPPSRCS = \
   AsyncConnectionHelper.cpp \
+  DatabaseInfo.cpp \
   IDBDatabaseRequest.cpp \
   IDBEvents.cpp \
   IDBObjectStoreRequest.cpp \
   IDBRequest.cpp \
   IDBTransactionRequest.cpp \
   IndexedDatabaseRequest.cpp \
   LazyIdleThread.cpp \
   $(NULL)
--- a/dom/indexedDB/test/Makefile.in
+++ b/dom/indexedDB/test/Makefile.in
@@ -43,16 +43,17 @@ relativesrcdir = dom/indexedDB/test
 
 include $(DEPTH)/config/autoconf.mk
 include $(topsrcdir)/config/rules.mk
 
 _TEST_FILES = \
   helpers.js \
   test_create_objectStore.html \
   test_event_source.html \
+  test_global_data.html \
   test_objectStore_inline_autoincrement_key_added_on_put.html \
   test_objectStore_remove_values.html \
   test_open_empty_db.html \
   test_open_objectStore.html \
   test_put_get_values.html \
   test_put_get_values_autoIncrement.html \
   test_remove_objectStore.html \
   test_setVersion.html \
new file mode 100644
--- /dev/null
+++ b/dom/indexedDB/test/test_global_data.html
@@ -0,0 +1,61 @@
+<!--
+  Any copyright is dedicated to the Public Domain.
+  http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<html>
+<head>
+  <title>Indexed Database Property Test</title>
+
+  <script type="text/javascript" src="/MochiKit/packed.js"></script>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+
+  <script type="text/javascript;version=1.7">
+    function testSteps()
+    {
+      const name = window.location.pathname;
+      const description = "My Test Database";
+      const objectStore =  { name: "Objects", keyPath: "id", autoIncr: true };
+
+      let request = indexedDB.open(name, description);
+      request.onerror = errorHandler;
+      request.onsuccess = grabEventAndContinueHandler;
+      let event = yield;
+
+      let db1 = event.result;
+
+      request = indexedDB.open(name, description);
+      request.onerror = errorHandler;
+      request.onsuccess = grabEventAndContinueHandler;
+      event = yield;
+
+      let db2 = event.result;
+
+      ok(db1 !== db2, "Databases are not the same object");
+      is(db1.objectStoreNames.length, 0, "No objectStores in db1");
+      is(db2.objectStoreNames.length, 0, "No objectStores in db2");
+
+      request = db1.createObjectStore(objectStore.name,
+                                      objectStore.keyPath,
+                                      objectStore.autoIncr);
+      request.onerror = errorHandler;
+      request.onsuccess = grabEventAndContinueHandler;
+      event = yield;
+
+      ok(db1 !== db2, "Databases are not the same object");
+      is(db1.objectStoreNames.length, 1, "1 objectStore in db1");
+      is(db1.objectStoreNames.item(0), objectStore.name, "Correct name");
+
+      is(db2.objectStoreNames.length, 1, "1 objectStore in db2");
+      is(db2.objectStoreNames.item(0), objectStore.name, "Correct name");
+
+      finishTest();
+      yield;
+    }
+  </script>
+  <script type="text/javascript;version=1.7" src="helpers.js"></script>
+</head>
+
+<body onload="runTest();"></body>
+
+</html>
--- a/layout/build/nsLayoutStatics.cpp
+++ b/layout/build/nsLayoutStatics.cpp
@@ -127,16 +127,18 @@ PRBool NS_SVGEnabled();
 
 #include "nsError.h"
 #include "nsTraceRefcnt.h"
 
 #include "nsCycleCollector.h"
 #include "nsJSEnvironment.h"
 #include "nsContentSink.h"
 
+#include "mozilla/dom/indexedDB/IndexedDatabase.h"
+
 extern void NS_ShutdownChainItemPool();
 
 static nsrefcnt sLayoutStaticRefcnt;
 
 nsresult
 nsLayoutStatics::Initialize()
 {
   NS_ASSERTION(sLayoutStaticRefcnt == 0,
@@ -386,16 +388,18 @@ nsLayoutStatics::Shutdown()
 
   nsRegion::ShutdownStatic();
 
   NS_ShutdownChainItemPool();
 
   nsFrameList::Shutdown();
 
   nsFileControlFrame::DestroyUploadLastDir();
+
+  mozilla::dom::indexedDB::Shutdown();
 }
 
 void
 nsLayoutStatics::AddRef()
 {
   NS_ASSERTION(NS_IsMainThread(),
                "nsLayoutStatics reference counting must be on main thread");