Bug 1422335 - Using a sandbox on main-thread when deserializing data for IDB - part 1 - Deserialize on main-thread using a sandbox, r=asuth
authorAndrea Marchesini <amarchesini@mozilla.com>
Thu, 18 Jan 2018 13:19:06 +0100
changeset 399863 265ec11162b8e606bcd47dd7861ecd3230bd565c
parent 399862 4f42c8b2e10caa1295c3bb5029e86e8261e0662d
child 399864 6987e044649fbcc4746a4d5173e175cdd892ed92
push id33279
push useraciure@mozilla.com
push dateThu, 18 Jan 2018 21:53:37 +0000
treeherdermozilla-central@cffb3cd9dbb1 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersasuth
bugs1422335
milestone59.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1422335 - Using a sandbox on main-thread when deserializing data for IDB - part 1 - Deserialize on main-thread using a sandbox, r=asuth
dom/indexedDB/ActorsParent.cpp
dom/indexedDB/FileInfo.cpp
dom/indexedDB/FileInfo.h
dom/indexedDB/IDBObjectStore.cpp
dom/indexedDB/IDBObjectStore.h
--- a/dom/indexedDB/ActorsParent.cpp
+++ b/dom/indexedDB/ActorsParent.cpp
@@ -8023,24 +8023,18 @@ private:
   DoDatabaseWork(DatabaseConnection* aConnection) override;
 };
 
 class CreateIndexOp final
   : public VersionChangeTransactionOp
 {
   friend class VersionChangeTransaction;
 
-  class ThreadLocalJSContext;
   class UpdateIndexDataValuesFunction;
 
-  static const unsigned int kBadThreadLocalIndex =
-    static_cast<unsigned int>(-1);
-
-  static unsigned int sThreadLocalIndex;
-
   const IndexMetadata mMetadata;
   Maybe<UniqueIndexTable> mMaybeUniqueIndexTable;
   RefPtr<FileManager> mFileManager;
   const nsCString mDatabaseId;
   const uint64_t mObjectStoreId;
 
 private:
   // Only created by VersionChangeTransaction.
@@ -8105,57 +8099,31 @@ protected:
       JS_DestroyContext(mContext);
     }
   }
 
   bool
   Init();
 };
 
-class CreateIndexOp::ThreadLocalJSContext final
-  : public NormalJSContext
-{
-  friend class CreateIndexOp;
-  friend class nsAutoPtr<ThreadLocalJSContext>;
-
-public:
-  static ThreadLocalJSContext*
-  GetOrCreate();
-
-private:
-  ThreadLocalJSContext()
-  {
-    MOZ_COUNT_CTOR(CreateIndexOp::ThreadLocalJSContext);
-  }
-
-  ~ThreadLocalJSContext()
-  {
-    MOZ_COUNT_DTOR(CreateIndexOp::ThreadLocalJSContext);
-  }
-};
-
 class CreateIndexOp::UpdateIndexDataValuesFunction final
   : public mozIStorageFunction
 {
   RefPtr<CreateIndexOp> mOp;
   RefPtr<DatabaseConnection> mConnection;
-  JSContext* mCx;
 
 public:
   UpdateIndexDataValuesFunction(CreateIndexOp* aOp,
-                                DatabaseConnection* aConnection,
-                                JSContext* aCx)
+                                DatabaseConnection* aConnection)
     : mOp(aOp)
     , mConnection(aConnection)
-    , mCx(aCx)
   {
     MOZ_ASSERT(aOp);
     MOZ_ASSERT(aConnection);
     aConnection->AssertIsOnConnectionThread();
-    MOZ_ASSERT(aCx);
   }
 
   NS_DECL_ISUPPORTS
 
 private:
   ~UpdateIndexDataValuesFunction() = default;
 
   NS_DECL_MOZISTORAGEFUNCTION
@@ -10149,34 +10117,16 @@ SerializeStructuredCloneFiles(
       default:
         MOZ_CRASH("Should never get here!");
     }
   }
 
   return NS_OK;
 }
 
-already_AddRefed<nsIFile>
-GetFileForFileInfo(FileInfo* aFileInfo)
-{
-  FileManager* fileManager = aFileInfo->Manager();
-  nsCOMPtr<nsIFile> directory = fileManager->GetDirectory();
-  if (NS_WARN_IF(!directory)) {
-    return nullptr;
-  }
-
-  nsCOMPtr<nsIFile> file = fileManager->GetFileForId(directory,
-                                                     aFileInfo->Id());
-  if (NS_WARN_IF(!file)) {
-    return nullptr;
-  }
-
-  return file.forget();
-}
-
 /*******************************************************************************
  * Globals
  ******************************************************************************/
 
 // Counts the number of "live" Factory, FactoryOp and Database instances.
 uint64_t gBusyCount = 0;
 
 typedef nsTArray<RefPtr<FactoryOp>> FactoryOpArray;
@@ -19875,17 +19825,17 @@ DatabaseOperationBase::GetStructuredClon
     MOZ_ASSERT(false, "Bad index value!");
     return NS_ERROR_UNEXPECTED;
   }
 
   StructuredCloneFile& file = aInfo->mFiles[index];
   MOZ_ASSERT(file.mFileInfo);
   MOZ_ASSERT(file.mType == StructuredCloneFile::eStructuredClone);
 
-  nsCOMPtr<nsIFile> nativeFile = GetFileForFileInfo(file.mFileInfo);
+  nsCOMPtr<nsIFile> nativeFile = FileInfo::GetFileForFileInfo(file.mFileInfo);
   if (NS_WARN_IF(!nativeFile)) {
     return NS_ERROR_FAILURE;
   }
 
   nsCOMPtr<nsIInputStream> fileInputStream;
   rv = NS_NewLocalFileInputStream(getter_AddRefs(fileInputStream), nativeFile);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
@@ -24238,17 +24188,17 @@ CreateFileOp::CreateFileOp(Database* aDa
   , mParams(aParams.get_CreateFileParams())
 {
   MOZ_ASSERT(aParams.type() == DatabaseRequestParams::TCreateFileParams);
 }
 
 nsresult
 CreateFileOp::CreateMutableFile(MutableFile** aMutableFile)
 {
-  nsCOMPtr<nsIFile> file = GetFileForFileInfo(mFileInfo);
+  nsCOMPtr<nsIFile> file = FileInfo::GetFileForFileInfo(mFileInfo);
   if (NS_WARN_IF(!file)) {
     IDB_REPORT_INTERNAL_ERR();
     return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
   }
 
   RefPtr<MutableFile> mutableFile =
     MutableFile::Create(file, mDatabase, mFileInfo);
   if (NS_WARN_IF(!mutableFile)) {
@@ -24825,44 +24775,32 @@ CreateIndexOp::CreateIndexOp(VersionChan
   , mObjectStoreId(aObjectStoreId)
 {
   MOZ_ASSERT(aObjectStoreId);
   MOZ_ASSERT(aMetadata.id());
   MOZ_ASSERT(mFileManager);
   MOZ_ASSERT(!mDatabaseId.IsEmpty());
 }
 
-unsigned int CreateIndexOp::sThreadLocalIndex = kBadThreadLocalIndex;
-
 nsresult
 CreateIndexOp::InsertDataFromObjectStore(DatabaseConnection* aConnection)
 {
   MOZ_ASSERT(aConnection);
   aConnection->AssertIsOnConnectionThread();
   MOZ_ASSERT(!IndexedDatabaseManager::InLowDiskSpaceMode());
   MOZ_ASSERT(mMaybeUniqueIndexTable);
 
   AUTO_PROFILER_LABEL("CreateIndexOp::InsertDataFromObjectStore", STORAGE);
 
   nsCOMPtr<mozIStorageConnection> storageConnection =
     aConnection->GetStorageConnection();
   MOZ_ASSERT(storageConnection);
 
-  ThreadLocalJSContext* context = ThreadLocalJSContext::GetOrCreate();
-  if (NS_WARN_IF(!context)) {
-    IDB_REPORT_INTERNAL_ERR();
-    return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
-  }
-
-  JSContext* cx = context->Context();
-  JSAutoRequest ar(cx);
-  JSAutoCompartment ac(cx, context->Global());
-
   RefPtr<UpdateIndexDataValuesFunction> updateFunction =
-    new UpdateIndexDataValuesFunction(this, aConnection, cx);
+    new UpdateIndexDataValuesFunction(this, aConnection);
 
   NS_NAMED_LITERAL_CSTRING(updateFunctionName, "update_index_data_values");
 
   nsresult rv =
     storageConnection->CreateFunction(updateFunctionName,
                                       4,
                                       updateFunction);
   if (NS_WARN_IF(NS_FAILED(rv))) {
@@ -24918,35 +24856,16 @@ CreateIndexOp::InsertDataFromObjectStore
 }
 
 bool
 CreateIndexOp::Init(TransactionBase* aTransaction)
 {
   AssertIsOnBackgroundThread();
   MOZ_ASSERT(aTransaction);
 
-  struct MOZ_STACK_CLASS Helper final
-  {
-    static void
-    Destroy(void* aThreadLocal)
-    {
-      delete static_cast<ThreadLocalJSContext*>(aThreadLocal);
-    }
-  };
-
-  if (sThreadLocalIndex == kBadThreadLocalIndex) {
-    if (NS_WARN_IF(PR_SUCCESS !=
-                     PR_NewThreadPrivateIndex(&sThreadLocalIndex,
-                                              &Helper::Destroy))) {
-      return false;
-    }
-  }
-
-  MOZ_ASSERT(sThreadLocalIndex != kBadThreadLocalIndex);
-
   nsresult rv =
     GetUniqueIndexTableForObjectStore(aTransaction,
                                       mObjectStoreId,
                                       mMaybeUniqueIndexTable);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return false;
   }
 
@@ -25149,58 +25068,29 @@ NormalJSContext::Create()
 
   if (NS_WARN_IF(!newContext->Init())) {
     return nullptr;
   }
 
   return newContext.forget();
 }
 
-// static
-auto
-CreateIndexOp::
-ThreadLocalJSContext::GetOrCreate() -> ThreadLocalJSContext*
-{
-  MOZ_ASSERT(!IsOnBackgroundThread());
-  MOZ_ASSERT(CreateIndexOp::kBadThreadLocalIndex !=
-             CreateIndexOp::sThreadLocalIndex);
-
-  auto* context = static_cast<ThreadLocalJSContext*>(
-    PR_GetThreadPrivate(CreateIndexOp::sThreadLocalIndex));
-  if (context) {
-    return context;
-  }
-
-  nsAutoPtr<ThreadLocalJSContext> newContext(new ThreadLocalJSContext());
-
-  if (NS_WARN_IF(!newContext->Init())) {
-    return nullptr;
-  }
-
-  DebugOnly<PRStatus> status =
-    PR_SetThreadPrivate(CreateIndexOp::sThreadLocalIndex, newContext);
-  MOZ_ASSERT(status == PR_SUCCESS);
-
-  return newContext.forget();
-}
-
 NS_IMPL_ISUPPORTS(CreateIndexOp::UpdateIndexDataValuesFunction,
                   mozIStorageFunction);
 
 NS_IMETHODIMP
 CreateIndexOp::
 UpdateIndexDataValuesFunction::OnFunctionCall(mozIStorageValueArray* aValues,
                                               nsIVariant** _retval)
 {
   MOZ_ASSERT(aValues);
   MOZ_ASSERT(_retval);
   MOZ_ASSERT(mConnection);
   mConnection->AssertIsOnConnectionThread();
   MOZ_ASSERT(mOp);
-  MOZ_ASSERT(mCx);
 
   AUTO_PROFILER_LABEL(
     "CreateIndexOp::UpdateIndexDataValuesFunction::OnFunctionCall", STORAGE);
 
 #ifdef DEBUG
   {
     uint32_t argCount;
     MOZ_ALWAYS_SUCCEEDS(aValues->GetNumEntries(&argCount));
@@ -25230,35 +25120,28 @@ UpdateIndexDataValuesFunction::OnFunctio
                                              /* aDataIndex */ 3,
                                              /* aFileIdsIndex */ 2,
                                              mOp->mFileManager,
                                              &cloneInfo);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
-  JS::Rooted<JS::Value> clone(mCx);
-  if (NS_WARN_IF(!IDBObjectStore::DeserializeIndexValue(mCx,
-                                                        cloneInfo,
-                                                        &clone))) {
-    return NS_ERROR_DOM_DATA_CLONE_ERR;
-  }
-
   const IndexMetadata& metadata = mOp->mMetadata;
   const int64_t& objectStoreId = mOp->mObjectStoreId;
 
   AutoTArray<IndexUpdateInfo, 32> updateInfos;
-  rv = IDBObjectStore::AppendIndexUpdateInfo(metadata.id(),
-                                             metadata.keyPath(),
-                                             metadata.unique(),
-                                             metadata.multiEntry(),
-                                             metadata.locale(),
-                                             mCx,
-                                             clone,
-                                             updateInfos);
+  rv = IDBObjectStore::DeserializeIndexValueToUpdateInfos(
+         metadata.id(),
+         metadata.keyPath(),
+         metadata.unique(),
+         metadata.multiEntry(),
+         metadata.locale(),
+         cloneInfo,
+         updateInfos);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   if (updateInfos.IsEmpty()) {
     // XXX See if we can do this without copying...
 
     nsCOMPtr<nsIVariant> unmodifiedValue;
--- a/dom/indexedDB/FileInfo.cpp
+++ b/dom/indexedDB/FileInfo.cpp
@@ -251,11 +251,29 @@ CleanupFileRunnable::Run()
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   DoCleanup(mFileManager, mFileId);
 
   return NS_OK;
 }
 
+/* static */ already_AddRefed<nsIFile>
+FileInfo::GetFileForFileInfo(FileInfo* aFileInfo)
+{
+  FileManager* fileManager = aFileInfo->Manager();
+  nsCOMPtr<nsIFile> directory = fileManager->GetDirectory();
+  if (NS_WARN_IF(!directory)) {
+    return nullptr;
+  }
+
+  nsCOMPtr<nsIFile> file = fileManager->GetFileForId(directory,
+                                                     aFileInfo->Id());
+  if (NS_WARN_IF(!file)) {
+    return nullptr;
+  }
+
+  return file.forget();
+}
+
 } // namespace indexedDB
 } // namespace dom
 } // namespace mozilla
--- a/dom/indexedDB/FileInfo.h
+++ b/dom/indexedDB/FileInfo.h
@@ -64,16 +64,19 @@ public:
   Manager() const
   {
     return mFileManager;
   }
 
   virtual int64_t
   Id() const = 0;
 
+  static already_AddRefed<nsIFile>
+  GetFileForFileInfo(FileInfo* aFileInfo);
+
 protected:
   virtual ~FileInfo();
 
 private:
   void
   UpdateReferences(ThreadSafeAutoRefCnt& aRefCount,
                    int32_t aDelta,
                    CustomCleanupCallback* aCustomCleanupCallback = nullptr);
--- a/dom/indexedDB/IDBObjectStore.cpp
+++ b/dom/indexedDB/IDBObjectStore.cpp
@@ -18,34 +18,39 @@
 #include "IDBTransaction.h"
 #include "IndexedDatabase.h"
 #include "IndexedDatabaseInlines.h"
 #include "IndexedDatabaseManager.h"
 #include "js/Class.h"
 #include "js/Date.h"
 #include "js/StructuredClone.h"
 #include "KeyPath.h"
+#include "NullPrincipal.h"
+#include "mozilla/ClearOnShutdown.h"
 #include "mozilla/EndianUtils.h"
 #include "mozilla/ErrorResult.h"
+#include "mozilla/JSObjectHolder.h"
 #include "mozilla/Move.h"
 #include "mozilla/dom/BindingUtils.h"
 #include "mozilla/dom/ContentChild.h"
 #include "mozilla/dom/ContentParent.h"
 #include "mozilla/dom/DOMStringList.h"
 #include "mozilla/dom/File.h"
+#include "mozilla/dom/FileBlobImpl.h"
 #include "mozilla/dom/IDBMutableFileBinding.h"
 #include "mozilla/dom/BlobBinding.h"
 #include "mozilla/dom/IDBObjectStoreBinding.h"
 #include "mozilla/dom/MemoryBlobImpl.h"
 #include "mozilla/dom/StreamBlobImpl.h"
 #include "mozilla/dom/StructuredCloneHolder.h"
 #include "mozilla/dom/StructuredCloneTags.h"
 #include "mozilla/dom/indexedDB/PBackgroundIDBSharedTypes.h"
 #include "mozilla/ipc/BackgroundChild.h"
 #include "mozilla/ipc/PBackgroundSharedTypes.h"
+#include "mozilla/SystemGroup.h"
 #include "nsCOMPtr.h"
 #include "nsQueryObject.h"
 #include "nsStreamUtils.h"
 #include "nsStringStream.h"
 #include "ProfilerHelpers.h"
 #include "ReportInternalError.h"
 #include "WorkerPrivate.h"
 #include "WorkerScope.h"
@@ -895,17 +900,36 @@ public:
                           const BlobOrFileData& aData,
                           JS::MutableHandle<JSObject*> aResult)
   {
     MOZ_ASSERT(aCx);
     MOZ_ASSERT(aData.tag == SCTAG_DOM_FILE ||
                aData.tag == SCTAG_DOM_FILE_WITHOUT_LASTMODIFIEDDATE ||
                aData.tag == SCTAG_DOM_BLOB);
     MOZ_ASSERT(aFile.mType == StructuredCloneFile::eBlob);
-    MOZ_ASSERT(aFile.mBlob);
+
+    RefPtr<Blob> blob = aFile.mBlob;
+
+    /* If we are creating an index, we do not have an mBlob but do have an
+     * mInfo.  Unlike other index or upgrade cases, we do need a real-looking
+     * Blob/File instance because the index's key path can reference their
+     * properties.  Rather than create a fake-looking object, create a real
+     * Blob. */
+    if (!blob) {
+      MOZ_ASSERT(aFile.mFileInfo);
+      nsCOMPtr<nsIFile> file = FileInfo::GetFileForFileInfo(aFile.mFileInfo);
+      if (!file) {
+        return false;
+      }
+
+
+      RefPtr<FileBlobImpl> impl = new FileBlobImpl(file);
+      impl->SetFileId(aFile.mFileInfo->Id());
+      blob = File::Create(nullptr, impl);
+    }
 
     // It can happen that this IDB is chrome code, so there is no parent, but
     // still we want to set a correct parent for the new File object.
     nsCOMPtr<nsISupports> parent;
     if (NS_IsMainThread()) {
       if (aDatabase && aDatabase->GetParentObject()) {
         parent = aDatabase->GetParentObject();
       } else {
@@ -919,45 +943,45 @@ public:
       MOZ_ASSERT(globalScope);
 
       parent = do_QueryObject(globalScope);
     }
 
     MOZ_ASSERT(parent);
 
     if (aData.tag == SCTAG_DOM_BLOB) {
-      aFile.mBlob->Impl()->SetLazyData(
+      blob->Impl()->SetLazyData(
         VoidString(), aData.type, aData.size, INT64_MAX);
-      MOZ_ASSERT(!aFile.mBlob->IsFile());
+      MOZ_ASSERT(!blob->IsFile());
 
       // ActorsParent sends here a kind of half blob and half file wrapped into
       // a DOM File object. DOM File and DOM Blob are a WebIDL wrapper around a
       // BlobImpl object. SetLazyData() has just changed the BlobImpl to be a
-      // Blob (see the previous assert), but 'aFile.mBlob' still has the WebIDL
-      // DOM File wrapping.
+      // Blob (see the previous assert), but 'blob' still has the WebIDL DOM
+      // File wrapping.
       // Before exposing it to content, we must recreate a DOM Blob object.
 
-      RefPtr<Blob> blob =
-        Blob::Create(aFile.mBlob->GetParentObject(), aFile.mBlob->Impl());
-      MOZ_ASSERT(blob);
+      RefPtr<Blob> exposedBlob =
+        Blob::Create(blob->GetParentObject(), blob->Impl());
+      MOZ_ASSERT(exposedBlob);
       JS::Rooted<JS::Value> wrappedBlob(aCx);
-      if (!ToJSValue(aCx, blob, &wrappedBlob)) {
+      if (!ToJSValue(aCx, exposedBlob, &wrappedBlob)) {
         return false;
       }
 
       aResult.set(&wrappedBlob.toObject());
       return true;
     }
 
-    aFile.mBlob->Impl()->SetLazyData(
+    blob->Impl()->SetLazyData(
       aData.name, aData.type, aData.size,
       aData.lastModifiedDate * PR_USEC_PER_MSEC);
 
-    MOZ_ASSERT(aFile.mBlob->IsFile());
-    RefPtr<File> file = aFile.mBlob->ToFile();
+    MOZ_ASSERT(blob->IsFile());
+    RefPtr<File> file = blob->ToFile();
     MOZ_ASSERT(file);
 
     JS::Rooted<JS::Value> wrappedFile(aCx);
     if (!ToJSValue(aCx, file, &wrappedFile)) {
       return false;
     }
 
     aResult.set(&wrappedFile.toObject());
@@ -980,124 +1004,16 @@ public:
       return false;
     }
 
     aResult.set(moduleObj);
     return true;
   }
 };
 
-class IndexDeserializationHelper
-{
-public:
-  static bool
-  CreateAndWrapMutableFile(JSContext* aCx,
-                           StructuredCloneFile& aFile,
-                           const MutableFileData& aData,
-                           JS::MutableHandle<JSObject*> aResult)
-  {
-    // MutableFile can't be used in index creation, so just make a dummy object.
-    JS::Rooted<JSObject*> obj(aCx, JS_NewPlainObject(aCx));
-    if (NS_WARN_IF(!obj)) {
-      return false;
-    }
-
-    aResult.set(obj);
-    return true;
-  }
-
-  static bool
-  CreateAndWrapBlobOrFile(JSContext* aCx,
-                          IDBDatabase* aDatabase,
-                          StructuredCloneFile& aFile,
-                          const BlobOrFileData& aData,
-                          JS::MutableHandle<JSObject*> aResult)
-  {
-    MOZ_ASSERT(aData.tag == SCTAG_DOM_FILE ||
-               aData.tag == SCTAG_DOM_FILE_WITHOUT_LASTMODIFIEDDATE ||
-               aData.tag == SCTAG_DOM_BLOB);
-
-    // The following properties are available for use in index creation
-    //   Blob.size
-    //   Blob.type
-    //   File.name
-    //   File.lastModifiedDate
-
-    JS::Rooted<JSObject*> obj(aCx, JS_NewPlainObject(aCx));
-    if (NS_WARN_IF(!obj)) {
-      return false;
-    }
-
-    // Technically these props go on the proto, but this detail won't change
-    // the results of index creation.
-
-    JS::Rooted<JSString*> type(aCx,
-      JS_NewUCStringCopyN(aCx, aData.type.get(), aData.type.Length()));
-    if (NS_WARN_IF(!type)) {
-      return false;
-    }
-
-    if (NS_WARN_IF(!JS_DefineProperty(aCx,
-                                      obj,
-                                      "size",
-                                      double(aData.size),
-                                      0))) {
-      return false;
-    }
-
-    if (NS_WARN_IF(!JS_DefineProperty(aCx, obj, "type", type, 0))) {
-      return false;
-    }
-
-    if (aData.tag == SCTAG_DOM_BLOB) {
-      aResult.set(obj);
-      return true;
-    }
-
-    JS::Rooted<JSString*> name(aCx,
-      JS_NewUCStringCopyN(aCx, aData.name.get(), aData.name.Length()));
-    if (NS_WARN_IF(!name)) {
-      return false;
-    }
-
-    JS::ClippedTime time = JS::TimeClip(aData.lastModifiedDate);
-    JS::Rooted<JSObject*> date(aCx, JS::NewDateObject(aCx, time));
-    if (NS_WARN_IF(!date)) {
-      return false;
-    }
-
-    if (NS_WARN_IF(!JS_DefineProperty(aCx, obj, "name", name, 0))) {
-      return false;
-    }
-
-    if (NS_WARN_IF(!JS_DefineProperty(aCx, obj, "lastModifiedDate", date, 0))) {
-      return false;
-    }
-
-    aResult.set(obj);
-    return true;
-  }
-
-  static bool
-  CreateAndWrapWasmModule(JSContext* aCx,
-                          StructuredCloneFile& aFile,
-                          const WasmModuleData& aData,
-                          JS::MutableHandle<JSObject*> aResult)
-  {
-    // Wasm module can't be used in index creation, so just make a dummy object.
-    JS::Rooted<JSObject*> obj(aCx, JS_NewPlainObject(aCx));
-    if (NS_WARN_IF(!obj)) {
-      return false;
-    }
-
-    aResult.set(obj);
-    return true;
-  }
-};
-
 // We don't need to upgrade database on B2G. See the comment in ActorsParent.cpp,
 // UpgradeSchemaFrom18_0To19_0()
 class UpgradeDeserializationHelper
 {
 public:
   static bool
   CreateAndWrapMutableFile(JSContext* aCx,
                            StructuredCloneFile& aFile,
@@ -1455,47 +1371,227 @@ IDBObjectStore::DeserializeValue(JSConte
                               JS::StructuredCloneScope::SameProcessSameThread,
                               aValue, &callbacks, &aCloneReadInfo)) {
     return false;
   }
 
   return true;
 }
 
+namespace {
+
+// This class helps to create only 1 sandbox.
+class SandboxHolder final
+{
+public:
+  NS_INLINE_DECL_REFCOUNTING(SandboxHolder)
+
+  static JSObject*
+  GetSandbox(JSContext* aCx)
+  {
+    SandboxHolder* holder = GetOrCreate();
+    return holder->GetSandboxInternal(aCx);
+  }
+
+private:
+  ~SandboxHolder() = default;
+
+  static SandboxHolder*
+  GetOrCreate()
+  {
+    MOZ_ASSERT(XRE_IsParentProcess());
+    MOZ_ASSERT(NS_IsMainThread());
+
+    static StaticRefPtr<SandboxHolder> sHolder;
+    if (!sHolder) {
+      sHolder = new SandboxHolder();
+      ClearOnShutdown(&sHolder);
+    }
+    return sHolder;
+  }
+
+  JSObject*
+  GetSandboxInternal(JSContext* aCx)
+  {
+    if (!mSandbox) {
+      nsIXPConnect* xpc = nsContentUtils::XPConnect();
+      MOZ_ASSERT(xpc, "This should never be null!");
+
+      // Let's use a null principal.
+      nsCOMPtr<nsIPrincipal> principal = NullPrincipal::Create();
+
+      JS::Rooted<JSObject*> sandbox(aCx);
+      nsresult rv = xpc->CreateSandbox(aCx, principal, sandbox.address());
+      if (NS_WARN_IF(NS_FAILED(rv))) {
+        return nullptr;
+      }
+
+      mSandbox = new JSObjectHolder(aCx, sandbox);
+    }
+
+    return mSandbox->GetJSObject();
+  }
+
+  RefPtr<JSObjectHolder> mSandbox;
+};
+
+class DeserializeIndexValueHelper final : public Runnable
+{
+public:
+  DeserializeIndexValueHelper(int64_t aIndexID,
+                              const KeyPath& aKeyPath,
+                              bool aUnique,
+                              bool aMultiEntry,
+                              const nsCString& aLocale,
+                              StructuredCloneReadInfo& aCloneReadInfo,
+                              nsTArray<IndexUpdateInfo>& aUpdateInfoArray)
+    : Runnable("DeserializeIndexValueHelper")
+    , mMonitor("DeserializeIndexValueHelper::mMonitor")
+    , mIndexID(aIndexID)
+    , mKeyPath(aKeyPath)
+    , mUnique(aUnique)
+    , mMultiEntry(aMultiEntry)
+    , mLocale(aLocale)
+    , mCloneReadInfo(aCloneReadInfo)
+    , mUpdateInfoArray(aUpdateInfoArray)
+    , mStatus(NS_ERROR_FAILURE)
+  {}
+
+  nsresult
+  DispatchAndWait()
+  {
+    // We don't need to go to the main-thread and use the sandbox. Let's create
+    // the updateInfo data here.
+    if (!mCloneReadInfo.mData.Size()) {
+      AutoJSAPI jsapi;
+      jsapi.Init();
+
+      JS::Rooted<JS::Value> value(jsapi.cx());
+      value.setUndefined();
+
+      return IDBObjectStore::AppendIndexUpdateInfo(mIndexID, mKeyPath, mUnique,
+                                                   mMultiEntry, mLocale,
+                                                   jsapi.cx(), value,
+                                                   mUpdateInfoArray);
+    }
+
+    // The operation will continue on the main-thread.
+
+    MOZ_ASSERT(!(mCloneReadInfo.mData.Size() % sizeof(uint64_t)));
+
+    MonitorAutoLock lock(mMonitor);
+
+    RefPtr<Runnable> self = this;
+    nsresult rv = SystemGroup::Dispatch(TaskCategory::Other, self.forget());
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+
+    lock.Wait();
+    return mStatus;
+  }
+
+  NS_IMETHOD
+  Run() override
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+
+    AutoJSAPI jsapi;
+    jsapi.Init();
+    JSContext* cx = jsapi.cx();
+
+    JS::Rooted<JSObject*> global(cx, SandboxHolder::GetSandbox(cx));
+    if (NS_WARN_IF(!global)) {
+      OperationCompleted(NS_ERROR_FAILURE);
+      return NS_OK;
+    }
+
+    JSAutoCompartment ac(cx, global);
+
+    JS::Rooted<JS::Value> value(cx);
+    nsresult rv = DeserializeIndexValue(cx, &value);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      OperationCompleted(rv);
+      return NS_OK;
+    }
+
+    rv = IDBObjectStore::AppendIndexUpdateInfo(mIndexID, mKeyPath, mUnique,
+                                               mMultiEntry, mLocale, cx,
+                                               value, mUpdateInfoArray);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      OperationCompleted(rv);
+      return NS_OK;
+    }
+
+    OperationCompleted(NS_OK);
+    return NS_OK;
+  }
+
+private:
+  nsresult
+  DeserializeIndexValue(JSContext* aCx, JS::MutableHandle<JS::Value> aValue)
+  {
+    static const JSStructuredCloneCallbacks callbacks = {
+      CommonStructuredCloneReadCallback<ValueDeserializationHelper>,
+      nullptr,
+      nullptr,
+      nullptr,
+      nullptr,
+      nullptr
+    };
+
+    if (!JS_ReadStructuredClone(aCx, mCloneReadInfo.mData,
+                                JS_STRUCTURED_CLONE_VERSION,
+                                JS::StructuredCloneScope::SameProcessSameThread,
+                                aValue, &callbacks, &mCloneReadInfo)) {
+      return NS_ERROR_DOM_DATA_CLONE_ERR;
+    }
+
+    return NS_OK;
+  }
+
+  void
+  OperationCompleted(nsresult aStatus)
+  {
+    mStatus = aStatus;
+
+    MonitorAutoLock lock(mMonitor);
+    lock.Notify();
+  }
+
+  Monitor mMonitor;
+
+  int64_t mIndexID;
+  const KeyPath& mKeyPath;
+  bool mUnique;
+  bool mMultiEntry;
+  const nsCString mLocale;
+  StructuredCloneReadInfo& mCloneReadInfo;
+  nsTArray<IndexUpdateInfo>& mUpdateInfoArray;
+  nsresult mStatus;
+};
+
+} // anonymous
+
 // static
-bool
-IDBObjectStore::DeserializeIndexValue(JSContext* aCx,
-                                      StructuredCloneReadInfo& aCloneReadInfo,
-                                      JS::MutableHandle<JS::Value> aValue)
+nsresult
+IDBObjectStore::DeserializeIndexValueToUpdateInfos(int64_t aIndexID,
+                                                   const KeyPath& aKeyPath,
+                                                   bool aUnique,
+                                                   bool aMultiEntry,
+                                                   const nsCString& aLocale,
+                                                   StructuredCloneReadInfo& aCloneReadInfo,
+                                                   nsTArray<IndexUpdateInfo>& aUpdateInfoArray)
 {
   MOZ_ASSERT(!NS_IsMainThread());
-  MOZ_ASSERT(aCx);
-
-  if (!aCloneReadInfo.mData.Size()) {
-    aValue.setUndefined();
-    return true;
-  }
-
-  MOZ_ASSERT(!(aCloneReadInfo.mData.Size() % sizeof(uint64_t)));
-
-  JSAutoRequest ar(aCx);
-
-  static const JSStructuredCloneCallbacks callbacks = {
-    CommonStructuredCloneReadCallback<IndexDeserializationHelper>,
-    nullptr,
-    nullptr
-  };
-
-  if (!JS_ReadStructuredClone(aCx, aCloneReadInfo.mData, JS_STRUCTURED_CLONE_VERSION,
-                              JS::StructuredCloneScope::SameProcessSameThread,
-                              aValue, &callbacks, &aCloneReadInfo)) {
-    return false;
-  }
-
-  return true;
+
+  RefPtr<DeserializeIndexValueHelper> helper =
+    new DeserializeIndexValueHelper(aIndexID, aKeyPath, aUnique, aMultiEntry,
+                                    aLocale, aCloneReadInfo, aUpdateInfoArray);
+  return helper->DispatchAndWait();
 }
 
 // static
 bool
 IDBObjectStore::DeserializeUpgradeValue(JSContext* aCx,
                                         StructuredCloneReadInfo& aCloneReadInfo,
                                         JS::MutableHandle<JS::Value> aValue)
 {
--- a/dom/indexedDB/IDBObjectStore.h
+++ b/dom/indexedDB/IDBObjectStore.h
@@ -83,30 +83,34 @@ public:
                         const KeyPath& aKeyPath,
                         bool aUnique,
                         bool aMultiEntry,
                         const nsCString& aLocale,
                         JSContext* aCx,
                         JS::Handle<JS::Value> aObject,
                         nsTArray<IndexUpdateInfo>& aUpdateInfoArray);
 
+  static nsresult
+  DeserializeIndexValueToUpdateInfos(int64_t aIndexID,
+                                     const KeyPath& aKeyPath,
+                                     bool aUnique,
+                                     bool aMultiEntry,
+                                     const nsCString& aLocale,
+                                     StructuredCloneReadInfo& aCloneInfo,
+                                     nsTArray<IndexUpdateInfo>& aUpdateInfoArray);
+
   static void
   ClearCloneReadInfo(StructuredCloneReadInfo& aReadInfo);
 
   static bool
   DeserializeValue(JSContext* aCx,
                    StructuredCloneReadInfo& aCloneReadInfo,
                    JS::MutableHandle<JS::Value> aValue);
 
   static bool
-  DeserializeIndexValue(JSContext* aCx,
-                        StructuredCloneReadInfo& aCloneReadInfo,
-                        JS::MutableHandle<JS::Value> aValue);
-
-  static bool
   DeserializeUpgradeValue(JSContext* aCx,
                           StructuredCloneReadInfo& aCloneReadInfo,
                           JS::MutableHandle<JS::Value> aValue);
 
   static const JSClass*
   DummyPropClass()
   {
     return &sDummyPropJSClass;