Bug 692669 - 'IndexedDB: remove nsIVariant from IDB* interfaces'. r=sicking.
☠☠ backed out by 72042cd154b9 ☠ ☠
authorBen Turner <bent.mozilla@gmail.com>
Wed, 02 Nov 2011 18:03:15 -0700
changeset 79644 eb32aca6d54da877fed264274d69b4354ff03044
parent 79643 5e75c166fd2c56138577a9f19ca6e07dd8bd8443
child 79645 b5c0bfd03fcfc74e43fb9fa76fdc35e5ebdfe43a
push id21418
push usermak77@bonardo.net
push dateThu, 03 Nov 2011 14:57:14 +0000
treeherdermozilla-central@6cbeabc07c59 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssicking
bugs692669
milestone10.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 692669 - 'IndexedDB: remove nsIVariant from IDB* interfaces'. r=sicking.
content/base/public/nsINode.h
dom/indexedDB/DatabaseInfo.h
dom/indexedDB/IDBCursor.cpp
dom/indexedDB/IDBCursor.h
dom/indexedDB/IDBDatabase.cpp
dom/indexedDB/IDBIndex.cpp
dom/indexedDB/IDBKeyRange.cpp
dom/indexedDB/IDBKeyRange.h
dom/indexedDB/IDBObjectStore.cpp
dom/indexedDB/IDBObjectStore.h
dom/indexedDB/IDBRequest.cpp
dom/indexedDB/Key.h
dom/indexedDB/Makefile.in
dom/indexedDB/nsIIDBCursor.idl
dom/indexedDB/nsIIDBCursorWithValue.idl
dom/indexedDB/nsIIDBDatabase.idl
dom/indexedDB/nsIIDBDatabaseException.idl
dom/indexedDB/nsIIDBFactory.idl
dom/indexedDB/nsIIDBIndex.idl
dom/indexedDB/nsIIDBKeyRange.idl
dom/indexedDB/nsIIDBObjectStore.idl
dom/indexedDB/nsIIDBOpenDBRequest.idl
dom/indexedDB/nsIIDBRequest.idl
dom/indexedDB/nsIIDBTransaction.idl
dom/indexedDB/nsIIDBVersionChangeEvent.idl
dom/indexedDB/nsIIndexedDatabaseManager.idl
dom/indexedDB/test/test_object_identity.html
js/xpconnect/public/nsAutoJSValHolder.h
--- a/content/base/public/nsINode.h
+++ b/content/base/public/nsINode.h
@@ -46,16 +46,23 @@
 #include "nsCOMPtr.h"
 #include "nsWrapperCache.h"
 #include "nsIProgrammingLanguage.h" // for ::JAVASCRIPT
 #include "nsDOMError.h"
 #include "nsDOMString.h"
 #include "jspubtd.h"
 #include "nsDOMMemoryReporter.h"
 
+// Including 'windows.h' will #define GetClassInfo to something else.
+#ifdef XP_WIN
+#ifdef GetClassInfo
+#undef GetClassInfo
+#endif
+#endif
+
 class nsIContent;
 class nsIDocument;
 class nsIDOMEvent;
 class nsIDOMNode;
 class nsIDOMElement;
 class nsIDOMNodeList;
 class nsINodeList;
 class nsIPresShell;
--- a/dom/indexedDB/DatabaseInfo.h
+++ b/dom/indexedDB/DatabaseInfo.h
@@ -38,16 +38,17 @@
  * ***** 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"
 
+#include "Key.h"
 #include "IDBObjectStore.h"
 
 BEGIN_INDEXEDDB_NAMESPACE
 
 struct DatabaseInfo
 {
 #ifdef NS_BUILD_REFCNT_LOGGING
   DatabaseInfo();
--- a/dom/indexedDB/IDBCursor.cpp
+++ b/dom/indexedDB/IDBCursor.cpp
@@ -34,18 +34,16 @@
  * 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 "IDBCursor.h"
 
-#include "nsIVariant.h"
-
 #include "jscntxt.h"
 #include "mozilla/storage.h"
 #include "nsComponentManagerUtils.h"
 #include "nsContentUtils.h"
 #include "nsDOMClassInfoID.h"
 #include "nsEventDispatcher.h"
 #include "nsJSUtils.h"
 #include "nsThreadUtils.h"
@@ -270,18 +268,20 @@ IDBCursor::CreateCommon(IDBRequest* aReq
   cursor->mRangeKey = aRangeKey;
 
   return cursor.forget();
 }
 
 IDBCursor::IDBCursor()
 : mType(OBJECTSTORE),
   mDirection(nsIIDBCursor::NEXT),
+  mCachedKey(JSVAL_VOID),
   mCachedPrimaryKey(JSVAL_VOID),
   mCachedValue(JSVAL_VOID),
+  mHaveCachedKey(false),
   mHaveCachedPrimaryKey(false),
   mHaveCachedValue(false),
   mRooted(false),
   mContinueCalled(false),
   mHaveValue(true)
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 }
@@ -306,37 +306,45 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(
                                                        nsIDOMEventTarget)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mObjectStore)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mIndex)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mOwner)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mScriptContext)
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(IDBCursor)
+  NS_ASSERTION(tmp->mHaveCachedKey || JSVAL_IS_VOID(tmp->mCachedKey),
+               "Should have a cached key");
   NS_ASSERTION(tmp->mHaveCachedPrimaryKey ||
                JSVAL_IS_VOID(tmp->mCachedPrimaryKey),
                "Should have a cached primary key");
   NS_ASSERTION(tmp->mHaveCachedValue || JSVAL_IS_VOID(tmp->mCachedValue),
                "Should have a cached value");
-  if (JSVAL_IS_GCTHING(tmp->mCachedValue)) {
-    void *gcThing = JSVAL_TO_GCTHING(tmp->mCachedValue);
-    NS_IMPL_CYCLE_COLLECTION_TRACE_JS_CALLBACK(gcThing, "mCachedValue")
+  if (JSVAL_IS_GCTHING(tmp->mCachedKey)) {
+    void *gcThing = JSVAL_TO_GCTHING(tmp->mCachedKey);
+    NS_IMPL_CYCLE_COLLECTION_TRACE_JS_CALLBACK(gcThing, "mCachedKey")
   }
   if (JSVAL_IS_GCTHING(tmp->mCachedPrimaryKey)) {
     void *gcThing = JSVAL_TO_GCTHING(tmp->mCachedPrimaryKey);
     NS_IMPL_CYCLE_COLLECTION_TRACE_JS_CALLBACK(gcThing, "mCachedPrimaryKey")
   }
+  if (JSVAL_IS_GCTHING(tmp->mCachedValue)) {
+    void *gcThing = JSVAL_TO_GCTHING(tmp->mCachedValue);
+    NS_IMPL_CYCLE_COLLECTION_TRACE_JS_CALLBACK(gcThing, "mCachedValue")
+  }
 NS_IMPL_CYCLE_COLLECTION_TRACE_END
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(IDBCursor)
   // Don't unlink mObjectStore, mIndex, or mTransaction!
   if (tmp->mRooted) {
     NS_DROP_JS_OBJECTS(tmp, IDBCursor);
+    tmp->mCachedKey = JSVAL_VOID;
     tmp->mCachedPrimaryKey = JSVAL_VOID;
     tmp->mCachedValue = JSVAL_VOID;
+    tmp->mHaveCachedKey = false;
     tmp->mHaveCachedPrimaryKey = false;
     tmp->mHaveCachedValue = false;
     tmp->mRooted = false;
     tmp->mHaveValue = false;
   }
   NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mRequest)
   NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mOwner)
   NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mScriptContext)
@@ -373,53 +381,43 @@ IDBCursor::GetSource(nsISupports** aSour
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
   return mType == OBJECTSTORE ?
          CallQueryInterface(mObjectStore, aSource) :
          CallQueryInterface(mIndex, aSource);
 }
 
 NS_IMETHODIMP
-IDBCursor::GetKey(nsIVariant** aKey)
+IDBCursor::GetKey(JSContext* aCx,
+                  jsval* aKey)
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
-  if (!mCachedKey) {
-    nsresult rv;
-    nsCOMPtr<nsIWritableVariant> variant =
-      do_CreateInstance(NS_VARIANT_CONTRACTID, &rv);
-    NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
-
-    NS_ASSERTION(!mKey.IsUnset() || !mHaveValue, "Bad key!");
+  NS_ASSERTION(!mKey.IsUnset() || !mHaveValue, "Bad key!");
 
-    if (!mHaveValue) {
-      rv = variant->SetAsVoid();
-    }
-    else if (mKey.IsString()) {
-      rv = variant->SetAsAString(mKey.StringValue());
-    }
-    else if (mKey.IsInt()) {
-      rv = variant->SetAsInt64(mKey.IntValue());
-    }
-    else {
-      NS_NOTREACHED("Huh?!");
-    }
-    NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
-
-    rv = variant->SetWritable(false);
-    NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
-
-    nsIWritableVariant* result;
-    variant.forget(&result);
-
-    mCachedKey = dont_AddRef(static_cast<nsIVariant*>(result));
+  if (!mHaveValue) {
+    *aKey = JSVAL_VOID;
+    return NS_OK;
   }
 
-  nsCOMPtr<nsIVariant> result(mCachedKey);
-  result.forget(aKey);
+  if (!mHaveCachedKey) {
+    if (!mRooted) {
+      NS_HOLD_JS_OBJECTS(this, IDBCursor);
+      mRooted = true;
+    }
+
+    JSAutoRequest ar(aCx);
+
+    nsresult rv = mKey.ToJSVal(aCx, &mCachedKey);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    mHaveCachedKey = true;
+  }
+
+  *aKey = mCachedKey;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 IDBCursor::GetPrimaryKey(JSContext* aCx,
                          jsval* aValue)
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
@@ -435,19 +433,19 @@ IDBCursor::GetPrimaryKey(JSContext* aCx,
       mRooted = true;
     }
 
     JSAutoRequest ar(aCx);
 
     NS_ASSERTION(mType == OBJECTSTORE ? !mKey.IsUnset() :
                                         !mObjectKey.IsUnset(), "Bad key!");
 
-    nsresult rv =
-      IDBObjectStore::GetJSValFromKey(mType == OBJECTSTORE ? mKey : mObjectKey,
-                                      aCx, &mCachedPrimaryKey);
+    const Key& key = mType == OBJECTSTORE ? mKey : mObjectKey;
+
+    nsresult rv = key.ToJSVal(aCx, &mCachedPrimaryKey);
     NS_ENSURE_SUCCESS(rv, rv);
 
     mHaveCachedPrimaryKey = true;
   }
 
   *aValue = mCachedPrimaryKey;
   return NS_OK;
 }
@@ -493,17 +491,17 @@ IDBCursor::Continue(const jsval &aKey,
     return NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR;
   }
 
   if (!mHaveValue || mContinueCalled) {
     return NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR;
   }
 
   Key key;
-  nsresult rv = IDBObjectStore::GetKeyFromJSVal(aKey, aCx, key);
+  nsresult rv = key.SetFromJSVal(aCx, aKey);
   NS_ENSURE_SUCCESS(rv, rv);
 
   if (!key.IsUnset()) {
     switch (mDirection) {
       case nsIIDBCursor::NEXT:
       case nsIIDBCursor::NEXT_NO_DUPLICATE:
         if (key <= mKey) {
           return NS_ERROR_DOM_INDEXEDDB_DATA_ERR;
@@ -583,17 +581,17 @@ IDBCursor::Update(const jsval& aValue,
   NS_ASSERTION(mObjectStore, "This cannot be null!");
   NS_ASSERTION(!mKey.IsUnset() , "Bad key!");
   NS_ASSERTION(mType != INDEXOBJECT || !mObjectKey.IsUnset(), "Bad key!");
 
   nsresult rv;
 
   JSAutoRequest ar(aCx);
 
-  const Key& objectKey = (mType == OBJECTSTORE) ? mKey : mObjectKey;
+  Key& objectKey = (mType == OBJECTSTORE) ? mKey : mObjectKey;
 
   if (!mObjectStore->KeyPath().IsEmpty()) {
     // This has to be an object.
     if (JSVAL_IS_PRIMITIVE(aValue)) {
       return NS_ERROR_DOM_INDEXEDDB_DATA_ERR;
     }
 
     // Make sure the object given has the correct keyPath value set on it.
@@ -601,30 +599,30 @@ IDBCursor::Update(const jsval& aValue,
 
     jsval prop;
     JSBool ok = JS_GetUCProperty(aCx, JSVAL_TO_OBJECT(aValue),
                                  reinterpret_cast<const jschar*>(keyPath.get()),
                                  keyPath.Length(), &prop);
     NS_ENSURE_TRUE(ok, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
     Key key;
-    rv = IDBObjectStore::GetKeyFromJSVal(prop, aCx, key);
+    rv = key.SetFromJSVal(aCx, prop);
     if (NS_FAILED(rv)) {
       return rv;
     }
 
     if (key != objectKey) {
       return NS_ERROR_DOM_INDEXEDDB_DATA_ERR;
     }
 
     return mObjectStore->Put(aValue, JSVAL_VOID, aCx, 0, _retval);
   }
 
   jsval keyVal;
-  rv = IDBObjectStore::GetJSValFromKey(objectKey, aCx, &keyVal);
+  rv = objectKey.ToJSVal(aCx, &keyVal);
   NS_ENSURE_SUCCESS(rv, rv);
 
   return mObjectStore->Put(aValue, keyVal, aCx, 1, _retval);
 }
 
 NS_IMETHODIMP
 IDBCursor::Delete(JSContext* aCx,
                   nsIIDBRequest** _retval)
@@ -641,20 +639,20 @@ IDBCursor::Delete(JSContext* aCx,
 
   if (!mHaveValue || mType == INDEXKEY) {
     return NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR;
   }
 
   NS_ASSERTION(mObjectStore, "This cannot be null!");
   NS_ASSERTION(!mKey.IsUnset() , "Bad key!");
 
-  const Key& objectKey = (mType == OBJECTSTORE) ? mKey : mObjectKey;
+  Key& objectKey = (mType == OBJECTSTORE) ? mKey : mObjectKey;
 
   jsval key;
-  nsresult rv = IDBObjectStore::GetJSValFromKey(objectKey, aCx, &key);
+  nsresult rv = objectKey.ToJSVal(aCx, &key);
   NS_ENSURE_SUCCESS(rv, rv);
 
   return mObjectStore->Delete(key, aCx, _retval);
 }
 
 nsresult
 ContinueHelper::DoDatabaseWork(mozIStorageConnection* aConnection)
 {
@@ -683,46 +681,47 @@ ContinueHelper::DoDatabaseWork(mozIStora
   rv = stmt->ExecuteStep(&hasResult);
   NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
   if (hasResult) {
     rv = GatherResultsFromStatement(stmt);
     NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
   }
   else {
-    mKey = Key::UNSETKEY;
+    mKey.Unset();
   }
 
   return NS_OK;
 }
 
 nsresult
 ContinueHelper::GetSuccessResult(JSContext* aCx,
                                  jsval* aVal)
 {
   // Remove cached stuff from last time.
-  mCursor->mCachedKey = nsnull;
-  mCursor->mCachedValue = JSVAL_VOID;
+  mCursor->mCachedKey = JSVAL_VOID;
   mCursor->mCachedPrimaryKey = JSVAL_VOID;
+  mCursor->mCachedValue = JSVAL_VOID;
+  mCursor->mHaveCachedKey = false;
+  mCursor->mHaveCachedPrimaryKey = false;
   mCursor->mHaveCachedValue = false;
-  mCursor->mHaveCachedPrimaryKey = false;
   mCursor->mContinueCalled = false;
 
   if (mKey.IsUnset()) {
     mCursor->mHaveValue = false;
     *aVal = JSVAL_VOID;
   }
   else {
     NS_ASSERTION(mCursor->mType == IDBCursor::OBJECTSTORE ||
                  !mObjectKey.IsUnset(), "Bad key!");
 
     // Set new values.
     mCursor->mKey = mKey;
     mCursor->mObjectKey = mObjectKey;
-    mCursor->mContinueToKey = Key::UNSETKEY;
+    mCursor->mContinueToKey.Unset();
 
     mCursor->mCloneBuffer.swap(mCloneBuffer);
     mCloneBuffer.clear();
 
     nsresult rv = WrapNative(aCx, mCursor, aVal);
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
@@ -741,65 +740,37 @@ ContinueObjectStoreHelper::BindArguments
   NS_NAMED_LITERAL_CSTRING(currentKeyName, "current_key");
   NS_NAMED_LITERAL_CSTRING(rangeKeyName, "range_key");
 
   // Bind current key.
   const Key& currentKey = mCursor->mContinueToKey.IsUnset() ?
                           mCursor->mKey :
                           mCursor->mContinueToKey;
 
-  if (currentKey.IsString()) {
-    rv = aStatement->BindStringByName(currentKeyName, currentKey.StringValue());
-  }
-  else if (currentKey.IsInt()) {
-    rv = aStatement->BindInt64ByName(currentKeyName, currentKey.IntValue());
-  }
-  else {
-    NS_NOTREACHED("Bad key!");
-  }
-  NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
+  rv = currentKey.BindToStatement(aStatement, currentKeyName);
+  NS_ENSURE_SUCCESS(rv, rv);
 
   // Bind range key if it is specified.
   const Key& rangeKey = mCursor->mRangeKey;
 
   if (!rangeKey.IsUnset()) {
-    if (rangeKey.IsString()) {
-      rv = aStatement->BindStringByName(rangeKeyName, rangeKey.StringValue());
-    }
-    else if (rangeKey.IsInt()) {
-      rv = aStatement->BindInt64ByName(rangeKeyName, rangeKey.IntValue());
-    }
-    else {
-      NS_NOTREACHED("Bad key!");
-    }
-    NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
+    rv = rangeKey.BindToStatement(aStatement, rangeKeyName);
+    NS_ENSURE_SUCCESS(rv, rv);
   }
 
   return NS_OK;
 }
 
 nsresult
 ContinueObjectStoreHelper::GatherResultsFromStatement(
                                                mozIStorageStatement* aStatement)
 {
   // Figure out what kind of key we have next.
-  PRInt32 keyType;
-  nsresult rv = aStatement->GetTypeOfIndex(0, &keyType);
-  NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
-
-  if (keyType == mozIStorageStatement::VALUE_TYPE_INTEGER) {
-    mKey = aStatement->AsInt64(0);
-  }
-  else if (keyType == mozIStorageStatement::VALUE_TYPE_TEXT) {
-    rv = aStatement->GetString(0, mKey.ToString());
-    NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
-  }
-  else {
-    NS_NOTREACHED("Bad SQLite type!");
-  }
+  nsresult rv = mKey.SetFromStatement(aStatement, 0);
+  NS_ENSURE_SUCCESS(rv, rv);
 
   rv = IDBObjectStore::GetStructuredCloneDataFromStatement(aStatement, 1,
                                                            mCloneBuffer);
   NS_ENSURE_SUCCESS(rv, rv);
 
   return NS_OK;
 }
 
@@ -813,152 +784,62 @@ ContinueIndexHelper::BindArgumentsToStat
 
   NS_NAMED_LITERAL_CSTRING(currentKeyName, "current_key");
 
   // Bind current key.
   const Key& currentKey = mCursor->mContinueToKey.IsUnset() ?
                           mCursor->mKey :
                           mCursor->mContinueToKey;
 
-  if (currentKey.IsString()) {
-    rv = aStatement->BindStringByName(currentKeyName, currentKey.StringValue());
-  }
-  else if (currentKey.IsInt()) {
-    rv = aStatement->BindInt64ByName(currentKeyName, currentKey.IntValue());
-  }
-  else {
-    NS_NOTREACHED("Bad key!");
-  }
-  NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
+  rv = currentKey.BindToStatement(aStatement, currentKeyName);
+  NS_ENSURE_SUCCESS(rv, rv);
 
   // Bind range key if it is specified.
   if (!mCursor->mRangeKey.IsUnset()) {
     NS_NAMED_LITERAL_CSTRING(rangeKeyName, "range_key");
-    if (mCursor->mRangeKey.IsString()) {
-      rv = aStatement->BindStringByName(rangeKeyName,
-                                        mCursor->mRangeKey.StringValue());
-    }
-    else if (mCursor->mRangeKey.IsInt()) {
-      rv = aStatement->BindInt64ByName(rangeKeyName,
-                                       mCursor->mRangeKey.IntValue());
-    }
-    else {
-      NS_NOTREACHED("Bad key!");
-    }
-    NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
+    rv = mCursor->mRangeKey.BindToStatement(aStatement, rangeKeyName);
+    NS_ENSURE_SUCCESS(rv, rv);
   }
 
   // Bind object key if duplicates are allowed and we're not continuing to a
   // specific key.
   if ((mCursor->mDirection == nsIIDBCursor::NEXT ||
        mCursor->mDirection == nsIIDBCursor::PREV) &&
        mCursor->mContinueToKey.IsUnset()) {
     NS_ASSERTION(!mCursor->mObjectKey.IsUnset(), "Bad key!");
 
     NS_NAMED_LITERAL_CSTRING(objectKeyName, "object_key");
-    if (mCursor->mObjectKey.IsString()) {
-      rv = aStatement->BindStringByName(objectKeyName,
-                                        mCursor->mObjectKey.StringValue());
-    }
-    else if (mCursor->mObjectKey.IsInt()) {
-      rv = aStatement->BindInt64ByName(objectKeyName,
-                                       mCursor->mObjectKey.IntValue());
-    }
-    else {
-      NS_NOTREACHED("Bad key!");
-    }
-    NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
+    rv = mCursor->mObjectKey.BindToStatement(aStatement, objectKeyName);
+    NS_ENSURE_SUCCESS(rv, rv);
   }
 
   return NS_OK;
 }
 
 nsresult
 ContinueIndexHelper::GatherResultsFromStatement(
                                                mozIStorageStatement* aStatement)
 {
-  PRInt32 keyType;
-  nsresult rv = aStatement->GetTypeOfIndex(0, &keyType);
-  NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
-
-  NS_ASSERTION(keyType == mozIStorageStatement::VALUE_TYPE_INTEGER ||
-               keyType == mozIStorageStatement::VALUE_TYPE_TEXT,
-               "Bad key type!");
+  nsresult rv = mKey.SetFromStatement(aStatement, 0);
+  NS_ENSURE_SUCCESS(rv, rv);
 
-  if (keyType == mozIStorageStatement::VALUE_TYPE_INTEGER) {
-    mKey = aStatement->AsInt64(0);
-  }
-  else if (keyType == mozIStorageStatement::VALUE_TYPE_TEXT) {
-    rv = aStatement->GetString(0, mKey.ToString());
-    NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
-  }
-  else {
-    NS_NOTREACHED("Bad SQLite type!");
-  }
-
-  rv = aStatement->GetTypeOfIndex(1, &keyType);
-  NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
-
-  NS_ASSERTION(keyType == mozIStorageStatement::VALUE_TYPE_INTEGER ||
-               keyType == mozIStorageStatement::VALUE_TYPE_TEXT,
-               "Bad key type!");
-
-  if (keyType == mozIStorageStatement::VALUE_TYPE_INTEGER) {
-    mObjectKey = aStatement->AsInt64(1);
-  }
-  else if (keyType == mozIStorageStatement::VALUE_TYPE_TEXT) {
-    rv = aStatement->GetString(1, mObjectKey.ToString());
-    NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
-  }
-  else {
-    NS_NOTREACHED("Bad SQLite type!");
-  }
+  rv = mObjectKey.SetFromStatement(aStatement, 1);
+  NS_ENSURE_SUCCESS(rv, rv);
 
   return NS_OK;
 }
 
 nsresult
 ContinueIndexObjectHelper::GatherResultsFromStatement(
                                                mozIStorageStatement* aStatement)
 {
-  PRInt32 keyType;
-  nsresult rv = aStatement->GetTypeOfIndex(0, &keyType);
-  NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
-
-  NS_ASSERTION(keyType == mozIStorageStatement::VALUE_TYPE_INTEGER ||
-               keyType == mozIStorageStatement::VALUE_TYPE_TEXT,
-               "Bad key type!");
+  nsresult rv = mKey.SetFromStatement(aStatement, 0);
+  NS_ENSURE_SUCCESS(rv, rv);
 
-  if (keyType == mozIStorageStatement::VALUE_TYPE_INTEGER) {
-    mKey = aStatement->AsInt64(0);
-  }
-  else if (keyType == mozIStorageStatement::VALUE_TYPE_TEXT) {
-    rv = aStatement->GetString(0, mKey.ToString());
-    NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
-  }
-  else {
-    NS_NOTREACHED("Bad SQLite type!");
-  }
-
-  rv = aStatement->GetTypeOfIndex(1, &keyType);
-  NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
-
-  NS_ASSERTION(keyType == mozIStorageStatement::VALUE_TYPE_INTEGER ||
-               keyType == mozIStorageStatement::VALUE_TYPE_TEXT,
-               "Bad key type!");
-
-  if (keyType == mozIStorageStatement::VALUE_TYPE_INTEGER) {
-    mObjectKey = aStatement->AsInt64(1);
-  }
-  else if (keyType == mozIStorageStatement::VALUE_TYPE_TEXT) {
-    rv = aStatement->GetString(1, mObjectKey.ToString());
-    NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
-  }
-  else {
-    NS_NOTREACHED("Bad SQLite type!");
-  }
+  rv = mObjectKey.SetFromStatement(aStatement, 1);
+  NS_ENSURE_SUCCESS(rv, rv);
 
   rv = IDBObjectStore::GetStructuredCloneDataFromStatement(aStatement, 2,
                                                            mCloneBuffer);
   NS_ENSURE_SUCCESS(rv, rv);
 
   return NS_OK;
 }
--- a/dom/indexedDB/IDBCursor.h
+++ b/dom/indexedDB/IDBCursor.h
@@ -37,16 +37,17 @@
  *
  * ***** END LICENSE BLOCK ***** */
 
 #ifndef mozilla_dom_indexeddb_idbcursor_h__
 #define mozilla_dom_indexeddb_idbcursor_h__
 
 #include "mozilla/dom/indexedDB/IndexedDatabase.h"
 #include "mozilla/dom/indexedDB/IDBObjectStore.h"
+#include "mozilla/dom/indexedDB/Key.h"
 
 #include "nsIIDBCursorWithValue.h"
 
 #include "nsCycleCollectionParticipant.h"
 
 class nsIRunnable;
 class nsIScriptContext;
 class nsPIDOMWindow;
@@ -145,35 +146,34 @@ protected:
   nsRefPtr<IDBRequest> mRequest;
   nsRefPtr<IDBTransaction> mTransaction;
   nsRefPtr<IDBObjectStore> mObjectStore;
   nsRefPtr<IDBIndex> mIndex;
 
   nsCOMPtr<nsIScriptContext> mScriptContext;
   nsCOMPtr<nsPIDOMWindow> mOwner;
 
-  // Not cycle-collected, this is guaranteed to be primitive!
-  nsCOMPtr<nsIVariant> mCachedKey;
-
   Type mType;
   PRUint16 mDirection;
   nsCString mContinueQuery;
   nsCString mContinueToQuery;
 
   // These are cycle-collected!
+  jsval mCachedKey;
   jsval mCachedPrimaryKey;
   jsval mCachedValue;
 
   Key mRangeKey;
 
   Key mKey;
   Key mObjectKey;
   JSAutoStructuredCloneBuffer mCloneBuffer;
   Key mContinueToKey;
 
+  bool mHaveCachedKey;
   bool mHaveCachedPrimaryKey;
   bool mHaveCachedValue;
   bool mRooted;
   bool mContinueCalled;
   bool mHaveValue;
 };
 
 END_INDEXEDDB_NAMESPACE
--- a/dom/indexedDB/IDBDatabase.cpp
+++ b/dom/indexedDB/IDBDatabase.cpp
@@ -123,26 +123,16 @@ public:
   }
 
 private:
   // In-params.
   PRInt64 mObjectStoreId;
 };
 
 NS_STACK_CLASS
-class AutoFree
-{
-public:
-  AutoFree(void* aPtr) : mPtr(aPtr) { }
-  ~AutoFree() { NS_Free(mPtr); }
-private:
-  void* mPtr;
-};
-
-NS_STACK_CLASS
 class AutoRemoveObjectStore
 {
 public:
   AutoRemoveObjectStore(nsIAtom* aId, const nsAString& aName)
   : mId(aId), mName(aName)
   { }
 
   ~AutoRemoveObjectStore()
@@ -157,85 +147,16 @@ public:
     mId = 0;
   }
 
 private:
   nsCOMPtr<nsIAtom> mId;
   nsString mName;
 };
 
-inline
-nsresult
-ConvertVariantToStringArray(nsIVariant* aVariant,
-                            nsTArray<nsString>& aStringArray)
-{
-#ifdef DEBUG
-  PRUint16 type;
-  NS_ASSERTION(NS_SUCCEEDED(aVariant->GetDataType(&type)) &&
-               type == nsIDataType::VTYPE_ARRAY, "Bad arg!");
-#endif
-
-  PRUint16 valueType;
-  nsIID iid;
-  PRUint32 valueCount;
-  void* rawArray;
-
-  nsresult rv = aVariant->GetAsArray(&valueType, &iid, &valueCount, &rawArray);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  AutoFree af(rawArray);
-
-  // Just delete anything that we don't expect and return.
-  if (valueType != nsIDataType::VTYPE_WCHAR_STR) {
-    switch (valueType) {
-      case nsIDataType::VTYPE_ID:
-      case nsIDataType::VTYPE_CHAR_STR: {
-        char** charArray = reinterpret_cast<char**>(rawArray);
-        for (PRUint32 index = 0; index < valueCount; index++) {
-          if (charArray[index]) {
-            NS_Free(charArray[index]);
-          }
-        }
-      } break;
-
-      case nsIDataType::VTYPE_INTERFACE:
-      case nsIDataType::VTYPE_INTERFACE_IS: {
-        nsISupports** supportsArray = reinterpret_cast<nsISupports**>(rawArray);
-        for (PRUint32 index = 0; index < valueCount; index++) {
-          NS_IF_RELEASE(supportsArray[index]);
-        }
-      } break;
-
-      default: {
-        // The other types are primitives that do not need to be freed.
-      }
-    }
-
-    return NS_ERROR_ILLEGAL_VALUE;
-  }
-
-  PRUnichar** strings = reinterpret_cast<PRUnichar**>(rawArray);
-
-  for (PRUint32 index = 0; index < valueCount; index++) {
-    nsString* newString = aStringArray.AppendElement();
-
-    if (!newString) {
-      NS_ERROR("Out of memory?");
-      for (; index < valueCount; index++) {
-        NS_Free(strings[index]);
-      }
-      return NS_ERROR_OUT_OF_MEMORY;
-    }
-
-    newString->Adopt(strings[index], -1);
-  }
-
-  return NS_OK;
-}
-
 } // anonymous namespace
 
 // static
 already_AddRefed<IDBDatabase>
 IDBDatabase::Create(nsIScriptContext* aScriptContext,
                     nsPIDOMWindow* aOwner,
                     DatabaseInfo* aDatabaseInfo,
                     const nsACString& aASCIIOrigin)
@@ -689,147 +610,147 @@ IDBDatabase::DeleteObjectStore(const nsA
   nsresult rv = helper->DispatchToTransactionPool();
   NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
   ObjectStoreInfo::Remove(mDatabaseId, aName);
   return NS_OK;
 }
 
 NS_IMETHODIMP
-IDBDatabase::Transaction(nsIVariant* aStoreNames,
+IDBDatabase::Transaction(const jsval& aStoreNames,
                          PRUint16 aMode,
-                         PRUint32 aTimeout,
                          JSContext* aCx,
                          PRUint8 aOptionalArgCount,
                          nsIIDBTransaction** _retval)
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
   if (IndexedDatabaseManager::IsShuttingDown()) {
     return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
   }
 
   if (mClosed) {
     return NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR;
   }
 
+  DatabaseInfo* info;
+  if (!DatabaseInfo::Get(mDatabaseId, &info)) {
+    NS_ERROR("This should never fail!");
+  }
+
+  if (info->runningVersionChange) {
+    return NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR;
+  }
+
   if (aOptionalArgCount) {
     if (aMode != nsIIDBTransaction::READ_WRITE &&
         aMode != nsIIDBTransaction::READ_ONLY) {
       return NS_ERROR_DOM_INDEXEDDB_NON_TRANSIENT_ERR;
     }
   }
   else {
     aMode = nsIIDBTransaction::READ_ONLY;
   }
 
-  if (aOptionalArgCount <= 1) {
-    aTimeout = kDefaultDatabaseTimeoutSeconds;
-  }
-
-  PRUint16 type;
-  nsresult rv = aStoreNames->GetDataType(&type);
-  NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
-
-  DatabaseInfo* info;
-  if (!DatabaseInfo::Get(mDatabaseId, &info)) {
-    NS_ERROR("This should never fail!");
-  }
-
-  if (info->runningVersionChange) {
-    return NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR;
-  }
-
+  nsresult rv;
   nsTArray<nsString> storesToOpen;
 
-  switch (type) {
-    case nsIDataType::VTYPE_VOID:
-    case nsIDataType::VTYPE_EMPTY:
-    case nsIDataType::VTYPE_EMPTY_ARRAY: {
-      // Empty, request all object stores
-      if (!info->GetObjectStoreNames(storesToOpen)) {
-        NS_WARNING("Out of memory?");
+  if (!JSVAL_IS_PRIMITIVE(aStoreNames)) {
+    JSObject* obj = JSVAL_TO_OBJECT(aStoreNames);
+
+    // See if this is a JS array.
+    if (JS_IsArrayObject(aCx, obj)) {
+      jsuint length;
+      if (!JS_GetArrayLength(aCx, obj, &length)) {
         return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
       }
-    } break;
 
-    case nsIDataType::VTYPE_WSTRING_SIZE_IS: {
-      // Single name
-      nsString name;
-      rv = aStoreNames->GetAsAString(name);
-      NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
-
-      if (!info->ContainsStoreName(name)) {
-        return NS_ERROR_DOM_INDEXEDDB_NOT_FOUND_ERR;
+      if (!length) {
+        return NS_ERROR_DOM_INVALID_ACCESS_ERR;
       }
 
-      if (!storesToOpen.AppendElement(name)) {
-        NS_WARNING("Out of memory?");
-        return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
+      storesToOpen.SetCapacity(length);
+
+      for (jsuint index = 0; index < length; index++) {
+        jsval val;
+        JSString* jsstr;
+        nsDependentJSString str;
+        if (!JS_GetElement(aCx, obj, index, &val) ||
+            !(jsstr = JS_ValueToString(aCx, val)) ||
+            !str.init(aCx, jsstr)) {
+          return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
+        }
+
+        storesToOpen.AppendElement(str);
       }
-    } break;
 
-    case nsIDataType::VTYPE_ARRAY: {
-      nsTArray<nsString> names;
-      rv = ConvertVariantToStringArray(aStoreNames, names);
+      NS_ASSERTION(!storesToOpen.IsEmpty(),
+                   "Must have something here or else code below will "
+                   "misbehave!");
+    }
+    else {
+      // Perhaps some kind of wrapped object?
+      nsIXPConnect* xpc = nsContentUtils::XPConnect();
+      NS_ASSERTION(xpc, "This should never be null!");
+
+      nsCOMPtr<nsIXPConnectWrappedNative> wrapper;
+      rv = xpc->GetWrappedNativeOfJSObject(aCx, obj, getter_AddRefs(wrapper));
       NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
-      PRUint32 nameCount = names.Length();
-      for (PRUint32 nameIndex = 0; nameIndex < nameCount; nameIndex++) {
-        nsString& name = names[nameIndex];
+      if (wrapper) {
+        nsISupports* wrappedObject = wrapper->Native();
+        NS_ENSURE_TRUE(wrappedObject, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
+
+        // We only accept DOMStringList.
+        nsCOMPtr<nsIDOMDOMStringList> list = do_QueryInterface(wrappedObject);
+        if (list) {
+          PRUint32 length;
+          rv = list->GetLength(&length);
+          NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
-        if (!info->ContainsStoreName(name)) {
-          return NS_ERROR_DOM_INDEXEDDB_NOT_FOUND_ERR;
-        }
+          if (!length) {
+            return NS_ERROR_DOM_INVALID_ACCESS_ERR;
+          }
+
+          storesToOpen.SetCapacity(length);
 
-        if (!storesToOpen.AppendElement(name)) {
-          NS_WARNING("Out of memory?");
-          return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
+          for (PRUint32 index = 0; index < length; index++) {
+            nsString* item = storesToOpen.AppendElement();
+            NS_ASSERTION(item, "This should never fail!");
+
+            rv = list->Item(index, *item);
+            NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
+          }
+
+          NS_ASSERTION(!storesToOpen.IsEmpty(),
+                       "Must have something here or else code below will "
+                       "misbehave!");
         }
       }
-      NS_ASSERTION(nameCount == storesToOpen.Length(), "Should have bailed!");
-    } break;
-
-    case nsIDataType::VTYPE_INTERFACE:
-    case nsIDataType::VTYPE_INTERFACE_IS: {
-      nsCOMPtr<nsISupports> supports;
-      nsID *iid;
-      rv = aStoreNames->GetAsInterface(&iid, getter_AddRefs(supports));
-      NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
-
-      NS_Free(iid);
-
-      nsCOMPtr<nsIDOMDOMStringList> stringList(do_QueryInterface(supports));
-      if (!stringList) {
-        // We don't support anything other than nsIDOMDOMStringList.
-        return NS_ERROR_DOM_INDEXEDDB_NON_TRANSIENT_ERR;
-      }
+    }
+  }
 
-      PRUint32 stringCount;
-      rv = stringList->GetLength(&stringCount);
-      NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
-
-      for (PRUint32 stringIndex = 0; stringIndex < stringCount; stringIndex++) {
-        nsString name;
-        rv = stringList->Item(stringIndex, name);
-        NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
+  // If our list is empty here then the argument must have been an object that
+  // we don't support or a primitive. Either way we convert to a string.
+  if (storesToOpen.IsEmpty()) {
+    JSString* jsstr;
+    nsDependentJSString str;
+    if (!(jsstr = JS_ValueToString(aCx, aStoreNames)) ||
+        !str.init(aCx, jsstr)) {
+      return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
+    }
 
-        if (!info->ContainsStoreName(name)) {
-          return NS_ERROR_DOM_INDEXEDDB_NOT_FOUND_ERR;
-        }
+    storesToOpen.AppendElement(str);
+  }
 
-        if (!storesToOpen.AppendElement(name)) {
-          NS_WARNING("Out of memory?");
-          return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
-        }
-      }
-    } break;
-
-    default:
-      return NS_ERROR_DOM_INDEXEDDB_NON_TRANSIENT_ERR;
+  // Now check to make sure the object store names we collected actually exist.
+  for (PRUint32 index = 0; index < storesToOpen.Length(); index++) {
+    if (!info->ContainsStoreName(storesToOpen[index])) {
+      return NS_ERROR_DOM_INDEXEDDB_NOT_FOUND_ERR;
+    }
   }
 
   nsRefPtr<IDBTransaction> transaction =
     IDBTransaction::Create(this, storesToOpen, aMode,
                            kDefaultDatabaseTimeoutSeconds);
   NS_ENSURE_TRUE(transaction, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
   transaction.forget(_retval);
--- a/dom/indexedDB/IDBIndex.cpp
+++ b/dom/indexedDB/IDBIndex.cpp
@@ -47,16 +47,17 @@
 #include "nsDOMClassInfoID.h"
 #include "nsEventDispatcher.h"
 #include "nsThreadUtils.h"
 #include "mozilla/storage.h"
 
 #include "AsyncConnectionHelper.h"
 #include "IDBCursor.h"
 #include "IDBEvents.h"
+#include "IDBKeyRange.h"
 #include "IDBObjectStore.h"
 #include "IDBTransaction.h"
 #include "DatabaseInfo.h"
 
 USING_INDEXEDDB_NAMESPACE
 
 namespace {
 
@@ -162,91 +163,79 @@ protected:
 };
 
 class OpenKeyCursorHelper : public AsyncConnectionHelper
 {
 public:
   OpenKeyCursorHelper(IDBTransaction* aTransaction,
                       IDBRequest* aRequest,
                       IDBIndex* aIndex,
-                      const Key& aLowerKey,
-                      const Key& aUpperKey,
-                      bool aLowerOpen,
-                      bool aUpperOpen,
+                      IDBKeyRange* aKeyRange,
                       PRUint16 aDirection)
   : AsyncConnectionHelper(aTransaction, aRequest), mIndex(aIndex),
-    mLowerKey(aLowerKey), mUpperKey(aUpperKey), mLowerOpen(aLowerOpen),
-    mUpperOpen(aUpperOpen), mDirection(aDirection)
+    mKeyRange(aKeyRange), mDirection(aDirection)
   { }
 
   nsresult DoDatabaseWork(mozIStorageConnection* aConnection);
   nsresult GetSuccessResult(JSContext* aCx,
                             jsval* aVal);
 
   void ReleaseMainThreadObjects()
   {
     mIndex = nsnull;
+    mKeyRange = nsnull;
     AsyncConnectionHelper::ReleaseMainThreadObjects();
   }
 
 private:
   // In-params.
   nsRefPtr<IDBIndex> mIndex;
-  const Key mLowerKey;
-  const Key mUpperKey;
-  const bool mLowerOpen;
-  const bool mUpperOpen;
+  nsRefPtr<IDBKeyRange> mKeyRange;
   const PRUint16 mDirection;
 
   // Out-params.
   Key mKey;
   Key mObjectKey;
   nsCString mContinueQuery;
   nsCString mContinueToQuery;
   Key mRangeKey;
 };
 
 class OpenCursorHelper : public AsyncConnectionHelper
 {
 public:
   OpenCursorHelper(IDBTransaction* aTransaction,
                    IDBRequest* aRequest,
                    IDBIndex* aIndex,
-                   const Key& aLowerKey,
-                   const Key& aUpperKey,
-                   bool aLowerOpen,
-                   bool aUpperOpen,
+                   IDBKeyRange* aKeyRange,
                    PRUint16 aDirection)
   : AsyncConnectionHelper(aTransaction, aRequest), mIndex(aIndex),
-    mLowerKey(aLowerKey), mUpperKey(aUpperKey), mLowerOpen(aLowerOpen),
-    mUpperOpen(aUpperOpen), mDirection(aDirection)
+    mKeyRange(aKeyRange), mDirection(aDirection)
   { }
 
   ~OpenCursorHelper()
   {
     IDBObjectStore::ClearStructuredCloneBuffer(mCloneBuffer);
   }
 
   nsresult DoDatabaseWork(mozIStorageConnection* aConnection);
   nsresult GetSuccessResult(JSContext* aCx,
                             jsval* aVal);
 
   void ReleaseMainThreadObjects()
   {
     mIndex = nsnull;
+    mKeyRange = nsnull;
     AsyncConnectionHelper::ReleaseMainThreadObjects();
   }
 
 private:
   // In-params.
   nsRefPtr<IDBIndex> mIndex;
-  const Key mLowerKey;
-  const Key mUpperKey;
-  const bool mLowerOpen;
-  const bool mUpperOpen;
+  nsRefPtr<IDBKeyRange> mKeyRange;
   const PRUint16 mDirection;
 
   // Out-params.
   Key mKey;
   Key mObjectKey;
   JSAutoStructuredCloneBuffer mCloneBuffer;
   nsCString mContinueQuery;
   nsCString mContinueToQuery;
@@ -266,17 +255,17 @@ GenerateRequest(IDBIndex* aIndex)
 
 } // anonymous namespace
 
 // static
 already_AddRefed<IDBIndex>
 IDBIndex::Create(IDBObjectStore* aObjectStore,
                  const IndexInfo* aIndexInfo)
 {
-  NS_PRECONDITION(NS_IsMainThread(), "Wrong thread!");
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
   NS_ASSERTION(aObjectStore, "Null pointer!");
   NS_ASSERTION(aIndexInfo, "Null pointer!");
 
   IDBDatabase* database = aObjectStore->Transaction()->Database();
 
   nsRefPtr<IDBIndex> index = new IDBIndex();
 
   index->mScriptContext = database->ScriptContext();
@@ -292,22 +281,22 @@ IDBIndex::Create(IDBObjectStore* aObject
   return index.forget();
 }
 
 IDBIndex::IDBIndex()
 : mId(LL_MININT),
   mUnique(false),
   mAutoIncrement(false)
 {
-  NS_PRECONDITION(NS_IsMainThread(), "Wrong thread!");
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 }
 
 IDBIndex::~IDBIndex()
 {
-  NS_PRECONDITION(NS_IsMainThread(), "Wrong thread!");
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 }
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(IDBIndex)
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(IDBIndex)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mObjectStore)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mOwner)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mScriptContext)
@@ -328,193 +317,174 @@ NS_INTERFACE_MAP_END
 NS_IMPL_CYCLE_COLLECTING_ADDREF(IDBIndex)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(IDBIndex)
 
 DOMCI_DATA(IDBIndex, IDBIndex)
 
 NS_IMETHODIMP
 IDBIndex::GetName(nsAString& aName)
 {
-  NS_PRECONDITION(NS_IsMainThread(), "Wrong thread!");
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
   aName.Assign(mName);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 IDBIndex::GetStoreName(nsAString& aStoreName)
 {
-  NS_PRECONDITION(NS_IsMainThread(), "Wrong thread!");
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
   return mObjectStore->GetName(aStoreName);
 }
 
 NS_IMETHODIMP
 IDBIndex::GetKeyPath(nsAString& aKeyPath)
 {
-  NS_PRECONDITION(NS_IsMainThread(), "Wrong thread!");
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
   aKeyPath.Assign(mKeyPath);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 IDBIndex::GetUnique(bool* aUnique)
 {
-  NS_PRECONDITION(NS_IsMainThread(), "Wrong thread!");
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
   *aUnique = mUnique;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 IDBIndex::GetObjectStore(nsIIDBObjectStore** aObjectStore)
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
   nsCOMPtr<nsIIDBObjectStore> objectStore(mObjectStore);
   objectStore.forget(aObjectStore);
   return NS_OK;
 }
 
 NS_IMETHODIMP
-IDBIndex::Get(nsIVariant* aKey,
+IDBIndex::Get(const jsval& aKey,
+              JSContext* aCx,
               nsIIDBRequest** _retval)
 {
-  NS_PRECONDITION(NS_IsMainThread(), "Wrong thread!");
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
   IDBTransaction* transaction = mObjectStore->Transaction();
   if (!transaction->IsOpen()) {
     return NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR;
   }
 
   Key key;
-  nsresult rv = IDBObjectStore::GetKeyFromVariant(aKey, key);
+  nsresult rv = key.SetFromJSVal(aCx, aKey);
   if (NS_FAILED(rv)) {
     return rv;
   }
 
-  if (key.IsUnset()) {
-    return NS_ERROR_DOM_INDEXEDDB_DATA_ERR;
-  }
-
   nsRefPtr<IDBRequest> request = GenerateRequest(this);
   NS_ENSURE_TRUE(request, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
-  nsRefPtr<GetHelper> helper = new GetHelper(transaction, request, this, key);
+  nsRefPtr<GetHelper> helper =
+    new GetHelper(transaction, request, this, key);
   rv = helper->DispatchToTransactionPool();
   NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
   request.forget(_retval);
   return NS_OK;
 }
 
 NS_IMETHODIMP
-IDBIndex::GetKey(nsIVariant* aKey,
+IDBIndex::GetKey(const jsval& aKey,
+                 JSContext* aCx,
                  nsIIDBRequest** _retval)
 {
-  NS_PRECONDITION(NS_IsMainThread(), "Wrong thread!");
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
   IDBTransaction* transaction = mObjectStore->Transaction();
   if (!transaction->IsOpen()) {
     return NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR;
   }
 
   Key key;
-  nsresult rv = IDBObjectStore::GetKeyFromVariant(aKey, key);
+  nsresult rv = key.SetFromJSVal(aCx, aKey);
   if (NS_FAILED(rv)) {
     return rv;
   }
 
-  if (key.IsUnset()) {
-    return NS_ERROR_DOM_INDEXEDDB_DATA_ERR;
-  }
-
   nsRefPtr<IDBRequest> request = GenerateRequest(this);
   NS_ENSURE_TRUE(request, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
   nsRefPtr<GetKeyHelper> helper =
     new GetKeyHelper(transaction, request, this, key);
 
   rv = helper->DispatchToTransactionPool();
   NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
   request.forget(_retval);
   return NS_OK;
 }
 
 NS_IMETHODIMP
-IDBIndex::GetAll(nsIVariant* aKey,
+IDBIndex::GetAll(const jsval& aKey,
                  PRUint32 aLimit,
+                 JSContext* aCx,
                  PRUint8 aOptionalArgCount,
                  nsIIDBRequest** _retval)
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
   IDBTransaction* transaction = mObjectStore->Transaction();
   if (!transaction->IsOpen()) {
     return NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR;
   }
 
-  nsresult rv;
-
   Key key;
-  if (aOptionalArgCount &&
-      NS_FAILED(IDBObjectStore::GetKeyFromVariant(aKey, key))) {
-    PRUint16 type;
-    rv = aKey->GetDataType(&type);
-    NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
-
-    if (type != nsIDataType::VTYPE_EMPTY) {
-      return NS_ERROR_DOM_INDEXEDDB_DATA_ERR;
-    }
+  if (aOptionalArgCount && NS_FAILED(key.SetFromJSVal(aCx, aKey))) {
+    return NS_ERROR_DOM_INDEXEDDB_DATA_ERR;
   }
 
   if (aOptionalArgCount < 2) {
     aLimit = PR_UINT32_MAX;
   }
 
   nsRefPtr<IDBRequest> request = GenerateRequest(this);
   NS_ENSURE_TRUE(request, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
   nsRefPtr<GetAllHelper> helper =
     new GetAllHelper(transaction, request, this, key, aLimit);
 
-  rv = helper->DispatchToTransactionPool();
+  nsresult rv = helper->DispatchToTransactionPool();
   NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
   request.forget(_retval);
   return NS_OK;
 }
 
 NS_IMETHODIMP
-IDBIndex::GetAllKeys(nsIVariant* aKey,
+IDBIndex::GetAllKeys(const jsval& aKey,
                      PRUint32 aLimit,
+                     JSContext* aCx,
                      PRUint8 aOptionalArgCount,
                      nsIIDBRequest** _retval)
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
   IDBTransaction* transaction = mObjectStore->Transaction();
   if (!transaction->IsOpen()) {
     return NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR;
   }
 
   nsresult rv;
 
   Key key;
-  if (aOptionalArgCount &&
-      NS_FAILED(IDBObjectStore::GetKeyFromVariant(aKey, key))) {
-    PRUint16 type;
-    rv = aKey->GetDataType(&type);
-    NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
-
-    if (type != nsIDataType::VTYPE_EMPTY) {
-      return NS_ERROR_DOM_INDEXEDDB_DATA_ERR;
-    }
+  if (aOptionalArgCount && NS_FAILED(key.SetFromJSVal(aCx, aKey))) {
+    return NS_ERROR_DOM_INDEXEDDB_DATA_ERR;
   }
 
   if (aOptionalArgCount < 2) {
     aLimit = PR_UINT32_MAX;
   }
 
   nsRefPtr<IDBRequest> request = GenerateRequest(this);
   NS_ENSURE_TRUE(request, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
@@ -525,151 +495,125 @@ IDBIndex::GetAllKeys(nsIVariant* aKey,
   rv = helper->DispatchToTransactionPool();
   NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
   request.forget(_retval);
   return NS_OK;
 }
 
 NS_IMETHODIMP
-IDBIndex::OpenCursor(nsIIDBKeyRange* aKeyRange,
+IDBIndex::OpenCursor(const jsval& aKey,
                      PRUint16 aDirection,
+                     JSContext* aCx,
                      PRUint8 aOptionalArgCount,
                      nsIIDBRequest** _retval)
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
   IDBTransaction* transaction = mObjectStore->Transaction();
   if (!transaction->IsOpen()) {
     return NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR;
   }
 
   nsresult rv;
-  Key lowerKey, upperKey;
-  bool lowerOpen = false, upperOpen = false;
 
-  if (aKeyRange) {
-    nsCOMPtr<nsIVariant> variant;
-    rv = aKeyRange->GetLower(getter_AddRefs(variant));
-    NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
-
-    rv = IDBObjectStore::GetKeyFromVariant(variant, lowerKey);
-    if (NS_FAILED(rv)) {
-      return rv;
-    }
-
-    rv = aKeyRange->GetUpper(getter_AddRefs(variant));
-    NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
+  nsRefPtr<IDBKeyRange> keyRange;
+  if (aOptionalArgCount) {
+    rv = IDBKeyRange::FromJSVal(aCx, aKey, getter_AddRefs(keyRange));
+    NS_ENSURE_SUCCESS(rv, rv);
 
-    rv = IDBObjectStore::GetKeyFromVariant(variant, upperKey);
-    if (NS_FAILED(rv)) {
-      return rv;
+    if (aOptionalArgCount >= 2) {
+      if (aDirection != nsIIDBCursor::NEXT &&
+          aDirection != nsIIDBCursor::NEXT_NO_DUPLICATE &&
+          aDirection != nsIIDBCursor::PREV &&
+          aDirection != nsIIDBCursor::PREV_NO_DUPLICATE) {
+        return NS_ERROR_DOM_INDEXEDDB_NON_TRANSIENT_ERR;
+      }
     }
-
-    rv = aKeyRange->GetLowerOpen(&lowerOpen);
-    NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
-
-    rv = aKeyRange->GetUpperOpen(&upperOpen);
-    NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
-  }
-
-  if (aOptionalArgCount >= 2) {
-    if (aDirection != nsIIDBCursor::NEXT &&
-        aDirection != nsIIDBCursor::NEXT_NO_DUPLICATE &&
-        aDirection != nsIIDBCursor::PREV &&
-        aDirection != nsIIDBCursor::PREV_NO_DUPLICATE) {
-      return NS_ERROR_DOM_INDEXEDDB_NON_TRANSIENT_ERR;
+    else {
+      aDirection = nsIIDBCursor::NEXT;
     }
   }
-  else {
-    aDirection = nsIIDBCursor::NEXT;
-  }
 
   nsRefPtr<IDBRequest> request = GenerateRequest(this);
   NS_ENSURE_TRUE(request, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
   nsRefPtr<OpenCursorHelper> helper =
-    new OpenCursorHelper(transaction, request, this, lowerKey, upperKey,
-                         lowerOpen, upperOpen, aDirection);
+    new OpenCursorHelper(transaction, request, this, keyRange, aDirection);
 
   rv = helper->DispatchToTransactionPool();
   NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
   request.forget(_retval);
   return NS_OK;
 }
 
 NS_IMETHODIMP
-IDBIndex::OpenKeyCursor(nsIIDBKeyRange* aKeyRange,
+IDBIndex::OpenKeyCursor(const jsval& aKey,
                         PRUint16 aDirection,
+                        JSContext* aCx,
                         PRUint8 aOptionalArgCount,
                         nsIIDBRequest** _retval)
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
   IDBTransaction* transaction = mObjectStore->Transaction();
   if (!transaction->IsOpen()) {
     return NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR;
   }
 
   nsresult rv;
-  Key lowerKey, upperKey;
-  bool lowerOpen = false, upperOpen = false;
 
-  if (aKeyRange) {
-    nsCOMPtr<nsIVariant> variant;
-    rv = aKeyRange->GetLower(getter_AddRefs(variant));
-    NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
-
-    rv = IDBObjectStore::GetKeyFromVariant(variant, lowerKey);
-    if (NS_FAILED(rv)) {
-      return rv;
-    }
-
-    rv = aKeyRange->GetUpper(getter_AddRefs(variant));
-    NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
+  nsRefPtr<IDBKeyRange> keyRange;
+  if (aOptionalArgCount) {
+    rv = IDBKeyRange::FromJSVal(aCx, aKey, getter_AddRefs(keyRange));
+    NS_ENSURE_SUCCESS(rv, rv);
 
-    rv = IDBObjectStore::GetKeyFromVariant(variant, upperKey);
-    if (NS_FAILED(rv)) {
-      return rv;
+    if (aOptionalArgCount >= 2) {
+      if (aDirection != nsIIDBCursor::NEXT &&
+          aDirection != nsIIDBCursor::NEXT_NO_DUPLICATE &&
+          aDirection != nsIIDBCursor::PREV &&
+          aDirection != nsIIDBCursor::PREV_NO_DUPLICATE) {
+        return NS_ERROR_DOM_INDEXEDDB_NON_TRANSIENT_ERR;
+      }
     }
-
-    rv = aKeyRange->GetLowerOpen(&lowerOpen);
-    NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
-
-    rv = aKeyRange->GetUpperOpen(&upperOpen);
-    NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
-  }
-
-  if (aOptionalArgCount >= 2) {
-    if (aDirection != nsIIDBCursor::NEXT &&
-        aDirection != nsIIDBCursor::NEXT_NO_DUPLICATE &&
-        aDirection != nsIIDBCursor::PREV &&
-        aDirection != nsIIDBCursor::PREV_NO_DUPLICATE) {
-      return NS_ERROR_DOM_INDEXEDDB_NON_TRANSIENT_ERR;
+    else {
+      aDirection = nsIIDBCursor::NEXT;
     }
   }
-  else {
-    aDirection = nsIIDBCursor::NEXT;
-  }
 
   nsRefPtr<IDBRequest> request = GenerateRequest(this);
   NS_ENSURE_TRUE(request, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
   nsRefPtr<OpenKeyCursorHelper> helper =
-    new OpenKeyCursorHelper(transaction, request, this, lowerKey, upperKey,
-                            lowerOpen, upperOpen, aDirection);
+    new OpenKeyCursorHelper(transaction, request, this, keyRange, aDirection);
 
   rv = helper->DispatchToTransactionPool();
   NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
   request.forget(_retval);
   return NS_OK;
 }
 
+/*
+NS_IMETHODIMP
+IDBIndex::Count(const jsval& aKey,
+                JSContext* aCx,
+                PRUint8 aOptionalArgCount,
+                nsIIDBRequest** _retval)
+{
+  IDBTransaction* transaction = mObjectStore->Transaction();
+  if (!transaction->IsOpen()) {
+    return NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR;
+  }
+
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+*/
+
 nsresult
 GetKeyHelper::DoDatabaseWork(mozIStorageConnection* aConnection)
 {
   NS_ASSERTION(aConnection, "Passed a null connection!");
 
   nsCOMPtr<mozIStorageStatement> stmt =
     mTransaction->IndexGetStatement(mIndex->IsUnique(),
                                     mIndex->IsAutoIncrement());
@@ -678,63 +622,39 @@ GetKeyHelper::DoDatabaseWork(mozIStorage
   mozStorageStatementScoper scoper(stmt);
 
   nsresult rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("index_id"),
                                       mIndex->Id());
   NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
   NS_NAMED_LITERAL_CSTRING(value, "value");
 
-  if (mKey.IsInt()) {
-    rv = stmt->BindInt64ByName(value, mKey.IntValue());
-  }
-  else if (mKey.IsString()) {
-    rv = stmt->BindStringByName(value, mKey.StringValue());
-  }
-  else {
-    NS_NOTREACHED("Bad key type!");
-  }
-  NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
+  rv = mKey.BindToStatement(stmt, value);
+  NS_ENSURE_SUCCESS(rv, rv);
 
-  mKey = Key::UNSETKEY;
+  mKey.Unset();
 
   bool hasResult;
   rv = stmt->ExecuteStep(&hasResult);
   NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
   if (hasResult) {
-    PRInt32 keyType;
-    rv = stmt->GetTypeOfIndex(0, &keyType);
-    NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
-
-    NS_ASSERTION(keyType == mozIStorageStatement::VALUE_TYPE_INTEGER ||
-                 keyType == mozIStorageStatement::VALUE_TYPE_TEXT,
-                 "Bad key type!");
-
-    if (keyType == mozIStorageStatement::VALUE_TYPE_INTEGER) {
-      mKey = stmt->AsInt64(0);
-    }
-    else if (keyType == mozIStorageStatement::VALUE_TYPE_TEXT) {
-      rv = stmt->GetString(0, mKey.ToString());
-      NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
-    }
-    else {
-      NS_NOTREACHED("Bad SQLite type!");
-    }
+    rv = mKey.SetFromStatement(stmt, 0);
+    NS_ENSURE_SUCCESS(rv, rv);
   }
 
   return NS_OK;
 }
 
 nsresult
 GetKeyHelper::GetSuccessResult(JSContext* aCx,
                                jsval* aVal)
 {
   NS_ASSERTION(!mKey.IsUnset(), "Badness!");
-  return IDBObjectStore::GetJSValFromKey(mKey, aCx, aVal);
+  return mKey.ToJSVal(aCx, aVal);
 }
 
 nsresult
 GetHelper::DoDatabaseWork(mozIStorageConnection* aConnection)
 {
   NS_ASSERTION(aConnection, "Passed a null connection!");
 
   nsCOMPtr<mozIStorageStatement> stmt =
@@ -745,28 +665,20 @@ GetHelper::DoDatabaseWork(mozIStorageCon
   mozStorageStatementScoper scoper(stmt);
 
   nsresult rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("index_id"),
                                       mIndex->Id());
   NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
   NS_NAMED_LITERAL_CSTRING(value, "value");
 
-  if (mKey.IsInt()) {
-    rv = stmt->BindInt64ByName(value, mKey.IntValue());
-  }
-  else if (mKey.IsString()) {
-    rv = stmt->BindStringByName(value, mKey.StringValue());
-  }
-  else {
-    NS_NOTREACHED("Bad key type!");
-  }
-  NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
+  rv = mKey.BindToStatement(stmt, value);
+  NS_ENSURE_SUCCESS(rv, rv);
 
-  mKey = Key::UNSETKEY;
+  mKey.Unset();
 
   bool hasResult;
   rv = stmt->ExecuteStep(&hasResult);
   NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
   if (hasResult) {
     rv = IDBObjectStore::GetStructuredCloneDataFromStatement(stmt, 0,
                                                              mCloneBuffer);
@@ -845,58 +757,31 @@ GetAllKeysHelper::DoDatabaseWork(mozISto
   NS_ENSURE_TRUE(stmt, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
   mozStorageStatementScoper scoper(stmt);
 
   nsresult rv = stmt->BindInt64ByName(indexId, mIndex->Id());
   NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
   if (!mKey.IsUnset()) {
-    if (mKey.IsInt()) {
-      rv = stmt->BindInt64ByName(value, mKey.IntValue());
-    }
-    else if (mKey.IsString()) {
-      rv = stmt->BindStringByName(value, mKey.StringValue());
-    }
-    else {
-      NS_NOTREACHED("Bad key type!");
-    }
-    NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
+    rv = mKey.BindToStatement(stmt, value);
+    NS_ENSURE_SUCCESS(rv, rv);
   }
 
   bool hasResult;
   while(NS_SUCCEEDED((rv = stmt->ExecuteStep(&hasResult))) && hasResult) {
     if (mKeys.Capacity() == mKeys.Length()) {
-      if (!mKeys.SetCapacity(mKeys.Capacity() * 2)) {
-        NS_ERROR("Out of memory!");
-        return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
-      }
+      mKeys.SetCapacity(mKeys.Capacity() * 2);
     }
 
     Key* key = mKeys.AppendElement();
     NS_ASSERTION(key, "This shouldn't fail!");
 
-    PRInt32 keyType;
-    rv = stmt->GetTypeOfIndex(0, &keyType);
-    NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
-
-    NS_ASSERTION(keyType == mozIStorageStatement::VALUE_TYPE_INTEGER ||
-                 keyType == mozIStorageStatement::VALUE_TYPE_TEXT,
-                 "Bad key type!");
-
-    if (keyType == mozIStorageStatement::VALUE_TYPE_INTEGER) {
-      *key = stmt->AsInt64(0);
-    }
-    else if (keyType == mozIStorageStatement::VALUE_TYPE_TEXT) {
-      rv = stmt->GetString(0, key->ToString());
-      NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
-    }
-    else {
-      NS_NOTREACHED("Bad SQLite type!");
-    }
+    rv = key->SetFromStatement(stmt, 0);
+    NS_ENSURE_SUCCESS(rv, rv);
   }
   NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
   return NS_OK;
 }
 
 nsresult
 GetAllKeysHelper::GetSuccessResult(JSContext* aCx,
@@ -924,17 +809,17 @@ GetAllKeysHelper::GetSuccessResult(JSCon
       return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
     }
 
     for (uint32 index = 0, count = keys.Length(); index < count; index++) {
       const Key& key = keys[index];
       NS_ASSERTION(!key.IsUnset(), "Bad key!");
 
       jsval value;
-      nsresult rv = IDBObjectStore::GetJSValFromKey(key, aCx, &value);
+      nsresult rv = key.ToJSVal(aCx, &value);
       if (NS_FAILED(rv)) {
         NS_WARNING("Failed to get jsval for key!");
         return rv;
       }
 
       if (!JS_SetElement(aCx, array, index, &value)) {
         NS_WARNING("Failed to set array element!");
         return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
@@ -1009,26 +894,18 @@ GetAllHelper::DoDatabaseWork(mozIStorage
   NS_ENSURE_TRUE(stmt, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
   mozStorageStatementScoper scoper(stmt);
 
   nsresult rv = stmt->BindInt64ByName(indexId, mIndex->Id());
   NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
   if (!mKey.IsUnset()) {
-    if (mKey.IsInt()) {
-      rv = stmt->BindInt64ByName(value, mKey.IntValue());
-    }
-    else if (mKey.IsString()) {
-      rv = stmt->BindStringByName(value, mKey.StringValue());
-    }
-    else {
-      NS_NOTREACHED("Bad key type!");
-    }
-    NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
+    rv = mKey.BindToStatement(stmt, value);
+    NS_ENSURE_SUCCESS(rv, rv);
   }
 
   bool hasResult;
   while(NS_SUCCEEDED((rv = stmt->ExecuteStep(&hasResult))) && hasResult) {
     if (mCloneBuffers.Capacity() == mCloneBuffers.Length()) {
       if (!mCloneBuffers.SetCapacity(mCloneBuffers.Capacity() * 2)) {
         NS_ERROR("Out of memory!");
         return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
@@ -1091,23 +968,25 @@ OpenKeyCursorHelper::DoDatabaseWork(mozI
 
   NS_NAMED_LITERAL_CSTRING(id, "id");
   NS_NAMED_LITERAL_CSTRING(lowerKeyName, "lower_key");
   NS_NAMED_LITERAL_CSTRING(upperKeyName, "upper_key");
 
   NS_NAMED_LITERAL_CSTRING(value, "value");
 
   nsCAutoString keyRangeClause;
-  if (!mLowerKey.IsUnset()) {
-    AppendConditionClause(value, lowerKeyName, false, !mLowerOpen,
-                          keyRangeClause);
-  }
-  if (!mUpperKey.IsUnset()) {
-    AppendConditionClause(value, upperKeyName, true, !mUpperOpen,
-                          keyRangeClause);
+  if (mKeyRange) {
+    if (!mKeyRange->Lower().IsUnset()) {
+      AppendConditionClause(value, lowerKeyName, false,
+                            !mKeyRange->IsLowerOpen(), keyRangeClause);
+    }
+    if (!mKeyRange->Upper().IsUnset()) {
+      AppendConditionClause(value, upperKeyName, true,
+                            !mKeyRange->IsUpperOpen(), keyRangeClause);
+    }
   }
 
   nsCAutoString directionClause = NS_LITERAL_CSTRING(" ORDER BY ") + value;
   switch (mDirection) {
     case nsIIDBCursor::NEXT:
     case nsIIDBCursor::NEXT_NO_DUPLICATE:
       directionClause.AppendLiteral(" ASC");
       break;
@@ -1133,145 +1012,103 @@ OpenKeyCursorHelper::DoDatabaseWork(mozI
     mTransaction->GetCachedStatement(firstQuery);
   NS_ENSURE_TRUE(stmt, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
   mozStorageStatementScoper scoper(stmt);
 
   nsresult rv = stmt->BindInt64ByName(id, mIndex->Id());
   NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
-  if (!mLowerKey.IsUnset()) {
-    if (mLowerKey.IsString()) {
-      rv = stmt->BindStringByName(lowerKeyName, mLowerKey.StringValue());
-    }
-    else if (mLowerKey.IsInt()) {
-      rv = stmt->BindInt64ByName(lowerKeyName, mLowerKey.IntValue());
-    }
-    else {
-      NS_NOTREACHED("Bad key!");
+  if (mKeyRange) {
+    if (!mKeyRange->Lower().IsUnset()) {
+      rv = mKeyRange->Lower().BindToStatement(stmt, lowerKeyName);
+      NS_ENSURE_SUCCESS(rv, rv);
     }
-    NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
-  }
-
-  if (!mUpperKey.IsUnset()) {
-    if (mUpperKey.IsString()) {
-      rv = stmt->BindStringByName(upperKeyName, mUpperKey.StringValue());
+    if (!mKeyRange->Upper().IsUnset()) {
+      rv = mKeyRange->Upper().BindToStatement(stmt, upperKeyName);
+      NS_ENSURE_SUCCESS(rv, rv);
     }
-    else if (mUpperKey.IsInt()) {
-      rv = stmt->BindInt64ByName(upperKeyName, mUpperKey.IntValue());
-    }
-    else {
-      NS_NOTREACHED("Bad key!");
-    }
-    NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
   }
 
   bool hasResult;
   rv = stmt->ExecuteStep(&hasResult);
   NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
   if (!hasResult) {
-    mKey = Key::UNSETKEY;
+    mKey.Unset();
     return NS_OK;
   }
 
-  PRInt32 keyType;
-  rv = stmt->GetTypeOfIndex(0, &keyType);
-  NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
-
-  NS_ASSERTION(keyType == mozIStorageStatement::VALUE_TYPE_INTEGER ||
-               keyType == mozIStorageStatement::VALUE_TYPE_TEXT,
-               "Bad key type!");
+  rv = mKey.SetFromStatement(stmt, 0);
+  NS_ENSURE_SUCCESS(rv, rv);
 
-  if (keyType == mozIStorageStatement::VALUE_TYPE_INTEGER) {
-    mKey = stmt->AsInt64(0);
-  }
-  else if (keyType == mozIStorageStatement::VALUE_TYPE_TEXT) {
-    rv = stmt->GetString(0, mKey.ToString());
-    NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
-  }
-  else {
-    NS_NOTREACHED("Bad SQLite type!");
-  }
-
-  rv = stmt->GetTypeOfIndex(1, &keyType);
-  NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
-
-  NS_ASSERTION(keyType == mozIStorageStatement::VALUE_TYPE_INTEGER ||
-               keyType == mozIStorageStatement::VALUE_TYPE_TEXT,
-               "Bad key type!");
-
-  if (keyType == mozIStorageStatement::VALUE_TYPE_INTEGER) {
-    mObjectKey = stmt->AsInt64(1);
-  }
-  else if (keyType == mozIStorageStatement::VALUE_TYPE_TEXT) {
-    rv = stmt->GetString(1, mObjectKey.ToString());
-    NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
-  }
-  else {
-    NS_NOTREACHED("Bad SQLite type!");
-  }
+  rv = mObjectKey.SetFromStatement(stmt, 1);
+  NS_ENSURE_SUCCESS(rv, rv);
 
   // Now we need to make the query to get the next match.
   nsCAutoString queryStart = NS_LITERAL_CSTRING("SELECT value, ") + keyColumn +
                              NS_LITERAL_CSTRING(" FROM ") + table +
                              NS_LITERAL_CSTRING(" WHERE index_id = :") + id;
 
   NS_NAMED_LITERAL_CSTRING(currentKey, "current_key");
   NS_NAMED_LITERAL_CSTRING(rangeKey, "range_key");
   NS_NAMED_LITERAL_CSTRING(objectKey, "object_key");
 
   switch (mDirection) {
     case nsIIDBCursor::NEXT:
-      if (!mUpperKey.IsUnset()) {
-        AppendConditionClause(value, rangeKey, true, !mUpperOpen, queryStart);
-        mRangeKey = mUpperKey;
+      if (mKeyRange && !mKeyRange->Upper().IsUnset()) {
+        AppendConditionClause(value, rangeKey, true, !mKeyRange->IsUpperOpen(),
+                              queryStart);
+        mRangeKey = mKeyRange->Upper();
       }
       mContinueQuery = queryStart + NS_LITERAL_CSTRING(" AND ( ( value = :") +
                        currentKey + NS_LITERAL_CSTRING(" AND ") + keyColumn +
                        NS_LITERAL_CSTRING(" > :") + objectKey +
                        NS_LITERAL_CSTRING(" ) OR ( value > :") + currentKey +
                        NS_LITERAL_CSTRING(" ) )") + directionClause +
                        NS_LITERAL_CSTRING(" LIMIT 1");
       mContinueToQuery = queryStart + NS_LITERAL_CSTRING(" AND value >= :") +
                          currentKey + NS_LITERAL_CSTRING(" LIMIT 1");
       break;
 
     case nsIIDBCursor::NEXT_NO_DUPLICATE:
-      if (!mUpperKey.IsUnset()) {
-        AppendConditionClause(value, rangeKey, true, !mUpperOpen, queryStart);
-        mRangeKey = mUpperKey;
+      if (mKeyRange && !mKeyRange->Upper().IsUnset()) {
+        AppendConditionClause(value, rangeKey, true, !mKeyRange->IsUpperOpen(),
+                              queryStart);
+        mRangeKey = mKeyRange->Upper();
       }
       mContinueQuery = queryStart + NS_LITERAL_CSTRING(" AND value > :") +
                        currentKey + directionClause +
                        NS_LITERAL_CSTRING(" LIMIT 1");
       mContinueToQuery = queryStart + NS_LITERAL_CSTRING(" AND value >= :") +
                          currentKey + directionClause +
                          NS_LITERAL_CSTRING(" LIMIT 1");
       break;
 
     case nsIIDBCursor::PREV:
-      if (!mLowerKey.IsUnset()) {
-        AppendConditionClause(value, rangeKey, false, !mLowerOpen, queryStart);
-        mRangeKey = mLowerKey;
+      if (mKeyRange && !mKeyRange->Lower().IsUnset()) {
+        AppendConditionClause(value, rangeKey, false, !mKeyRange->IsLowerOpen(),
+                              queryStart);
+        mRangeKey = mKeyRange->Lower();
       }
       mContinueQuery = queryStart + NS_LITERAL_CSTRING(" AND ( ( value = :") +
                        currentKey + NS_LITERAL_CSTRING(" AND ") + keyColumn +
                        NS_LITERAL_CSTRING(" < :") + objectKey +
                        NS_LITERAL_CSTRING(" ) OR ( value < :") + currentKey +
                        NS_LITERAL_CSTRING(" ) )") + directionClause +
                        NS_LITERAL_CSTRING(" LIMIT 1");
       mContinueToQuery = queryStart + NS_LITERAL_CSTRING(" AND value <= :") +
                          currentKey + NS_LITERAL_CSTRING(" LIMIT 1");
       break;
 
     case nsIIDBCursor::PREV_NO_DUPLICATE:
-      if (!mLowerKey.IsUnset()) {
-        AppendConditionClause(value, rangeKey, false, !mLowerOpen, queryStart);
-        mRangeKey = mLowerKey;
+      if (mKeyRange && !mKeyRange->Lower().IsUnset()) {
+        AppendConditionClause(value, rangeKey, false, !mKeyRange->IsLowerOpen(),
+                              queryStart);
+        mRangeKey = mKeyRange->Lower();
       }
       mContinueQuery = queryStart + NS_LITERAL_CSTRING(" AND value < :") +
                        currentKey + directionClause +
                        NS_LITERAL_CSTRING(" LIMIT 1");
       mContinueToQuery = queryStart + NS_LITERAL_CSTRING(" AND value <= :") +
                          currentKey + directionClause +
                          NS_LITERAL_CSTRING(" LIMIT 1");
       break;
@@ -1337,23 +1174,25 @@ OpenCursorHelper::DoDatabaseWork(mozISto
   NS_NAMED_LITERAL_CSTRING(lowerKeyName, "lower_key");
   NS_NAMED_LITERAL_CSTRING(upperKeyName, "upper_key");
 
   nsCString value = indexTable + NS_LITERAL_CSTRING(".value");
   nsCString data = objectTable + NS_LITERAL_CSTRING(".data");
   nsCString keyValue = objectTable + NS_LITERAL_CSTRING(".") + keyValueColumn;
 
   nsCAutoString keyRangeClause;
-  if (!mLowerKey.IsUnset()) {
-    AppendConditionClause(value, lowerKeyName, false, !mLowerOpen,
-                          keyRangeClause);
-  }
-  if (!mUpperKey.IsUnset()) {
-    AppendConditionClause(value, upperKeyName, true, !mUpperOpen,
-                          keyRangeClause);
+  if (mKeyRange) {
+    if (!mKeyRange->Lower().IsUnset()) {
+      AppendConditionClause(value, lowerKeyName, false,
+                            !mKeyRange->IsLowerOpen(), keyRangeClause);
+    }
+    if (!mKeyRange->Upper().IsUnset()) {
+      AppendConditionClause(value, upperKeyName, true,
+                            !mKeyRange->IsUpperOpen(), keyRangeClause);
+    }
   }
 
   nsCAutoString directionClause = NS_LITERAL_CSTRING(" ORDER BY ") + value;
   switch (mDirection) {
     case nsIIDBCursor::NEXT:
     case nsIIDBCursor::NEXT_NO_DUPLICATE:
       directionClause.AppendLiteral(" ASC");
       break;
@@ -1386,87 +1225,41 @@ OpenCursorHelper::DoDatabaseWork(mozISto
     mTransaction->GetCachedStatement(firstQuery);
   NS_ENSURE_TRUE(stmt, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
   mozStorageStatementScoper scoper(stmt);
 
   nsresult rv = stmt->BindInt64ByName(id, mIndex->Id());
   NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
-  if (!mLowerKey.IsUnset()) {
-    if (mLowerKey.IsString()) {
-      rv = stmt->BindStringByName(lowerKeyName, mLowerKey.StringValue());
-    }
-    else if (mLowerKey.IsInt()) {
-      rv = stmt->BindInt64ByName(lowerKeyName, mLowerKey.IntValue());
-    }
-    else {
-      NS_NOTREACHED("Bad key!");
+  if (mKeyRange) {
+    if (!mKeyRange->Lower().IsUnset()) {
+      rv = mKeyRange->Lower().BindToStatement(stmt, lowerKeyName);
+      NS_ENSURE_SUCCESS(rv, rv);
     }
-    NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
-  }
-
-  if (!mUpperKey.IsUnset()) {
-    if (mUpperKey.IsString()) {
-      rv = stmt->BindStringByName(upperKeyName, mUpperKey.StringValue());
+    if (!mKeyRange->Upper().IsUnset()) {
+      rv = mKeyRange->Upper().BindToStatement(stmt, upperKeyName);
+      NS_ENSURE_SUCCESS(rv, rv);
     }
-    else if (mUpperKey.IsInt()) {
-      rv = stmt->BindInt64ByName(upperKeyName, mUpperKey.IntValue());
-    }
-    else {
-      NS_NOTREACHED("Bad key!");
-    }
-    NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
   }
 
   bool hasResult;
   rv = stmt->ExecuteStep(&hasResult);
   NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
   if (!hasResult) {
-    mKey = Key::UNSETKEY;
+    mKey.Unset();
     return NS_OK;
   }
 
-  PRInt32 keyType;
-  rv = stmt->GetTypeOfIndex(0, &keyType);
-  NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
-
-  NS_ASSERTION(keyType == mozIStorageStatement::VALUE_TYPE_INTEGER ||
-               keyType == mozIStorageStatement::VALUE_TYPE_TEXT,
-               "Bad key type!");
+  rv = mKey.SetFromStatement(stmt, 0);
+  NS_ENSURE_SUCCESS(rv, rv);
 
-  if (keyType == mozIStorageStatement::VALUE_TYPE_INTEGER) {
-    mKey = stmt->AsInt64(0);
-  }
-  else if (keyType == mozIStorageStatement::VALUE_TYPE_TEXT) {
-    rv = stmt->GetString(0, mKey.ToString());
-    NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
-  }
-  else {
-    NS_NOTREACHED("Bad SQLite type!");
-  }
-
-  rv = stmt->GetTypeOfIndex(1, &keyType);
-  NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
-
-  NS_ASSERTION(keyType == mozIStorageStatement::VALUE_TYPE_INTEGER ||
-               keyType == mozIStorageStatement::VALUE_TYPE_TEXT,
-               "Bad key type!");
-
-  if (keyType == mozIStorageStatement::VALUE_TYPE_INTEGER) {
-    mObjectKey = stmt->AsInt64(1);
-  }
-  else if (keyType == mozIStorageStatement::VALUE_TYPE_TEXT) {
-    rv = stmt->GetString(1, mObjectKey.ToString());
-    NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
-  }
-  else {
-    NS_NOTREACHED("Bad SQLite type!");
-  }
+  rv = mObjectKey.SetFromStatement(stmt, 1);
+  NS_ENSURE_SUCCESS(rv, rv);
 
   rv = IDBObjectStore::GetStructuredCloneDataFromStatement(stmt, 2,
                                                            mCloneBuffer);
   NS_ENSURE_SUCCESS(rv, rv);
 
   // Now we need to make the query to get the next match.
   nsCAutoString queryStart = NS_LITERAL_CSTRING("SELECT ") + value +
                              NS_LITERAL_CSTRING(", ") + keyValue +
@@ -1480,70 +1273,74 @@ OpenCursorHelper::DoDatabaseWork(mozISto
                              NS_LITERAL_CSTRING(".index_id = :id");
 
   NS_NAMED_LITERAL_CSTRING(currentKey, "current_key");
   NS_NAMED_LITERAL_CSTRING(rangeKey, "range_key");
   NS_NAMED_LITERAL_CSTRING(objectKey, "object_key");
 
   switch (mDirection) {
     case nsIIDBCursor::NEXT:
-      if (!mUpperKey.IsUnset()) {
-        AppendConditionClause(value, rangeKey, true, !mUpperOpen, queryStart);
-        mRangeKey = mUpperKey;
+      if (mKeyRange && !mKeyRange->Upper().IsUnset()) {
+        AppendConditionClause(value, rangeKey, true, !mKeyRange->IsUpperOpen(),
+                              queryStart);
+        mRangeKey = mKeyRange->Upper();
       }
       mContinueQuery = queryStart + NS_LITERAL_CSTRING(" AND ( ( ") +
                        value + NS_LITERAL_CSTRING(" = :") + currentKey +
                        NS_LITERAL_CSTRING(" AND ") + keyValue +
                        NS_LITERAL_CSTRING(" > :") + objectKey +
                        NS_LITERAL_CSTRING(" ) OR ( ") + value +
                        NS_LITERAL_CSTRING(" > :") + currentKey +
                        NS_LITERAL_CSTRING(" ) )") + directionClause +
                        NS_LITERAL_CSTRING(" LIMIT 1");
       mContinueToQuery = queryStart + NS_LITERAL_CSTRING(" AND ") + value +
                          NS_LITERAL_CSTRING(" >= :") +
                          currentKey + directionClause +
                          NS_LITERAL_CSTRING(" LIMIT 1");
       break;
 
     case nsIIDBCursor::NEXT_NO_DUPLICATE:
-      if (!mUpperKey.IsUnset()) {
-        AppendConditionClause(value, rangeKey, true, !mUpperOpen, queryStart);
-        mRangeKey = mUpperKey;
+      if (mKeyRange && !mKeyRange->Upper().IsUnset()) {
+        AppendConditionClause(value, rangeKey, true, !mKeyRange->IsUpperOpen(),
+                              queryStart);
+        mRangeKey = mKeyRange->Upper();
       }
       mContinueQuery = queryStart + NS_LITERAL_CSTRING(" AND ") + value +
                        NS_LITERAL_CSTRING(" > :") + currentKey +
                        directionClause + NS_LITERAL_CSTRING(" LIMIT 1");
       mContinueToQuery = queryStart + NS_LITERAL_CSTRING(" AND ") + value +
                          NS_LITERAL_CSTRING(" >= :") + currentKey +
                          directionClause + NS_LITERAL_CSTRING(" LIMIT 1");
       break;
 
     case nsIIDBCursor::PREV:
-      if (!mLowerKey.IsUnset()) {
-        AppendConditionClause(value, rangeKey, false, !mLowerOpen, queryStart);
-        mRangeKey = mLowerKey;
+      if (mKeyRange && !mKeyRange->Lower().IsUnset()) {
+        AppendConditionClause(value, rangeKey, false, !mKeyRange->IsLowerOpen(),
+                              queryStart);
+        mRangeKey = mKeyRange->Lower();
       }
       mContinueQuery = queryStart + NS_LITERAL_CSTRING(" AND ( ( ") +
                        value + NS_LITERAL_CSTRING(" = :") + currentKey +
                        NS_LITERAL_CSTRING(" AND ") + keyValue +
                        NS_LITERAL_CSTRING(" < :") + objectKey +
                        NS_LITERAL_CSTRING(" ) OR ( ") + value +
                        NS_LITERAL_CSTRING(" < :") + currentKey +
                        NS_LITERAL_CSTRING(" ) )") + directionClause +
                        NS_LITERAL_CSTRING(" LIMIT 1");
       mContinueToQuery = queryStart + NS_LITERAL_CSTRING(" AND ") + value +
                          NS_LITERAL_CSTRING(" <= :") +
                          currentKey + directionClause +
                          NS_LITERAL_CSTRING(" LIMIT 1");
       break;
 
     case nsIIDBCursor::PREV_NO_DUPLICATE:
-      if (!mLowerKey.IsUnset()) {
-        AppendConditionClause(value, rangeKey, false, !mLowerOpen, queryStart);
-        mRangeKey = mLowerKey;
+      if (mKeyRange && !mKeyRange->Lower().IsUnset()) {
+        AppendConditionClause(value, rangeKey, false, !mKeyRange->IsLowerOpen(),
+                              queryStart);
+        mRangeKey = mKeyRange->Lower();
       }
       mContinueQuery = queryStart + NS_LITERAL_CSTRING(" AND ") + value +
                        NS_LITERAL_CSTRING(" < :") + currentKey +
                        directionClause + NS_LITERAL_CSTRING(" LIMIT 1");
       mContinueToQuery = queryStart + NS_LITERAL_CSTRING(" AND ") + value +
                          NS_LITERAL_CSTRING(" <= :") + currentKey +
                          directionClause + NS_LITERAL_CSTRING(" LIMIT 1");
       break;
--- a/dom/indexedDB/IDBKeyRange.cpp
+++ b/dom/indexedDB/IDBKeyRange.cpp
@@ -37,230 +37,206 @@
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "IDBKeyRange.h"
 
 #include "nsIXPConnect.h"
 
 #include "jscntxt.h"
-#include "nsDOMClassInfoID.h"
+#include "nsDOMClassInfo.h"
 #include "nsJSUtils.h"
 #include "nsThreadUtils.h"
 #include "nsContentUtils.h"
 
 #include "Key.h"
 
 USING_INDEXEDDB_NAMESPACE
 
 namespace {
 
 inline
-JSBool
-ConvertArguments(JSContext* aCx,
-                 uintN aArgc,
-                 jsval* aVp,
-                 const char* aMethodName,
-                 nsTArray<nsCOMPtr<nsIVariant> >& aKeys)
-{
-  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
-  NS_ASSERTION(aCx, "Null pointer!");
-  NS_ASSERTION(aVp, "Null pointer!");
-  NS_ASSERTION(aMethodName, "Null pointer!");
-  NS_ASSERTION(aKeys.Capacity(), "Need guaranteed capacity!");
-  NS_ASSERTION(aKeys.IsEmpty(), "Not an empty array!");
-
-  if (aArgc < aKeys.Capacity()) {
-    nsCString num;
-    num.AppendInt(aKeys.Length());
-    JS_ReportErrorNumberUC(aCx, js_GetErrorMessage, nsnull,
-                           JSMSG_MORE_ARGS_NEEDED, aMethodName, num.get(),
-                           aKeys.Capacity() == 1 ? "" : "s");
-    return JS_FALSE;
-  }
-
-  for (uintN i = 0; i < aKeys.Capacity(); i++) {
-    jsval& arg = JS_ARGV(aCx, aVp)[i];
-    if (JSVAL_IS_VOID(arg) || JSVAL_IS_NULL(arg) ||
-        !Key::CanBeConstructedFromJSVal(arg)) {
-      JS_ReportError(aCx, "Argument is not a supported key type.");
-      return JS_FALSE;
-    }
-
-    nsIXPConnect* xpc = nsContentUtils::XPConnect();
-    NS_ASSERTION(xpc, "This should never be null!");
-
-    nsCOMPtr<nsIVariant>* key = aKeys.AppendElement();
-    NS_ASSERTION(key, "This should never fail!");
-
-    if (NS_FAILED(xpc->JSValToVariant(aCx, &arg, getter_AddRefs(*key)))) {
-      JS_ReportError(aCx, "Could not convert argument to variant.");
-      return JS_FALSE;
-    }
-  }
-
-  return JS_TRUE;
-}
-
-inline
-JSBool
+bool
 ReturnKeyRange(JSContext* aCx,
                jsval* aVp,
                IDBKeyRange* aKeyRange)
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
   NS_ASSERTION(aCx, "Null pointer!");
   NS_ASSERTION(aVp, "Null pointer!");
   NS_ASSERTION(aKeyRange, "Null pointer!");
 
   nsIXPConnect* xpc = nsContentUtils::XPConnect();
   NS_ASSERTION(xpc, "This should never be null!");
 
   JSObject* global = JS_GetGlobalForScopeChain(aCx);
-  NS_ENSURE_TRUE(global, JS_FALSE);
+  if (!global) {
+    NS_WARNING("Couldn't get global object!");
+    return false;
+  }
 
   nsCOMPtr<nsIXPConnectJSObjectHolder> holder;
   if (NS_FAILED(xpc->WrapNative(aCx, global, aKeyRange,
                                 NS_GET_IID(nsIIDBKeyRange),
                                 getter_AddRefs(holder)))) {
     JS_ReportError(aCx, "Couldn't wrap IDBKeyRange object.");
-    return JS_FALSE;
+    return false;
   }
 
   JSObject* result;
   if (NS_FAILED(holder->GetJSObject(&result))) {
     JS_ReportError(aCx, "Couldn't get JSObject from wrapper.");
-    return JS_FALSE;
+    return false;
   }
 
   JS_SET_RVAL(aCx, aVp, OBJECT_TO_JSVAL(result));
-  return JS_TRUE;
+  return true;
+}
+
+inline
+nsresult
+GetKeyFromJSVal(JSContext* aCx,
+                jsval aVal,
+                Key& aKey,
+                bool aAllowUnset = false)
+{
+  nsresult rv = aKey.SetFromJSVal(aCx, aVal);
+  if (NS_FAILED(rv)) {
+    NS_ASSERTION(NS_ERROR_GET_MODULE(rv) == NS_ERROR_MODULE_DOM_INDEXEDDB,
+                 "Bad error code!");
+    return rv;
+  }
+
+  if (aKey.IsUnset() && !aAllowUnset) {
+    return NS_ERROR_DOM_INDEXEDDB_DATA_ERR;
+  }
+
+  return NS_OK;
+}
+
+inline
+void
+ThrowException(JSContext* aCx,
+               nsresult aErrorCode)
+{
+  NS_ASSERTION(NS_FAILED(aErrorCode), "Not an error code!");
+  if (!JS_IsExceptionPending(aCx)) {
+    nsDOMClassInfo::ThrowJSException(aCx, aErrorCode);
+  }
+}
+
+inline
+bool
+GetKeyFromJSValOrThrow(JSContext* aCx,
+                       jsval aVal,
+                       Key& aKey)
+{
+  nsresult rv = GetKeyFromJSVal(aCx, aVal, aKey);
+  if (NS_FAILED(rv)) {
+    ThrowException(aCx, rv);
+    return false;
+  }
+  return true;
 }
 
 JSBool
 MakeOnlyKeyRange(JSContext* aCx,
                  uintN aArgc,
                  jsval* aVp)
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
-  nsAutoTArray<nsCOMPtr<nsIVariant>, 1> keys;
-  if (!ConvertArguments(aCx, aArgc, aVp, "IDBKeyRange.only", keys)) {
-    return JS_FALSE;
-  }
-  NS_ASSERTION(keys.Length() == 1, "Didn't set all keys!");
-
-  nsRefPtr<IDBKeyRange> range =
-    IDBKeyRange::Create(keys[0], keys[0], false, false);
-  NS_ASSERTION(range, "Out of memory?");
-
-  if (!ReturnKeyRange(aCx, aVp, range)) {
-    return JS_FALSE;
+  jsval val;
+  if (!JS_ConvertArguments(aCx, aArgc, JS_ARGV(aCx, aVp), "v", &val)) {
+    return false;
   }
 
-  return JS_TRUE;
+  nsRefPtr<IDBKeyRange> keyRange = new IDBKeyRange(false, false, true);
+
+  if (!GetKeyFromJSValOrThrow(aCx, val, keyRange->Lower())) {
+    return false;
+  }
+
+  return ReturnKeyRange(aCx, aVp, keyRange);
 }
 
 JSBool
 MakeLowerBoundKeyRange(JSContext* aCx,
                        uintN aArgc,
                        jsval* aVp)
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
-  nsAutoTArray<nsCOMPtr<nsIVariant>, 1> keys;
-  if (!ConvertArguments(aCx, aArgc, aVp, "IDBKeyRange.lowerBound", keys)) {
-    return JS_FALSE;
-  }
-  NS_ASSERTION(keys.Length() == 1, "Didn't set all keys!");
-
-  JSBool open = JS_FALSE;
-  if (aArgc > 1 && !JS_ValueToBoolean(aCx, JS_ARGV(aCx, aVp)[1], &open)) {
-    JS_ReportError(aCx, "Couldn't convert argument 2 to boolean.");
-    return JS_FALSE;
+  jsval val;
+  JSBool open = false;
+  if (!JS_ConvertArguments(aCx, aArgc, JS_ARGV(aCx, aVp), "v/b", &val, &open)) {
+    return false;
   }
 
-  nsRefPtr<IDBKeyRange> range =
-    IDBKeyRange::Create(keys[0], nsnull, !!open, true);
-  NS_ASSERTION(range, "Out of memory?");
+  nsRefPtr<IDBKeyRange> keyRange = new IDBKeyRange(open, true, false);
 
-  if (!ReturnKeyRange(aCx, aVp, range)) {
-    return JS_FALSE;
+  if (!GetKeyFromJSValOrThrow(aCx, val, keyRange->Lower())) {
+    return false;
   }
 
-  return JS_TRUE;
+  return ReturnKeyRange(aCx, aVp, keyRange);
 }
 
 JSBool
 MakeUpperBoundKeyRange(JSContext* aCx,
                        uintN aArgc,
                        jsval* aVp)
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
-  nsAutoTArray<nsCOMPtr<nsIVariant>, 1> keys;
-  if (!ConvertArguments(aCx, aArgc, aVp, "IDBKeyRange.upperBound", keys)) {
-    return JS_FALSE;
-  }
-  NS_ASSERTION(keys.Length() == 1, "Didn't set all keys!");
-
-  JSBool open = JS_FALSE;
-  if (aArgc > 1 && !JS_ValueToBoolean(aCx, JS_ARGV(aCx, aVp)[1], &open)) {
-    JS_ReportError(aCx, "Couldn't convert argument 2 to boolean.");
-    return JS_FALSE;
+  jsval val;
+  JSBool open = false;
+  if (!JS_ConvertArguments(aCx, aArgc, JS_ARGV(aCx, aVp), "v/b", &val, &open)) {
+    return false;
   }
 
-  nsRefPtr<IDBKeyRange> range =
-    IDBKeyRange::Create(nsnull, keys[0], true, !!open);
-  NS_ASSERTION(range, "Out of memory?");
+  nsRefPtr<IDBKeyRange> keyRange = new IDBKeyRange(true, open, false);
 
-  if (!ReturnKeyRange(aCx, aVp, range)) {
-    return JS_FALSE;
+  if (!GetKeyFromJSValOrThrow(aCx, val, keyRange->Upper())) {
+    return false;
   }
 
-  return JS_TRUE;
+  return ReturnKeyRange(aCx, aVp, keyRange);
 }
 
 JSBool
 MakeBoundKeyRange(JSContext* aCx,
                   uintN aArgc,
                   jsval* aVp)
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
-  nsAutoTArray<nsCOMPtr<nsIVariant>, 2> keys;
-  if (!ConvertArguments(aCx, aArgc, aVp, "IDBKeyRange.bound", keys)) {
-    return JS_FALSE;
-  }
-  NS_ASSERTION(keys.Length() == 2, "Didn't set all keys!");
-
-  JSBool lowerOpen = JS_FALSE;
-  if (aArgc > 2 && !JS_ValueToBoolean(aCx, JS_ARGV(aCx, aVp)[2], &lowerOpen)) {
-    JS_ReportError(aCx, "Couldn't convert argument 3 to boolean.");
-    return JS_FALSE;
+  jsval lowerVal, upperVal;
+  JSBool lowerOpen = false, upperOpen = false;
+  if (!JS_ConvertArguments(aCx, aArgc, JS_ARGV(aCx, aVp), "vv/bb", &lowerVal,
+                           &upperVal, &lowerOpen, &upperOpen)) {
+    return false;
   }
 
-  JSBool upperOpen = JS_FALSE;
-  if (aArgc > 3 && !JS_ValueToBoolean(aCx, JS_ARGV(aCx, aVp)[3], &upperOpen)) {
-    JS_ReportError(aCx, "Couldn't convert argument 3 to boolean.");
-    return JS_FALSE;
+  nsRefPtr<IDBKeyRange> keyRange = new IDBKeyRange(lowerOpen, upperOpen, false);
+
+  if (!GetKeyFromJSValOrThrow(aCx, lowerVal, keyRange->Lower()) ||
+      !GetKeyFromJSValOrThrow(aCx, upperVal, keyRange->Upper())) {
+    return false;
   }
 
-  nsRefPtr<IDBKeyRange> range =
-    IDBKeyRange::Create(keys[0], keys[1], lowerOpen, upperOpen);
-  NS_ASSERTION(range, "Out of memory?");
-
-  if (!ReturnKeyRange(aCx, aVp, range)) {
-    return JS_FALSE;
+  if (keyRange->Lower() > keyRange->Upper() ||
+      (keyRange->Lower() == keyRange->Upper() && (lowerOpen || upperOpen))) {
+    ThrowException(aCx, NS_ERROR_DOM_INDEXEDDB_DATA_ERR);
+    return false;
   }
 
-  return JS_TRUE;
+  return ReturnKeyRange(aCx, aVp, keyRange);
 }
 
-
 #define KEYRANGE_FUNCTION_FLAGS (JSPROP_ENUMERATE | JSPROP_PERMANENT)
 
 const JSFunctionSpec gKeyRangeConstructors[] = {
   JS_FN("only", MakeOnlyKeyRange, 1, KEYRANGE_FUNCTION_FLAGS),
   JS_FN("lowerBound", MakeLowerBoundKeyRange, 1, KEYRANGE_FUNCTION_FLAGS),
   JS_FN("upperBound", MakeUpperBoundKeyRange, 1, KEYRANGE_FUNCTION_FLAGS),
   JS_FN("bound", MakeBoundKeyRange, 2, KEYRANGE_FUNCTION_FLAGS),
   JS_FS_END
@@ -280,72 +256,152 @@ IDBKeyRange::DefineConstructors(JSContex
   NS_ASSERTION(aObject, "Null pointer!");
 
   // Add the constructor methods for key ranges.
   return JS_DefineFunctions(aCx, aObject,
                             const_cast<JSFunctionSpec*>(gKeyRangeConstructors));
 }
 
 // static
-already_AddRefed<IDBKeyRange>
-IDBKeyRange::Create(nsIVariant* aLower,
-                    nsIVariant* aUpper,
-                    bool aLowerOpen,
-                    bool aUpperOpen)
+nsresult
+IDBKeyRange::FromJSVal(JSContext* aCx,
+                       const jsval& aVal,
+                       IDBKeyRange** aKeyRange)
 {
-  nsRefPtr<IDBKeyRange> keyRange(new IDBKeyRange());
-  keyRange->mLower = aLower;
-  keyRange->mUpper = aUpper;
-  keyRange->mLowerOpen = aLowerOpen;
-  keyRange->mUpperOpen = aUpperOpen;
+  nsresult rv;
+  nsRefPtr<IDBKeyRange> keyRange;
+
+  if (JSVAL_IS_VOID(aVal) || JSVAL_IS_NULL(aVal)) {
+    // undefined and null returns no IDBKeyRange.
+  }
+  else if (JSVAL_IS_PRIMITIVE(aVal)) {
+    // A valid key returns an 'only' IDBKeyRange.
+    keyRange = new IDBKeyRange(false, false, true);
 
-  return keyRange.forget();
+    rv = GetKeyFromJSVal(aCx, aVal, keyRange->Lower());
+    if (NS_FAILED(rv)) {
+      return rv;
+    }
+  }
+  else {
+    // An object is not permitted unless it's another IDBKeyRange.
+    nsIXPConnect* xpc = nsContentUtils::XPConnect();
+    NS_ASSERTION(xpc, "This should never be null!");
+
+    nsCOMPtr<nsIXPConnectWrappedNative> wrapper;
+    rv = xpc->GetWrappedNativeOfJSObject(aCx, JSVAL_TO_OBJECT(aVal),
+                                         getter_AddRefs(wrapper));
+    NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
+
+    nsCOMPtr<nsIIDBKeyRange> iface;
+    if (!wrapper || !(iface = do_QueryInterface(wrapper->Native()))) {
+      // Some random JS object?
+      return NS_ERROR_DOM_INDEXEDDB_DATA_ERR;
+    }
+
+    keyRange = static_cast<IDBKeyRange*>(iface.get());
+  }
+
+  keyRange.forget(aKeyRange);
+  return NS_OK;
 }
 
-NS_IMPL_ADDREF(IDBKeyRange)
-NS_IMPL_RELEASE(IDBKeyRange)
+NS_IMPL_CYCLE_COLLECTION_CLASS(IDBKeyRange)
+
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(IDBKeyRange)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
-NS_INTERFACE_MAP_BEGIN(IDBKeyRange)
-  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIIDBKeyRange)
+NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(IDBKeyRange)
+  if (JSVAL_IS_GCTHING(tmp->mCachedLowerVal)) {
+    void *gcThing = JSVAL_TO_GCTHING(tmp->mCachedLowerVal);
+    NS_IMPL_CYCLE_COLLECTION_TRACE_JS_CALLBACK(gcThing, "mCachedLowerVal")
+  }
+  if (JSVAL_IS_GCTHING(tmp->mCachedUpperVal)) {
+    void *gcThing = JSVAL_TO_GCTHING(tmp->mCachedUpperVal);
+    NS_IMPL_CYCLE_COLLECTION_TRACE_JS_CALLBACK(gcThing, "mCachedUpperVal")
+  }
+NS_IMPL_CYCLE_COLLECTION_TRACE_END
+
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(IDBKeyRange)
+  if (tmp->mRooted) {
+    NS_DROP_JS_OBJECTS(tmp, IDBKeyRange);
+    tmp->mCachedLowerVal = JSVAL_VOID;
+    tmp->mCachedUpperVal = JSVAL_VOID;
+    tmp->mHaveCachedLowerVal = false;
+    tmp->mHaveCachedUpperVal = false;
+    tmp->mRooted = false;
+  }
+NS_IMPL_CYCLE_COLLECTION_UNLINK_END
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(IDBKeyRange)
+  NS_INTERFACE_MAP_ENTRY(nsISupports)
   NS_INTERFACE_MAP_ENTRY(nsIIDBKeyRange)
   NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(IDBKeyRange)
 NS_INTERFACE_MAP_END
 
+NS_IMPL_CYCLE_COLLECTING_ADDREF(IDBKeyRange)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(IDBKeyRange)
+
 DOMCI_DATA(IDBKeyRange, IDBKeyRange)
 
 NS_IMETHODIMP
-IDBKeyRange::GetLower(nsIVariant** aLower)
+IDBKeyRange::GetLower(JSContext* aCx,
+                      jsval* aLower)
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
-  nsCOMPtr<nsIVariant> result(mLower);
-  result.forget(aLower);
+  if (!mHaveCachedLowerVal) {
+    if (!mRooted) {
+      NS_HOLD_JS_OBJECTS(this, IDBKeyRange);
+      mRooted = true;
+    }
+
+    nsresult rv = Lower().ToJSVal(aCx, &mCachedLowerVal);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    mHaveCachedLowerVal = true;
+  }
+
+  *aLower = mCachedLowerVal;
   return NS_OK;
 }
 
 NS_IMETHODIMP
-IDBKeyRange::GetUpper(nsIVariant** aUpper)
+IDBKeyRange::GetUpper(JSContext* aCx,
+                      jsval* aUpper)
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
-  nsCOMPtr<nsIVariant> result(mUpper);
-  result.forget(aUpper);
+  if (!mHaveCachedUpperVal) {
+    if (!mRooted) {
+      NS_HOLD_JS_OBJECTS(this, IDBKeyRange);
+      mRooted = true;
+    }
+
+    nsresult rv = Upper().ToJSVal(aCx, &mCachedUpperVal);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    mHaveCachedUpperVal = true;
+  }
+
+  *aUpper = mCachedUpperVal;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 IDBKeyRange::GetLowerOpen(bool* aLowerOpen)
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
-  *aLowerOpen = mLowerOpen ? true : false;
+  *aLowerOpen = mLowerOpen;
   return NS_OK;
 }
 
 
 NS_IMETHODIMP
 IDBKeyRange::GetUpperOpen(bool* aUpperOpen)
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
-  *aUpperOpen = mUpperOpen ? true : false;
+  *aUpperOpen = mUpperOpen;
   return NS_OK;
 }
--- a/dom/indexedDB/IDBKeyRange.h
+++ b/dom/indexedDB/IDBKeyRange.h
@@ -36,45 +36,85 @@
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #ifndef mozilla_dom_indexeddb_idbkeyrange_h__
 #define mozilla_dom_indexeddb_idbkeyrange_h__
 
 #include "mozilla/dom/indexedDB/IndexedDatabase.h"
+#include "mozilla/dom/indexedDB/Key.h"
 
 #include "nsIIDBKeyRange.h"
-#include "nsIVariant.h"
+
+#include "nsCycleCollectionParticipant.h"
 
 BEGIN_INDEXEDDB_NAMESPACE
 
 class IDBKeyRange : public nsIIDBKeyRange
 {
 public:
-  NS_DECL_ISUPPORTS
+  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_NSIIDBKEYRANGE
+  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(IDBKeyRange)
 
   static JSBool DefineConstructors(JSContext* aCx,
                                    JSObject* aObject);
 
   static
-  already_AddRefed<IDBKeyRange> Create(nsIVariant* aLower,
-                                       nsIVariant* aUpper,
-                                       bool aLowerOpen,
-                                       bool aUpperOpen);
+  nsresult FromJSVal(JSContext* aCx,
+                     const jsval& aVal,
+                     IDBKeyRange** aKeyRange);
+
+  IDBKeyRange(bool aLowerOpen, bool aUpperOpen, bool aIsOnly)
+  : mCachedLowerVal(JSVAL_VOID), mCachedUpperVal(JSVAL_VOID),
+    mLowerOpen(aLowerOpen), mUpperOpen(aUpperOpen), mIsOnly(aIsOnly),
+    mHaveCachedLowerVal(false), mHaveCachedUpperVal(false), mRooted(false)
+  { }
+
+  const Key& Lower() const
+  {
+    return mLower;
+  }
+
+  Key& Lower()
+  {
+    return mLower;
+  }
+
+  const Key& Upper() const
+  {
+    return mIsOnly ? mLower : mUpper;
+  }
+
+  Key& Upper()
+  {
+    return mIsOnly ? mLower : mUpper;
+  }
+
+  bool IsLowerOpen() const
+  {
+    return mLowerOpen;
+  }
+
+  bool IsUpperOpen() const
+  {
+    return mUpperOpen;
+  }
 
 protected:
-  IDBKeyRange()
-  : mLowerOpen(false), mUpperOpen(false)
-  { }
-
   ~IDBKeyRange() { }
 
-  nsCOMPtr<nsIVariant> mLower;
-  nsCOMPtr<nsIVariant> mUpper;
+  Key mLower;
+  Key mUpper;
+  jsval mCachedLowerVal;
+  jsval mCachedUpperVal;
   bool mLowerOpen;
   bool mUpperOpen;
+  bool mIsOnly;
+  bool mHaveCachedLowerVal;
+  bool mHaveCachedUpperVal;
+  bool mRooted;
 };
 
 END_INDEXEDDB_NAMESPACE
 
 #endif // mozilla_dom_indexeddb_idbkeyrange_h__
--- a/dom/indexedDB/IDBObjectStore.cpp
+++ b/dom/indexedDB/IDBObjectStore.cpp
@@ -35,19 +35,17 @@
  * 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 "IDBObjectStore.h"
 
 #include "nsIJSContextStack.h"
-#include "nsIVariant.h"
-
-#include "jscntxt.h"
+
 #include "jsclone.h"
 #include "mozilla/storage.h"
 #include "nsContentUtils.h"
 #include "nsDOMClassInfo.h"
 #include "nsEventDispatcher.h"
 #include "nsJSUtils.h"
 #include "nsServiceManagerUtils.h"
 #include "nsThreadUtils.h"
@@ -59,19 +57,16 @@
 #include "IDBKeyRange.h"
 #include "IDBTransaction.h"
 #include "DatabaseInfo.h"
 
 USING_INDEXEDDB_NAMESPACE
 
 namespace {
 
-// This is just to give us some random marker in the byte stream
-static const PRUint64 kTotallyRandomNumber = LL_INIT(0x286F258B, 0x177D47A9);
-
 class AddHelper : public AsyncConnectionHelper
 {
 public:
   AddHelper(IDBTransaction* aTransaction,
             IDBRequest* aRequest,
             IDBObjectStore* aObjectStore,
             JSAutoStructuredCloneBuffer& aCloneBuffer,
             const Key& aKey,
@@ -141,17 +136,17 @@ public:
     mObjectStore = nsnull;
     IDBObjectStore::ClearStructuredCloneBuffer(mCloneBuffer);
     AsyncConnectionHelper::ReleaseMainThreadObjects();
   }
 
 protected:
   // In-params.
   nsRefPtr<IDBObjectStore> mObjectStore;
-  const Key mKey;
+  Key mKey;
 
 private:
   // Out-params.
   JSAutoStructuredCloneBuffer mCloneBuffer;
 };
 
 class DeleteHelper : public GetHelper
 {
@@ -191,49 +186,43 @@ protected:
 };
 
 class OpenCursorHelper : public AsyncConnectionHelper
 {
 public:
   OpenCursorHelper(IDBTransaction* aTransaction,
                    IDBRequest* aRequest,
                    IDBObjectStore* aObjectStore,
-                   const Key& aLowerKey,
-                   const Key& aUpperKey,
-                   bool aLowerOpen,
-                   bool aUpperOpen,
+                   IDBKeyRange* aKeyRange,
                    PRUint16 aDirection)
   : AsyncConnectionHelper(aTransaction, aRequest), mObjectStore(aObjectStore),
-    mLowerKey(aLowerKey), mUpperKey(aUpperKey), mLowerOpen(aLowerOpen),
-    mUpperOpen(aUpperOpen), mDirection(aDirection)
+    mKeyRange(aKeyRange), mDirection(aDirection)
   { }
 
   ~OpenCursorHelper()
   {
     IDBObjectStore::ClearStructuredCloneBuffer(mCloneBuffer);
   }
 
   nsresult DoDatabaseWork(mozIStorageConnection* aConnection);
   nsresult GetSuccessResult(JSContext* aCx,
                             jsval* aVal);
 
   void ReleaseMainThreadObjects()
   {
     mObjectStore = nsnull;
+    mKeyRange = nsnull;
     IDBObjectStore::ClearStructuredCloneBuffer(mCloneBuffer);
     AsyncConnectionHelper::ReleaseMainThreadObjects();
   }
 
 private:
   // In-params.
   nsRefPtr<IDBObjectStore> mObjectStore;
-  const Key mLowerKey;
-  const Key mUpperKey;
-  const bool mLowerOpen;
-  const bool mUpperOpen;
+  nsRefPtr<IDBKeyRange> mKeyRange;
   const PRUint16 mDirection;
 
   // Out-params.
   Key mKey;
   JSAutoStructuredCloneBuffer mCloneBuffer;
   nsCString mContinueQuery;
   nsCString mContinueToQuery;
   Key mRangeKey;
@@ -268,18 +257,17 @@ private:
   static void DestroyTLSEntry(void* aPtr);
 
   static PRUintn sTLSIndex;
 
   // In-params.
   nsRefPtr<IDBIndex> mIndex;
 };
 
-static const PRUintn BAD_TLS_INDEX = (PRUint32)-1;
-PRUintn CreateIndexHelper::sTLSIndex = BAD_TLS_INDEX;
+PRUintn CreateIndexHelper::sTLSIndex = PRUintn(BAD_TLS_INDEX);
 
 class DeleteIndexHelper : public AsyncConnectionHelper
 {
 public:
   DeleteIndexHelper(IDBTransaction* aTransaction,
                     const nsAString& aName,
                     IDBObjectStore* aObjectStore)
   : AsyncConnectionHelper(aTransaction, nsnull), mName(aName),
@@ -311,53 +299,47 @@ private:
 };
 
 class GetAllHelper : public AsyncConnectionHelper
 {
 public:
   GetAllHelper(IDBTransaction* aTransaction,
                IDBRequest* aRequest,
                IDBObjectStore* aObjectStore,
-               const Key& aLowerKey,
-               const Key& aUpperKey,
-               const bool aLowerOpen,
-               const bool aUpperOpen,
+               IDBKeyRange* aKeyRange,
                const PRUint32 aLimit)
   : AsyncConnectionHelper(aTransaction, aRequest), mObjectStore(aObjectStore),
-    mLowerKey(aLowerKey), mUpperKey(aUpperKey), mLowerOpen(aLowerOpen),
-    mUpperOpen(aUpperOpen), mLimit(aLimit)
+    mKeyRange(aKeyRange), mLimit(aLimit)
   { }
 
   ~GetAllHelper()
   {
     for (PRUint32 index = 0; index < mCloneBuffers.Length(); index++) {
       IDBObjectStore::ClearStructuredCloneBuffer(mCloneBuffers[index]);
     }
   }
 
   nsresult DoDatabaseWork(mozIStorageConnection* aConnection);
   nsresult GetSuccessResult(JSContext* aCx,
                             jsval* aVal);
 
   void ReleaseMainThreadObjects()
   {
     mObjectStore = nsnull;
+    mKeyRange = nsnull;
     for (PRUint32 index = 0; index < mCloneBuffers.Length(); index++) {
       IDBObjectStore::ClearStructuredCloneBuffer(mCloneBuffers[index]);
     }
     AsyncConnectionHelper::ReleaseMainThreadObjects();
   }
 
 protected:
   // In-params.
   nsRefPtr<IDBObjectStore> mObjectStore;
-  const Key mLowerKey;
-  const Key mUpperKey;
-  const bool mLowerOpen;
-  const bool mUpperOpen;
+  nsRefPtr<IDBKeyRange> mKeyRange;
   const PRUint32 mLimit;
 
 private:
   // Out-params.
   nsTArray<JSAutoStructuredCloneBuffer> mCloneBuffers;
 };
 
 NS_STACK_CLASS
@@ -409,17 +391,17 @@ GetKeyFromObject(JSContext* aCx,
 
   const jschar* keyPathChars = reinterpret_cast<const jschar*>(aKeyPath.get());
   const size_t keyPathLen = aKeyPath.Length();
 
   jsval key;
   JSBool ok = JS_GetUCProperty(aCx, aObj, keyPathChars, keyPathLen, &key);
   NS_ENSURE_TRUE(ok, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
-  nsresult rv = IDBObjectStore::GetKeyFromJSVal(key, aCx, aKey);
+  nsresult rv = aKey.SetFromJSVal(aCx, key);
   NS_ENSURE_SUCCESS(rv, rv);
 
   return NS_OK;
 }
 
 inline
 already_AddRefed<IDBRequest>
 GenerateRequest(IDBObjectStore* aObjectStore)
@@ -486,117 +468,16 @@ IDBObjectStore::Create(IDBTransaction* a
   objectStore->mAutoIncrement = aStoreInfo->autoIncrement;
   objectStore->mDatabaseId = aStoreInfo->databaseId;
 
   return objectStore.forget();
 }
 
 // static
 nsresult
-IDBObjectStore::GetKeyFromVariant(nsIVariant* aKeyVariant,
-                                  Key& aKey)
-{
-  if (!aKeyVariant) {
-    aKey = Key::UNSETKEY;
-    return NS_OK;
-  }
-
-  PRUint16 type;
-  nsresult rv = aKeyVariant->GetDataType(&type);
-  NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
-
-  // See xpcvariant.cpp, these are the only types we should expect.
-  switch (type) {
-    case nsIDataType::VTYPE_VOID:
-      aKey = Key::UNSETKEY;
-      return NS_OK;
-
-    case nsIDataType::VTYPE_WSTRING_SIZE_IS:
-      rv = aKeyVariant->GetAsAString(aKey.ToString());
-      NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
-      return NS_OK;
-
-    case nsIDataType::VTYPE_INT32:
-    case nsIDataType::VTYPE_DOUBLE:
-      rv = aKeyVariant->GetAsInt64(aKey.ToIntPtr());
-      NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
-      return NS_OK;
-
-    default:
-      return NS_ERROR_DOM_INDEXEDDB_DATA_ERR;
-  }
-
-  NS_NOTREACHED("Can't get here!");
-  return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
-}
-
-// static
-nsresult
-IDBObjectStore::GetKeyFromJSVal(jsval aKeyVal,
-                                JSContext* aCx,
-                                Key& aKey)
-{
-  if (JSVAL_IS_VOID(aKeyVal)) {
-    aKey = Key::UNSETKEY;
-  }
-  else if (JSVAL_IS_STRING(aKeyVal)) {
-    nsDependentJSString depStr;
-    if (!depStr.init(aCx, JSVAL_TO_STRING(aKeyVal))) {
-      return NS_ERROR_OUT_OF_MEMORY;
-    }
-    aKey = depStr;
-  }
-  else if (JSVAL_IS_INT(aKeyVal)) {
-    aKey = JSVAL_TO_INT(aKeyVal);
-  }
-  else if (JSVAL_IS_DOUBLE(aKeyVal)) {
-    aKey = JSVAL_TO_DOUBLE(aKeyVal);
-  }
-  else {
-    return NS_ERROR_DOM_INDEXEDDB_DATA_ERR;
-  }
-
-  return NS_OK;
-}
-
-// static
-nsresult
-IDBObjectStore::GetJSValFromKey(const Key& aKey,
-                                JSContext* aCx,
-                                jsval* aKeyVal)
-{
-  if (aKey.IsUnset()) {
-    *aKeyVal = JSVAL_VOID;
-    return NS_OK;
-  }
-
-  if (aKey.IsInt()) {
-    JSBool ok = JS_NewNumberValue(aCx, aKey.IntValue(), aKeyVal);
-    NS_ENSURE_TRUE(ok, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
-    return NS_OK;
-  }
-
-  if (aKey.IsString()) {
-    const nsString& keyString = aKey.StringValue();
-    JSString* str =
-      JS_NewUCStringCopyN(aCx,
-                          reinterpret_cast<const jschar*>(keyString.get()),
-                          keyString.Length());
-    NS_ENSURE_TRUE(str, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
-
-    *aKeyVal = STRING_TO_JSVAL(str);
-    return NS_OK;
-  }
-
-  NS_NOTREACHED("Unknown key type!");
-  return NS_ERROR_DOM_INDEXEDDB_DATA_ERR;
-}
-
-// static
-nsresult
 IDBObjectStore::GetKeyPathValueFromStructuredData(const PRUint8* aData,
                                                   PRUint32 aDataLength,
                                                   const nsAString& aKeyPath,
                                                   JSContext* aCx,
                                                   Key& aValue)
 {
   NS_ASSERTION(aData, "Null pointer!");
   NS_ASSERTION(aDataLength, "Empty data!");
@@ -609,35 +490,35 @@ IDBObjectStore::GetKeyPathValueFromStruc
   if (!JS_ReadStructuredClone(aCx, reinterpret_cast<const uint64*>(aData),
                               aDataLength, JS_STRUCTURED_CLONE_VERSION,
                               &clone, NULL, NULL)) {
     return NS_ERROR_DOM_DATA_CLONE_ERR;
   }
 
   if (JSVAL_IS_PRIMITIVE(clone)) {
     // This isn't an object, so just leave the key unset.
-    aValue = Key::UNSETKEY;
+    aValue.Unset();
     return NS_OK;
   }
 
   JSObject* obj = JSVAL_TO_OBJECT(clone);
 
   const jschar* keyPathChars =
     reinterpret_cast<const jschar*>(aKeyPath.BeginReading());
   const size_t keyPathLen = aKeyPath.Length();
 
   jsval keyVal;
   JSBool ok = JS_GetUCProperty(aCx, obj, keyPathChars, keyPathLen, &keyVal);
   NS_ENSURE_TRUE(ok, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
-  nsresult rv = GetKeyFromJSVal(keyVal, aCx, aValue);
+  nsresult rv = aValue.SetFromJSVal(aCx, keyVal);
   if (NS_FAILED(rv)) {
     // If the object doesn't have a value that we can use for our index then we
     // leave it unset.
-    aValue = Key::UNSETKEY;
+    aValue.Unset();
   }
 
   return NS_OK;
 }
 
 /* static */
 nsresult
 IDBObjectStore::GetIndexUpdateInfo(ObjectStoreInfo* aObjectStoreInfo,
@@ -664,17 +545,17 @@ IDBObjectStore::GetIndexUpdateInfo(Objec
       const size_t keyPathLen = indexInfo.keyPath.Length();
 
       jsval keyPathValue;
       JSBool ok = JS_GetUCProperty(aCx, cloneObj, keyPathChars, keyPathLen,
                                    &keyPathValue);
       NS_ENSURE_TRUE(ok, NS_ERROR_FAILURE);
 
       Key value;
-      nsresult rv = GetKeyFromJSVal(keyPathValue, aCx, value);
+      nsresult rv = value.SetFromJSVal(aCx, keyPathValue);
       if (NS_FAILED(rv) || value.IsUnset()) {
         // Not a value we can do anything with, ignore it.
         continue;
       }
 
       IndexUpdateInfo* updateInfo = aUpdateInfoArray.AppendElement();
       updateInfo->info = indexInfo;
       updateInfo->value = value;
@@ -723,27 +604,17 @@ IDBObjectStore::UpdateIndexes(IDBTransac
 
     mozStorageStatementScoper scoper(stmt);
 
     rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("osid"), aObjectStoreId);
     NS_ENSURE_SUCCESS(rv, rv);
 
     NS_ASSERTION(!aObjectStoreKey.IsUnset(), "This shouldn't happen!");
 
-    NS_NAMED_LITERAL_CSTRING(keyValue, "key_value");
-
-    if (aObjectStoreKey.IsInt()) {
-      rv = stmt->BindInt64ByName(keyValue, aObjectStoreKey.IntValue());
-    }
-    else if (aObjectStoreKey.IsString()) {
-      rv = stmt->BindStringByName(keyValue, aObjectStoreKey.StringValue());
-    }
-    else {
-      NS_NOTREACHED("Unknown key type!");
-    }
+    rv = aObjectStoreKey.BindToStatement(stmt, NS_LITERAL_CSTRING("key_value"));
     NS_ENSURE_SUCCESS(rv, rv);
 
     bool hasResult;
     rv = stmt->ExecuteStep(&hasResult);
     NS_ENSURE_SUCCESS(rv, rv);
 
     if (!hasResult) {
       NS_ERROR("This is bad, we just added this value! Where'd it go?!");
@@ -769,45 +640,25 @@ IDBObjectStore::UpdateIndexes(IDBTransac
                                updateInfo.info.id);
     NS_ENSURE_SUCCESS(rv, rv);
 
     rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("object_data_id"),
                                aObjectDataId);
     NS_ENSURE_SUCCESS(rv, rv);
 
     if (!updateInfo.info.autoIncrement) {
-      NS_NAMED_LITERAL_CSTRING(objectDataKey, "object_data_key");
-
-      if (aObjectStoreKey.IsInt()) {
-        rv = stmt->BindInt64ByName(objectDataKey, aObjectStoreKey.IntValue());
-      }
-      else if (aObjectStoreKey.IsString()) {
-        rv = stmt->BindStringByName(objectDataKey,
-                                    aObjectStoreKey.StringValue());
-      }
-      else {
-        NS_NOTREACHED("Unknown key type!");
-      }
+      rv =
+        aObjectStoreKey.BindToStatement(stmt,
+                                        NS_LITERAL_CSTRING("object_data_key"));
       NS_ENSURE_SUCCESS(rv, rv);
     }
 
-    NS_NAMED_LITERAL_CSTRING(value, "value");
-
-    if (updateInfo.value.IsInt()) {
-      rv = stmt->BindInt64ByName(value, updateInfo.value.IntValue());
-    }
-    else if (updateInfo.value.IsString()) {
-      rv = stmt->BindStringByName(value, updateInfo.value.StringValue());
-    }
-    else if (updateInfo.value.IsUnset()) {
-      rv = stmt->BindStringByName(value, updateInfo.value.StringValue());
-    }
-    else {
-      NS_NOTREACHED("Unknown key type!");
-    }
+    rv =
+      updateInfo.value.BindToStatementAllowUnset(stmt,
+                                                 NS_LITERAL_CSTRING("value"));
     NS_ENSURE_SUCCESS(rv, rv);
 
     rv = stmt->Execute();
     if (NS_FAILED(rv)) {
       return rv;
     }
   }
 
@@ -896,36 +747,35 @@ SwapBytes(PRUint64 u)
            ((u & 0x000000000000ff00LLU) << 40) |
            ((u & 0x0000000000ff0000LLU) << 24) |
             ((u & 0x00000000ff000000LLU) << 8) |
             ((u & 0x000000ff00000000LLU) >> 8) |
            ((u & 0x0000ff0000000000LLU) >> 24) |
            ((u & 0x00ff000000000000LLU) >> 40) |
            ((u & 0xff00000000000000LLU) >> 56);
 #else
-     return u;
+     return jsdouble(u);
 #endif
 }
 
 nsresult
 IDBObjectStore::ModifyValueForNewKey(JSAutoStructuredCloneBuffer& aBuffer,
                                      Key& aKey,
                                      PRUint64 aOffsetToKeyProp)
 {
-  NS_ASSERTION(IsAutoIncrement() && KeyPath().IsEmpty() && aKey.IsInt(),
-               "Don't call me!");
+  NS_ASSERTION(IsAutoIncrement() && aKey.IsInteger(), "Don't call me!");
   NS_ASSERTION(!NS_IsMainThread(), "Wrong thread");
 
   // This is a duplicate of the js engine's byte munging here
   union {
     jsdouble d;
     PRUint64 u;
   } pun;
 
-  pun.d = SwapBytes(aKey.IntValue());
+  pun.d = SwapBytes(aKey.ToInteger());
 
   memcpy((char*)aBuffer.data() + aOffsetToKeyProp, &pun.u, sizeof(PRUint64));
   return NS_OK;
 }
 
 IDBObjectStore::IDBObjectStore()
 : mId(LL_MININT),
   mAutoIncrement(false)
@@ -954,17 +804,17 @@ IDBObjectStore::GetAddInfo(JSContext* aC
   if (!JSVAL_IS_VOID(aKeyVal) && !mKeyPath.IsEmpty()) {
     return NS_ERROR_DOM_INDEXEDDB_DATA_ERR;
   }
 
   JSAutoRequest ar(aCx);
 
   if (mKeyPath.IsEmpty()) {
     // Out-of-line keys must be passed in.
-    rv = GetKeyFromJSVal(aKeyVal, aCx, aKey);
+    rv = aKey.SetFromJSVal(aCx, aKeyVal);
     NS_ENSURE_SUCCESS(rv, rv);
   }
   else {
     // Inline keys live on the object. Make sure that the value passed in is an
     // object.
     if (JSVAL_IS_PRIMITIVE(aValue)) {
       return NS_ERROR_DOM_INDEXEDDB_DATA_ERR;
     }
@@ -1168,52 +1018,31 @@ IDBObjectStore::GetIndexNames(nsIDOMDOMS
                    NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
   }
 
   list.forget(aIndexNames);
   return NS_OK;
 }
 
 NS_IMETHODIMP
-IDBObjectStore::Get(nsIVariant* aKey,
+IDBObjectStore::Get(const jsval& aKey,
+                    JSContext* aCx,
                     nsIIDBRequest** _retval)
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
   if (!mTransaction->IsOpen()) {
     return NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR;
   }
 
   Key key;
-  nsresult rv = GetKeyFromVariant(aKey, key);
+  nsresult rv = key.SetFromJSVal(aCx, aKey);
   if (NS_FAILED(rv)) {
-    // Check to see if this is a key range.
-    PRUint16 type;
-    rv = aKey->GetDataType(&type);
-    NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
-
-    if (type != nsIDataType::VTYPE_INTERFACE &&
-        type != nsIDataType::VTYPE_INTERFACE_IS) {
-      return NS_ERROR_DOM_INDEXEDDB_DATA_ERR;
-    }
-
-    // XXX I hate this API. Move to jsvals, stat.
-    nsID* iid;
-    nsCOMPtr<nsISupports> supports;
-    rv = aKey->GetAsInterface(&iid, getter_AddRefs(supports));
-    NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
-
-    NS_Free(iid);
-
-    nsCOMPtr<nsIIDBKeyRange> keyRange = do_QueryInterface(supports);
-    if (!keyRange) {
-      return NS_ERROR_DOM_INDEXEDDB_DATA_ERR;
-    }
-
-    return GetAll(keyRange, 0, 0, _retval);
+    // Maybe this is a key range.
+    return GetAll(aKey, 0, aCx, 1, _retval);
   }
 
   if (key.IsUnset()) {
     return NS_ERROR_DOM_INDEXEDDB_DATA_ERR;
   }
 
   nsRefPtr<IDBRequest> request = GenerateRequest(this);
   NS_ENSURE_TRUE(request, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
@@ -1223,66 +1052,45 @@ IDBObjectStore::Get(nsIVariant* aKey,
   rv = helper->DispatchToTransactionPool();
   NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
   request.forget(_retval);
   return NS_OK;
 }
 
 NS_IMETHODIMP
-IDBObjectStore::GetAll(nsIIDBKeyRange* aKeyRange,
+IDBObjectStore::GetAll(const jsval& aKey,
                        PRUint32 aLimit,
+                       JSContext* aCx,
                        PRUint8 aOptionalArgCount,
                        nsIIDBRequest** _retval)
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
   if (!mTransaction->IsOpen()) {
     return NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR;
   }
 
+  nsresult rv;
+
+  nsRefPtr<IDBKeyRange> keyRange;
+  if (aOptionalArgCount) {
+    rv = IDBKeyRange::FromJSVal(aCx, aKey, getter_AddRefs(keyRange));
+    NS_ENSURE_SUCCESS(rv, rv);
+  }
+
   if (aOptionalArgCount < 2) {
     aLimit = PR_UINT32_MAX;
   }
 
-  nsresult rv;
-  Key lowerKey, upperKey;
-  bool lowerOpen = false, upperOpen = false;
-
-  if (aKeyRange) {
-    nsCOMPtr<nsIVariant> variant;
-    rv = aKeyRange->GetLower(getter_AddRefs(variant));
-    NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
-
-    rv = IDBObjectStore::GetKeyFromVariant(variant, lowerKey);
-    if (NS_FAILED(rv)) {
-      return rv;
-    }
-
-    rv = aKeyRange->GetUpper(getter_AddRefs(variant));
-    NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
-
-    rv = IDBObjectStore::GetKeyFromVariant(variant, upperKey);
-    if (NS_FAILED(rv)) {
-      return rv;
-    }
-
-    rv = aKeyRange->GetLowerOpen(&lowerOpen);
-    NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
-
-    rv = aKeyRange->GetUpperOpen(&upperOpen);
-    NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
-  }
-
   nsRefPtr<IDBRequest> request = GenerateRequest(this);
   NS_ENSURE_TRUE(request, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
   nsRefPtr<GetAllHelper> helper =
-    new GetAllHelper(mTransaction, request, this, lowerKey, upperKey, lowerOpen,
-                     upperOpen, aLimit);
+    new GetAllHelper(mTransaction, request, this, keyRange, aLimit);
 
   rv = helper->DispatchToTransactionPool();
   NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
   request.forget(_retval);
   return NS_OK;
 }
 
@@ -1321,17 +1129,17 @@ IDBObjectStore::Delete(const jsval& aKey
     return NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR;
   }
 
   if (!IsWriteAllowed()) {
     return NS_ERROR_DOM_INDEXEDDB_READ_ONLY_ERR;
   }
 
   Key key;
-  nsresult rv = GetKeyFromJSVal(aKey, aCx, key);
+  nsresult rv = key.SetFromJSVal(aCx, aKey);
   if (NS_FAILED(rv)) {
     return rv;
   }
 
   if (key.IsUnset()) {
     return NS_ERROR_DOM_INDEXEDDB_DATA_ERR;
   }
 
@@ -1369,74 +1177,53 @@ IDBObjectStore::Clear(nsIIDBRequest** _r
   nsresult rv = helper->DispatchToTransactionPool();
   NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
   request.forget(_retval);
   return NS_OK;
 }
 
 NS_IMETHODIMP
-IDBObjectStore::OpenCursor(nsIIDBKeyRange* aKeyRange,
+IDBObjectStore::OpenCursor(const jsval& aKey,
                            PRUint16 aDirection,
+                           JSContext* aCx,
                            PRUint8 aOptionalArgCount,
                            nsIIDBRequest** _retval)
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
   if (!mTransaction->IsOpen()) {
     return NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR;
   }
 
   nsresult rv;
-  Key lowerKey, upperKey;
-  bool lowerOpen = false, upperOpen = false;
-
-  if (aKeyRange) {
-    nsCOMPtr<nsIVariant> variant;
-    rv = aKeyRange->GetLower(getter_AddRefs(variant));
-    NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
-
-    rv = IDBObjectStore::GetKeyFromVariant(variant, lowerKey);
-    if (NS_FAILED(rv)) {
-      return rv;
+
+  nsRefPtr<IDBKeyRange> keyRange;
+  if (aOptionalArgCount) {
+    rv = IDBKeyRange::FromJSVal(aCx, aKey, getter_AddRefs(keyRange));
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    if (aOptionalArgCount >= 2) {
+      if (aDirection != nsIIDBCursor::NEXT &&
+          aDirection != nsIIDBCursor::NEXT_NO_DUPLICATE &&
+          aDirection != nsIIDBCursor::PREV &&
+          aDirection != nsIIDBCursor::PREV_NO_DUPLICATE) {
+        return NS_ERROR_DOM_INDEXEDDB_NON_TRANSIENT_ERR;
+      }
     }
-
-    rv = aKeyRange->GetUpper(getter_AddRefs(variant));
-    NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
-
-    rv = IDBObjectStore::GetKeyFromVariant(variant, upperKey);
-    if (NS_FAILED(rv)) {
-      return rv;
+    else {
+      aDirection = nsIIDBCursor::NEXT;
     }
-
-    rv = aKeyRange->GetLowerOpen(&lowerOpen);
-    NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
-
-    rv = aKeyRange->GetUpperOpen(&upperOpen);
-    NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
-  }
-
-  if (aOptionalArgCount >= 2) {
-    if (aDirection != nsIIDBCursor::NEXT &&
-        aDirection != nsIIDBCursor::NEXT_NO_DUPLICATE &&
-        aDirection != nsIIDBCursor::PREV &&
-        aDirection != nsIIDBCursor::PREV_NO_DUPLICATE) {
-      return NS_ERROR_DOM_INDEXEDDB_NON_TRANSIENT_ERR;
-    }
-  }
-  else {
-    aDirection = nsIIDBCursor::NEXT;
   }
 
   nsRefPtr<IDBRequest> request = GenerateRequest(this);
   NS_ENSURE_TRUE(request, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
   nsRefPtr<OpenCursorHelper> helper =
-    new OpenCursorHelper(mTransaction, request, this, lowerKey, upperKey,
-                         lowerOpen, upperOpen, aDirection);
+    new OpenCursorHelper(mTransaction, request, this, keyRange, aDirection);
 
   rv = helper->DispatchToTransactionPool();
   NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
   request.forget(_retval);
   return NS_OK;
 }
 
@@ -1668,16 +1455,31 @@ IDBObjectStore::DeleteIndex(const nsAStr
 
   nsresult rv = helper->DispatchToTransactionPool();
   NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
   info->indexes.RemoveElementAt(index);
   return NS_OK;
 }
 
+/*
+NS_IMETHODIMP
+IDBObjectStore::Count(jsval aKey,
+                      JSContext* aCx,
+                      PRUint8 aOptionalArgCount,
+                      nsIIDBRequest** _retval)
+{
+  if (!mTransaction->IsOpen()) {
+    return NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR;
+  }
+
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+*/
+
 nsresult
 AddHelper::DoDatabaseWork(mozIStorageConnection* aConnection)
 {
   NS_PRECONDITION(aConnection, "Passed a null connection!");
 
   nsresult rv;
   bool mayOverwrite = mOverwrite;
   bool unsetKey = mKey.IsUnset();
@@ -1703,28 +1505,18 @@ AddHelper::DoDatabaseWork(mozIStorageCon
     stmt = mTransaction->GetStatement(autoIncrement);
     NS_ENSURE_TRUE(stmt, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
     mozStorageStatementScoper scoper(stmt);
 
     rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("osid"), osid);
     NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
-    NS_NAMED_LITERAL_CSTRING(id, "id");
-
-    if (mKey.IsInt()) {
-      rv = stmt->BindInt64ByName(id, mKey.IntValue());
-    }
-    else if (mKey.IsString()) {
-      rv = stmt->BindStringByName(id, mKey.StringValue());
-    }
-    else {
-      NS_NOTREACHED("Unknown key type!");
-    }
-    NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
+    rv = mKey.BindToStatement(stmt, NS_LITERAL_CSTRING("id"));
+    NS_ENSURE_SUCCESS(rv, rv);
 
     bool hasResult;
     rv = stmt->ExecuteStep(&hasResult);
     NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
     if (hasResult) {
       return NS_ERROR_DOM_INDEXEDDB_CONSTRAINT_ERR;
     }
@@ -1739,26 +1531,18 @@ AddHelper::DoDatabaseWork(mozIStorageCon
   NS_NAMED_LITERAL_CSTRING(keyValue, "key_value");
 
   rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("osid"), osid);
   NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
   if (!autoIncrement || mayOverwrite) {
     NS_ASSERTION(!mKey.IsUnset(), "This shouldn't happen!");
 
-    if (mKey.IsInt()) {
-      rv = stmt->BindInt64ByName(keyValue, mKey.IntValue());
-    }
-    else if (mKey.IsString()) {
-      rv = stmt->BindStringByName(keyValue, mKey.StringValue());
-    }
-    else {
-      NS_NOTREACHED("Unknown key type!");
-    }
-    NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
+    rv = mKey.BindToStatement(stmt, keyValue);
+    NS_ENSURE_SUCCESS(rv, rv);
   }
 
   const PRUint8* buffer = reinterpret_cast<const PRUint8*>(mCloneBuffer.data());
   size_t bufferLength = mCloneBuffer.nbytes();
 
   rv = stmt->BindBlobByName(NS_LITERAL_CSTRING("data"), buffer, bufferLength);
   NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
@@ -1775,52 +1559,48 @@ AddHelper::DoDatabaseWork(mozIStorageCon
 
       mozStorageStatementScoper scoper2(stmt);
 
       rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("osid"), osid);
       NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
       NS_ASSERTION(!mKey.IsUnset(), "This shouldn't happen!");
 
-      if (mKey.IsInt()) {
-        rv = stmt->BindInt64ByName(keyValue, mKey.IntValue());
-      }
-      else if (mKey.IsString()) {
-        rv = stmt->BindStringByName(keyValue, mKey.StringValue());
-      }
-      else {
-        NS_NOTREACHED("Unknown key type!");
-      }
-      NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
+      rv = mKey.BindToStatement(stmt, keyValue);
+      NS_ENSURE_SUCCESS(rv, rv);
 
       rv = stmt->BindBlobByName(NS_LITERAL_CSTRING("data"), buffer,
                                 bufferLength);
       NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
       rv = stmt->Execute();
     }
 
     if (NS_FAILED(rv)) {
       return NS_ERROR_DOM_INDEXEDDB_CONSTRAINT_ERR;
     }
   }
 
   // If we are supposed to generate a key, get the new id.
   if (autoIncrement && !mOverwrite) {
 #ifdef DEBUG
-    PRInt64 oldKey = unsetKey ? 0 : mKey.IntValue();
+    PRInt64 oldKey = unsetKey ? 0 : mKey.ToInteger();
 #endif
 
-    rv = aConnection->GetLastInsertRowID(mKey.ToIntPtr());
+    PRInt64 newIntKey;
+    rv = aConnection->GetLastInsertRowID(&newIntKey);
     NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
+    rv = mKey.SetFromInteger(newIntKey);
+    NS_ENSURE_SUCCESS(rv, rv);
+
 #ifdef DEBUG
-    NS_ASSERTION(mKey.IsInt(), "Bad key value!");
+    NS_ASSERTION(mKey.IsInteger(), "Bad key value!");
     if (!unsetKey) {
-      NS_ASSERTION(mKey.IntValue() == oldKey, "Something went haywire!");
+      NS_ASSERTION(mKey.ToInteger() == oldKey, "Something went haywire!");
     }
 #endif
 
     if (!keyPath.IsEmpty() && unsetKey) {
       // Special case where someone put an object into an autoIncrement'ing
       // objectStore with no key in its keyPath set. We needed to figure out
       // which row id we would get above before we could set that properly.
       rv = mObjectStore->ModifyValueForNewKey(mCloneBuffer, mKey,
@@ -1834,34 +1614,34 @@ AddHelper::DoDatabaseWork(mozIStorageCon
       stmt = mTransaction->AddStatement(false, true, true);
       NS_ENSURE_TRUE(stmt, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
       mozStorageStatementScoper scoper2(stmt);
 
       rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("osid"), osid);
       NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
-      rv = stmt->BindInt64ByName(keyValue, mKey.IntValue());
-      NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
+      rv = mKey.BindToStatement(stmt, keyValue);
+      NS_ENSURE_SUCCESS(rv, rv);
 
       buffer = reinterpret_cast<const PRUint8*>(mCloneBuffer.data());
       bufferLength = mCloneBuffer.nbytes();
 
       rv = stmt->BindBlobByName(NS_LITERAL_CSTRING("data"), buffer,
                                 bufferLength);
       NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
       rv = stmt->Execute();
       NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
     }
   }
 
   // Update our indexes if needed.
   if (!mIndexUpdateInfo.IsEmpty()) {
-    PRInt64 objectDataId = autoIncrement ? mKey.IntValue() : LL_MININT;
+    PRInt64 objectDataId = autoIncrement ? mKey.ToInteger() : LL_MININT;
     rv = IDBObjectStore::UpdateIndexes(mTransaction, osid, mKey,
                                        autoIncrement, mOverwrite,
                                        objectDataId, mIndexUpdateInfo);
     if (rv == NS_ERROR_STORAGE_CONSTRAINT) {
       return NS_ERROR_DOM_INDEXEDDB_CONSTRAINT_ERR;
     }
     NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
   }
@@ -1872,17 +1652,17 @@ AddHelper::DoDatabaseWork(mozIStorageCon
 nsresult
 AddHelper::GetSuccessResult(JSContext* aCx,
                             jsval* aVal)
 {
   NS_ASSERTION(!mKey.IsUnset(), "Badness!");
 
   mCloneBuffer.clear();
 
-  return IDBObjectStore::GetJSValFromKey(mKey, aCx, aVal);
+  return mKey.ToJSVal(aCx, aVal);
 }
 
 nsresult
 GetHelper::DoDatabaseWork(mozIStorageConnection* aConnection)
 {
   NS_PRECONDITION(aConnection, "Passed a null connection!");
 
   nsCOMPtr<mozIStorageStatement> stmt =
@@ -1892,28 +1672,18 @@ GetHelper::DoDatabaseWork(mozIStorageCon
   mozStorageStatementScoper scoper(stmt);
 
   nsresult rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("osid"),
                                       mObjectStore->Id());
   NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
   NS_ASSERTION(!mKey.IsUnset(), "Must have a key here!");
 
-  NS_NAMED_LITERAL_CSTRING(id, "id");
-
-  if (mKey.IsInt()) {
-    rv = stmt->BindInt64ByName(id, mKey.IntValue());
-  }
-  else if (mKey.IsString()) {
-    rv = stmt->BindStringByName(id, mKey.StringValue());
-  }
-  else {
-    NS_NOTREACHED("Unknown key type!");
-  }
-  NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
+  rv = mKey.BindToStatement(stmt, NS_LITERAL_CSTRING("id"));
+  NS_ENSURE_SUCCESS(rv, rv);
 
   // Search for it!
   bool hasResult;
   rv = stmt->ExecuteStep(&hasResult);
   NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
   if (hasResult) {
     rv = IDBObjectStore::GetStructuredCloneDataFromStatement(stmt, 0,
@@ -1948,41 +1718,31 @@ DeleteHelper::DoDatabaseWork(mozIStorage
   mozStorageStatementScoper scoper(stmt);
 
   nsresult rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("osid"),
                                       mObjectStore->Id());
   NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
   NS_ASSERTION(!mKey.IsUnset(), "Must have a key here!");
 
-  NS_NAMED_LITERAL_CSTRING(key_value, "key_value");
-
-  if (mKey.IsInt()) {
-    rv = stmt->BindInt64ByName(key_value, mKey.IntValue());
-  }
-  else if (mKey.IsString()) {
-    rv = stmt->BindStringByName(key_value, mKey.StringValue());
-  }
-  else {
-    NS_NOTREACHED("Unknown key type!");
-  }
-  NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
+  rv = mKey.BindToStatement(stmt, NS_LITERAL_CSTRING("key_value"));
+  NS_ENSURE_SUCCESS(rv, rv);
 
   rv = stmt->Execute();
   NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
   return NS_OK;
 }
 
 nsresult
 DeleteHelper::GetSuccessResult(JSContext* aCx,
                                jsval* aVal)
 {
   NS_ASSERTION(!mKey.IsUnset(), "Badness!");
-  return IDBObjectStore::GetJSValFromKey(mKey, aCx, aVal);
+  return mKey.ToJSVal(aCx, aVal);
 }
 
 nsresult
 ClearHelper::DoDatabaseWork(mozIStorageConnection* aConnection)
 {
   NS_PRECONDITION(aConnection, "Passed a null connection!");
 
   nsCString table;
@@ -2026,23 +1786,25 @@ OpenCursorHelper::DoDatabaseWork(mozISto
     keyColumn.AssignLiteral("key_value");
   }
 
   NS_NAMED_LITERAL_CSTRING(id, "id");
   NS_NAMED_LITERAL_CSTRING(lowerKeyName, "lower_key");
   NS_NAMED_LITERAL_CSTRING(upperKeyName, "upper_key");
 
   nsCAutoString keyRangeClause;
-  if (!mLowerKey.IsUnset()) {
-    AppendConditionClause(keyColumn, lowerKeyName, false, !mLowerOpen,
-                          keyRangeClause);
-  }
-  if (!mUpperKey.IsUnset()) {
-    AppendConditionClause(keyColumn, upperKeyName, true, !mUpperOpen,
-                          keyRangeClause);
+  if (mKeyRange) {
+    if (!mKeyRange->Lower().IsUnset()) {
+      AppendConditionClause(keyColumn, lowerKeyName, false,
+                            !mKeyRange->IsLowerOpen(), keyRangeClause);
+    }
+    if (!mKeyRange->Upper().IsUnset()) {
+      AppendConditionClause(keyColumn, upperKeyName, true,
+                            !mKeyRange->IsUpperOpen(), keyRangeClause);
+    }
   }
 
   nsCAutoString directionClause = NS_LITERAL_CSTRING(" ORDER BY ") + keyColumn;
   switch (mDirection) {
     case nsIIDBCursor::NEXT:
     case nsIIDBCursor::NEXT_NO_DUPLICATE:
       directionClause += NS_LITERAL_CSTRING(" ASC");
       break;
@@ -2065,65 +1827,38 @@ OpenCursorHelper::DoDatabaseWork(mozISto
     mTransaction->GetCachedStatement(firstQuery);
   NS_ENSURE_TRUE(stmt, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
   mozStorageStatementScoper scoper(stmt);
 
   nsresult rv = stmt->BindInt64ByName(id, mObjectStore->Id());
   NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
-  if (!mLowerKey.IsUnset()) {
-    if (mLowerKey.IsString()) {
-      rv = stmt->BindStringByName(lowerKeyName, mLowerKey.StringValue());
-    }
-    else if (mLowerKey.IsInt()) {
-      rv = stmt->BindInt64ByName(lowerKeyName, mLowerKey.IntValue());
-    }
-    else {
-      NS_NOTREACHED("Bad key!");
+  if (mKeyRange) {
+    if (!mKeyRange->Lower().IsUnset()) {
+      rv = mKeyRange->Lower().BindToStatement(stmt, lowerKeyName);
+      NS_ENSURE_SUCCESS(rv, rv);
     }
-    NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
-  }
-
-  if (!mUpperKey.IsUnset()) {
-    if (mUpperKey.IsString()) {
-      rv = stmt->BindStringByName(upperKeyName, mUpperKey.StringValue());
+    if (!mKeyRange->Upper().IsUnset()) {
+      rv = mKeyRange->Upper().BindToStatement(stmt, upperKeyName);
+      NS_ENSURE_SUCCESS(rv, rv);
     }
-    else if (mUpperKey.IsInt()) {
-      rv = stmt->BindInt64ByName(upperKeyName, mUpperKey.IntValue());
-    }
-    else {
-      NS_NOTREACHED("Bad key!");
-    }
-    NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
   }
 
   bool hasResult;
   rv = stmt->ExecuteStep(&hasResult);
   NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
   if (!hasResult) {
-    mKey = Key::UNSETKEY;
+    mKey.Unset();
     return NS_OK;
   }
 
-  PRInt32 keyType;
-  rv = stmt->GetTypeOfIndex(0, &keyType);
-  NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
-
-  if (keyType == mozIStorageStatement::VALUE_TYPE_INTEGER) {
-    mKey = stmt->AsInt64(0);
-  }
-  else if (keyType == mozIStorageStatement::VALUE_TYPE_TEXT) {
-    rv = stmt->GetString(0, mKey.ToString());
-    NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
-  }
-  else {
-    NS_NOTREACHED("Bad SQLite type!");
-  }
+  rv = mKey.SetFromStatement(stmt, 0);
+  NS_ENSURE_SUCCESS(rv, rv);
 
   rv = IDBObjectStore::GetStructuredCloneDataFromStatement(stmt, 1,
                                                            mCloneBuffer);
   NS_ENSURE_SUCCESS(rv, rv);
 
   // Now we need to make the query to get the next match.
   keyRangeClause.Truncate();
   nsCAutoString continueToKeyRangeClause;
@@ -2133,36 +1868,38 @@ OpenCursorHelper::DoDatabaseWork(mozISto
 
   switch (mDirection) {
     case nsIIDBCursor::NEXT:
     case nsIIDBCursor::NEXT_NO_DUPLICATE:
       AppendConditionClause(keyColumn, currentKey, false, false,
                             keyRangeClause);
       AppendConditionClause(keyColumn, currentKey, false, true,
                             continueToKeyRangeClause);
-      if (!mUpperKey.IsUnset()) {
-        AppendConditionClause(keyColumn, rangeKey, true, !mUpperOpen,
-                              keyRangeClause);
-        AppendConditionClause(keyColumn, rangeKey, true, !mUpperOpen,
+      if (mKeyRange && !mKeyRange->Upper().IsUnset()) {
+        AppendConditionClause(keyColumn, rangeKey, true,
+                              !mKeyRange->IsUpperOpen(), keyRangeClause);
+        AppendConditionClause(keyColumn, rangeKey, true,
+                              !mKeyRange->IsUpperOpen(),
                               continueToKeyRangeClause);
-        mRangeKey = mUpperKey;
+        mRangeKey = mKeyRange->Upper();
       }
       break;
 
     case nsIIDBCursor::PREV:
     case nsIIDBCursor::PREV_NO_DUPLICATE:
       AppendConditionClause(keyColumn, currentKey, true, false, keyRangeClause);
       AppendConditionClause(keyColumn, currentKey, true, true,
                            continueToKeyRangeClause);
-      if (!mLowerKey.IsUnset()) {
-        AppendConditionClause(keyColumn, rangeKey, false, !mLowerOpen,
-                              keyRangeClause);
-        AppendConditionClause(keyColumn, rangeKey, false, !mLowerOpen,
+      if (mKeyRange && !mKeyRange->Lower().IsUnset()) {
+        AppendConditionClause(keyColumn, rangeKey, false,
+                              !mKeyRange->IsLowerOpen(), keyRangeClause);
+        AppendConditionClause(keyColumn, rangeKey, false,
+                              !mKeyRange->IsLowerOpen(),
                               continueToKeyRangeClause);
-        mRangeKey = mLowerKey;
+        mRangeKey = mKeyRange->Lower();
       }
       break;
 
     default:
       NS_NOTREACHED("Unknown direction type!");
   }
 
   mContinueQuery = NS_LITERAL_CSTRING("SELECT ") + keyColumn +
@@ -2387,34 +2124,23 @@ CreateIndexHelper::InsertDataFromObjectS
 
     rv = insertStmt->BindInt64ByName(NS_LITERAL_CSTRING("object_data_id"),
                                      stmt->AsInt64(0));
     NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
     if (!mIndex->IsAutoIncrement()) {
       NS_NAMED_LITERAL_CSTRING(objectDataKey, "object_data_key");
 
-      PRInt32 keyType;
-      rv = stmt->GetTypeOfIndex(2, &keyType);
-      NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
-
-      if (keyType == mozIStorageStatement::VALUE_TYPE_INTEGER) {
-        rv = insertStmt->BindInt64ByName(objectDataKey, stmt->AsInt64(2));
-      }
-      else if (keyType == mozIStorageStatement::VALUE_TYPE_TEXT) {
-        nsString stringKey;
-        rv = stmt->GetString(2, stringKey);
-        NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
-
-        rv = insertStmt->BindStringByName(objectDataKey, stringKey);
-      }
-      else {
-        NS_NOTREACHED("Bad SQLite type!");
-      }
-      NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
+      Key key;
+      rv = key.SetFromStatement(stmt, 2);
+      NS_ENSURE_SUCCESS(rv, rv);
+
+      rv =
+        key.BindToStatement(insertStmt, NS_LITERAL_CSTRING("object_data_key"));
+      NS_ENSURE_SUCCESS(rv, rv);
     }
 
     const PRUint8* data;
     PRUint32 dataLength;
     rv = stmt->GetSharedBlob(1, &dataLength, &data);
     NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
     NS_ENSURE_TRUE(sTLSIndex != BAD_TLS_INDEX, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
@@ -2427,34 +2153,26 @@ CreateIndexHelper::InsertDataFromObjectS
       NS_ENSURE_TRUE(tlsEntry, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
       PR_SetThreadPrivate(sTLSIndex, tlsEntry);
     }
 
     Key key;
     rv = IDBObjectStore::GetKeyPathValueFromStructuredData(data, dataLength,
                                                            mIndex->KeyPath(),
-                                                           tlsEntry->Context(), key);
+                                                           tlsEntry->Context(),
+                                                           key);
     NS_ENSURE_SUCCESS(rv, rv);
 
     if (key.IsUnset()) {
       continue;
     }
 
-    NS_NAMED_LITERAL_CSTRING(value, "value");
-    if (key.IsInt()) {
-      rv = insertStmt->BindInt64ByName(value, key.IntValue());
-    }
-    else if (key.IsString()) {
-      rv = insertStmt->BindStringByName(value, key.StringValue());
-    }
-    else {
-      return NS_ERROR_DOM_INDEXEDDB_CONSTRAINT_ERR;
-    }
-    NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
+    rv = key.BindToStatement(insertStmt, NS_LITERAL_CSTRING("value"));
+    NS_ENSURE_SUCCESS(rv, rv);
 
     rv = insertStmt->Execute();
     NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
   }
 
   return NS_OK;
 }
 
@@ -2497,86 +2215,70 @@ GetAllHelper::DoDatabaseWork(mozIStorage
     keyColumn.AssignLiteral("key_value");
   }
 
   NS_NAMED_LITERAL_CSTRING(osid, "osid");
   NS_NAMED_LITERAL_CSTRING(lowerKeyName, "lower_key");
   NS_NAMED_LITERAL_CSTRING(upperKeyName, "upper_key");
 
   nsCAutoString keyRangeClause;
-  if (!mLowerKey.IsUnset()) {
-    keyRangeClause = NS_LITERAL_CSTRING(" AND ") + keyColumn;
-    if (mLowerOpen) {
-      keyRangeClause.AppendLiteral(" > :");
-    }
-    else {
-      keyRangeClause.AppendLiteral(" >= :");
+  if (mKeyRange) {
+    if (!mKeyRange->Lower().IsUnset()) {
+      keyRangeClause = NS_LITERAL_CSTRING(" AND ") + keyColumn;
+      if (mKeyRange->IsLowerOpen()) {
+        keyRangeClause.AppendLiteral(" > :");
+      }
+      else {
+        keyRangeClause.AppendLiteral(" >= :");
+      }
+      keyRangeClause.Append(lowerKeyName);
     }
-    keyRangeClause.Append(lowerKeyName);
-  }
-
-  if (!mUpperKey.IsUnset()) {
-    keyRangeClause += NS_LITERAL_CSTRING(" AND ") + keyColumn;
-    if (mUpperOpen) {
-      keyRangeClause.AppendLiteral(" < :");
+
+    if (!mKeyRange->Upper().IsUnset()) {
+      keyRangeClause += NS_LITERAL_CSTRING(" AND ") + keyColumn;
+      if (mKeyRange->IsUpperOpen()) {
+        keyRangeClause.AppendLiteral(" < :");
+      }
+      else {
+        keyRangeClause.AppendLiteral(" <= :");
+      }
+      keyRangeClause.Append(upperKeyName);
     }
-    else {
-      keyRangeClause.AppendLiteral(" <= :");
-    }
-    keyRangeClause.Append(upperKeyName);
   }
 
   nsCAutoString limitClause;
   if (mLimit != PR_UINT32_MAX) {
     limitClause.AssignLiteral(" LIMIT ");
     limitClause.AppendInt(mLimit);
   }
 
   nsCString query = NS_LITERAL_CSTRING("SELECT data FROM ") + table +
                     NS_LITERAL_CSTRING(" WHERE object_store_id = :") + osid +
                     keyRangeClause + NS_LITERAL_CSTRING(" ORDER BY ") +
                     keyColumn + NS_LITERAL_CSTRING(" ASC") + limitClause;
 
-  if (!mCloneBuffers.SetCapacity(50)) {
-    NS_ERROR("Out of memory!");
-    return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
-  }
+  mCloneBuffers.SetCapacity(50);
 
   nsCOMPtr<mozIStorageStatement> stmt = mTransaction->GetCachedStatement(query);
   NS_ENSURE_TRUE(stmt, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
   mozStorageStatementScoper scoper(stmt);
 
   nsresult rv = stmt->BindInt64ByName(osid, mObjectStore->Id());
   NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
-  if (!mLowerKey.IsUnset()) {
-    if (mLowerKey.IsString()) {
-      rv = stmt->BindStringByName(lowerKeyName, mLowerKey.StringValue());
-    }
-    else if (mLowerKey.IsInt()) {
-      rv = stmt->BindInt64ByName(lowerKeyName, mLowerKey.IntValue());
-    }
-    else {
-      NS_NOTREACHED("Bad key!");
+  if (mKeyRange) {
+    if (!mKeyRange->Lower().IsUnset()) {
+      rv = mKeyRange->Lower().BindToStatement(stmt, lowerKeyName);
+      NS_ENSURE_SUCCESS(rv, rv);
     }
-    NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
-  }
-
-  if (!mUpperKey.IsUnset()) {
-    if (mUpperKey.IsString()) {
-      rv = stmt->BindStringByName(upperKeyName, mUpperKey.StringValue());
+    if (!mKeyRange->Upper().IsUnset()) {
+      rv = mKeyRange->Upper().BindToStatement(stmt, upperKeyName);
+      NS_ENSURE_SUCCESS(rv, rv);
     }
-    else if (mUpperKey.IsInt()) {
-      rv = stmt->BindInt64ByName(upperKeyName, mUpperKey.IntValue());
-    }
-    else {
-      NS_NOTREACHED("Bad key!");
-    }
-    NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
   }
 
   bool hasResult;
   while (NS_SUCCEEDED((rv = stmt->ExecuteStep(&hasResult))) && hasResult) {
     if (mCloneBuffers.Capacity() == mCloneBuffers.Length()) {
       if (!mCloneBuffers.SetCapacity(mCloneBuffers.Capacity() * 2)) {
         NS_ERROR("Out of memory!");
         return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
--- a/dom/indexedDB/IDBObjectStore.h
+++ b/dom/indexedDB/IDBObjectStore.h
@@ -37,29 +37,29 @@
  *
  * ***** END LICENSE BLOCK ***** */
 
 #ifndef mozilla_dom_indexeddb_idbobjectstore_h__
 #define mozilla_dom_indexeddb_idbobjectstore_h__
 
 #include "mozilla/dom/indexedDB/IndexedDatabase.h"
 #include "mozilla/dom/indexedDB/IDBTransaction.h"
-#include "mozilla/dom/indexedDB/Key.h"
 
 #include "nsIIDBObjectStore.h"
 #include "nsIIDBTransaction.h"
 
 #include "nsCycleCollectionParticipant.h"
 
 class nsIScriptContext;
 class nsPIDOMWindow;
 
 BEGIN_INDEXEDDB_NAMESPACE
 
 class AsyncConnectionHelper;
+class Key;
 
 struct ObjectStoreInfo;
 struct IndexInfo;
 struct IndexUpdateInfo;
 
 class IDBObjectStore : public nsIIDBObjectStore
 {
 public:
@@ -68,30 +68,16 @@ public:
 
   NS_DECL_CYCLE_COLLECTION_CLASS(IDBObjectStore)
 
   static already_AddRefed<IDBObjectStore>
   Create(IDBTransaction* aTransaction,
          const ObjectStoreInfo* aInfo);
 
   static nsresult
-  GetKeyFromVariant(nsIVariant* aKeyVariant,
-                    Key& aKey);
-
-  static nsresult
-  GetKeyFromJSVal(jsval aKeyVal,
-                  JSContext* aCx,
-                  Key& aKey);
-
-  static nsresult
-  GetJSValFromKey(const Key& aKey,
-                  JSContext* aCx,
-                  jsval* aKeyVal);
-
-  static nsresult
   GetKeyPathValueFromStructuredData(const PRUint8* aData,
                                     PRUint32 aDataLength,
                                     const nsAString& aKeyPath,
                                     JSContext* aCx,
                                     Key& aValue);
 
   static nsresult
   GetIndexUpdateInfo(ObjectStoreInfo* aObjectStoreInfo,
--- a/dom/indexedDB/IDBRequest.cpp
+++ b/dom/indexedDB/IDBRequest.cpp
@@ -36,17 +36,16 @@
  * 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 "IDBRequest.h"
 
 #include "nsIScriptContext.h"
-#include "nsIVariant.h"
 
 #include "nsComponentManagerUtils.h"
 #include "nsDOMClassInfoID.h"
 #include "nsDOMJSUtils.h"
 #include "nsContentUtils.h"
 #include "nsEventDispatcher.h"
 #include "nsPIDOMWindow.h"
 #include "nsStringGlue.h"
--- a/dom/indexedDB/Key.h
+++ b/dom/indexedDB/Key.h
@@ -37,119 +37,88 @@
  *
  * ***** END LICENSE BLOCK ***** */
 
 #ifndef mozilla_dom_indexeddb_key_h__
 #define mozilla_dom_indexeddb_key_h__
 
 #include "mozilla/dom/indexedDB/IndexedDatabase.h"
 
+#include "mozIStorageStatement.h"
+
+#include "xpcprivate.h"
+#include "XPCQuickStubs.h"
+
 BEGIN_INDEXEDDB_NAMESPACE
 
 class Key
 {
 public:
-  enum Type { UNSETKEY, STRINGKEY, INTKEY };
-
   Key()
-  : mType(UNSETKEY), mInt(0)
-  { }
-
-  Key(const Key& aOther)
-  {
-    *this = aOther;
-  }
-
-  Key& operator=(const Key& aOther)
   {
-    if (this != &aOther) {
-      mType = aOther.mType;
-      mString = aOther.mString;
-      mInt = aOther.mInt;
-    }
-    return *this;
-  }
-
-  Key& operator=(Type aType)
-  {
-    NS_ASSERTION(aType == UNSETKEY ,
-                 "Use one of the other operators to assign your value!");
-    mType = aType;
-    mString.Truncate();
-    mInt = 0;
-    return *this;
+    Unset();
   }
 
   Key& operator=(const nsAString& aString)
   {
-    mType = STRINGKEY;
-    mString = aString;
-    mInt = 0;
+    SetFromString(aString);
     return *this;
   }
 
   Key& operator=(PRInt64 aInt)
   {
-    mType = INTKEY;
-    mString.Truncate();
-    mInt = aInt;
+    SetFromInteger(aInt);
     return *this;
   }
 
   bool operator==(const Key& aOther) const
   {
+     NS_ASSERTION(mType != KEYTYPE_VOID && aOther.mType != KEYTYPE_VOID,
+                 "Don't compare unset keys!");
+
     if (mType == aOther.mType) {
       switch (mType) {
-        case UNSETKEY:
-          return true;
+        case KEYTYPE_STRING:
+          return ToString() == aOther.ToString();
 
-        case STRINGKEY:
-          return mString == aOther.mString;
-
-        case INTKEY:
-          return mInt == aOther.mInt;
+        case KEYTYPE_INTEGER:
+          return ToInteger() == aOther.ToInteger();
 
         default:
           NS_NOTREACHED("Unknown type!");
       }
     }
     return false;
   }
 
   bool operator!=(const Key& aOther) const
   {
     return !(*this == aOther);
   }
 
   bool operator<(const Key& aOther) const
   {
+    NS_ASSERTION(mType != KEYTYPE_VOID && aOther.mType != KEYTYPE_VOID,
+                 "Don't compare unset keys!");
+
     switch (mType) {
-      case UNSETKEY:
-        if (aOther.mType == UNSETKEY) {
-          return false;
-        }
-        return true;
-
-      case STRINGKEY:
-        if (aOther.mType == UNSETKEY ||
-            aOther.mType == INTKEY) {
+      case KEYTYPE_STRING: {
+        if (aOther.mType == KEYTYPE_INTEGER) {
           return false;
         }
-        NS_ASSERTION(aOther.mType == STRINGKEY, "Unknown type!");
-        return mString < aOther.mString;
+        NS_ASSERTION(aOther.mType == KEYTYPE_STRING, "Unknown type!");
+        return ToString() < aOther.ToString();
+      }
 
-      case INTKEY:
-        if (aOther.mType == UNSETKEY) {
-          return false;
-        }
-        if (aOther.mType == STRINGKEY) {
+      case KEYTYPE_INTEGER:
+        if (aOther.mType == KEYTYPE_STRING) {
           return true;
         }
-        NS_ASSERTION(aOther.mType == INTKEY, "Unknown type!");
-        return mInt < aOther.mInt;
+        NS_ASSERTION(aOther.mType == KEYTYPE_INTEGER, "Unknown type!");
+        return ToInteger() < aOther.ToInteger();
 
       default:
         NS_NOTREACHED("Unknown type!");
     }
     return false;
   }
 
   bool operator>(const Key& aOther) const
@@ -162,49 +131,194 @@ public:
     return (*this == aOther || *this < aOther);
   }
 
   bool operator>=(const Key& aOther) const
   {
     return (*this == aOther || !(*this < aOther));
   }
 
-  bool IsUnset() const { return mType == UNSETKEY; }
-  bool IsString() const { return mType == STRINGKEY; }
-  bool IsInt() const { return mType == INTKEY; }
+  void
+  Unset()
+  {
+    mType = KEYTYPE_VOID;
+    mStringKey.SetIsVoid(true);
+    mIntKey = 0;
+  }
+
+  bool IsUnset() const { return mType == KEYTYPE_VOID; }
+  bool IsString() const { return mType == KEYTYPE_STRING; }
+  bool IsInteger() const { return mType == KEYTYPE_INTEGER; }
+
+  nsresult SetFromString(const nsAString& aString)
+  {
+    mType = KEYTYPE_STRING;
+    mStringKey = aString;
+    mIntKey = 0;
+    return NS_OK;
+  }
+
+  nsresult SetFromInteger(PRInt64 aInt)
+  {
+    mType = KEYTYPE_INTEGER;
+    mStringKey.SetIsVoid(true);
+    mIntKey = aInt;
+    return NS_OK;
+  }
 
-  const nsString& StringValue() const {
-    NS_ASSERTION(IsString(), "Wrong type!");
-    return mString;
+  nsresult SetFromJSVal(JSContext* aCx,
+                        jsval aVal)
+  {
+    if (JSVAL_IS_STRING(aVal)) {
+      jsval tempRoot = JSVAL_VOID;
+      SetFromString(xpc_qsAString(aCx, aVal, &tempRoot));
+      return NS_OK;
+    }
+
+    if (JSVAL_IS_INT(aVal)) {
+      SetFromInteger(JSVAL_TO_INT(aVal));
+      return NS_OK;
+    }
+
+    if (JSVAL_IS_DOUBLE(aVal)) {
+      jsdouble doubleActual = JSVAL_TO_DOUBLE(aVal);
+      int64 doubleAsInt = static_cast<int64>(doubleActual);
+      if (doubleActual == doubleAsInt) {
+        SetFromInteger(doubleAsInt);
+        return NS_OK;
+      }
+    }
+
+    if (JSVAL_IS_NULL(aVal) || JSVAL_IS_VOID(aVal)) {
+      Unset();
+      return NS_OK;
+    }
+
+    return NS_ERROR_DOM_INDEXEDDB_DATA_ERR;
   }
 
-  PRInt64 IntValue() const {
-    NS_ASSERTION(IsInt(), "Wrong type!");
-    return mInt;
+  nsresult ToJSVal(JSContext* aCx,
+                   jsval* aVal) const
+  {
+    if (IsString()) {
+      if (!xpc_qsStringToJsval(aCx, nsString(ToString()), aVal)) {
+        return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
+      }
+    }
+    else if (IsInteger()) {
+      if (!JS_NewNumberValue(aCx, static_cast<jsdouble>(ToInteger()), aVal)) {
+        return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
+      }
+    }
+    else if (IsUnset()) {
+      *aVal = JSVAL_VOID;
+    }
+    else {
+      NS_NOTREACHED("Unknown key type!");
+    }
+    return NS_OK;
+  }
+
+  PRInt64 ToInteger() const
+  {
+    NS_ASSERTION(IsInteger(), "Don't call me!");
+    return mIntKey;
+  }
+
+  const nsAString& ToString() const
+  {
+    NS_ASSERTION(IsString(), "Don't call me!");
+    return mStringKey;
   }
 
-  nsAString& ToString() {
-    mType = STRINGKEY;
-    mInt = 0;
-    return mString;
+  nsresult BindToStatement(mozIStorageStatement* aStatement,
+                           const nsACString& aParamName) const
+  {
+    nsresult rv;
+
+    if (IsString()) {
+      rv = aStatement->BindStringByName(aParamName, ToString());
+    }
+    else if (IsInteger()) {
+      rv = aStatement->BindInt64ByName(aParamName, ToInteger());
+    }
+    else {
+      NS_NOTREACHED("Bad key!");
+    }
+
+    return NS_SUCCEEDED(rv) ? NS_OK : NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
+  }
+
+  nsresult BindToStatementAllowUnset(mozIStorageStatement* aStatement,
+                                     const nsACString& aParamName) const
+  {
+    nsresult rv;
+
+    if (IsUnset()) {
+      rv = aStatement->BindStringByName(aParamName, EmptyString());
+      return NS_SUCCEEDED(rv) ? NS_OK : NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
+    }
+
+    return BindToStatement(aStatement, aParamName);
   }
 
-  PRInt64* ToIntPtr() {
-    mType = INTKEY;
-    mString.Truncate();
-    return &mInt;
+  nsresult SetFromStatement(mozIStorageStatement* aStatement,
+                            PRUint32 aIndex)
+  {
+    PRInt32 columnType;
+    nsresult rv = aStatement->GetTypeOfIndex(aIndex, &columnType);
+    NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
+
+    NS_ASSERTION(columnType == mozIStorageStatement::VALUE_TYPE_INTEGER ||
+                 columnType == mozIStorageStatement::VALUE_TYPE_TEXT,
+                 "Unsupported column type!");
+
+    return SetFromStatement(aStatement, aIndex, columnType);
+  }
+
+  nsresult SetFromStatement(mozIStorageStatement* aStatement,
+                            PRUint32 aIndex,
+                            PRInt32 aColumnType)
+  {
+    if (aColumnType == mozIStorageStatement::VALUE_TYPE_INTEGER) {
+      return SetFromInteger(aStatement->AsInt64(aIndex));
+    }
+
+    if (aColumnType == mozIStorageStatement::VALUE_TYPE_TEXT) {
+      nsString keyString;
+      nsresult rv = aStatement->GetString(aIndex, keyString);
+      NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
+
+      return SetFromString(keyString);
+    }
+
+    NS_NOTREACHED("Unsupported column type!");
+    return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
   }
 
   static
-  JSBool CanBeConstructedFromJSVal(jsval aVal) {
-    return JSVAL_IS_VOID(aVal) || JSVAL_IS_NULL(aVal) || JSVAL_IS_INT(aVal) ||
-           JSVAL_IS_DOUBLE(aVal) || JSVAL_IS_STRING(aVal);
+  bool CanBeConstructedFromJSVal(jsval aVal)
+  {
+    return JSVAL_IS_INT(aVal) || JSVAL_IS_DOUBLE(aVal) || JSVAL_IS_STRING(aVal);
   }
 
 private:
+  // Wish we could use JSType here but we will end up supporting types like Date
+  // which JSType can't really identify. Rolling our own for now.
+  enum Type {
+    KEYTYPE_VOID,
+    KEYTYPE_STRING,
+    KEYTYPE_INTEGER
+  };
+
+  // Type of value in mJSVal.
   Type mType;
-  nsString mString;
-  PRInt64 mInt;
+
+  // The string if mType is KEYTYPE_STRING, otherwise a void string.
+  nsString mStringKey;
+
+  // The integer value if mType is KEYTYPE_INTEGER, otherwise 0.
+  int64 mIntKey;
 };
 
 END_INDEXEDDB_NAMESPACE
 
 #endif /* mozilla_dom_indexeddb_key_h__ */
--- a/dom/indexedDB/Makefile.in
+++ b/dom/indexedDB/Makefile.in
@@ -88,16 +88,17 @@ EXPORTS_mozilla/dom/indexedDB = \
   $(NULL)
 
 LOCAL_INCLUDES = \
   -I$(topsrcdir)/xpcom/build \
   -I$(topsrcdir)/dom/base \
   -I$(topsrcdir)/dom/src/storage \
   -I$(topsrcdir)/content/base/src \
   -I$(topsrcdir)/content/events/src \
+  -I$(topsrcdir)/js/xpconnect/src \
   $(NULL)
 
 DEFINES += -D_IMPL_NS_LAYOUT
 
 # Make sure to quickstub as much as possible here! See
 # js/xpconnect/src/dom_quickstubs.qsconf.
 XPIDLSRCS = \
   nsIIDBCursor.idl \
--- a/dom/indexedDB/nsIIDBCursor.idl
+++ b/dom/indexedDB/nsIIDBCursor.idl
@@ -35,35 +35,35 @@
  * 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 "nsISupports.idl"
 
 interface nsIIDBRequest;
-interface nsIVariant;
 
 /**
  * IDBCursor interface.  See
  * http://dev.w3.org/2006/webapi/WebSimpleDB/#idl-def-IDBCursor for more
  * information.
  */
-[scriptable, uuid(adee4085-68cd-4568-9d74-e3d32b6dc5c5)]
+[scriptable, builtinclass, uuid(462a3607-b2d6-4f4b-9dd7-8ca0b26d3414)]
 interface nsIIDBCursor : nsISupports
 {
   const unsigned short NEXT = 0;
   const unsigned short NEXT_NO_DUPLICATE = 1;
   const unsigned short PREV = 2;
   const unsigned short PREV_NO_DUPLICATE = 3;
   readonly attribute unsigned short direction;
 
   readonly attribute nsISupports source;
 
-  readonly attribute nsIVariant key;
+  [implicit_jscontext]
+  readonly attribute jsval key;
 
   [implicit_jscontext]
   readonly attribute jsval primaryKey;
 
   // Calling continue means that the same onsuccess function will be called
   // again with the new key/value (or null if no more matches).
   [implicit_jscontext]
   void continue([optional /* undefined */] in jsval key);
--- a/dom/indexedDB/nsIIDBCursorWithValue.idl
+++ b/dom/indexedDB/nsIIDBCursorWithValue.idl
@@ -34,23 +34,19 @@
  * 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 "nsIIDBCursor.idl"
 
-%{C++
-#include "jsapi.h"
-%}
-
 /**
  * IDBCursor interface.  See
  * http://dev.w3.org/2006/webapi/WebSimpleDB/#idl-def-IDBCursor for more
  * information.
  */
-[scriptable, uuid(b6b7e08a-4379-4441-a176-447c5c96df69)]
+[scriptable, builtinclass, uuid(b6b7e08a-4379-4441-a176-447c5c96df69)]
 interface nsIIDBCursorWithValue : nsIIDBCursor
 {
   [implicit_jscontext]
   readonly attribute jsval value;
 };
--- a/dom/indexedDB/nsIIDBDatabase.idl
+++ b/dom/indexedDB/nsIIDBDatabase.idl
@@ -34,29 +34,28 @@
  * 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 "nsISupports.idl"
 
-interface nsIVariant;
 interface nsIIDBObjectStore;
 interface nsIIDBRequest;
 interface nsIIDBTransaction;
 interface nsIDOMDOMStringList;
 interface nsIDOMEventListener;
 
 /**
  * IDBDatabase interface.  See
  * http://dvcs.w3.org/hg/IndexedDB/raw-file/tip/Overview.html#idl-def-IDBDatabase
  * for more information.
  */
-[scriptable, uuid(ac14faa5-261c-4a84-9616-a700fd606f83)]
+[scriptable, builtinclass, uuid(ddc9dd43-704f-45da-bb91-20f4b3103117)]
 interface nsIIDBDatabase : nsISupports
 {
   readonly attribute DOMString name;
 
   readonly attribute unsigned long long version;
 
   readonly attribute nsIDOMDOMStringList objectStoreNames;
 
@@ -73,19 +72,18 @@ interface nsIIDBDatabase : nsISupports
   createObjectStore(in AString name,
                     [optional /* none */] in jsval options);
 
   void
   deleteObjectStore(in AString name);
 
   [optional_argc, implicit_jscontext]
   nsIIDBTransaction
-  transaction(in nsIVariant storeNames, // js array of strings
-              [optional /* READ_ONLY */] in unsigned short mode,
-              [optional /* 5000ms */] in unsigned long timeout);
+  transaction(in jsval storeNames, // js array of strings
+              [optional /* READ_ONLY */] in unsigned short mode);
 
   void
   close();
 
   attribute nsIDOMEventListener onerror;
 
   attribute nsIDOMEventListener onversionchange;
 };
--- a/dom/indexedDB/nsIIDBDatabaseException.idl
+++ b/dom/indexedDB/nsIIDBDatabaseException.idl
@@ -34,17 +34,17 @@
  * 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 "nsISupports.idl"
 
-[scriptable, uuid(7aad2542-a5cb-4a57-b20c-c7d16b8582ab)]
+[scriptable, builtinclass, uuid(7aad2542-a5cb-4a57-b20c-c7d16b8582ab)]
 interface nsIIDBDatabaseException : nsISupports
 {
   // const unsigned short NO_ERR = 0;
   const unsigned short UNKNOWN_ERR = 1;
   const unsigned short NON_TRANSIENT_ERR = 2;
   const unsigned short NOT_FOUND_ERR = 3;
   const unsigned short CONSTRAINT_ERR = 4;
   const unsigned short DATA_ERR = 5;
--- a/dom/indexedDB/nsIIDBFactory.idl
+++ b/dom/indexedDB/nsIIDBFactory.idl
@@ -37,23 +37,22 @@
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "nsISupports.idl"
 
 interface nsIIDBKeyRange;
 interface nsIIDBOpenDBRequest;
-interface nsIVariant;
 
 /**
  * Interface that defines the indexedDB property on a window.  See
  * http://dvcs.w3.org/hg/IndexedDB/raw-file/tip/Overview.html#idl-def-IDBFactory
  * for more information.
  */
-[scriptable, uuid(d2889b8f-662a-42d3-8a8f-ac5179b9d5b0)]
+[scriptable, builtinclass, uuid(d2889b8f-662a-42d3-8a8f-ac5179b9d5b0)]
 interface nsIIDBFactory : nsISupports
 {
   [implicit_jscontext, optional_argc]
   nsIIDBOpenDBRequest
   open(in AString name,
        [optional] in long long version);
 };
--- a/dom/indexedDB/nsIIDBIndex.idl
+++ b/dom/indexedDB/nsIIDBIndex.idl
@@ -34,57 +34,64 @@
  * 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 "nsISupports.idl"
 
-interface nsIIDBKeyRange;
 interface nsIIDBObjectStore;
 interface nsIIDBRequest;
-interface nsIVariant;
 
 /**
  * IDBIndex interface.  See
  * http://dev.w3.org/2006/webapi/WebSimpleDB/#idl-def-IDBIndex for more
  * information.
  */
-[scriptable, uuid(9df1ac24-06cf-47d1-9159-3b3d65975b80)]
+[scriptable, builtinclass, uuid(1da60889-3db4-4f66-9fd7-b78c1e7969b7)]
 interface nsIIDBIndex : nsISupports
 {
   readonly attribute DOMString name;
 
   readonly attribute DOMString storeName;
 
   readonly attribute DOMString keyPath;
 
   readonly attribute boolean unique;
 
   readonly attribute nsIIDBObjectStore objectStore;
 
+  [implicit_jscontext]
   nsIIDBRequest
-  get(in nsIVariant key);
+  get(in jsval key);
 
+  [implicit_jscontext]
   nsIIDBRequest
-  getKey(in nsIVariant key);
+  getKey(in jsval key);
 
-  [optional_argc]
+  [implicit_jscontext, optional_argc]
   nsIIDBRequest
-  getAll([optional /* null */] in nsIVariant key,
+  getAll([optional /* null */] in jsval key,
          [optional /* unlimited */] in unsigned long limit);
 
-  [optional_argc]
+  [implicit_jscontext, optional_argc]
   nsIIDBRequest
-  getAllKeys([optional /* null */] in nsIVariant key,
+  getAllKeys([optional /* null */] in jsval key,
              [optional /* unlimited */] in unsigned long limit);
 
-  [optional_argc]
+  [implicit_jscontext, optional_argc]
   nsIIDBRequest
-  openCursor([optional /* null */] in nsIIDBKeyRange range,
+  openCursor([optional /* null */] in jsval key,
              [optional /* nsIIDBCursor::NEXT */] in unsigned short direction);
 
-  [optional_argc]
+  [implicit_jscontext, optional_argc]
   nsIIDBRequest
-  openKeyCursor([optional /* null */] in nsIIDBKeyRange range,
+  openKeyCursor([optional /* null */] in jsval key,
                 [optional /* nsIIDBCursor::NEXT */] in unsigned short direction);
+
+  /*
+  // Accepts null, a key value, or a nsIIDBKeyRange object.
+  [implicit_jscontext, optional_argc]
+  nsIIDBRequest
+  count([optional] in jsval key);
+  */
 };
--- a/dom/indexedDB/nsIIDBKeyRange.idl
+++ b/dom/indexedDB/nsIIDBKeyRange.idl
@@ -34,23 +34,25 @@
  * 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 "nsISupports.idl"
 
-interface nsIVariant;
-
 /**
  * IDBKeyRange interface.  See
  * http://dev.w3.org/2006/webapi/WebSimpleDB/#idl-def-IDBKeyRange for more
  * information.
  */
-[scriptable, uuid(a1505e41-7e48-4542-9fa5-3f2c98233567)]
+[scriptable, builtinclass, uuid(8aeb8660-76b3-4651-b8c2-9894ae6dfe68)]
 interface nsIIDBKeyRange : nsISupports
 {
-  readonly attribute nsIVariant lower;
-  readonly attribute nsIVariant upper;
+  [implicit_jscontext]
+  readonly attribute jsval lower;
+
+  [implicit_jscontext]
+  readonly attribute jsval upper;
+
   readonly attribute boolean lowerOpen;
   readonly attribute boolean upperOpen;
 };
--- a/dom/indexedDB/nsIIDBObjectStore.idl
+++ b/dom/indexedDB/nsIIDBObjectStore.idl
@@ -38,43 +38,43 @@
  * ***** END LICENSE BLOCK ***** */
 
 #include "nsISupports.idl"
 
 interface nsIIDBIndex;
 interface nsIIDBKeyRange;
 interface nsIIDBRequest;
 interface nsIIDBTransaction;
-interface nsIVariant;
 interface nsIDOMDOMStringList;
 
 /**
  * nsIIDBObjectStore interface.  See
  * http://dev.w3.org/2006/webapi/WebSimpleDB/#idl-def-nsIIDBObjectStore
  * for more information.
  */
-[scriptable, uuid(6a65dc92-66e3-407a-a370-590a6c54664a)]
+[scriptable, builtinclass, uuid(d25dff2f-81ad-4531-bcbe-e85c8a19f11a)]
 interface nsIIDBObjectStore : nsISupports
 {
   readonly attribute DOMString name;
 
   readonly attribute DOMString keyPath;
 
   readonly attribute nsIDOMDOMStringList indexNames;
 
   readonly attribute nsIIDBTransaction transaction;
 
   // Success fires IDBTransactionEvent, result == value for key
+  [implicit_jscontext]
   nsIIDBRequest
-  get(in nsIVariant key);
+  get(in jsval key);
 
   // Success fires IDBTransactionEvent, result == array of values for given keys
-  [optional_argc]
+  [implicit_jscontext, optional_argc]
   nsIIDBRequest
-  getAll([optional /* null */] in nsIIDBKeyRange key,
+  getAll([optional /* null */] in jsval key,
          [optional /* unlimited */] in unsigned long limit);
 
   // Success fires IDBTransactionEvent, result == key
   [implicit_jscontext, optional_argc]
   nsIIDBRequest
   add(in jsval value,
       [optional /* undefined */] in jsval key);
 
@@ -90,19 +90,19 @@ interface nsIIDBObjectStore : nsISupport
   delete(in jsval key);
 
   // Success fires IDBTransactionEvent, result == null
   nsIIDBRequest
   clear();
 
   // Success fires IDBTransactionEvent, result == IDBCursor or result == null if
   // no match.
-  [optional_argc]
+  [implicit_jscontext, optional_argc]
   nsIIDBRequest
-  openCursor([optional /* null */] in nsIIDBKeyRange range,
+  openCursor([optional /* null */] in jsval range,
              [optional /* NEXT */] in unsigned short direction);
 
   /**
    * Optional arguments:
    *   - unique (boolean):
    *       Specifies whether values in the index must be unique. Defaults to
    *       false.
    */
@@ -113,9 +113,16 @@ interface nsIIDBObjectStore : nsISupport
               [optional /* none */] in jsval options);
 
   // Returns object immediately
   nsIIDBIndex
   index(in AString name);
 
   void
   deleteIndex(in AString name);
+
+  /*
+  // Accepts null, a key value, or a nsIIDBKeyRange object.
+  [implicit_jscontext, optional_argc]
+  nsIIDBRequest
+  count([optional] in jsval key);
+  */
 };
--- a/dom/indexedDB/nsIIDBOpenDBRequest.idl
+++ b/dom/indexedDB/nsIIDBOpenDBRequest.idl
@@ -42,14 +42,14 @@
 
 interface nsIDOMEventListener;
 
 /**
  * IDBOpenDBRequest interface.  See
  * http://dvcs.w3.org/hg/IndexedDB/raw-file/tip/Overview.html#idl-def-IDBOpenDBRequest
  * for more information.
  */
-[scriptable, uuid(91010fbe-1dfb-435d-852e-288d2804c0a7)]
+[scriptable, builtinclass, uuid(91010fbe-1dfb-435d-852e-288d2804c0a7)]
 interface nsIIDBOpenDBRequest : nsISupports
 {
   attribute nsIDOMEventListener onblocked;
   attribute nsIDOMEventListener onupgradeneeded;
 };
--- a/dom/indexedDB/nsIIDBRequest.idl
+++ b/dom/indexedDB/nsIIDBRequest.idl
@@ -43,17 +43,17 @@
 interface nsIDOMEventListener;
 interface nsIIDBTransaction;
 
 /**
  * IDBReqeust interface.  See
  * http://dev.w3.org/2006/webapi/WebSimpleDB/#idl-def-IDBRequest for more
  * information.
  */
-[scriptable, uuid(a1e4a0ff-e0b2-431c-89cf-43b078189e27)]
+[scriptable, builtinclass, uuid(a1e4a0ff-e0b2-431c-89cf-43b078189e27)]
 interface nsIIDBRequest : nsISupports
 {
   const unsigned short LOADING = 1;
   const unsigned short DONE = 2;
   readonly attribute unsigned short readyState;
 
   readonly attribute nsISupports source;
 
--- a/dom/indexedDB/nsIIDBTransaction.idl
+++ b/dom/indexedDB/nsIIDBTransaction.idl
@@ -45,17 +45,17 @@ interface nsIIDBRequest;
 interface nsIIDBDatabase;
 interface nsIDOMDOMStringList;
 
 /**
  * IDBDTransaction interface.  See
  * http://dvcs.w3.org/hg/IndexedDB/raw-file/tip/Overview.html#idl-def-IDBTransaction
  * for more information.
  */
-[scriptable, uuid(13e551a1-1a58-42ec-b0bd-7102ec0f64d6)]
+[scriptable, builtinclass, uuid(13e551a1-1a58-42ec-b0bd-7102ec0f64d6)]
 interface nsIIDBTransaction : nsISupports
 {
   readonly attribute nsIIDBDatabase db;
 
   const unsigned short INITIAL = 0;
   const unsigned short LOADING = 1;
   const unsigned short COMMITTING = 2;
   const unsigned short DONE = 3;
--- a/dom/indexedDB/nsIIDBVersionChangeEvent.idl
+++ b/dom/indexedDB/nsIIDBVersionChangeEvent.idl
@@ -34,14 +34,14 @@
  * 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 "nsIDOMEvent.idl"
 
-[scriptable, uuid(2c5159dc-7d71-4fc6-a3b3-884ed7586456)]
+[scriptable, builtinclass, uuid(2c5159dc-7d71-4fc6-a3b3-884ed7586456)]
 interface nsIIDBVersionChangeEvent : nsIDOMEvent
 {
   readonly attribute unsigned long long oldVersion;
   readonly attribute unsigned long long newVersion;
 };
--- a/dom/indexedDB/nsIIndexedDatabaseManager.idl
+++ b/dom/indexedDB/nsIIndexedDatabaseManager.idl
@@ -46,17 +46,17 @@ interface nsIIndexedDatabaseUsageCallbac
 {
   /**
    *
    */
   void onUsageResult(in nsIURI aURI,
                      in unsigned long long aUsage);
 };
 
-[scriptable, uuid(415f5684-6c84-4a8b-b777-d01f5df778f2)]
+[scriptable, builtinclass, uuid(415f5684-6c84-4a8b-b777-d01f5df778f2)]
 interface nsIIndexedDatabaseManager : nsISupports
 {
   /**
    * Schedules an asynchronous callback that will return the total amount of
    * disk space being used by databases for the given origin.
    *
    * @param aURI
    *        The URI whose usage is being queried.
--- a/dom/indexedDB/test/test_object_identity.html
+++ b/dom/indexedDB/test/test_object_identity.html
@@ -26,17 +26,17 @@
 
       let index1 = objectStore1.createIndex("bar", "key");
       let index2 = objectStore2.index("bar");
       ok(index1 === index2, "Got same indexes");
 
       request.onsuccess = continueToNextStep;
       yield;
 
-      transaction = db.transaction("foo");
+      transaction = db.transaction(db.objectStoreNames);
 
       let objectStore3 = transaction.objectStore("foo");
       let objectStore4 = transaction.objectStore("foo");
       ok(objectStore3 === objectStore4, "Got same objectStores");
 
       ok(objectStore3 !== objectStore1, "Different objectStores");
       ok(objectStore4 !== objectStore2, "Different objectStores");
 
--- a/js/xpconnect/public/nsAutoJSValHolder.h
+++ b/js/xpconnect/public/nsAutoJSValHolder.h
@@ -47,118 +47,125 @@
 /**
  * Simple class that looks and acts like a jsval except that it unroots
  * itself automatically if Root() is ever called. Designed to be rooted on the
  * context or runtime (but not both!).
  */
 class nsAutoJSValHolder
 {
 public:
-
-  nsAutoJSValHolder()
-    : mRt(NULL)
-    , mVal(JSVAL_NULL)
-    , mHeld(JS_FALSE)
+  nsAutoJSValHolder() : mVal(JSVAL_NULL), mRt(nsnull)
   {
     // nothing to do
   }
 
   /**
    * Always release on destruction.
    */
   virtual ~nsAutoJSValHolder() {
     Release();
   }
 
+  nsAutoJSValHolder(const nsAutoJSValHolder& aOther) {
+    *this = aOther;
+  }
+
+  nsAutoJSValHolder& operator=(const nsAutoJSValHolder& aOther) {
+    if (this != &aOther) {
+      if (aOther.IsHeld()) {
+        // XXX No error handling here...
+        this->Hold(aOther.mRt);
+      }
+      else {
+        this->Release();
+      }
+      *this = static_cast<jsval>(aOther);
+    }
+    return *this;
+  }
+
   /**
    * Hold by rooting on the context's runtime.
    */
-  JSBool Hold(JSContext* aCx) {
+  bool Hold(JSContext* aCx) {
     return Hold(JS_GetRuntime(aCx));
   }
 
   /**
    * Hold by rooting on the runtime.
    * Note that mVal may be JSVAL_NULL, which is not a problem.
    */
-  JSBool Hold(JSRuntime* aRt) {
-    if (!mHeld) {
-      if (js_AddRootRT(aRt, &mVal, "nsAutoJSValHolder")) {
-        mRt = aRt;
-        mHeld = JS_TRUE;
-      } else {
-        Release(); // out of memory
-      }
+  bool Hold(JSRuntime* aRt) {
+    // Do we really care about different runtimes?
+    if (mRt && aRt != mRt) {
+      js_RemoveRoot(mRt, &mVal);
+      mRt = nsnull;
     }
-    return mHeld;
+
+    if (!mRt && js_AddRootRT(aRt, &mVal, "nsAutoJSValHolder")) {
+      mRt = aRt;
+    }
+
+    return !!mRt;
   }
 
   /**
    * Manually release, nullifying mVal, and mRt, but returning
    * the original jsval.
    */
   jsval Release() {
-    NS_ASSERTION(!mHeld || mRt, "Bad!");
-
     jsval oldval = mVal;
 
-    if (mHeld) {
+    if (mRt) {
       js_RemoveRoot(mRt, &mVal); // infallible
-      mHeld = JS_FALSE;
+      mRt = nsnull;
     }
 
     mVal = JSVAL_NULL;
-    mRt = NULL;
 
     return oldval;
   }
 
   /**
    * Determine if Hold has been called.
    */
-  JSBool IsHeld() {
-    return mHeld;
+  bool IsHeld() const {
+    return !!mRt;
   }
 
   /**
    * Explicit JSObject* conversion.
    */
   JSObject* ToJSObject() const {
     return JSVAL_IS_OBJECT(mVal)
          ? JSVAL_TO_OBJECT(mVal)
-         : NULL;
+         : nsnull;
   }
 
   jsval* ToJSValPtr() {
     return &mVal;
   }
 
   /**
    * Pretend to be a jsval.
    */
   operator jsval() const { return mVal; }
 
   nsAutoJSValHolder &operator=(JSObject* aOther) {
-#ifdef DEBUG
-    if (aOther) {
-      NS_ASSERTION(mHeld, "Not rooted!");
-    }
-#endif
     return *this = OBJECT_TO_JSVAL(aOther);
   }
 
   nsAutoJSValHolder &operator=(jsval aOther) {
 #ifdef DEBUG
-    if (JSVAL_IS_OBJECT(aOther) && JSVAL_TO_OBJECT(aOther)) {
-      NS_ASSERTION(mHeld, "Not rooted!");
+    if (JSVAL_IS_GCTHING(aOther) && !JSVAL_IS_NULL(aOther)) {
+      NS_ASSERTION(IsHeld(), "Not rooted!");
     }
 #endif
     mVal = aOther;
     return *this;
   }
 
 private:
+  jsval mVal;
   JSRuntime* mRt;
-  jsval mVal;
-  JSBool mHeld;
 };
 
 #endif /* __NSAUTOJSVALHOLDER_H__ */