Bug 618143 - 'IndexedDB: Don't accept null as a valid key'. r=sicking, a=blocking.
authorBen Turner <bent.mozilla@gmail.com>
Wed, 15 Dec 2010 13:21:07 -0800
changeset 59267 5d1671710f9e40ac74e24d3c4a107b67687dbb58
parent 59266 9190301dbd6160b23dc5565b0a2606d7e3da4985
child 59268 398f4a06a6750837860d43c5c39d915656684b21
push id1
push usershaver@mozilla.com
push dateTue, 04 Jan 2011 17:58:04 +0000
reviewerssicking, blocking
bugs618143
milestone2.0b9pre
Bug 618143 - 'IndexedDB: Don't accept null as a valid key'. r=sicking, a=blocking.
dom/indexedDB/IDBCursor.cpp
dom/indexedDB/IDBEvents.cpp
dom/indexedDB/IDBIndex.cpp
dom/indexedDB/IDBKeyRange.cpp
dom/indexedDB/IDBObjectStore.cpp
dom/indexedDB/IDBObjectStore.h
dom/indexedDB/Key.h
dom/indexedDB/nsIIDBCursor.idl
dom/indexedDB/test/Makefile.in
dom/indexedDB/test/test_key_requirements.html
dom/indexedDB/test/test_null_keys.html
js/src/xpconnect/src/qsgen.py
--- a/dom/indexedDB/IDBCursor.cpp
+++ b/dom/indexedDB/IDBCursor.cpp
@@ -204,17 +204,17 @@ IDBCursor::Create(IDBRequest* aRequest,
                   PRUint16 aDirection,
                   const Key& aRangeKey,
                   const nsACString& aContinueQuery,
                   const nsACString& aContinueToQuery,
                   const Key& aKey,
                   const nsAString& aValue)
 {
   NS_ASSERTION(aObjectStore, "Null pointer!");
-  NS_ASSERTION(!aKey.IsUnset() && !aKey.IsNull(), "Bad key!");
+  NS_ASSERTION(!aKey.IsUnset(), "Bad key!");
 
   nsRefPtr<IDBCursor> cursor =
     IDBCursor::CreateCommon(aRequest, aTransaction, aObjectStore, aDirection,
                             aRangeKey, aContinueQuery, aContinueToQuery);
   NS_ASSERTION(cursor, "This shouldn't fail!");
 
   cursor->mObjectStore = aObjectStore;
   cursor->mType = OBJECTSTORE;
@@ -232,18 +232,18 @@ IDBCursor::Create(IDBRequest* aRequest,
                   PRUint16 aDirection,
                   const Key& aRangeKey,
                   const nsACString& aContinueQuery,
                   const nsACString& aContinueToQuery,
                   const Key& aKey,
                   const Key& aObjectKey)
 {
   NS_ASSERTION(aIndex, "Null pointer!");
-  NS_ASSERTION(!aKey.IsUnset() && !aKey.IsNull(), "Bad key!");
-  NS_ASSERTION(!aObjectKey.IsUnset() && !aObjectKey.IsNull(), "Bad key!");
+  NS_ASSERTION(!aKey.IsUnset(), "Bad key!");
+  NS_ASSERTION(!aObjectKey.IsUnset(), "Bad key!");
 
   nsRefPtr<IDBCursor> cursor =
     IDBCursor::CreateCommon(aRequest, aTransaction, aIndex->ObjectStore(),
                             aDirection, aRangeKey, aContinueQuery,
                             aContinueToQuery);
   NS_ASSERTION(cursor, "This shouldn't fail!");
 
   cursor->mIndex = aIndex;
@@ -263,17 +263,17 @@ IDBCursor::Create(IDBRequest* aRequest,
                   const Key& aRangeKey,
                   const nsACString& aContinueQuery,
                   const nsACString& aContinueToQuery,
                   const Key& aKey,
                   const Key& aObjectKey,
                   const nsAString& aValue)
 {
   NS_ASSERTION(aIndex, "Null pointer!");
-  NS_ASSERTION(!aKey.IsUnset() && !aKey.IsNull(), "Bad key!");
+  NS_ASSERTION(!aKey.IsUnset(), "Bad key!");
 
   nsRefPtr<IDBCursor> cursor =
     IDBCursor::CreateCommon(aRequest, aTransaction, aIndex->ObjectStore(),
                             aDirection, aRangeKey, aContinueQuery,
                             aContinueToQuery);
   NS_ASSERTION(cursor, "This shouldn't fail!");
 
   cursor->mObjectStore = aIndex->ObjectStore();
@@ -411,17 +411,17 @@ IDBCursor::GetKey(nsIVariant** 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() && !mKey.IsNull(), "Bad key!");
+    NS_ASSERTION(!mKey.IsUnset(), "Bad key!");
 
     if (mKey.IsString()) {
       rv = variant->SetAsAString(mKey.StringValue());
       NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
     }
     else if (mKey.IsInt()) {
       rv = variant->SetAsInt64(mKey.IntValue());
       NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
@@ -448,20 +448,20 @@ NS_IMETHODIMP
 IDBCursor::GetValue(JSContext* aCx,
                     jsval* aValue)
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
   nsresult rv;
 
   if (mType == INDEX) {
-    NS_ASSERTION(!mObjectKey.IsUnset() && !mObjectKey.IsNull(), "Bad key!");
+    NS_ASSERTION(!mObjectKey.IsUnset(), "Bad key!");
 
     rv = IDBObjectStore::GetJSValFromKey(mObjectKey, aCx, aValue);
-    NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
+    NS_ENSURE_SUCCESS(rv, rv);
 
     return NS_OK;
   }
 
   if (!mHaveCachedValue) {
     JSAutoRequest ar(aCx);
 
     nsCOMPtr<nsIJSON> json(new nsJSON());
@@ -477,37 +477,27 @@ IDBCursor::GetValue(JSContext* aCx,
   }
 
   *aValue = mCachedValue;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 IDBCursor::Continue(const jsval &aKey,
-                    JSContext* aCx,
-                    PRUint8 aOptionalArgCount)
+                    JSContext* aCx)
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
   if (!mTransaction->TransactionIsOpen() || mContinueCalled) {
     return NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR;
   }
 
   Key key;
   nsresult rv = IDBObjectStore::GetKeyFromJSVal(aKey, key);
-  NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_NON_TRANSIENT_ERR);
-
-  if (key.IsNull()) {
-    if (aOptionalArgCount) {
-      return NS_ERROR_DOM_INDEXEDDB_NON_TRANSIENT_ERR;
-    }
-    else {
-      key = Key::UNSETKEY;
-    }
-  }
+  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;
         }
@@ -576,17 +566,17 @@ IDBCursor::Update(const jsval& aValue,
   }
 
   if (mType != OBJECTSTORE) {
     NS_WARNING("Update for non-objectStore cursors is not implemented!");
     return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
   }
 
   NS_ASSERTION(mObjectStore, "This cannot be null!");
-  NS_ASSERTION(!mKey.IsUnset() && !mKey.IsNull(), "Bad key!");
+  NS_ASSERTION(!mKey.IsUnset() , "Bad key!");
 
   JSAutoRequest ar(aCx);
 
   js::AutoValueRooter clone(aCx, aValue);
 
   nsresult rv;
   if (!mObjectStore->KeyPath().IsEmpty()) {
     // Make sure the object given has the correct keyPath value set on it or
@@ -597,32 +587,32 @@ IDBCursor::Update(const jsval& aValue,
 
     js::AutoValueRooter prop(aCx);
     JSBool ok = JS_GetUCProperty(aCx, JSVAL_TO_OBJECT(clone.jsval_value()),
                                  keyPathChars, keyPathLen, prop.jsval_addr());
     NS_ENSURE_TRUE(ok, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
     if (JSVAL_IS_VOID(prop.jsval_value())) {
       rv = IDBObjectStore::GetJSValFromKey(mKey, aCx, prop.jsval_addr());
-      NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
+      NS_ENSURE_SUCCESS(rv, rv);
 
       ok = JS_StructuredClone(aCx, clone.jsval_value(), clone.jsval_addr());
       NS_ENSURE_TRUE(ok, NS_ERROR_DOM_INDEXEDDB_SERIAL_ERR);
 
       ok = JS_DefineUCProperty(aCx, JSVAL_TO_OBJECT(clone.jsval_value()),
                                keyPathChars, keyPathLen, prop.jsval_value(), nsnull,
                                nsnull, JSPROP_ENUMERATE);
       NS_ENSURE_TRUE(ok, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
     }
     else {
       Key newKey;
       rv = IDBObjectStore::GetKeyFromJSVal(prop.jsval_value(), newKey);
       NS_ENSURE_SUCCESS(rv, rv);
 
-      if (newKey.IsUnset() || newKey.IsNull() || newKey != mKey) {
+      if (newKey.IsUnset() || newKey != mKey) {
         return NS_ERROR_DOM_INDEXEDDB_DATA_ERR;
       }
     }
   }
 
   ObjectStoreInfo* info;
   if (!ObjectStoreInfo::Get(mTransaction->Database()->Id(),
                             mObjectStore->Name(), &info)) {
@@ -664,17 +654,17 @@ IDBCursor::Delete(nsIIDBRequest** _retva
   }
 
   if (mType != OBJECTSTORE) {
     NS_WARNING("Delete for non-objectStore cursors is not implemented!");
     return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
   }
 
   NS_ASSERTION(mObjectStore, "This cannot be null!");
-  NS_ASSERTION(!mKey.IsUnset() && !mKey.IsNull(), "Bad key!");
+  NS_ASSERTION(!mKey.IsUnset(), "Bad key!");
 
   nsRefPtr<IDBRequest> request = GenerateRequest(this);
   NS_ENSURE_TRUE(request, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
   nsRefPtr<DeleteHelper> helper =
     new DeleteHelper(mTransaction, request, mObjectStore->Id(), mKey,
                      mObjectStore->IsAutoIncrement());
 
@@ -686,17 +676,17 @@ IDBCursor::Delete(nsIIDBRequest** _retva
 }
 
 nsresult
 UpdateHelper::DoDatabaseWork(mozIStorageConnection* aConnection)
 {
   NS_PRECONDITION(aConnection, "Passed a null connection!");
 
   nsresult rv;
-  NS_ASSERTION(!mKey.IsUnset() && !mKey.IsNull(), "Badness!");
+  NS_ASSERTION(!mKey.IsUnset(), "Badness!");
 
   nsCOMPtr<mozIStorageStatement> stmt =
     mTransaction->AddStatement(false, true, mAutoIncrement);
   NS_ENSURE_TRUE(stmt, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
   mozStorageStatementScoper scoper(stmt);
 
   NS_NAMED_LITERAL_CSTRING(keyValue, "key_value");
@@ -735,17 +725,17 @@ UpdateHelper::DoDatabaseWork(mozIStorage
   }
 
   return NS_OK;
 }
 
 nsresult
 UpdateHelper::GetSuccessResult(nsIWritableVariant* aResult)
 {
-  NS_ASSERTION(!mKey.IsUnset() && !mKey.IsNull(), "Badness!");
+  NS_ASSERTION(!mKey.IsUnset(), "Badness!");
 
   if (mKey.IsString()) {
     aResult->SetAsAString(mKey.StringValue());
   }
   else if (mKey.IsInt()) {
     aResult->SetAsInt64(mKey.IntValue());
   }
   else {
@@ -762,17 +752,17 @@ DeleteHelper::DoDatabaseWork(mozIStorage
   nsCOMPtr<mozIStorageStatement> stmt =
     mTransaction->DeleteStatement(mAutoIncrement);
   NS_ENSURE_TRUE(stmt, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
   mozStorageStatementScoper scoper(stmt);
 
   nsresult rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("osid"), mOSID);
   NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
-  NS_ASSERTION(!mKey.IsUnset() && !mKey.IsNull(), "Must have a key here!");
+  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());
@@ -787,17 +777,17 @@ DeleteHelper::DoDatabaseWork(mozIStorage
   NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
   return NS_OK;
 }
 
 nsresult
 DeleteHelper::GetSuccessResult(nsIWritableVariant* aResult)
 {
-  NS_ASSERTION(!mKey.IsUnset() && !mKey.IsNull(), "Badness!");
+  NS_ASSERTION(!mKey.IsUnset(), "Badness!");
 
   if (mKey.IsString()) {
     aResult->SetAsAString(mKey.StringValue());
   }
   else if (mKey.IsInt()) {
     aResult->SetAsInt64(mKey.IntValue());
   }
   else {
@@ -853,19 +843,18 @@ ContinueHelper::GetSuccessResult(nsIWrit
   if (mKey.IsUnset()) {
     rv = aResult->SetAsEmpty();
     NS_ENSURE_SUCCESS(rv, rv);
 
     return NS_OK;
   }
 
 #ifdef DEBUG
-  NS_ASSERTION(!mKey.IsNull(), "Huh?!");
   if (mCursor->mType != IDBCursor::OBJECTSTORE) {
-    NS_ASSERTION(!mObjectKey.IsUnset() && !mObjectKey.IsNull(), "Bad key!");
+    NS_ASSERTION(!mObjectKey.IsUnset(), "Bad key!");
   }
 #endif
 
   // Remove cached stuff from last time.
   mCursor->mCachedKey = nsnull;
   mCursor->mCachedObjectKey = nsnull;
   mCursor->mCachedValue = JSVAL_VOID;
   mCursor->mHaveCachedValue = false;
@@ -1008,18 +997,17 @@ ContinueIndexHelper::BindArgumentsToStat
     NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
   }
 
   // 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() &&
-                 !mCursor->mObjectKey.IsNull(), "Bad key!");
+    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,
--- a/dom/indexedDB/IDBEvents.cpp
+++ b/dom/indexedDB/IDBEvents.cpp
@@ -543,17 +543,17 @@ GetAllKeySuccessEvent::GetResult(JSConte
       }
 
       js::AutoValueRooter value(aCx);
 
       jsint count = jsint(keys.Length());
 
       for (jsint index = 0; index < count; index++) {
         const Key& key = keys[index];
-        NS_ASSERTION(!key.IsUnset() && !key.IsNull(), "Bad key!");
+        NS_ASSERTION(!key.IsUnset(), "Bad key!");
 
         nsresult rv = IDBObjectStore::GetJSValFromKey(key, aCx, value.jsval_addr());
         if (NS_FAILED(rv)) {
           mCachedValue = JSVAL_VOID;
           NS_WARNING("Failed to get jsval for key!");
           return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
         }
 
--- a/dom/indexedDB/IDBIndex.cpp
+++ b/dom/indexedDB/IDBIndex.cpp
@@ -354,20 +354,22 @@ IDBIndex::Get(nsIVariant* aKey,
 
   IDBTransaction* transaction = mObjectStore->Transaction();
   if (!transaction->TransactionIsOpen()) {
     return NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR;
   }
 
   Key key;
   nsresult rv = IDBObjectStore::GetKeyFromVariant(aKey, key);
-  NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_NON_TRANSIENT_ERR);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
 
-  if (key.IsUnset() || key.IsNull()) {
-    return NS_ERROR_DOM_INDEXEDDB_NON_TRANSIENT_ERR;
+  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);
   rv = helper->DispatchToTransactionPool();
   NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
@@ -384,20 +386,22 @@ IDBIndex::GetKey(nsIVariant* aKey,
 
   IDBTransaction* transaction = mObjectStore->Transaction();
   if (!transaction->TransactionIsOpen()) {
     return NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR;
   }
 
   Key key;
   nsresult rv = IDBObjectStore::GetKeyFromVariant(aKey, key);
-  NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_NON_TRANSIENT_ERR);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
 
-  if (key.IsUnset() || key.IsNull()) {
-    return NS_ERROR_DOM_INDEXEDDB_NON_TRANSIENT_ERR;
+  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);
 
@@ -416,22 +420,28 @@ IDBIndex::GetAll(nsIVariant* aKey,
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
   IDBTransaction* transaction = mObjectStore->Transaction();
   if (!transaction->TransactionIsOpen()) {
     return NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR;
   }
 
+  nsresult rv;
+
   Key key;
-  nsresult rv = IDBObjectStore::GetKeyFromVariant(aKey, key);
-  NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_NON_TRANSIENT_ERR);
+  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 (key.IsNull()) {
-    key = Key::UNSETKEY;
+    if (type != nsIDataType::VTYPE_EMPTY) {
+      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);
@@ -454,22 +464,28 @@ IDBIndex::GetAllKeys(nsIVariant* aKey,
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
   IDBTransaction* transaction = mObjectStore->Transaction();
   if (!transaction->TransactionIsOpen()) {
     return NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR;
   }
 
+  nsresult rv;
+
   Key key;
-  nsresult rv = IDBObjectStore::GetKeyFromVariant(aKey, key);
-  NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_NON_TRANSIENT_ERR);
+  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 (key.IsNull()) {
-    key = Key::UNSETKEY;
+    if (type != nsIDataType::VTYPE_EMPTY) {
+      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);
@@ -502,23 +518,27 @@ IDBIndex::OpenCursor(nsIIDBKeyRange* aKe
   PRBool lowerOpen = PR_FALSE, upperOpen = PR_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);
-    NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
+    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);
-    NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
+    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);
   }
 
@@ -566,23 +586,27 @@ IDBIndex::OpenKeyCursor(nsIIDBKeyRange* 
   PRBool lowerOpen = PR_FALSE, upperOpen = PR_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);
-    NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
+    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);
-    NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
+    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);
   }
 
@@ -669,18 +693,16 @@ GetKeyHelper::DoDatabaseWork(mozIStorage
   }
 
   return NS_OK;
 }
 
 nsresult
 GetKeyHelper::GetSuccessResult(nsIWritableVariant* aResult)
 {
-  NS_ASSERTION(!mKey.IsNull(), "Badness!");
-
   if (mKey.IsUnset()) {
     aResult->SetAsEmpty();
   }
   else if (mKey.IsString()) {
     aResult->SetAsAString(mKey.StringValue());
   }
   else if (mKey.IsInt()) {
     aResult->SetAsInt64(mKey.IntValue());
--- a/dom/indexedDB/IDBKeyRange.cpp
+++ b/dom/indexedDB/IDBKeyRange.cpp
@@ -74,17 +74,18 @@ ConvertArguments(JSContext* aCx,
     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 (!Key::CanBeConstructedFromJSVal(arg)) {
+    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();
--- a/dom/indexedDB/IDBObjectStore.cpp
+++ b/dom/indexedDB/IDBObjectStore.cpp
@@ -35,17 +35,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 "IDBObjectStore.h"
 
 #include "nsIJSContextStack.h"
-#include "nsIUUIDGenerator.h"
 #include "nsIVariant.h"
 
 #include "jscntxt.h"
 #include "mozilla/storage.h"
 #include "nsContentUtils.h"
 #include "nsDOMClassInfo.h"
 #include "nsEventDispatcher.h"
 #include "nsJSON.h"
@@ -354,17 +353,17 @@ GetKeyFromObject(JSContext* aCx,
   NS_PRECONDITION(aCx && aObj, "Null pointers!");
   NS_ASSERTION(!aKeyPath.IsVoid(), "This will explode!");
 
   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_FAILURE);
+  NS_ENSURE_TRUE(ok, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
   nsresult rv = IDBObjectStore::GetKeyFromJSVal(key, aKey);
   NS_ENSURE_SUCCESS(rv, rv);
 
   return NS_OK;
 }
 
 inline
@@ -408,148 +407,98 @@ IDBObjectStore::GetKeyFromVariant(nsIVar
 {
   if (!aKeyVariant) {
     aKey = Key::UNSETKEY;
     return NS_OK;
   }
 
   PRUint16 type;
   nsresult rv = aKeyVariant->GetDataType(&type);
-  NS_ENSURE_SUCCESS(rv, rv);
+  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;
-      break;
-
-    case nsIDataType::VTYPE_EMPTY:
-      aKey = Key::NULLKEY;
-      break;
+      return NS_OK;
 
     case nsIDataType::VTYPE_WSTRING_SIZE_IS:
       rv = aKeyVariant->GetAsAString(aKey.ToString());
-      NS_ENSURE_SUCCESS(rv, rv);
-      break;
+      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, rv);
-      break;
+      NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
+      return NS_OK;
 
     default:
-      return NS_ERROR_INVALID_ARG;
+      return NS_ERROR_DOM_INDEXEDDB_DATA_ERR;
   }
 
-  return NS_OK;
+  NS_NOTREACHED("Can't get here!");
+  return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
 }
 
 // static
 nsresult
 IDBObjectStore::GetKeyFromJSVal(jsval aKeyVal,
                                 Key& aKey)
 {
   if (JSVAL_IS_VOID(aKeyVal)) {
     aKey = Key::UNSETKEY;
   }
-  else if (JSVAL_IS_NULL(aKeyVal)) {
-    aKey = Key::NULLKEY;
-  }
   else if (JSVAL_IS_STRING(aKeyVal)) {
     aKey = nsDependentJSString(aKeyVal);
   }
   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_INVALID_ARG;
+    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.IsNull()) {
-    *aKeyVal = JSVAL_NULL;
-    return NS_OK;
-  }
-
   if (aKey.IsInt()) {
     JSBool ok = JS_NewNumberValue(aCx, aKey.IntValue(), aKeyVal);
-    NS_ENSURE_TRUE(ok, NS_ERROR_FAILURE);
+    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_FAILURE);
+    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_INVALID_ARG;
-}
-
-// static
-nsresult
-IDBObjectStore::GetJSONFromArg0(/* jsval arg0, */
-                                nsAString& aJSON)
-{
-  nsIXPConnect* xpc = nsContentUtils::XPConnect();
-  NS_ENSURE_TRUE(xpc, NS_ERROR_UNEXPECTED);
-
-  nsAXPCNativeCallContext* cc;
-  nsresult rv = xpc->GetCurrentNativeCallContext(&cc);
-  NS_ENSURE_SUCCESS(rv, rv);
-  NS_ENSURE_TRUE(cc, NS_ERROR_UNEXPECTED);
-
-  PRUint32 argc;
-  rv = cc->GetArgc(&argc);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  if (argc < 1) {
-    return NS_ERROR_XPC_NOT_ENOUGH_ARGS;
-  }
-
-  jsval* argv;
-  rv = cc->GetArgvPtr(&argv);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  JSContext* cx;
-  rv = cc->GetJSContext(&cx);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  JSAutoRequest ar(cx);
-
-  nsCOMPtr<nsIJSON> json(new nsJSON());
-
-  rv = json->EncodeFromJSVal(&argv[0], cx, aJSON);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  return NS_OK;
+  return NS_ERROR_DOM_INDEXEDDB_DATA_ERR;
 }
 
 // static
 nsresult
 IDBObjectStore::GetKeyPathValueFromJSON(const nsAString& aJSON,
                                         const nsAString& aKeyPath,
                                         JSContext** aCx,
                                         Key& aValue)
@@ -557,46 +506,46 @@ IDBObjectStore::GetKeyPathValueFromJSON(
   NS_ASSERTION(!aJSON.IsEmpty(), "Empty JSON!");
   NS_ASSERTION(!aKeyPath.IsEmpty(), "Empty keyPath!");
   NS_ASSERTION(aCx, "Null pointer!");
 
   nsresult rv;
 
   if (!*aCx) {
     rv = nsContentUtils::ThreadJSContextStack()->GetSafeJSContext(aCx);
-    NS_ENSURE_SUCCESS(rv, rv);
+    NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
   }
 
   JSAutoRequest ar(*aCx);
 
   js::AutoValueRooter clone(*aCx);
 
   nsCOMPtr<nsIJSON> json(new nsJSON());
   rv = json->DecodeToJSVal(aJSON, *aCx, clone.jsval_addr());
-  NS_ENSURE_SUCCESS(rv, rv);
+  NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
   if (JSVAL_IS_PRIMITIVE(clone.jsval_value())) {
     // This isn't an object, so just leave the key unset.
     aValue = Key::UNSETKEY;
     return NS_OK;
   }
 
   JSObject* obj = JSVAL_TO_OBJECT(clone.jsval_value());
 
   const jschar* keyPathChars =
     reinterpret_cast<const jschar*>(aKeyPath.BeginReading());
   const size_t keyPathLen = aKeyPath.Length();
 
   js::AutoValueRooter value(*aCx);
   JSBool ok = JS_GetUCProperty(*aCx, obj, keyPathChars, keyPathLen,
                                value.jsval_addr());
-  NS_ENSURE_TRUE(ok, NS_ERROR_FAILURE);
+  NS_ENSURE_TRUE(ok, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
   rv = GetKeyFromJSVal(value.jsval_value(), aValue);
-  if (NS_FAILED(rv) || aValue.IsNull()) {
+  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;
   }
 
   return NS_OK;
 }
 
@@ -627,17 +576,17 @@ IDBObjectStore::GetIndexUpdateInfo(Objec
 
       jsval keyPathValue;
       JSBool ok = JS_GetUCProperty(aCx, cloneObj, keyPathChars, keyPathLen,
                                    &keyPathValue);
       NS_ENSURE_TRUE(ok, NS_ERROR_FAILURE);
 
       Key value;
       nsresult rv = GetKeyFromJSVal(keyPathValue, value);
-      if (NS_FAILED(rv) || value.IsUnset() || value.IsNull()) {
+      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;
     }
@@ -794,34 +743,30 @@ IDBObjectStore::GetAddInfo(JSContext* aC
                            nsTArray<IndexUpdateInfo>& aUpdateInfoArray)
 {
   nsresult rv;
 
   JSAutoRequest ar(aCx);
 
   if (mKeyPath.IsEmpty()) {
     rv = GetKeyFromJSVal(aKeyVal, aKey);
-    NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_NON_TRANSIENT_ERR);
+    NS_ENSURE_SUCCESS(rv, rv);
   }
   else {
     // Inline keys live on the object. Make sure it is an object.
     if (JSVAL_IS_PRIMITIVE(aValue)) {
       return NS_ERROR_DOM_INDEXEDDB_NON_TRANSIENT_ERR;
     }
 
     rv = GetKeyFromObject(aCx, JSVAL_TO_OBJECT(aValue), mKeyPath, aKey);
-    NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_DATA_ERR);
-
-    // Except if null was passed, in which case we're supposed to generate the
-    // key.
-    if (aKey.IsUnset() && JSVAL_IS_NULL(aKeyVal)) {
-      aKey = Key::NULLKEY;
-    }
+    NS_ENSURE_SUCCESS(rv, rv);
   }
 
+  // Return DATA_ERR if no key was specified this isn't an autoIncrement
+  // objectStore.
   if (aKey.IsUnset() && !mAutoIncrement) {
     return NS_ERROR_DOM_INDEXEDDB_DATA_ERR;
   }
 
   // Figure out indexes and the index values to update here.
   ObjectStoreInfo* info;
   if (!ObjectStoreInfo::Get(mTransaction->Database()->Id(), mName, &info)) {
     NS_ERROR("This should never fail!");
@@ -832,16 +777,60 @@ IDBObjectStore::GetAddInfo(JSContext* aC
 
   nsCOMPtr<nsIJSON> json(new nsJSON());
   rv = json->EncodeFromJSVal(&aValue, aCx, aJSON);
   NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_SERIAL_ERR);
 
   return NS_OK;
 }
 
+nsresult
+IDBObjectStore::AddOrPut(const jsval& aValue,
+                         const jsval& aKey,
+                         JSContext* aCx,
+                         PRUint8 aOptionalArgCount,
+                         nsIIDBRequest** _retval,
+                         bool aOverwrite)
+{
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+
+  if (!mTransaction->TransactionIsOpen() || !IsWriteAllowed()) {
+    return NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR;
+  }
+
+  jsval keyval = (aOptionalArgCount >= 1) ? aKey : JSVAL_VOID;
+
+  nsString jsonValue;
+  Key key;
+  nsTArray<IndexUpdateInfo> updateInfo;
+
+  nsresult rv = GetAddInfo(aCx, aValue, keyval, jsonValue, key, updateInfo);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+
+  // Put requires a key.
+  if (aOverwrite && 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<AddHelper> helper =
+    new AddHelper(mTransaction, request, this, jsonValue, key, aOverwrite,
+                  updateInfo);
+
+  rv = helper->DispatchToTransactionPool();
+  NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
+
+  request.forget(_retval);
+  return NS_OK;
+}
+
 NS_IMPL_CYCLE_COLLECTION_CLASS(IDBObjectStore)
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(IDBObjectStore)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR_AMBIGUOUS(mTransaction,
                                                        nsPIDOMEventTarget)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mOwner)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mScriptContext)
 
@@ -927,20 +916,22 @@ IDBObjectStore::Get(nsIVariant* aKey,
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
   if (!mTransaction->TransactionIsOpen()) {
     return NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR;
   }
 
   Key key;
   nsresult rv = GetKeyFromVariant(aKey, key);
-  NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
 
   if (key.IsUnset()) {
-    return NS_ERROR_DOM_INDEXEDDB_NON_TRANSIENT_ERR;
+    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(mTransaction, request, this, key));
 
   rv = helper->DispatchToTransactionPool();
@@ -971,23 +962,27 @@ IDBObjectStore::GetAll(nsIIDBKeyRange* a
   PRBool lowerOpen = PR_FALSE, upperOpen = PR_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);
-    NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
+    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);
-    NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
+    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);
   }
 
@@ -1001,106 +996,37 @@ IDBObjectStore::GetAll(nsIIDBKeyRange* a
   rv = helper->DispatchToTransactionPool();
   NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
   request.forget(_retval);
   return NS_OK;
 }
 
 NS_IMETHODIMP
-IDBObjectStore::Add(const jsval &aValue,
-                    const jsval &aKey,
+IDBObjectStore::Add(const jsval& aValue,
+                    const jsval& aKey,
                     JSContext* aCx,
                     PRUint8 aOptionalArgCount,
                     nsIIDBRequest** _retval)
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
-  if (!mTransaction->TransactionIsOpen() || !IsWriteAllowed()) {
-    return NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR;
-  }
-
-  jsval keyval;
-  if (aOptionalArgCount >= 1) {
-    keyval = aKey;
-    if (mAutoIncrement && JSVAL_IS_NULL(keyval)) {
-      return NS_ERROR_DOM_INDEXEDDB_DATA_ERR;
-    }
-  }
-  else {
-    keyval = JSVAL_VOID;
-  }
-
-  nsString jsonValue;
-  Key key;
-  nsTArray<IndexUpdateInfo> updateInfo;
-
-  nsresult rv = GetAddInfo(aCx, aValue, keyval, jsonValue, key, updateInfo);
-  if (NS_FAILED(rv)) {
-    return rv;
-  }
-
-  if (key.IsUnset() && !mAutoIncrement) {
-    return NS_ERROR_DOM_INDEXEDDB_DATA_ERR;
-  }
-
-  nsRefPtr<IDBRequest> request = GenerateRequest(this);
-  NS_ENSURE_TRUE(request, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
-
-  nsRefPtr<AddHelper> helper =
-    new AddHelper(mTransaction, request, this, jsonValue, key, false,
-                  updateInfo);
-
-  rv = helper->DispatchToTransactionPool();
-  NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
-
-  request.forget(_retval);
-  return NS_OK;
+  return AddOrPut(aValue, aKey, aCx, aOptionalArgCount, _retval, false);
 }
 
 NS_IMETHODIMP
-IDBObjectStore::Put(const jsval &aValue,
-                    const jsval &aKey,
+IDBObjectStore::Put(const jsval& aValue,
+                    const jsval& aKey,
                     JSContext* aCx,
                     PRUint8 aOptionalArgCount,
                     nsIIDBRequest** _retval)
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
-  if (!mTransaction->TransactionIsOpen() || !IsWriteAllowed()) {
-    return NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR;
-  }
-
-  jsval keyval = (aOptionalArgCount >= 1) ? aKey : JSVAL_VOID;
-
-  nsString jsonValue;
-  Key key;
-  nsTArray<IndexUpdateInfo> updateInfo;
-
-  nsresult rv = GetAddInfo(aCx, aValue, keyval, jsonValue, key, updateInfo);
-  if (NS_FAILED(rv)) {
-    return rv;
-  }
-
-  if (key.IsUnset() || key.IsNull()) {
-    return NS_ERROR_DOM_INDEXEDDB_DATA_ERR;
-  }
-
-  nsRefPtr<IDBRequest> request = GenerateRequest(this);
-  NS_ENSURE_TRUE(request, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
-
-  nsRefPtr<AddHelper> helper =
-    new AddHelper(mTransaction, request, this, jsonValue, key, true,
-                  updateInfo);
-
-  rv = helper->DispatchToTransactionPool();
-  NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
-
-  request.forget(_retval);
-  return NS_OK;
+  return AddOrPut(aValue, aKey, aCx, aOptionalArgCount, _retval, true);
 }
 
 NS_IMETHODIMP
 IDBObjectStore::Delete(nsIVariant* aKey,
                        nsIIDBRequest** _retval)
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
@@ -1109,17 +1035,17 @@ IDBObjectStore::Delete(nsIVariant* aKey,
   }
 
   Key key;
   nsresult rv = GetKeyFromVariant(aKey, key);
   if (NS_FAILED(rv)) {
     return rv;
   }
 
-  if (key.IsUnset() || key.IsNull()) {
+  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<DeleteHelper> helper =
     new DeleteHelper(mTransaction, request, this, key);
@@ -1169,23 +1095,27 @@ IDBObjectStore::OpenCursor(nsIIDBKeyRang
   PRBool lowerOpen = PR_FALSE, upperOpen = PR_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);
-    NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
+    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);
-    NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
+    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);
   }
 
@@ -1403,33 +1333,16 @@ IDBObjectStore::DeleteIndex(const nsAStr
 }
 
 nsresult
 AddHelper::DoDatabaseWork(mozIStorageConnection* aConnection)
 {
   NS_PRECONDITION(aConnection, "Passed a null connection!");
 
   nsresult rv;
-  if (mKey.IsNull()) {
-    NS_WARNING("Using a UUID for null keys, probably can do something faster!");
-
-    nsCOMPtr<nsIUUIDGenerator> uuidGen =
-      do_GetService("@mozilla.org/uuid-generator;1", &rv);
-    NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
-
-    nsID id;
-    rv = uuidGen->GenerateUUIDInPlace(&id);
-    NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
-
-    char idString[NSID_LENGTH] = { 0 };
-    id.ToProvidedString(idString);
-
-    mKey = NS_ConvertASCIItoUTF16(idString);
-  }
-
   bool mayOverwrite = mOverwrite;
   bool unsetKey = mKey.IsUnset();
 
   bool autoIncrement = mObjectStore->IsAutoIncrement();
   PRInt64 osid = mObjectStore->Id();
   const nsString& keyPath = mObjectStore->KeyPath();
 
   if (unsetKey) {
@@ -1603,17 +1516,17 @@ AddHelper::DoDatabaseWork(mozIStorageCon
   }
 
   return NS_OK;
 }
 
 nsresult
 AddHelper::GetSuccessResult(nsIWritableVariant* aResult)
 {
-  NS_ASSERTION(!mKey.IsUnset() && !mKey.IsNull(), "Badness!");
+  NS_ASSERTION(!mKey.IsUnset(), "Badness!");
 
   if (mKey.IsString()) {
     aResult->SetAsAString(mKey.StringValue());
   }
   else if (mKey.IsInt()) {
     aResult->SetAsInt64(mKey.IntValue());
   }
   else {
@@ -1679,17 +1592,17 @@ GetHelper::DoDatabaseWork(mozIStorageCon
   NS_ENSURE_TRUE(stmt, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
   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() && !mKey.IsNull(), "Must have a key here!");
+  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());
@@ -1736,17 +1649,17 @@ DeleteHelper::DoDatabaseWork(mozIStorage
   NS_ENSURE_TRUE(stmt, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
   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() && !mKey.IsNull(), "Must have a key here!");
+  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());
@@ -1767,17 +1680,17 @@ nsresult
 DeleteHelper::OnSuccess(nsIDOMEventTarget* aTarget)
 {
   return AsyncConnectionHelper::OnSuccess(aTarget);
 }
 
 nsresult
 DeleteHelper::GetSuccessResult(nsIWritableVariant* aResult)
 {
-  NS_ASSERTION(!mKey.IsUnset() && !mKey.IsNull(), "Badness!");
+  NS_ASSERTION(!mKey.IsUnset(), "Badness!");
 
   if (mKey.IsString()) {
     aResult->SetAsAString(mKey.StringValue());
   }
   else if (mKey.IsInt()) {
     aResult->SetAsInt64(mKey.IntValue());
   }
   else {
@@ -2122,18 +2035,17 @@ CreateIndexHelper::InsertDataFromObjectS
     nsString json;
     rv = stmt->GetString(1, json);
     NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
     Key key;
     JSContext* cx = nsnull;
     rv = IDBObjectStore::GetKeyPathValueFromJSON(json, mIndex->KeyPath(), &cx,
                                                  key);
-    // XXX this should be a constraint error maybe?
-    NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
+    NS_ENSURE_SUCCESS(rv, rv);
 
     NS_NAMED_LITERAL_CSTRING(value, "value");
 
     if (key.IsUnset()) {
       continue;
     }
 
     if (key.IsInt()) {
--- a/dom/indexedDB/IDBObjectStore.h
+++ b/dom/indexedDB/IDBObjectStore.h
@@ -81,20 +81,16 @@ public:
                   Key& aKey);
 
   static nsresult
   GetJSValFromKey(const Key& aKey,
                   JSContext* aCx,
                   jsval* aKeyVal);
 
   static nsresult
-  GetJSONFromArg0(/* jsval arg0, */
-                  nsAString& aJSON);
-
-  static nsresult
   GetKeyPathValueFromJSON(const nsAString& aJSON,
                           const nsAString& aKeyPath,
                           JSContext** aCx,
                           Key& aValue);
 
   static nsresult
   GetIndexUpdateInfo(ObjectStoreInfo* aObjectStoreInfo,
                      JSContext* aCx,
@@ -152,16 +148,23 @@ protected:
 
   nsresult GetAddInfo(JSContext* aCx,
                       jsval aValue,
                       jsval aKeyVal,
                       nsString& aJSON,
                       Key& aKey,
                       nsTArray<IndexUpdateInfo>& aUpdateInfoArray);
 
+  nsresult AddOrPut(const jsval& aValue,
+                    const jsval& aKey,
+                    JSContext* aCx,
+                    PRUint8 aOptionalArgCount,
+                    nsIIDBRequest** _retval,
+                    bool aOverwrite);
+
 private:
   nsRefPtr<IDBTransaction> mTransaction;
 
   nsCOMPtr<nsIScriptContext> mScriptContext;
   nsCOMPtr<nsPIDOMWindow> mOwner;
 
   PRInt64 mId;
   nsString mName;
--- a/dom/indexedDB/Key.h
+++ b/dom/indexedDB/Key.h
@@ -42,17 +42,17 @@
 
 #include "mozilla/dom/indexedDB/IndexedDatabase.h"
 
 BEGIN_INDEXEDDB_NAMESPACE
 
 class Key
 {
 public:
-  enum Type { UNSETKEY, NULLKEY, STRINGKEY, INTKEY };
+  enum Type { UNSETKEY, STRINGKEY, INTKEY };
 
   Key()
   : mType(UNSETKEY), mInt(0)
   { }
 
   Key(const Key& aOther)
   {
     *this = aOther;
@@ -65,17 +65,17 @@ public:
       mString = aOther.mString;
       mInt = aOther.mInt;
     }
     return *this;
   }
 
   Key& operator=(Type aType)
   {
-    NS_ASSERTION(aType == UNSETKEY || aType == NULLKEY,
+    NS_ASSERTION(aType == UNSETKEY ,
                  "Use one of the other operators to assign your value!");
     mType = aType;
     mString.Truncate();
     mInt = 0;
     return *this;
   }
 
   Key& operator=(const nsAString& aString)
@@ -94,17 +94,16 @@ public:
     return *this;
   }
 
   bool operator==(const Key& aOther) const
   {
     if (mType == aOther.mType) {
       switch (mType) {
         case UNSETKEY:
-        case NULLKEY:
           return true;
 
         case STRINGKEY:
           return mString == aOther.mString;
 
         case INTKEY:
           return mInt == aOther.mInt;
 
@@ -124,35 +123,26 @@ public:
   {
     switch (mType) {
       case UNSETKEY:
         if (aOther.mType == UNSETKEY) {
           return false;
         }
         return true;
 
-      case NULLKEY:
-        if (aOther.mType == UNSETKEY ||
-            aOther.mType == NULLKEY) {
-          return false;
-        }
-        return true;
-
       case STRINGKEY:
         if (aOther.mType == UNSETKEY ||
-            aOther.mType == NULLKEY ||
             aOther.mType == INTKEY) {
           return false;
         }
         NS_ASSERTION(aOther.mType == STRINGKEY, "Unknown type!");
         return mString < aOther.mString;
 
       case INTKEY:
-        if (aOther.mType == UNSETKEY ||
-            aOther.mType == NULLKEY) {
+        if (aOther.mType == UNSETKEY) {
           return false;
         }
         if (aOther.mType == STRINGKEY) {
           return true;
         }
         NS_ASSERTION(aOther.mType == INTKEY, "Unknown type!");
         return mInt < aOther.mInt;
 
@@ -173,17 +163,16 @@ public:
   }
 
   bool operator>=(const Key& aOther) const
   {
     return (*this == aOther || !(*this < aOther));
   }
 
   bool IsUnset() const { return mType == UNSETKEY; }
-  bool IsNull() const { return mType == NULLKEY; }
   bool IsString() const { return mType == STRINGKEY; }
   bool IsInt() const { return mType == INTKEY; }
 
   const nsString& StringValue() const {
     NS_ASSERTION(IsString(), "Wrong type!");
     return mString;
   }
 
--- a/dom/indexedDB/nsIIDBCursor.idl
+++ b/dom/indexedDB/nsIIDBCursor.idl
@@ -46,17 +46,17 @@ interface nsIVariant;
 #include "jsapi.h"
 %}
 
 /**
  * IDBCursor interface.  See
  * http://dev.w3.org/2006/webapi/WebSimpleDB/#idl-def-IDBCursor for more
  * information.
  */
-[scriptable, uuid(17cae1b9-b438-4cee-8d36-a2dc5f517c02)]
+[scriptable, uuid(12c99a5b-6e67-4d5c-8ae7-3a58da690375)]
 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;
 
@@ -64,17 +64,17 @@ interface nsIIDBCursor : nsISupports
 
   readonly attribute nsIVariant key;
 
   [implicit_jscontext]
   readonly attribute jsval value;
 
   // 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, optional_argc]
+  [implicit_jscontext]
   void continue([optional /* undefined */] in jsval key);
 
   // Success fires IDBTransactionEvent, result == key
   [implicit_jscontext]
   nsIIDBRequest update(in jsval value);
 
   // Success fires IDBTransactionEvent, result == null
   nsIIDBRequest delete();
--- a/dom/indexedDB/test/Makefile.in
+++ b/dom/indexedDB/test/Makefile.in
@@ -66,17 +66,16 @@ TEST_FILES = \
   test_exceptions_in_success_events.html \
   test_getAll.html \
   test_global_data.html \
   test_index_getAll.html \
   test_index_getAllObjects.html \
   test_indexes.html \
   test_indexes_bad_values.html \
   test_key_requirements.html \
-  test_null_keys.html \
   test_objectCursors.html \
   test_objectStore_inline_autoincrement_key_added_on_put.html \
   test_objectStore_remove_values.html \
   test_object_identity.html \
   test_open_empty_db.html \
   test_open_objectStore.html \
   test_overlapping_transactions.html \
   test_put_get_values.html \
--- a/dom/indexedDB/test/test_key_requirements.html
+++ b/dom/indexedDB/test/test_key_requirements.html
@@ -180,22 +180,23 @@
 
       is(event.result, key1, "put gave back the same key");
 
       request = objectStore.add({id:10});
       request.onerror = new ExpectError(IDBDatabaseException.CONSTRAINT_ERR);
       request.onsuccess = unexpectedSuccessHandler;
       event = yield;
 
-      request = objectStore.add({}, null);
-      request.onerror = errorHandler;
-      request.onsuccess = grabEventAndContinueHandler;
-      event = yield;
-
-      is(typeof(event.result), "string", "Good generated key");
+      try {
+        objectStore.add({}, null);
+        ok(false, "add with null key should throw!");
+      }
+      catch (e) {
+        ok(true, "add with null key threw");
+      }
 
       try {
         objectStore.put({}, null);
         ok(false, "put with null key should throw!");
       }
       catch (e) {
         ok(true, "put with null key threw");
       }
deleted file mode 100644
--- a/dom/indexedDB/test/test_null_keys.html
+++ /dev/null
@@ -1,64 +0,0 @@
-<!--
-  Any copyright is dedicated to the Public Domain.
-  http://creativecommons.org/publicdomain/zero/1.0/
--->
-<html>
-<head>
-  <title>Indexed Database Property Test</title>
-
-  <script type="text/javascript" src="/MochiKit/packed.js"></script>
-  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
-  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
-
-  <script type="text/javascript;version=1.7">
-    function testSteps()
-    {
-      const name = window.location.pathname;
-      const description = "My Test Database";
-
-      let request = moz_indexedDB.open(name, description);
-      request.onerror = errorHandler;
-      request.onsuccess = grabEventAndContinueHandler;
-      let event = yield;
-
-      let db = event.result;
-      db.onerror = function(event) {
-        event.preventDefault();
-      };
-
-      request = db.setVersion("1");
-      request.onerror = errorHandler;
-      request.onsuccess = grabEventAndContinueHandler;
-      let event = yield;
-
-      let objectStore = db.createObjectStore("foo", "");
-
-      request = objectStore.add({}, null);
-      request.onerror = errorHandler;
-      request.onsuccess = grabEventAndContinueHandler;
-      event = yield;
-
-      ok(event.result != null, "Valid key");
-
-      let key1 = event.result;
-
-      request = objectStore.add({}, null);
-      request.onerror = errorHandler;
-      request.onsuccess = grabEventAndContinueHandler;
-      event = yield;
-
-      ok(event.result != null, "Valid key");
-
-      ok(key1 != event.result, "Different unique keys");
-
-      finishTest();
-      yield;
-    }
-  </script>
-  <script type="text/javascript;version=1.7" src="helpers.js"></script>
-
-</head>
-
-<body onload="runTest();"></body>
-
-</html>
--- a/js/src/xpconnect/src/qsgen.py
+++ b/js/src/xpconnect/src/qsgen.py
@@ -503,51 +503,57 @@ argumentUnboxingTemplates = {
 
     '[jsval]':
         "    jsval ${name} = ${argVal};\n"
     }
 
 # From JSData2Native.
 #
 # Omitted optional arguments are treated as though the caller had passed JS
-# `null`; this behavior is from XPCWrappedNative::CallMethod.
+# `null`; this behavior is from XPCWrappedNative::CallMethod. The 'jsval' type,
+# however, defaults to 'undefined'.
 #
 def writeArgumentUnboxing(f, i, name, type, haveCcx, optional, rvdeclared,
                           nullBehavior, undefinedBehavior):
     # f - file to write to
     # i - int or None - Indicates the source jsval.  If i is an int, the source
     #     jsval is argv[i]; otherwise it is *vp.  But if Python i >= C++ argc,
     #     which can only happen if optional is True, the argument is missing;
     #     use JSVAL_NULL as the source jsval instead.
     # name - str - name of the native C++ variable to create.
     # type - xpidl.{Interface,Native,Builtin} - IDL type of argument
     # optional - bool - True if the parameter is optional.
     # rvdeclared - bool - False if no |nsresult rv| has been declared earlier.
 
+    typeName = getBuiltinOrNativeTypeName(type)
+
     isSetter = (i is None)
 
     if isSetter:
         argPtr = "vp"
         argVal = "*vp"
     elif optional:
-        argVal = "(%d < argc ? argv[%d] : JSVAL_NULL)" % (i, i)
+        if typeName == "[jsval]":
+            val = "JSVAL_VOID"
+        else:
+            val = "JSVAL_NULL"
+        argVal = "(%d < argc ? argv[%d] : %s)" % (i, i, val)
         argPtr = "(%d < argc ? &argv[%d] : NULL)" % (i, i)
     else:
         argVal = "argv[%d]" % i
         argPtr = "&" + argVal
 
     params = {
         'name': name,
         'argVal': argVal,
         'argPtr': argPtr,
         'nullBehavior': nullBehavior or 'DefaultNullBehavior',
         'undefinedBehavior': undefinedBehavior or 'DefaultUndefinedBehavior'
         }
 
-    typeName = getBuiltinOrNativeTypeName(type)
     if typeName is not None:
         template = argumentUnboxingTemplates.get(typeName)
         if template is not None:
             f.write(substitute(template, params))
             return rvdeclared
         # else fall through; the type isn't supported yet.
     elif isInterfaceType(type):
         if type.name == 'nsIVariant':