Bug 613083 - 'IndexedDB: Switch serialization format from JSON to structured clone bytestream'. r=sicking, a=blocking.
authorBen Turner <bent.mozilla@gmail.com>
Tue, 21 Dec 2010 11:02:04 -0500
changeset 59556 1b1561128c5dcc8717f896b26701aa3559b3b4fc
parent 59555 2d71a4dfb17b19d1c28d43355ead8364cb4fd895
child 59557 a599d358d01ac2b1653b8e20799677a1cf06edff
push id1
push usershaver@mozilla.com
push dateTue, 04 Jan 2011 17:58:04 +0000
reviewerssicking, blocking
bugs613083
milestone2.0b9pre
Bug 613083 - 'IndexedDB: Switch serialization format from JSON to structured clone bytestream'. r=sicking, a=blocking.
dom/indexedDB/IDBCursor.cpp
dom/indexedDB/IDBCursor.h
dom/indexedDB/IDBEvents.cpp
dom/indexedDB/IDBEvents.h
dom/indexedDB/IDBFactory.cpp
dom/indexedDB/IDBIndex.cpp
dom/indexedDB/IDBObjectStore.cpp
dom/indexedDB/IDBObjectStore.h
dom/src/threads/nsDOMWorker.cpp
dom/src/threads/nsDOMWorkerEvents.cpp
js/src/jsapi.cpp
js/src/jsapi.h
--- a/dom/indexedDB/IDBCursor.cpp
+++ b/dom/indexedDB/IDBCursor.cpp
@@ -42,17 +42,16 @@
 #include "nsIVariant.h"
 
 #include "jscntxt.h"
 #include "mozilla/storage.h"
 #include "nsComponentManagerUtils.h"
 #include "nsContentUtils.h"
 #include "nsDOMClassInfo.h"
 #include "nsEventDispatcher.h"
-#include "nsJSON.h"
 #include "nsJSUtils.h"
 #include "nsThreadUtils.h"
 
 #include "AsyncConnectionHelper.h"
 #include "DatabaseInfo.h"
 #include "IDBEvents.h"
 #include "IDBIndex.h"
 #include "IDBObjectStore.h"
@@ -86,31 +85,32 @@ public:
   { }
 
   nsresult DoDatabaseWork(mozIStorageConnection* aConnection);
   nsresult GetSuccessResult(nsIWritableVariant* aResult);
 
   void ReleaseMainThreadObjects()
   {
     mCursor = nsnull;
+    mCloneBuffer.clear();
     AsyncConnectionHelper::ReleaseMainThreadObjects();
   }
 
 protected:
   virtual nsresult
   BindArgumentsToStatement(mozIStorageStatement* aStatement) = 0;
 
   virtual nsresult
   GatherResultsFromStatement(mozIStorageStatement* aStatement) = 0;
 
 protected:
   nsRefPtr<IDBCursor> mCursor;
   Key mKey;
   Key mObjectKey;
-  nsString mValue;
+  JSAutoStructuredCloneBuffer mCloneBuffer;
 };
 
 class ContinueObjectStoreHelper : public ContinueHelper
 {
 public:
   ContinueObjectStoreHelper(IDBCursor* aCursor)
   : ContinueHelper(aCursor)
   { }
@@ -150,30 +150,30 @@ already_AddRefed<IDBCursor>
 IDBCursor::Create(IDBRequest* aRequest,
                   IDBTransaction* aTransaction,
                   IDBObjectStore* aObjectStore,
                   PRUint16 aDirection,
                   const Key& aRangeKey,
                   const nsACString& aContinueQuery,
                   const nsACString& aContinueToQuery,
                   const Key& aKey,
-                  const nsAString& aValue)
+                  JSAutoStructuredCloneBuffer& aCloneBuffer)
 {
   NS_ASSERTION(aObjectStore, "Null pointer!");
   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;
   cursor->mKey = aKey;
-  cursor->mValue = aValue;
+  cursor->mCloneBuffer.swap(aCloneBuffer);
 
   return cursor.forget();
 }
 
 // static
 already_AddRefed<IDBCursor>
 IDBCursor::Create(IDBRequest* aRequest,
                   IDBTransaction* aTransaction,
@@ -209,33 +209,33 @@ IDBCursor::Create(IDBRequest* aRequest,
                   IDBTransaction* aTransaction,
                   IDBIndex* aIndex,
                   PRUint16 aDirection,
                   const Key& aRangeKey,
                   const nsACString& aContinueQuery,
                   const nsACString& aContinueToQuery,
                   const Key& aKey,
                   const Key& aObjectKey,
-                  const nsAString& aValue)
+                  JSAutoStructuredCloneBuffer& aCloneBuffer)
 {
   NS_ASSERTION(aIndex, "Null pointer!");
   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();
   cursor->mIndex = aIndex;
   cursor->mType = INDEXOBJECT;
   cursor->mKey = aKey;
   cursor->mObjectKey = aObjectKey;
-  cursor->mValue = aValue;
+  cursor->mCloneBuffer.swap(aCloneBuffer);
 
   return cursor.forget();
 }
 
 // static
 already_AddRefed<IDBCursor>
 IDBCursor::CreateCommon(IDBRequest* aRequest,
                         IDBTransaction* aTransaction,
@@ -280,16 +280,17 @@ IDBCursor::IDBCursor()
 
 IDBCursor::~IDBCursor()
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
   if (mValueRooted) {
     NS_DROP_JS_OBJECTS(this, IDBCursor);
   }
+  IDBObjectStore::ClearStructuredCloneBuffer(mCloneBuffer);
 }
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(IDBCursor)
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(IDBCursor)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR_AMBIGUOUS(mRequest,
                                                        nsPIDOMEventTarget)
@@ -406,27 +407,29 @@ IDBCursor::GetValue(JSContext* aCx,
 
     rv = IDBObjectStore::GetJSValFromKey(mObjectKey, aCx, aValue);
     NS_ENSURE_SUCCESS(rv, rv);
 
     return NS_OK;
   }
 
   if (!mHaveCachedValue) {
-    JSAutoRequest ar(aCx);
-
-    nsCOMPtr<nsIJSON> json(new nsJSON());
-    rv = json->DecodeToJSVal(mValue, aCx, &mCachedValue);
-    NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_DATA_CLONE_ERR);
-
     if (!mValueRooted) {
       NS_HOLD_JS_OBJECTS(this, IDBCursor);
       mValueRooted = true;
     }
 
+    JSAutoRequest ar(aCx);
+
+    if (!mCloneBuffer.read(&mCachedValue, aCx)) {
+      mCachedValue = JSVAL_VOID;
+      return NS_ERROR_DOM_DATA_CLONE_ERR;
+    }
+
+    mCloneBuffer.clear(aCx);
     mHaveCachedValue = true;
   }
 
   *aValue = mCachedValue;
   return NS_OK;
 }
 
 NS_IMETHODIMP
@@ -663,17 +666,18 @@ ContinueHelper::GetSuccessResult(nsIWrit
   mCursor->mCachedObjectKey = nsnull;
   mCursor->mCachedValue = JSVAL_VOID;
   mCursor->mHaveCachedValue = false;
   mCursor->mContinueCalled = false;
 
   // And set new values.
   mCursor->mKey = mKey;
   mCursor->mObjectKey = mObjectKey;
-  mCursor->mValue = mValue;
+  mCursor->mCloneBuffer.clear();
+  mCursor->mCloneBuffer.swap(mCloneBuffer);
   mCursor->mContinueToKey = Key::UNSETKEY;
 
   rv = aResult->SetAsISupports(mCursor);
   NS_ENSURE_SUCCESS(rv, rv);
 
   return NS_OK;
 }
 
@@ -739,27 +743,19 @@ ContinueObjectStoreHelper::GatherResults
   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!");
   }
 
-#ifdef DEBUG
-  {
-    PRInt32 valueType;
-    NS_ASSERTION(NS_SUCCEEDED(aStatement->GetTypeOfIndex(1, &valueType)) &&
-                 valueType == mozIStorageStatement::VALUE_TYPE_TEXT,
-                 "Bad value type!");
-  }
-#endif
-
-  rv = aStatement->GetString(1, mValue);
-  NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
+  rv = IDBObjectStore::GetStructuredCloneDataFromStatement(aStatement, 1,
+                                                           mCloneBuffer);
+  NS_ENSURE_SUCCESS(rv, rv);
 
   return NS_OK;
 }
 
 nsresult
 ContinueIndexHelper::BindArgumentsToStatement(mozIStorageStatement* aStatement)
 {
   // Bind index id.
@@ -907,22 +903,14 @@ ContinueIndexObjectHelper::GatherResults
   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!");
   }
 
-#ifdef DEBUG
-  {
-    PRInt32 valueType;
-    NS_ASSERTION(NS_SUCCEEDED(aStatement->GetTypeOfIndex(2, &valueType)) &&
-                 valueType == mozIStorageStatement::VALUE_TYPE_TEXT,
-                 "Bad value type!");
-  }
-#endif
-
-  rv = aStatement->GetString(2, mValue);
-  NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
+  rv = IDBObjectStore::GetStructuredCloneDataFromStatement(aStatement, 2,
+                                                           mCloneBuffer);
+  NS_ENSURE_SUCCESS(rv, rv);
 
   return NS_OK;
 }
--- a/dom/indexedDB/IDBCursor.h
+++ b/dom/indexedDB/IDBCursor.h
@@ -81,17 +81,17 @@ public:
   Create(IDBRequest* aRequest,
          IDBTransaction* aTransaction,
          IDBObjectStore* aObjectStore,
          PRUint16 aDirection,
          const Key& aRangeKey,
          const nsACString& aContinueQuery,
          const nsACString& aContinueToQuery,
          const Key& aKey,
-         const nsAString& aValue);
+         JSAutoStructuredCloneBuffer& aCloneBuffer);
 
   // For INDEX cursors.
   static
   already_AddRefed<IDBCursor>
   Create(IDBRequest* aRequest,
          IDBTransaction* aTransaction,
          IDBIndex* aIndex,
          PRUint16 aDirection,
@@ -108,17 +108,17 @@ public:
          IDBTransaction* aTransaction,
          IDBIndex* aIndex,
          PRUint16 aDirection,
          const Key& aRangeKey,
          const nsACString& aContinueQuery,
          const nsACString& aContinueToQuery,
          const Key& aKey,
          const Key& aObjectKey,
-         const nsAString& aValue);
+         JSAutoStructuredCloneBuffer& aCloneBuffer);
 
   enum Type
   {
     OBJECTSTORE = 0,
     INDEXKEY,
     INDEXOBJECT
   };
 
@@ -158,17 +158,17 @@ protected:
   nsCString mContinueToQuery;
 
   jsval mCachedValue;
 
   Key mRangeKey;
 
   Key mKey;
   Key mObjectKey;
-  nsString mValue;
+  JSAutoStructuredCloneBuffer mCloneBuffer;
   Key mContinueToKey;
 
   bool mHaveCachedValue;
   bool mValueRooted;
   bool mContinueCalled;
 };
 
 END_INDEXEDDB_NAMESPACE
--- a/dom/indexedDB/IDBEvents.cpp
+++ b/dom/indexedDB/IDBEvents.cpp
@@ -350,19 +350,23 @@ IDBSuccessEvent::GetTransaction(nsIIDBTr
 {
   nsCOMPtr<nsIIDBTransaction> transaction(mTransaction);
   transaction.forget(aTransaction);
   return NS_OK;
 }
 
 GetSuccessEvent::~GetSuccessEvent()
 {
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+
   if (mValueRooted) {
     NS_DROP_JS_OBJECTS(this, GetSuccessEvent);
   }
+
+  IDBObjectStore::ClearStructuredCloneBuffer(mCloneBuffer);
 }
 
 nsresult
 GetSuccessEvent::Init(IDBRequest* aRequest,
                       IDBTransaction* aTransaction)
 {
   mSource = aRequest->Source();
   mTransaction = aTransaction;
@@ -376,36 +380,32 @@ GetSuccessEvent::Init(IDBRequest* aReque
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 GetSuccessEvent::GetResult(JSContext* aCx,
                            jsval* aResult)
 {
-  if (mValue.IsVoid()) {
-    *aResult = JSVAL_VOID;
-    return NS_OK;
-  }
-
   if (!mValueRooted) {
     RootCachedValue();
 
-    nsString jsonValue = mValue;
-    mValue.Truncate();
-
-    JSAutoRequest ar(aCx);
+    if (mCloneBuffer.data()) {
+      JSAutoRequest ar(aCx);
 
-    nsCOMPtr<nsIJSON> json(new nsJSON());
-    nsresult rv = json->DecodeToJSVal(jsonValue, aCx, &mCachedValue);
-    if (NS_FAILED(rv)) {
-      mCachedValue = JSVAL_VOID;
+      if (!mCloneBuffer.read(&mCachedValue, aCx)) {
+        mCachedValue = JSVAL_VOID;
+        NS_ERROR("Failed to decode!");
+        return NS_ERROR_DOM_DATA_CLONE_ERR;
+      }
 
-      NS_ERROR("Failed to decode!");
-      return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
+      mCloneBuffer.clear();
+    }
+    else {
+      NS_ASSERTION(JSVAL_IS_VOID(mCachedValue), "Should be undefined!");
     }
   }
 
   *aResult = mCachedValue;
   return NS_OK;
 }
 
 void
@@ -444,65 +444,71 @@ NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(Get
     void *gcThing = JSVAL_TO_GCTHING(tmp->mCachedValue);
     NS_IMPL_CYCLE_COLLECTION_TRACE_JS_CALLBACK(gcThing)
   }
 NS_IMPL_CYCLE_COLLECTION_TRACE_END
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(GetSuccessEvent)
 NS_INTERFACE_MAP_END_INHERITING(IDBSuccessEvent)
 
+GetAllSuccessEvent::~GetAllSuccessEvent()
+{
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+  for (PRUint32 index = 0; index < mCloneBuffers.Length(); index++) {
+    IDBObjectStore::ClearStructuredCloneBuffer(mCloneBuffers[index]);
+  }
+}
+
 NS_IMETHODIMP
 GetAllSuccessEvent::GetResult(JSContext* aCx,
                               jsval* aResult)
 {
   if (!mValueRooted) {
-    RootCachedValue();
-
-    JSAutoRequest ar(aCx);
-
-    // Swap into a stack array so that we don't hang on to the strings if
+    // Swap into a stack array so that we don't hang on to the buffers if
     // something fails.
-    nsTArray<nsString> values;
-    if (!mValues.SwapElements(values)) {
+    nsTArray<JSAutoStructuredCloneBuffer> cloneBuffers;
+    if (!mCloneBuffers.SwapElements(cloneBuffers)) {
       NS_ERROR("Failed to swap elements!");
       return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
     }
 
+    JSAutoRequest ar(aCx);
+
     JSObject* array = JS_NewArrayObject(aCx, 0, NULL);
     if (!array) {
       NS_ERROR("Failed to make array!");
       return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
     }
 
+    RootCachedValue();
+
     mCachedValue = OBJECT_TO_JSVAL(array);
 
-    if (!values.IsEmpty()) {
-      if (!JS_SetArrayLength(aCx, array, jsuint(values.Length()))) {
+    if (!cloneBuffers.IsEmpty()) {
+      if (!JS_SetArrayLength(aCx, array, jsuint(cloneBuffers.Length()))) {
         mCachedValue = JSVAL_VOID;
         NS_ERROR("Failed to set array length!");
         return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
       }
 
-      nsCOMPtr<nsIJSON> json(new nsJSON());
-      js::AutoValueRooter value(aCx);
-
-      jsint count = jsint(values.Length());
+      jsint count = jsint(cloneBuffers.Length());
 
       for (jsint index = 0; index < count; index++) {
-        nsString jsonValue = values[index];
-        values[index].Truncate();
+        JSAutoStructuredCloneBuffer& buffer = cloneBuffers[index];
 
-        nsresult rv = json->DecodeToJSVal(jsonValue, aCx, value.jsval_addr());
-        if (NS_FAILED(rv)) {
+        jsval val;
+        if (!buffer.read(&val, aCx)) {
           mCachedValue = JSVAL_VOID;
           NS_ERROR("Failed to decode!");
-          return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
+          return NS_ERROR_DOM_DATA_CLONE_ERR;
         }
 
-        if (!JS_SetElement(aCx, array, index, value.jsval_addr())) {
+        buffer.clear(aCx);
+
+        if (!JS_SetElement(aCx, array, index, &val)) {
           mCachedValue = JSVAL_VOID;
           NS_ERROR("Failed to set array element!");
           return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
         }
       }
     }
   }
 
@@ -510,34 +516,34 @@ GetAllSuccessEvent::GetResult(JSContext*
   return NS_OK;
 }
 
 NS_IMETHODIMP
 GetAllKeySuccessEvent::GetResult(JSContext* aCx,
                                  jsval* aResult)
 {
   if (!mValueRooted) {
-    RootCachedValue();
-
-    JSAutoRequest ar(aCx);
-
     // Swap into a stack array so that we don't hang on to the strings if
     // something fails.
     nsTArray<Key> keys;
     if (!mKeys.SwapElements(keys)) {
       NS_ERROR("Failed to swap elements!");
       return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
     }
 
+    JSAutoRequest ar(aCx);
+
     JSObject* array = JS_NewArrayObject(aCx, 0, NULL);
     if (!array) {
       NS_ERROR("Failed to make array!");
       return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
     }
 
+    RootCachedValue();
+
     mCachedValue = OBJECT_TO_JSVAL(array);
 
     if (!keys.IsEmpty()) {
       if (!JS_SetArrayLength(aCx, array, jsuint(keys.Length()))) {
         mCachedValue = JSVAL_VOID;
         NS_ERROR("Failed to set array length!");
         return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
       }
@@ -545,21 +551,22 @@ 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(), "Bad key!");
 
-        nsresult rv = IDBObjectStore::GetJSValFromKey(key, aCx, value.jsval_addr());
+        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;
+          return rv;
         }
 
         if (!JS_SetElement(aCx, array, index, value.jsval_addr())) {
           mCachedValue = JSVAL_VOID;
           NS_WARNING("Failed to set array element!");
           return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
         }
       }
--- a/dom/indexedDB/IDBEvents.h
+++ b/dom/indexedDB/IDBEvents.h
@@ -146,68 +146,74 @@ protected:
 
   nsCOMPtr<nsIVariant> mResult;
   nsCOMPtr<nsIIDBTransaction> mTransaction;
 };
 
 class GetSuccessEvent : public IDBSuccessEvent
 {
 public:
-  GetSuccessEvent(const nsAString& aValue)
-  : mValue(aValue),
-    mCachedValue(JSVAL_VOID),
+  GetSuccessEvent(JSAutoStructuredCloneBuffer& aCloneBuffer)
+  : mCachedValue(JSVAL_VOID),
+    mValueRooted(PR_FALSE)
+  {
+    mCloneBuffer.swap(aCloneBuffer);
+  }
+
+  GetSuccessEvent()
+  : mCachedValue(JSVAL_VOID),
     mValueRooted(PR_FALSE)
   { }
 
   ~GetSuccessEvent();
 
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(GetSuccessEvent,
                                                          IDBSuccessEvent)
 
   NS_IMETHOD GetResult(JSContext* aCx,
                        jsval* aResult);
 
   nsresult Init(IDBRequest* aRequest,
                 IDBTransaction* aTransaction);
 
 private:
-  nsString mValue;
+  JSAutoStructuredCloneBuffer mCloneBuffer;
 
 protected:
   void RootCachedValue();
 
   jsval mCachedValue;
   JSRuntime* mJSRuntime;
   PRBool mValueRooted;
 };
 
 class GetAllSuccessEvent : public GetSuccessEvent
 {
 public:
-  GetAllSuccessEvent(nsTArray<nsString>& aValues)
-  : GetSuccessEvent(EmptyString())
+  GetAllSuccessEvent(nsTArray<JSAutoStructuredCloneBuffer>& aCloneBuffers)
   {
-    if (!mValues.SwapElements(aValues)) {
+    if (!mCloneBuffers.SwapElements(aCloneBuffers)) {
       NS_ERROR("Failed to swap elements!");
     }
   }
 
+  ~GetAllSuccessEvent();
+
   NS_IMETHOD GetResult(JSContext* aCx,
                        jsval* aResult);
 
 private:
-  nsTArray<nsString> mValues;
+  nsTArray<JSAutoStructuredCloneBuffer> mCloneBuffers;
 };
 
 class GetAllKeySuccessEvent : public GetSuccessEvent
 {
 public:
   GetAllKeySuccessEvent(nsTArray<Key>& aKeys)
-  : GetSuccessEvent(EmptyString())
   {
     if (!mKeys.SwapElements(aKeys)) {
       NS_ERROR("Failed to swap elements!");
     }
   }
 
   NS_IMETHOD GetResult(JSContext* aCx,
                        jsval* aResult);
--- a/dom/indexedDB/IDBFactory.cpp
+++ b/dom/indexedDB/IDBFactory.cpp
@@ -65,17 +65,17 @@
 
 #define PREF_INDEXEDDB_QUOTA "dom.indexedDB.warningQuota"
 
 // megabytes
 #define DEFAULT_QUOTA 50
 
 #define BAD_TLS_INDEX (PRUintn)-1
 
-#define DB_SCHEMA_VERSION 3
+#define DB_SCHEMA_VERSION 4
 
 USING_INDEXEDDB_NAMESPACE
 
 namespace {
 
 PRUintn gCurrentDatabaseIndex = BAD_TLS_INDEX;
 
 PRInt32 gIndexedDBQuota = DEFAULT_QUOTA;
@@ -138,16 +138,17 @@ public:
 private:
   // In-params.
   nsString mName;
   nsCString mASCIIOrigin;
 
   // Out-params.
   nsTArray<nsAutoPtr<ObjectStoreInfo> > mObjectStores;
   nsString mVersion;
+  PRUint32 mDataVersion;
   nsString mDatabaseFilePath;
   PRUint32 mDatabaseId;
   PRInt64 mLastObjectStoreId;
   PRInt64 mLastIndexId;
 };
 
 nsresult
 CreateTables(mozIStorageConnection* aDBConn)
@@ -155,17 +156,18 @@ CreateTables(mozIStorageConnection* aDBC
   NS_PRECONDITION(!NS_IsMainThread(),
                   "Creating tables on the main thread!");
   NS_PRECONDITION(aDBConn, "Passing a null database connection!");
 
   // Table `database`
   nsresult rv = aDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
     "CREATE TABLE database ("
       "name TEXT NOT NULL, "
-      "version TEXT DEFAULT NULL"
+      "version TEXT DEFAULT NULL, "
+      "dataVersion INTEGER NOT NULL"
     ");"
   ));
   NS_ENSURE_SUCCESS(rv, rv);
 
   // Table `object_store`
   rv = aDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
     "CREATE TABLE object_store ("
       "id INTEGER, "
@@ -178,17 +180,17 @@ CreateTables(mozIStorageConnection* aDBC
   ));
   NS_ENSURE_SUCCESS(rv, rv);
 
   // Table `object_data`
   rv = aDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
     "CREATE TABLE object_data ("
       "id INTEGER, "
       "object_store_id INTEGER NOT NULL, "
-      "data TEXT NOT NULL, "
+      "data BLOB NOT NULL, "
       "key_value DEFAULT NULL, " // NONE affinity
       "PRIMARY KEY (id), "
       "FOREIGN KEY (object_store_id) REFERENCES object_store(id) ON DELETE "
         "CASCADE"
     ");"
   ));
   NS_ENSURE_SUCCESS(rv, rv);
 
@@ -198,17 +200,17 @@ CreateTables(mozIStorageConnection* aDBC
   ));
   NS_ENSURE_SUCCESS(rv, rv);
 
   // Table `ai_object_data`
   rv = aDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
     "CREATE TABLE ai_object_data ("
       "id INTEGER PRIMARY KEY AUTOINCREMENT, "
       "object_store_id INTEGER NOT NULL, "
-      "data TEXT NOT NULL, "
+      "data BLOB NOT NULL, "
       "FOREIGN KEY (object_store_id) REFERENCES object_store(id) ON DELETE "
         "CASCADE"
     ");"
   ));
   NS_ENSURE_SUCCESS(rv, rv);
 
   rv = aDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
     "CREATE UNIQUE INDEX ai_key_index "
@@ -323,24 +325,28 @@ nsresult
 CreateMetaData(mozIStorageConnection* aConnection,
                const nsAString& aName)
 {
   NS_PRECONDITION(!NS_IsMainThread(), "Wrong thread!");
   NS_PRECONDITION(aConnection, "Null database!");
 
   nsCOMPtr<mozIStorageStatement> stmt;
   nsresult rv = aConnection->CreateStatement(NS_LITERAL_CSTRING(
-    "INSERT OR REPLACE INTO database (name) "
-    "VALUES (:name)"
+    "INSERT OR REPLACE INTO database (name, dataVersion) "
+    "VALUES (:name, :dataVersion)"
   ), getter_AddRefs(stmt));
   NS_ENSURE_SUCCESS(rv, rv);
 
   rv = stmt->BindStringByName(NS_LITERAL_CSTRING("name"), aName);
   NS_ENSURE_SUCCESS(rv, rv);
 
+  rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("dataVersion"),
+                             JS_STRUCTURED_CLONE_VERSION);
+  NS_ENSURE_SUCCESS(rv, rv);
+
   return stmt->Execute();
 }
 
 nsresult
 CreateDatabaseConnection(const nsACString& aASCIIOrigin,
                          const nsAString& aName,
                          nsAString& aDatabaseFilePath,
                          mozIStorageConnection** aConnection)
@@ -544,17 +550,17 @@ IDBFactory::GetConnection(const nsAStrin
     // Check to make sure that the database schema is correct again.
     PRInt32 schemaVersion;
     NS_ASSERTION(NS_SUCCEEDED(connection->GetSchemaVersion(&schemaVersion)) &&
                  schemaVersion == DB_SCHEMA_VERSION,
                  "Wrong schema!");
   }
 #endif
 
-  // Turn on foreign key constraints in debug builds to catch bugs!
+  // Turn on foreign key constraints!
   rv = connection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
     "PRAGMA foreign_keys = ON;"
   ));
   NS_ENSURE_SUCCESS(rv, nsnull);
 
   return connection.forget();
 }
 
@@ -721,17 +727,16 @@ IDBFactory::LoadDatabaseInformation(mozI
 
   nsString version;
   rv = stmt->GetString(0, version);
   NS_ENSURE_SUCCESS(rv, rv);
 
   if (version.IsVoid()) {
     version.SetIsVoid(PR_FALSE);
   }
-
   aVersion = version;
   return NS_OK;
 }
 
 // static
 nsresult
 IDBFactory::UpdateDatabaseMetadata(DatabaseInfo* aDatabaseInfo,
                                    const nsAString& aVersion,
@@ -879,16 +884,47 @@ OpenDatabaseHelper::DoDatabaseWork(mozIS
     return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
   }
 
   nsCOMPtr<mozIStorageConnection> connection;
   nsresult rv = CreateDatabaseConnection(mASCIIOrigin, mName, mDatabaseFilePath,
                                          getter_AddRefs(connection));
   NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
+  // Get the data version.
+  nsCOMPtr<mozIStorageStatement> stmt;
+  rv = connection->CreateStatement(NS_LITERAL_CSTRING(
+    "SELECT dataVersion "
+    "FROM database"
+  ), getter_AddRefs(stmt));
+  NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
+
+  PRBool hasResult;
+  rv = stmt->ExecuteStep(&hasResult);
+  NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
+
+  if (!hasResult) {
+    NS_ERROR("Database has no dataVersion!");
+    return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
+  }
+
+  PRInt64 dataVersion;
+  rv = stmt->GetInt64(0, &dataVersion);
+  NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
+
+  if (dataVersion > JS_STRUCTURED_CLONE_VERSION) {
+    NS_ERROR("Bad data version!");
+    return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
+  }
+
+  if (dataVersion < JS_STRUCTURED_CLONE_VERSION) {
+    // Need to upgrade the database, here, before returning to the main thread.
+    NS_NOTYETIMPLEMENTED("Implement me!");
+  }
+
   mDatabaseId = HashString(mDatabaseFilePath);
   NS_ASSERTION(mDatabaseId, "HashString gave us 0?!");
 
   rv = IDBFactory::LoadDatabaseInformation(connection, mDatabaseId, mVersion,
                                            mObjectStores);
   NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
   for (PRUint32 i = 0; i < mObjectStores.Length(); i++) {
--- a/dom/indexedDB/IDBIndex.cpp
+++ b/dom/indexedDB/IDBIndex.cpp
@@ -37,16 +37,17 @@
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 
 #include "IDBIndex.h"
 
 #include "nsIIDBKeyRange.h"
+#include "nsIJSContextStack.h"
 
 #include "nsDOMClassInfo.h"
 #include "nsEventDispatcher.h"
 #include "nsThreadUtils.h"
 #include "mozilla/storage.h"
 
 #include "AsyncConnectionHelper.h"
 #include "IDBCursor.h"
@@ -92,18 +93,24 @@ public:
             IDBIndex* aIndex,
             const Key& aKey)
   : GetKeyHelper(aTransaction, aRequest, aIndex, aKey)
   { }
 
   nsresult DoDatabaseWork(mozIStorageConnection* aConnection);
   nsresult OnSuccess(nsIDOMEventTarget* aTarget);
 
+  void ReleaseMainThreadObjects()
+  {
+    IDBObjectStore::ClearStructuredCloneBuffer(mCloneBuffer);
+    GetKeyHelper::ReleaseMainThreadObjects();
+  }
+
 protected:
-  nsString mValue;
+  JSAutoStructuredCloneBuffer mCloneBuffer;
 };
 
 class GetAllKeysHelper : public GetKeyHelper
 {
 public:
   GetAllKeysHelper(IDBTransaction* aTransaction,
                    IDBRequest* aRequest,
                    IDBIndex* aIndex,
@@ -129,19 +136,27 @@ public:
                const Key& aKey,
                const PRUint32 aLimit)
   : GetKeyHelper(aTransaction, aRequest, aIndex, aKey), mLimit(aLimit)
   { }
 
   nsresult DoDatabaseWork(mozIStorageConnection* aConnection);
   nsresult OnSuccess(nsIDOMEventTarget* aTarget);
 
+  void ReleaseMainThreadObjects()
+  {
+    for (PRUint32 index = 0; index < mCloneBuffers.Length(); index++) {
+      IDBObjectStore::ClearStructuredCloneBuffer(mCloneBuffers[index]);
+    }
+    GetKeyHelper::ReleaseMainThreadObjects();
+  }
+
 protected:
   const PRUint32 mLimit;
-  nsTArray<nsString> mValues;
+  nsTArray<JSAutoStructuredCloneBuffer> mCloneBuffers;
 };
 
 class OpenKeyCursorHelper : public AsyncConnectionHelper
 {
 public:
   OpenKeyCursorHelper(IDBTransaction* aTransaction,
                       IDBRequest* aRequest,
                       IDBIndex* aIndex,
@@ -198,32 +213,33 @@ public:
   { }
 
   nsresult DoDatabaseWork(mozIStorageConnection* aConnection);
   nsresult GetSuccessResult(nsIWritableVariant* aResult);
 
   void ReleaseMainThreadObjects()
   {
     mIndex = nsnull;
+    IDBObjectStore::ClearStructuredCloneBuffer(mCloneBuffer);
     AsyncConnectionHelper::ReleaseMainThreadObjects();
   }
 
 private:
   // In-params.
   nsRefPtr<IDBIndex> mIndex;
   const Key mLowerKey;
   const Key mUpperKey;
   const PRPackedBool mLowerOpen;
   const PRPackedBool mUpperOpen;
   const PRUint16 mDirection;
 
   // Out-params.
   Key mKey;
   Key mObjectKey;
-  nsString mValue;
+  JSAutoStructuredCloneBuffer mCloneBuffer;
   nsCString mContinueQuery;
   nsCString mContinueToQuery;
   Key mRangeKey;
 };
 
 inline
 already_AddRefed<IDBRequest>
 GenerateRequest(IDBIndex* aIndex)
@@ -744,30 +760,28 @@ GetHelper::DoDatabaseWork(mozIStorageCon
 
   mKey = Key::UNSETKEY;
 
   PRBool hasResult;
   rv = stmt->ExecuteStep(&hasResult);
   NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
   if (hasResult) {
-    rv = stmt->GetString(0, mValue);
-    NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
-  }
-  else {
-    mValue.SetIsVoid(PR_TRUE);
+    rv = IDBObjectStore::GetStructuredCloneDataFromStatement(stmt, 0,
+                                                             mCloneBuffer);
+    NS_ENSURE_SUCCESS(rv, rv);
   }
 
   return NS_OK;
 }
 
 nsresult
 GetHelper::OnSuccess(nsIDOMEventTarget* aTarget)
 {
-  nsRefPtr<GetSuccessEvent> event(new GetSuccessEvent(mValue));
+  nsRefPtr<GetSuccessEvent> event(new GetSuccessEvent(mCloneBuffer));
   nsresult rv = event->Init(mRequest, mTransaction);
   NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
   PRBool dummy;
   aTarget->DispatchEvent(static_cast<nsDOMEvent*>(event), &dummy);
   return NS_OK;
 }
 
@@ -896,17 +910,17 @@ GetAllKeysHelper::OnSuccess(nsIDOMEventT
   return NS_OK;
 }
 
 nsresult
 GetAllHelper::DoDatabaseWork(mozIStorageConnection* aConnection)
 {
   NS_ASSERTION(aConnection, "Passed a null connection!");
 
-  if (!mValues.SetCapacity(50)) {
+  if (!mCloneBuffers.SetCapacity(50)) {
     NS_ERROR("Out of memory!");
     return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
   }
 
   nsCString dataTableName;
   nsCString objectDataId;
   nsCString indexTableName;
 
@@ -973,49 +987,41 @@ GetAllHelper::DoDatabaseWork(mozIStorage
     else {
       NS_NOTREACHED("Bad key type!");
     }
     NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
   }
 
   PRBool hasResult;
   while(NS_SUCCEEDED((rv = stmt->ExecuteStep(&hasResult))) && hasResult) {
-    if (mValues.Capacity() == mValues.Length()) {
-      if (!mValues.SetCapacity(mValues.Capacity() * 2)) {
+    if (mCloneBuffers.Capacity() == mCloneBuffers.Length()) {
+      if (!mCloneBuffers.SetCapacity(mCloneBuffers.Capacity() * 2)) {
         NS_ERROR("Out of memory!");
         return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
       }
     }
 
-    nsString* value = mValues.AppendElement();
-    NS_ASSERTION(value, "This shouldn't fail!");
+    JSAutoStructuredCloneBuffer* buffer = mCloneBuffers.AppendElement();
+    NS_ASSERTION(buffer, "This shouldn't fail!");
 
-#ifdef DEBUG
-    {
-      PRInt32 keyType;
-      NS_ASSERTION(NS_SUCCEEDED(stmt->GetTypeOfIndex(0, &keyType)) &&
-                   keyType == mozIStorageStatement::VALUE_TYPE_TEXT,
-                   "Bad SQLITE type!");
-    }
-#endif
-
-    rv = stmt->GetString(0, *value);
-    NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
+    rv = IDBObjectStore::GetStructuredCloneDataFromStatement(stmt, 0, *buffer);
+    NS_ENSURE_SUCCESS(rv, rv);
   }
   NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
   return NS_OK;
 }
 
 nsresult
 GetAllHelper::OnSuccess(nsIDOMEventTarget* aTarget)
 {
-  nsRefPtr<GetAllSuccessEvent> event(new GetAllSuccessEvent(mValues));
+  NS_ASSERTION(mCloneBuffers.Length() <= mLimit, "Too many results!");
 
-  NS_ASSERTION(mValues.IsEmpty(), "Should have swapped!");
+  nsRefPtr<GetAllSuccessEvent> event = new GetAllSuccessEvent(mCloneBuffers);
+  NS_ASSERTION(mCloneBuffers.IsEmpty(), "Should have swapped!");
 
   nsresult rv = event->Init(mRequest, mTransaction);
   NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
   PRBool dummy;
   aTarget->DispatchEvent(static_cast<nsDOMEvent*>(event), &dummy);
   return NS_OK;
 }
@@ -1416,27 +1422,19 @@ OpenCursorHelper::DoDatabaseWork(mozISto
   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!");
   }
 
-#ifdef DEBUG
-  {
-    PRInt32 valueType;
-    NS_ASSERTION(NS_SUCCEEDED(stmt->GetTypeOfIndex(2, &valueType)) &&
-                 valueType == mozIStorageStatement::VALUE_TYPE_TEXT,
-                 "Bad value type!");
-  }
-#endif
-
-  rv = stmt->GetString(2, mValue);
-  NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
+  rv = IDBObjectStore::GetStructuredCloneDataFromStatement(stmt, 2,
+                                                           mCloneBuffer);
+  NS_ENSURE_SUCCESS(rv, rv);
 
 /*
   SELECT index_data.value, object_data.key_value, object_data.data
   FROM object_data INNER JOIN index_data
   ON index_data.object_data_id = object_data.id
   WHERE index_data.index_id = 2 AND index_data.value < 73
   AND ( ( index_data.value = 65 AND object_data.key_value > "237-23-7736" )
   OR ( index_data.value > 65 ) )
@@ -1536,14 +1534,14 @@ OpenCursorHelper::GetSuccessResult(nsIWr
   if (mKey.IsUnset()) {
     aResult->SetAsEmpty();
     return NS_OK;
   }
 
   nsRefPtr<IDBCursor> cursor =
     IDBCursor::Create(mRequest, mTransaction, mIndex, mDirection, mRangeKey,
                       mContinueQuery, mContinueToQuery, mKey, mObjectKey,
-                      mValue);
+                      mCloneBuffer);
   NS_ENSURE_TRUE(cursor, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
   aResult->SetAsISupports(cursor);
   return NS_OK;
 }
--- a/dom/indexedDB/IDBObjectStore.cpp
+++ b/dom/indexedDB/IDBObjectStore.cpp
@@ -42,17 +42,16 @@
 #include "nsIJSContextStack.h"
 #include "nsIVariant.h"
 
 #include "jscntxt.h"
 #include "mozilla/storage.h"
 #include "nsContentUtils.h"
 #include "nsDOMClassInfo.h"
 #include "nsEventDispatcher.h"
-#include "nsJSON.h"
 #include "nsJSUtils.h"
 #include "nsServiceManagerUtils.h"
 #include "nsThreadUtils.h"
 
 #include "AsyncConnectionHelper.h"
 #include "IDBCursor.h"
 #include "IDBEvents.h"
 #include "IDBIndex.h"
@@ -65,45 +64,47 @@ USING_INDEXEDDB_NAMESPACE
 namespace {
 
 class AddHelper : public AsyncConnectionHelper
 {
 public:
   AddHelper(IDBTransaction* aTransaction,
             IDBRequest* aRequest,
             IDBObjectStore* aObjectStore,
-            const nsAString& aValue,
+            JSAutoStructuredCloneBuffer& aCloneBuffer,
             const Key& aKey,
             bool aOverwrite,
             nsTArray<IndexUpdateInfo>& aIndexUpdateInfo)
   : AsyncConnectionHelper(aTransaction, aRequest), mObjectStore(aObjectStore),
-    mValue(aValue), mKey(aKey), mOverwrite(aOverwrite)
+    mKey(aKey), mOverwrite(aOverwrite)
   {
+    mCloneBuffer.swap(aCloneBuffer);
     mIndexUpdateInfo.SwapElements(aIndexUpdateInfo);
   }
 
   nsresult DoDatabaseWork(mozIStorageConnection* aConnection);
   nsresult GetSuccessResult(nsIWritableVariant* aResult);
 
   void ReleaseMainThreadObjects()
   {
     mObjectStore = nsnull;
+    IDBObjectStore::ClearStructuredCloneBuffer(mCloneBuffer);
     AsyncConnectionHelper::ReleaseMainThreadObjects();
   }
 
   nsresult ModifyValueForNewKey();
   nsresult UpdateIndexes(mozIStorageConnection* aConnection,
                          PRInt64 aObjectDataId);
 
 private:
   // In-params.
   nsRefPtr<IDBObjectStore> mObjectStore;
 
   // These may change in the autoincrement case.
-  nsString mValue;
+  JSAutoStructuredCloneBuffer mCloneBuffer;
   Key mKey;
   const bool mOverwrite;
   nsTArray<IndexUpdateInfo> mIndexUpdateInfo;
 };
 
 class GetHelper : public AsyncConnectionHelper
 {
 public:
@@ -116,27 +117,28 @@ public:
   { }
 
   nsresult DoDatabaseWork(mozIStorageConnection* aConnection);
   nsresult OnSuccess(nsIDOMEventTarget* aTarget);
 
   void ReleaseMainThreadObjects()
   {
     mObjectStore = nsnull;
+    IDBObjectStore::ClearStructuredCloneBuffer(mCloneBuffer);
     AsyncConnectionHelper::ReleaseMainThreadObjects();
   }
 
 protected:
   // In-params.
   nsRefPtr<IDBObjectStore> mObjectStore;
   const Key mKey;
 
 private:
   // Out-params.
-  nsString mValue;
+  JSAutoStructuredCloneBuffer mCloneBuffer;
 };
 
 class DeleteHelper : public GetHelper
 {
 public:
   DeleteHelper(IDBTransaction* aTransaction,
                IDBRequest* aRequest,
                IDBObjectStore* aObjectStore,
@@ -188,31 +190,32 @@ public:
   { }
 
   nsresult DoDatabaseWork(mozIStorageConnection* aConnection);
   nsresult GetSuccessResult(nsIWritableVariant* aResult);
 
   void ReleaseMainThreadObjects()
   {
     mObjectStore = nsnull;
+    IDBObjectStore::ClearStructuredCloneBuffer(mCloneBuffer);
     AsyncConnectionHelper::ReleaseMainThreadObjects();
   }
 
 private:
   // In-params.
   nsRefPtr<IDBObjectStore> mObjectStore;
   const Key mLowerKey;
   const Key mUpperKey;
   const PRPackedBool mLowerOpen;
   const PRPackedBool mUpperOpen;
   const PRUint16 mDirection;
 
   // Out-params.
   Key mKey;
-  nsString mValue;
+  JSAutoStructuredCloneBuffer mCloneBuffer;
   nsCString mContinueQuery;
   nsCString mContinueToQuery;
   Key mRangeKey;
 };
 
 class CreateIndexHelper : public AsyncConnectionHelper
 {
 public:
@@ -284,31 +287,34 @@ public:
   { }
 
   nsresult DoDatabaseWork(mozIStorageConnection* aConnection);
   nsresult OnSuccess(nsIDOMEventTarget* aTarget);
 
   void ReleaseMainThreadObjects()
   {
     mObjectStore = 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 PRPackedBool mLowerOpen;
   const PRPackedBool mUpperOpen;
   const PRUint32 mLimit;
 
 private:
   // Out-params.
-  nsTArray<nsString> mValues;
+  nsTArray<JSAutoStructuredCloneBuffer> mCloneBuffers;
 };
 
 NS_STACK_CLASS
 class AutoRemoveIndex
 {
 public:
   AutoRemoveIndex(PRUint32 aDatabaseId,
                   const nsAString& aObjectStoreName,
@@ -493,58 +499,61 @@ IDBObjectStore::GetJSValFromKey(const Ke
   }
 
   NS_NOTREACHED("Unknown key type!");
   return NS_ERROR_DOM_INDEXEDDB_DATA_ERR;
 }
 
 // static
 nsresult
-IDBObjectStore::GetKeyPathValueFromJSON(const nsAString& aJSON,
-                                        const nsAString& aKeyPath,
-                                        JSContext** aCx,
-                                        Key& aValue)
+IDBObjectStore::GetKeyPathValueFromStructuredData(const PRUint8* aData,
+                                                  PRUint32 aDataLength,
+                                                  const nsAString& aKeyPath,
+                                                  JSContext** aCx,
+                                                  Key& aValue)
 {
-  NS_ASSERTION(!aJSON.IsEmpty(), "Empty JSON!");
+  NS_ASSERTION(aData, "Null pointer!");
+  NS_ASSERTION(aDataLength, "Empty data!");
   NS_ASSERTION(!aKeyPath.IsEmpty(), "Empty keyPath!");
   NS_ASSERTION(aCx, "Null pointer!");
 
   nsresult rv;
 
   if (!*aCx) {
     rv = nsContentUtils::ThreadJSContextStack()->GetSafeJSContext(aCx);
     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, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
-
-  if (JSVAL_IS_PRIMITIVE(clone.jsval_value())) {
+  JSContext*& cx = *aCx;
+
+  JSAutoRequest ar(cx);
+
+  jsval clone;
+  if (!JS_ReadStructuredClone(cx, reinterpret_cast<const uint64*>(aData),
+                              aDataLength, JS_STRUCTURED_CLONE_VERSION,
+                              &clone)) {
+    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;
     return NS_OK;
   }
 
-  JSObject* obj = JSVAL_TO_OBJECT(clone.jsval_value());
+  JSObject* obj = JSVAL_TO_OBJECT(clone);
 
   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());
+  jsval keyVal;
+  JSBool ok = JS_GetUCProperty(cx, obj, keyPathChars, keyPathLen, &keyVal);
   NS_ENSURE_TRUE(ok, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
-  rv = GetKeyFromJSVal(value.jsval_value(), aValue);
+  rv = GetKeyFromJSVal(keyVal, aValue);
   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;
 }
@@ -717,33 +726,89 @@ IDBObjectStore::UpdateIndexes(IDBTransac
     if (NS_FAILED(rv)) {
       return rv;
     }
   }
 
   return NS_OK;
 }
 
+// static
+nsresult
+IDBObjectStore::GetStructuredCloneDataFromStatement(
+                                           mozIStorageStatement* aStatement,
+                                           PRUint32 aIndex,
+                                           JSAutoStructuredCloneBuffer& aBuffer)
+{
+#ifdef DEBUG
+  {
+    PRInt32 valueType;
+    NS_ASSERTION(NS_SUCCEEDED(aStatement->GetTypeOfIndex(aIndex, &valueType)) &&
+                 valueType == mozIStorageStatement::VALUE_TYPE_BLOB,
+                 "Bad value type!");
+  }
+#endif
+
+  const PRUint8* data;
+  PRUint32 dataLength;
+  nsresult rv = aStatement->GetSharedBlob(aIndex, &dataLength, &data);
+  NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
+
+  JSContext* cx;
+  rv = nsContentUtils::ThreadJSContextStack()->GetSafeJSContext(&cx);
+  NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
+
+  JSAutoRequest ar(cx);
+
+  uint64* newData = static_cast<uint64*>(JS_malloc(cx, dataLength));
+  NS_ENSURE_TRUE(newData, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
+
+  memcpy(newData, data, dataLength);
+  aBuffer.adopt(cx, newData, dataLength);
+
+  return NS_OK;
+}
+
+// static
+void
+IDBObjectStore::ClearStructuredCloneBuffer(JSAutoStructuredCloneBuffer& aBuffer)
+{
+  if (aBuffer.data()) {
+    JSContext* cx;
+    if (NS_SUCCEEDED(nsContentUtils::ThreadJSContextStack()->
+                     GetSafeJSContext(&cx))) {
+      JSAutoRequest ar(cx);
+      aBuffer.clear(cx);
+    }
+    else {
+      NS_WARNING("Couldn't get safe JSContext! Leaking data!");
+      uint64* data;
+      size_t length;
+      aBuffer.steal(&data, &length);
+    }
+  }
+}
+
 IDBObjectStore::IDBObjectStore()
 : mId(LL_MININT),
   mAutoIncrement(PR_FALSE)
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 }
 
 IDBObjectStore::~IDBObjectStore()
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 }
 
 nsresult
 IDBObjectStore::GetAddInfo(JSContext* aCx,
                            jsval aValue,
                            jsval aKeyVal,
-                           nsString& aJSON,
+                           JSAutoStructuredCloneBuffer& aCloneBuffer,
                            Key& aKey,
                            nsTArray<IndexUpdateInfo>& aUpdateInfoArray)
 {
   nsresult rv;
 
   // Return DATA_ERR if a key was passed in and this objectStore uses inline
   // keys.
   if (!JSVAL_IS_VOID(aKeyVal) && !mKeyPath.IsEmpty()) {
@@ -778,19 +843,19 @@ IDBObjectStore::GetAddInfo(JSContext* aC
   ObjectStoreInfo* info;
   if (!ObjectStoreInfo::Get(mTransaction->Database()->Id(), mName, &info)) {
     NS_ERROR("This should never fail!");
   }
 
   rv = GetIndexUpdateInfo(info, aCx, aValue, aUpdateInfoArray);
   NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
-  nsCOMPtr<nsIJSON> json(new nsJSON());
-  rv = json->EncodeFromJSVal(&aValue, aCx, aJSON);
-  NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_DATA_CLONE_ERR);
+  if (!aCloneBuffer.write(aCx, aValue)) {
+    return NS_ERROR_DOM_DATA_CLONE_ERR;
+  }
 
   return NS_OK;
 }
 
 nsresult
 IDBObjectStore::AddOrPut(const jsval& aValue,
                          const jsval& aKey,
                          JSContext* aCx,
@@ -805,35 +870,35 @@ IDBObjectStore::AddOrPut(const jsval& aV
   }
 
   if (!IsWriteAllowed()) {
     return NS_ERROR_DOM_INDEXEDDB_READ_ONLY_ERR;
   }
 
   jsval keyval = (aOptionalArgCount >= 1) ? aKey : JSVAL_VOID;
 
-  nsString jsonValue;
+  JSAutoStructuredCloneBuffer cloneBuffer;
   Key key;
   nsTArray<IndexUpdateInfo> updateInfo;
 
-  nsresult rv = GetAddInfo(aCx, aValue, keyval, jsonValue, key, updateInfo);
+  nsresult rv = GetAddInfo(aCx, aValue, keyval, cloneBuffer, 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,
+    new AddHelper(mTransaction, request, this, cloneBuffer, key, aOverwrite,
                   updateInfo);
 
   rv = helper->DispatchToTransactionPool();
   NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
   request.forget(_retval);
   return NS_OK;
 }
@@ -1500,22 +1565,30 @@ AddHelper::DoDatabaseWork(mozIStorageCon
       rv = stmt->BindStringByName(keyValue, mKey.StringValue());
     }
     else {
       NS_NOTREACHED("Unknown key type!");
     }
     NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
   }
 
-  rv = stmt->BindStringByName(NS_LITERAL_CSTRING("data"), mValue);
+  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);
 
   rv = stmt->Execute();
   if (NS_FAILED(rv)) {
     if (mayOverwrite && rv == NS_ERROR_STORAGE_CONSTRAINT) {
+      scoper.Abandon();
+
+      rv = stmt->Reset();
+      NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
+
       stmt = mTransaction->AddStatement(false, true, autoIncrement);
       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);
 
@@ -1527,17 +1600,18 @@ AddHelper::DoDatabaseWork(mozIStorageCon
       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 = stmt->BindStringByName(NS_LITERAL_CSTRING("data"), mValue);
+      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;
     }
@@ -1576,17 +1650,21 @@ AddHelper::DoDatabaseWork(mozIStorageCon
       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 = stmt->BindStringByName(NS_LITERAL_CSTRING("data"), mValue);
+      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.
@@ -1632,43 +1710,48 @@ AddHelper::ModifyValueForNewKey()
   const nsString& keyPath = mObjectStore->KeyPath();
 
   JSContext* cx;
   nsresult rv = nsContentUtils::ThreadJSContextStack()->GetSafeJSContext(&cx);
   NS_ENSURE_SUCCESS(rv, rv);
 
   JSAutoRequest ar(cx);
 
-  js::AutoValueRooter clone(cx);
-
-  nsCOMPtr<nsIJSON> json(new nsJSON());
-  rv = json->DecodeToJSVal(mValue, cx, clone.jsval_addr());
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  JSObject* obj = JSVAL_TO_OBJECT(clone.jsval_value());
+  jsval clone;
+  if (!mCloneBuffer.read(&clone, cx)) {
+    return NS_ERROR_DOM_DATA_CLONE_ERR;
+  }
+
+  NS_ASSERTION(!JSVAL_IS_PRIMITIVE(clone), "We should have an object!");
+
+  JSObject* obj = JSVAL_TO_OBJECT(clone);
   JSBool ok;
-  js::AutoValueRooter key(cx);
 
   const jschar* keyPathChars = reinterpret_cast<const jschar*>(keyPath.get());
   const size_t keyPathLen = keyPath.Length();
 
 #ifdef DEBUG
-  ok = JS_GetUCProperty(cx, obj, keyPathChars, keyPathLen, key.jsval_addr());
-  NS_ASSERTION(ok && JSVAL_IS_VOID(key.jsval_value()), "Already has a key prop!");
+  {
+    jsval prop;
+    ok = JS_GetUCProperty(cx, obj, keyPathChars, keyPathLen, &prop);
+    NS_ASSERTION(ok && JSVAL_IS_VOID(prop), "Already has a key prop!");
+  }
 #endif
 
-  ok = JS_NewNumberValue(cx, mKey.IntValue(), key.jsval_addr());
+  jsval key;
+  ok = JS_NewNumberValue(cx, mKey.IntValue(), &key);
   NS_ENSURE_TRUE(ok, NS_ERROR_FAILURE);
 
-  ok = JS_DefineUCProperty(cx, obj, keyPathChars, keyPathLen, key.jsval_value(),
-                           nsnull, nsnull, JSPROP_ENUMERATE);
+  ok = JS_DefineUCProperty(cx, obj, keyPathChars, keyPathLen, key, nsnull,
+                           nsnull, JSPROP_ENUMERATE);
   NS_ENSURE_TRUE(ok, NS_ERROR_FAILURE);
 
-  rv = json->EncodeFromJSVal(clone.jsval_addr(), cx, mValue);
-  NS_ENSURE_SUCCESS(rv, rv);
+  if (!mCloneBuffer.write(cx, OBJECT_TO_JSVAL(obj))) {
+    return NS_ERROR_DOM_DATA_CLONE_ERR;
+  }
 
   return NS_OK;
 }
 
 nsresult
 GetHelper::DoDatabaseWork(mozIStorageConnection* aConnection)
 {
   NS_PRECONDITION(aConnection, "Passed a null connection!");
@@ -1699,36 +1782,33 @@ GetHelper::DoDatabaseWork(mozIStorageCon
   NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
   // Search for it!
   PRBool hasResult;
   rv = stmt->ExecuteStep(&hasResult);
   NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
   if (hasResult) {
-    // Set the value based on results.
-    rv = stmt->GetString(0, mValue);
-    NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
-  }
-  else {
-    mValue.SetIsVoid(PR_TRUE);
+    rv = IDBObjectStore::GetStructuredCloneDataFromStatement(stmt, 0,
+                                                             mCloneBuffer);
+    NS_ENSURE_SUCCESS(rv, rv);
   }
 
   return NS_OK;
 }
 
 nsresult
 GetHelper::OnSuccess(nsIDOMEventTarget* aTarget)
 {
-  if (mValue.IsVoid()) {
+  if (!mCloneBuffer.data()) {
     // Default is to have an undefined result.
     return AsyncConnectionHelper::OnSuccess(aTarget);
   }
 
-  nsRefPtr<GetSuccessEvent> event(new GetSuccessEvent(mValue));
+  nsRefPtr<GetSuccessEvent> event(new GetSuccessEvent(mCloneBuffer));
   nsresult rv = event->Init(mRequest, mTransaction);
   NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
   PRBool dummy;
   aTarget->DispatchEvent(static_cast<nsDOMEvent*>(event), &dummy);
   return NS_OK;
 }
 
@@ -1927,27 +2007,19 @@ OpenCursorHelper::DoDatabaseWork(mozISto
   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!");
   }
 
-#ifdef DEBUG
-  {
-    PRInt32 valueType;
-    NS_ASSERTION(NS_SUCCEEDED(stmt->GetTypeOfIndex(1, &valueType)) &&
-                 valueType == mozIStorageStatement::VALUE_TYPE_TEXT,
-                 "Bad value type!");
-  }
-#endif
-
-  rv = stmt->GetString(1, mValue);
-  NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
+  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;
 
   NS_NAMED_LITERAL_CSTRING(currentKey, "current_key");
   NS_NAMED_LITERAL_CSTRING(rangeKey, "range_key");
 
@@ -2006,17 +2078,17 @@ OpenCursorHelper::GetSuccessResult(nsIWr
   if (mKey.IsUnset()) {
     aResult->SetAsEmpty();
     return NS_OK;
   }
 
   nsRefPtr<IDBCursor> cursor =
     IDBCursor::Create(mRequest, mTransaction, mObjectStore, mDirection,
                       mRangeKey, mContinueQuery, mContinueToQuery, mKey,
-                      mValue);
+                      mCloneBuffer);
   NS_ENSURE_TRUE(cursor, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
   aResult->SetAsISupports(cursor);
   return NS_OK;
 }
 
 nsresult
 CreateIndexHelper::DoDatabaseWork(mozIStorageConnection* aConnection)
@@ -2122,24 +2194,26 @@ CreateIndexHelper::InsertDataFromObjectS
       rv = stmt->GetString(2, key);
       NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
       rv = insertStmt->BindStringByName(NS_LITERAL_CSTRING("object_data_key"),
                                         key);
       NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
     }
 
-    nsString json;
-    rv = stmt->GetString(1, json);
+    const PRUint8* data;
+    PRUint32 dataLength;
+    rv = stmt->GetSharedBlob(1, &dataLength, &data);
     NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
     Key key;
     JSContext* cx = nsnull;
-    rv = IDBObjectStore::GetKeyPathValueFromJSON(json, mIndex->KeyPath(), &cx,
-                                                 key);
+    rv = IDBObjectStore::GetKeyPathValueFromStructuredData(data, dataLength,
+                                                           mIndex->KeyPath(),
+                                                           &cx, key);
     NS_ENSURE_SUCCESS(rv, rv);
 
     NS_NAMED_LITERAL_CSTRING(value, "value");
 
     if (key.IsUnset()) {
       continue;
     }
 
@@ -2262,17 +2336,17 @@ GetAllHelper::DoDatabaseWork(mozIStorage
     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 (!mValues.SetCapacity(50)) {
+  if (!mCloneBuffers.SetCapacity(50)) {
     NS_ERROR("Out of memory!");
     return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
   }
 
   nsCOMPtr<mozIStorageStatement> stmt = mTransaction->GetCachedStatement(query);
   NS_ENSURE_TRUE(stmt, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
   mozStorageStatementScoper scoper(stmt);
@@ -2303,42 +2377,41 @@ GetAllHelper::DoDatabaseWork(mozIStorage
     else {
       NS_NOTREACHED("Bad key!");
     }
     NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
   }
 
   PRBool hasResult;
   while (NS_SUCCEEDED((rv = stmt->ExecuteStep(&hasResult))) && hasResult) {
-    if (mValues.Capacity() == mValues.Length()) {
-      if (!mValues.SetCapacity(mValues.Capacity() * 2)) {
+    if (mCloneBuffers.Capacity() == mCloneBuffers.Length()) {
+      if (!mCloneBuffers.SetCapacity(mCloneBuffers.Capacity() * 2)) {
         NS_ERROR("Out of memory!");
         return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
       }
     }
 
-    nsString* value = mValues.AppendElement();
-    NS_ASSERTION(value, "Shouldn't fail if SetCapacity succeeded!");
-
-    rv = stmt->GetString(0, *value);
-    NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
+    JSAutoStructuredCloneBuffer* buffer = mCloneBuffers.AppendElement();
+    NS_ASSERTION(buffer, "Shouldn't fail if SetCapacity succeeded!");
+
+    rv = IDBObjectStore::GetStructuredCloneDataFromStatement(stmt, 0, *buffer);
+    NS_ENSURE_SUCCESS(rv, rv);
   }
   NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
   return NS_OK;
 }
 
 nsresult
 GetAllHelper::OnSuccess(nsIDOMEventTarget* aTarget)
 {
-  NS_ASSERTION(mValues.Length() <= mLimit, "Too many results!");
-
-  nsRefPtr<GetAllSuccessEvent> event(new GetAllSuccessEvent(mValues));
-
-  NS_ASSERTION(mValues.IsEmpty(), "Should have swapped!");
+  NS_ASSERTION(mCloneBuffers.Length() <= mLimit, "Too many results!");
+
+  nsRefPtr<GetAllSuccessEvent> event = new GetAllSuccessEvent(mCloneBuffers);
+  NS_ASSERTION(mCloneBuffers.IsEmpty(), "Should have swapped!");
 
   nsresult rv = event->Init(mRequest, mTransaction);
   NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
   PRBool dummy;
   aTarget->DispatchEvent(static_cast<nsDOMEvent*>(event), &dummy);
   return NS_OK;
 }
--- a/dom/indexedDB/IDBObjectStore.h
+++ b/dom/indexedDB/IDBObjectStore.h
@@ -81,36 +81,45 @@ public:
                   Key& aKey);
 
   static nsresult
   GetJSValFromKey(const Key& aKey,
                   JSContext* aCx,
                   jsval* aKeyVal);
 
   static nsresult
-  GetKeyPathValueFromJSON(const nsAString& aJSON,
-                          const nsAString& aKeyPath,
-                          JSContext** aCx,
-                          Key& aValue);
+  GetKeyPathValueFromStructuredData(const PRUint8* aData,
+                                    PRUint32 aDataLength,
+                                    const nsAString& aKeyPath,
+                                    JSContext** aCx,
+                                    Key& aValue);
 
   static nsresult
   GetIndexUpdateInfo(ObjectStoreInfo* aObjectStoreInfo,
                      JSContext* aCx,
                      jsval aObject,
                      nsTArray<IndexUpdateInfo>& aUpdateInfoArray);
 
   static nsresult
   UpdateIndexes(IDBTransaction* aTransaction,
                 PRInt64 aObjectStoreId,
                 const Key& aObjectStoreKey,
                 bool aAutoIncrement,
                 bool aOverwrite,
                 PRInt64 aObjectDataId,
                 const nsTArray<IndexUpdateInfo>& aUpdateInfoArray);
 
+  static nsresult
+  GetStructuredCloneDataFromStatement(mozIStorageStatement* aStatement,
+                                      PRUint32 aIndex,
+                                      JSAutoStructuredCloneBuffer& aBuffer);
+
+  static void
+  ClearStructuredCloneBuffer(JSAutoStructuredCloneBuffer& aBuffer);
+
   const nsString& Name() const
   {
     return mName;
   }
 
   bool IsAutoIncrement() const
   {
     return mAutoIncrement;
@@ -139,17 +148,17 @@ public:
 
 protected:
   IDBObjectStore();
   ~IDBObjectStore();
 
   nsresult GetAddInfo(JSContext* aCx,
                       jsval aValue,
                       jsval aKeyVal,
-                      nsString& aJSON,
+                      JSAutoStructuredCloneBuffer& aCloneBuffer,
                       Key& aKey,
                       nsTArray<IndexUpdateInfo>& aUpdateInfoArray);
 
   nsresult AddOrPut(const jsval& aValue,
                     const jsval& aKey,
                     JSContext* aCx,
                     PRUint8 aOptionalArgCount,
                     nsIIDBRequest** _retval,
@@ -161,16 +170,17 @@ private:
   nsCOMPtr<nsIScriptContext> mScriptContext;
   nsCOMPtr<nsPIDOMWindow> mOwner;
 
   PRInt64 mId;
   nsString mName;
   nsString mKeyPath;
   PRBool mAutoIncrement;
   PRUint32 mDatabaseId;
+  PRUint32 mStructuredCloneVersion;
 
   nsTArray<nsRefPtr<IDBIndex> > mCreatedIndexes;
 
 };
 
 END_INDEXEDDB_NAMESPACE
 
 #endif // mozilla_dom_indexeddb_idbobjectstore_h__
--- a/dom/src/threads/nsDOMWorker.cpp
+++ b/dom/src/threads/nsDOMWorker.cpp
@@ -1593,19 +1593,19 @@ nsDOMWorker::PostMessageInternal(PRBool 
   NS_ENSURE_SUCCESS(rv, rv);
 
   JSContext* cx;
   rv = cc->GetJSContext(&cx);
   NS_ENSURE_SUCCESS(rv, rv);
 
   JSAutoRequest ar(cx);
 
-  JSAutoStructuredCloneBuffer buffer(cx);
+  JSAutoStructuredCloneBuffer buffer;
 
-  if (!buffer.write(argv[0])) {
+  if (!buffer.write(cx, argv[0])) {
     return NS_ERROR_DOM_DATA_CLONE_ERR;
   }
 
   nsRefPtr<nsDOMWorkerMessageEvent> message = new nsDOMWorkerMessageEvent();
   NS_ENSURE_TRUE(message, NS_ERROR_OUT_OF_MEMORY);
 
   rv = message->InitMessageEvent(NS_LITERAL_STRING("message"), PR_FALSE,
                                  PR_FALSE, EmptyString(), EmptyString(),
--- a/dom/src/threads/nsDOMWorkerEvents.cpp
+++ b/dom/src/threads/nsDOMWorkerEvents.cpp
@@ -305,18 +305,18 @@ nsDOMWorkerMessageEvent::GetData(nsAStri
   NS_ENSURE_TRUE(cc, NS_ERROR_UNEXPECTED);
 
   if (mData) {
     JSContext* cx;
     rv = cc->GetJSContext(&cx);
     NS_ENSURE_SUCCESS(rv, rv);
 
     JSAutoRequest ar(cx);
-    JSAutoStructuredCloneBuffer buffer(cx);
-    buffer.adopt(mData, mDataLen);
+    JSAutoStructuredCloneBuffer buffer;
+    buffer.adopt(cx, mData, mDataLen);
     mData = nsnull;
     mDataLen = 0;
 
     if (!buffer.read(mDataVal.ToJSValPtr())) {
       NS_WARNING("Failed to deserialize!");
       return NS_ERROR_FAILURE;
     }
   }
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -5494,18 +5494,18 @@ JS_PUBLIC_API(JSBool)
 JS_WriteStructuredClone(JSContext *cx, jsval v, uint64 **bufp, size_t *nbytesp)
 {
     return WriteStructuredClone(cx, Valueify(v), (uint64_t **) bufp, nbytesp);
 }
 
 JS_PUBLIC_API(JSBool)
 JS_StructuredClone(JSContext *cx, jsval v, jsval *vp)
 {
-    JSAutoStructuredCloneBuffer buf(cx);
-    return buf.write(v) && buf.read(vp);
+    JSAutoStructuredCloneBuffer buf;
+    return buf.write(cx, v) && buf.read(vp);
 }
 
 JS_PUBLIC_API(void)
 JS_SetStructuredCloneCallbacks(JSRuntime *rt, const JSStructuredCloneCallbacks *callbacks)
 {
     rt->structuredCloneCallbacks = callbacks;
 }
 
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -3215,74 +3215,120 @@ JS_PUBLIC_API(JSBool)
 JS_WriteStructuredClone(JSContext *cx, jsval v, uint64 **datap, size_t *nbytesp);
 
 JS_PUBLIC_API(JSBool)
 JS_StructuredClone(JSContext *cx, jsval v, jsval *vp);
 
 #ifdef __cplusplus
 /* RAII sugar for JS_WriteStructuredClone. */
 class JSAutoStructuredCloneBuffer {
-    JSContext *cx;
+    JSContext *cx_;
     uint64 *data_;
     size_t nbytes_;
     uint32 version_;
 
   public:
-    explicit JSAutoStructuredCloneBuffer(JSContext *cx)
-        : cx(cx), data_(NULL), nbytes_(0), version_(JS_STRUCTURED_CLONE_VERSION) {}
+    JSAutoStructuredCloneBuffer()
+        : cx_(NULL), data_(NULL), nbytes_(0), version_(JS_STRUCTURED_CLONE_VERSION) {}
 
     ~JSAutoStructuredCloneBuffer() { clear(); }
 
+    JSContext *cx() const { return cx_; }
     uint64 *data() const { return data_; }
     size_t nbytes() const { return nbytes_; }
 
-    void clear() {
+    void clear(JSContext *cx=NULL) {
         if (data_) {
+            if (!cx)
+                cx = cx_;
+            JS_ASSERT(cx);
             JS_free(cx, data_);
+            cx_ = NULL;
             data_ = NULL;
             nbytes_ = 0;
+            version_ = 0;
         }
     }
 
     /*
      * Adopt some memory. It will be automatically freed by the destructor.
      * data must have been allocated using JS_malloc.
      */
-    void adopt(uint64 *data, size_t nbytes, uint32 version=JS_STRUCTURED_CLONE_VERSION) {
-        clear();
+    void adopt(JSContext *cx, uint64 *data, size_t nbytes,
+               uint32 version=JS_STRUCTURED_CLONE_VERSION) {
+        clear(cx);
+        cx_ = cx;
         data_ = data;
         nbytes_ = nbytes;
         version_ = version;
     }
 
     /*
      * Remove the buffer so that it will not be automatically freed.
      * After this, the caller is responsible for calling JS_free(*datap).
      */
-    void steal(uint64 **datap, size_t *nbytesp) {
+    void steal(uint64 **datap, size_t *nbytesp, JSContext **cxp=NULL,
+               uint32 *versionp=NULL) {
         *datap = data_;
         *nbytesp = nbytes_;
+        if (cxp)
+            *cxp = cx_;
+        if (versionp)
+            *versionp = version_;
+
+        cx_ = NULL;
         data_ = NULL;
         nbytes_ = 0;
+        version_ = 0;
     }
 
-    bool read(jsval *vp) const {
+    bool read(jsval *vp, JSContext *cx=NULL) const {
+        if (!cx)
+            cx = cx_;
+        JS_ASSERT(cx);
         JS_ASSERT(data_);
         return !!JS_ReadStructuredClone(cx, data_, nbytes_, version_, vp);
     }
 
-    bool write(jsval v) {
-        clear();
+    bool write(JSContext *cx, jsval v) {
+        clear(cx);
+        cx_ = cx;
         bool ok = !!JS_WriteStructuredClone(cx, v, &data_, &nbytes_);
         if (!ok) {
             data_ = NULL;
             nbytes_ = 0;
+            version_ = JS_STRUCTURED_CLONE_VERSION;
         }
         return ok;
     }
+
+    /**
+     * Swap ownership with another JSAutoStructuredCloneBuffer.
+     */
+    void swap(JSAutoStructuredCloneBuffer &other) {
+        JSContext *cx = other.cx_;
+        uint64 *data = other.data_;
+        size_t nbytes = other.nbytes_;
+        uint32 version = other.version_;
+
+        other.cx_ = this->cx_;
+        other.data_ = this->data_;
+        other.nbytes_ = this->nbytes_;
+        other.version_ = this->version_;
+
+        this->cx_ = cx;
+        this->data_ = data;
+        this->nbytes_ = nbytes;
+        this->version_ = version;
+    }
+
+  private:
+    /* Copy and assignment are not supported. */
+    JSAutoStructuredCloneBuffer(const JSAutoStructuredCloneBuffer &other);
+    JSAutoStructuredCloneBuffer &operator=(const JSAutoStructuredCloneBuffer &other);
 };
 #endif
 
 /* API for implementing custom serialization behavior (for ImageData, File, etc.) */
 
 /* The range of tag values the application may use for its own custom object types. */
 #define JS_SCTAG_USER_MIN  ((uint32) 0xFFFF8000)
 #define JS_SCTAG_USER_MAX  ((uint32) 0xFFFFFFFF)