Bug 1422335 - Using a sandbox on main-thread when deserializing data for IDB - part 2 - Upgrade value, r=asuth
authorAndrea Marchesini <amarchesini@mozilla.com>
Thu, 18 Jan 2018 13:19:07 +0100
changeset 454134 6987e044649fbcc4746a4d5173e175cdd892ed92
parent 454133 265ec11162b8e606bcd47dd7861ecd3230bd565c
child 454135 bf4858a603292b46437715c42fde0e495c8bad22
push id1648
push usermtabara@mozilla.com
push dateThu, 01 Mar 2018 12:45:47 +0000
treeherdermozilla-release@cbb9688c2eeb [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 2 - Upgrade value, r=asuth
dom/indexedDB/ActorsParent.cpp
dom/indexedDB/IDBObjectStore.cpp
dom/indexedDB/IDBObjectStore.h
--- a/dom/indexedDB/ActorsParent.cpp
+++ b/dom/indexedDB/ActorsParent.cpp
@@ -3643,23 +3643,20 @@ UpgradeSchemaFrom18_0To19_0(mozIStorageC
   rv = aConnection->SetSchemaVersion(MakeSchemaVersion(19, 0));
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   return NS_OK;
 }
 
-class NormalJSContext;
-
 class UpgradeFileIdsFunction final
   : public mozIStorageFunction
 {
   RefPtr<FileManager> mFileManager;
-  nsAutoPtr<NormalJSContext> mContext;
 
 public:
   UpgradeFileIdsFunction()
   {
     AssertIsOnIOThread();
   }
 
   nsresult
@@ -8052,63 +8049,16 @@ private:
 
   bool
   Init(TransactionBase* aTransaction) override;
 
   nsresult
   DoDatabaseWork(DatabaseConnection* aConnection) override;
 };
 
-class NormalJSContext
-{
-  friend class nsAutoPtr<NormalJSContext>;
-
-  static const JSClass sGlobalClass;
-  static const uint32_t kContextHeapSize = 768 * 1024;
-
-  JSContext* mContext;
-  JSObject* mGlobal;
-
-public:
-  static NormalJSContext*
-  Create();
-
-  JSContext*
-  Context() const
-  {
-    return mContext;
-  }
-
-  JSObject*
-  Global() const
-  {
-    return mGlobal;
-  }
-
-protected:
-  NormalJSContext()
-    : mContext(nullptr)
-    , mGlobal(nullptr)
-  {
-    MOZ_COUNT_CTOR(NormalJSContext);
-  }
-
-  ~NormalJSContext()
-  {
-    MOZ_COUNT_DTOR(NormalJSContext);
-
-    if (mContext) {
-      JS_DestroyContext(mContext);
-    }
-  }
-
-  bool
-  Init();
-};
-
 class CreateIndexOp::UpdateIndexDataValuesFunction final
   : public mozIStorageFunction
 {
   RefPtr<CreateIndexOp> mOp;
   RefPtr<DatabaseConnection> mConnection;
 
 public:
   UpdateIndexDataValuesFunction(CreateIndexOp* aOp,
@@ -19530,36 +19480,29 @@ UpgradeFileIdsFunction::Init(nsIFile* aF
                     EmptyString(),
                     false);
 
   nsresult rv = fileManager->Init(aFMDirectory, aConnection);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
-  nsAutoPtr<NormalJSContext> context(NormalJSContext::Create());
-  if (NS_WARN_IF(!context)) {
-    return NS_ERROR_FAILURE;
-  }
-
   mFileManager.swap(fileManager);
-  mContext = context;
   return NS_OK;
 }
 
 NS_IMPL_ISUPPORTS(UpgradeFileIdsFunction, mozIStorageFunction)
 
 NS_IMETHODIMP
 UpgradeFileIdsFunction::OnFunctionCall(mozIStorageValueArray* aArguments,
                                        nsIVariant** aResult)
 {
   MOZ_ASSERT(aArguments);
   MOZ_ASSERT(aResult);
   MOZ_ASSERT(mFileManager);
-  MOZ_ASSERT(mContext);
 
   AUTO_PROFILER_LABEL("UpgradeFileIdsFunction::OnFunctionCall", STORAGE);
 
   uint32_t argc;
   nsresult rv = aArguments->GetNumEntries(&argc);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
@@ -19571,42 +19514,22 @@ UpgradeFileIdsFunction::OnFunctionCall(m
 
   StructuredCloneReadInfo cloneInfo;
   DatabaseOperationBase::GetStructuredCloneReadInfoFromValueArray(aArguments,
                                                                   1,
                                                                   0,
                                                                   mFileManager,
                                                                   &cloneInfo);
 
-  JSContext* cx = mContext->Context();
-  JSAutoRequest ar(cx);
-  JSAutoCompartment ac(cx, mContext->Global());
-
-  JS::Rooted<JS::Value> clone(cx);
-  if (NS_WARN_IF(!IDBObjectStore::DeserializeUpgradeValue(cx, cloneInfo,
-                                                          &clone))) {
+  nsAutoString fileIds;
+  rv = IDBObjectStore::DeserializeUpgradeValueToFileIds(cloneInfo, fileIds);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
     return NS_ERROR_DOM_DATA_CLONE_ERR;
   }
 
-  nsAutoString fileIds;
-
-  for (uint32_t count = cloneInfo.mFiles.Length(), index = 0;
-       index < count;
-       index++) {
-    StructuredCloneFile& file = cloneInfo.mFiles[index];
-    MOZ_ASSERT(file.mFileInfo);
-
-    const int64_t id = file.mFileInfo->Id();
-
-    if (index) {
-      fileIds.Append(' ');
-    }
-    fileIds.AppendInt(file.mType == StructuredCloneFile::eBlob ? id : -id);
-  }
-
   nsCOMPtr<nsIVariant> result = new mozilla::storage::TextVariant(fileIds);
 
   result.forget(aResult);
   return NS_OK;
 }
 
 // static
 void
@@ -25000,84 +24923,16 @@ CreateIndexOp::DoDatabaseWork(DatabaseCo
   rv = autoSave.Commit();
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   return NS_OK;
 }
 
-static const JSClassOps sNormalJSContextGlobalClassOps = {
-  /* addProperty */ nullptr,
-  /* delProperty */ nullptr,
-  /* enumerate */ nullptr,
-  /* newEnumerate */ nullptr,
-  /* resolve */ nullptr,
-  /* mayResolve */ nullptr,
-  /* finalize */ nullptr,
-  /* call */ nullptr,
-  /* hasInstance */ nullptr,
-  /* construct */ nullptr,
-  /* trace */ JS_GlobalObjectTraceHook
-};
-
-const JSClass NormalJSContext::sGlobalClass = {
-  "IndexedDBTransactionThreadGlobal",
-  JSCLASS_GLOBAL_FLAGS,
-  &sNormalJSContextGlobalClassOps
-};
-
-bool
-NormalJSContext::Init()
-{
-  MOZ_ASSERT(!IsOnBackgroundThread());
-
-  mContext = JS_NewContext(kContextHeapSize);
-  if (NS_WARN_IF(!mContext)) {
-    return false;
-  }
-
-  // Let everyone know that we might be able to call JS. This alerts the
-  // profiler about certain possible deadlocks.
-  NS_GetCurrentThread()->SetCanInvokeJS(true);
-
-  // Not setting this will cause JS_CHECK_RECURSION to report false positives.
-  JS_SetNativeStackQuota(mContext, 128 * sizeof(size_t) * 1024);
-
-  if (NS_WARN_IF(!JS::InitSelfHostedCode(mContext))) {
-    return false;
-  }
-
-  JSAutoRequest ar(mContext);
-
-  JS::CompartmentOptions options;
-  mGlobal = JS_NewGlobalObject(mContext, &sGlobalClass, nullptr,
-                               JS::FireOnNewGlobalHook, options);
-  if (NS_WARN_IF(!mGlobal)) {
-    return false;
-  }
-
-  return true;
-}
-
-// static
-NormalJSContext*
-NormalJSContext::Create()
-{
-  MOZ_ASSERT(!IsOnBackgroundThread());
-
-  nsAutoPtr<NormalJSContext> newContext(new NormalJSContext());
-
-  if (NS_WARN_IF(!newContext->Init())) {
-    return nullptr;
-  }
-
-  return newContext.forget();
-}
-
 NS_IMPL_ISUPPORTS(CreateIndexOp::UpdateIndexDataValuesFunction,
                   mozIStorageFunction);
 
 NS_IMETHODIMP
 CreateIndexOp::
 UpdateIndexDataValuesFunction::OnFunctionCall(mozIStorageValueArray* aValues,
                                               nsIVariant** _retval)
 {
--- a/dom/indexedDB/IDBObjectStore.cpp
+++ b/dom/indexedDB/IDBObjectStore.cpp
@@ -867,16 +867,34 @@ class ValueDeserializationHelper
 public:
   static bool
   CreateAndWrapMutableFile(JSContext* aCx,
                            StructuredCloneFile& aFile,
                            const MutableFileData& aData,
                            JS::MutableHandle<JSObject*> aResult)
   {
     MOZ_ASSERT(aCx);
+
+    // If we have eBlob, we are in an IDB SQLite schema upgrade where we don't
+    // care about a real 'MutableFile', but we just care of having a propert
+    // |mType| flag.
+    if (aFile.mType == StructuredCloneFile::eBlob) {
+      aFile.mType = StructuredCloneFile::eMutableFile;
+
+      // 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;
+    }
+
     MOZ_ASSERT(aFile.mType == StructuredCloneFile::eMutableFile);
 
     if (!aFile.mMutableFile || !NS_IsMainThread()) {
       return false;
     }
 
     if (NS_WARN_IF(!ResolveMysteryMutableFile(aFile.mMutableFile,
                                               aData.name,
@@ -1004,84 +1022,16 @@ public:
       return false;
     }
 
     aResult.set(moduleObj);
     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,
-                           const MutableFileData& aData,
-                           JS::MutableHandle<JSObject*> aResult)
-  {
-    MOZ_ASSERT(aCx);
-    MOZ_ASSERT(aFile.mType == StructuredCloneFile::eBlob);
-
-    aFile.mType = StructuredCloneFile::eMutableFile;
-
-    // Just make a dummy object. The file_ids upgrade function is only
-    // interested in the |mType| flag.
-    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(aCx);
-    MOZ_ASSERT(aFile.mType == StructuredCloneFile::eBlob);
-    MOZ_ASSERT(aData.tag == SCTAG_DOM_FILE ||
-               aData.tag == SCTAG_DOM_FILE_WITHOUT_LASTMODIFIEDDATE ||
-               aData.tag == SCTAG_DOM_BLOB);
-
-    // Just make a dummy object. The file_ids upgrade function is only interested
-    // in the |mType| flag.
-    JS::Rooted<JSObject*> obj(aCx, JS_NewPlainObject(aCx));
-
-    if (NS_WARN_IF(!obj)) {
-      return false;
-    }
-
-    aResult.set(obj);
-    return true;
-  }
-
-  static bool
-  CreateAndWrapWasmModule(JSContext* aCx,
-                          StructuredCloneFile& aFile,
-                          const WasmModuleData& aData,
-                          JS::MutableHandle<JSObject*> aResult)
-  {
-    MOZ_ASSERT(aCx);
-    MOZ_ASSERT(aFile.mType == StructuredCloneFile::eBlob);
-
-    MOZ_ASSERT(false, "This should never be possible!");
-
-    return false;
-  }
-};
-
 template <class Traits>
 JSObject*
 CommonStructuredCloneReadCallback(JSContext* aCx,
                                   JSStructuredCloneReader* aReader,
                                   uint32_t aTag,
                                   uint32_t aData,
                                   void* aClosure)
 {
@@ -1564,16 +1514,140 @@ private:
   bool mUnique;
   bool mMultiEntry;
   const nsCString mLocale;
   StructuredCloneReadInfo& mCloneReadInfo;
   nsTArray<IndexUpdateInfo>& mUpdateInfoArray;
   nsresult mStatus;
 };
 
+class DeserializeUpgradeValueHelper final : public Runnable
+{
+public:
+  explicit DeserializeUpgradeValueHelper(StructuredCloneReadInfo& aCloneReadInfo)
+    : Runnable("DeserializeUpgradeValueHelper")
+    , mMonitor("DeserializeUpgradeValueHelper::mMonitor")
+    , mCloneReadInfo(aCloneReadInfo)
+    , mStatus(NS_ERROR_FAILURE)
+  {}
+
+  nsresult
+  DispatchAndWait(nsAString& aFileIds)
+  {
+    // We don't need to go to the main-thread and use the sandbox.
+    if (!mCloneReadInfo.mData.Size()) {
+      PopulateFileIds(aFileIds);
+      return NS_OK;
+    }
+
+    // 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();
+
+    if (NS_FAILED(mStatus)) {
+      return mStatus;
+    }
+
+    PopulateFileIds(aFileIds);
+    return NS_OK;
+  }
+
+  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 = DeserializeUpgradeValue(cx, &value);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      OperationCompleted(rv);
+      return NS_OK;
+    }
+
+    OperationCompleted(NS_OK);
+    return NS_OK;
+  }
+
+private:
+  nsresult
+  DeserializeUpgradeValue(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
+  PopulateFileIds(nsAString& aFileIds)
+  {
+    for (uint32_t count = mCloneReadInfo.mFiles.Length(), index = 0;
+         index < count;
+         index++) {
+      StructuredCloneFile& file = mCloneReadInfo.mFiles[index];
+      MOZ_ASSERT(file.mFileInfo);
+
+      const int64_t id = file.mFileInfo->Id();
+
+      if (index) {
+        aFileIds.Append(' ');
+      }
+      aFileIds.AppendInt(file.mType == StructuredCloneFile::eBlob ? id : -id);
+    }
+  }
+
+  void
+  OperationCompleted(nsresult aStatus)
+  {
+    mStatus = aStatus;
+
+    MonitorAutoLock lock(mMonitor);
+    lock.Notify();
+  }
+
+  Monitor mMonitor;
+  StructuredCloneReadInfo& mCloneReadInfo;
+  nsresult mStatus;
+};
+
 } // anonymous
 
 // static
 nsresult
 IDBObjectStore::DeserializeIndexValueToUpdateInfos(int64_t aIndexID,
                                                    const KeyPath& aKeyPath,
                                                    bool aUnique,
                                                    bool aMultiEntry,
@@ -1585,49 +1659,25 @@ IDBObjectStore::DeserializeIndexValueToU
 
   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)
+nsresult
+IDBObjectStore::DeserializeUpgradeValueToFileIds(StructuredCloneReadInfo& aCloneReadInfo,
+                                                 nsAString& aFileIds)
 {
   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 JSStructuredCloneCallbacks callbacks = {
-    CommonStructuredCloneReadCallback<UpgradeDeserializationHelper>,
-    nullptr,
-    nullptr,
-    nullptr,
-    nullptr,
-    nullptr
-  };
-
-  if (!JS_ReadStructuredClone(aCx, aCloneReadInfo.mData, JS_STRUCTURED_CLONE_VERSION,
-                              JS::StructuredCloneScope::SameProcessSameThread,
-                              aValue, &callbacks, &aCloneReadInfo)) {
-    return false;
-  }
-
-  return true;
+
+  RefPtr<DeserializeUpgradeValueHelper> helper =
+    new DeserializeUpgradeValueHelper(aCloneReadInfo);
+  return helper->DispatchAndWait(aFileIds);
 }
 
 #ifdef DEBUG
 
 void
 IDBObjectStore::AssertIsOnOwningThread() const
 {
   MOZ_ASSERT(mTransaction);
--- a/dom/indexedDB/IDBObjectStore.h
+++ b/dom/indexedDB/IDBObjectStore.h
@@ -100,20 +100,19 @@ public:
   static void
   ClearCloneReadInfo(StructuredCloneReadInfo& aReadInfo);
 
   static bool
   DeserializeValue(JSContext* aCx,
                    StructuredCloneReadInfo& aCloneReadInfo,
                    JS::MutableHandle<JS::Value> aValue);
 
-  static bool
-  DeserializeUpgradeValue(JSContext* aCx,
-                          StructuredCloneReadInfo& aCloneReadInfo,
-                          JS::MutableHandle<JS::Value> aValue);
+  static nsresult
+  DeserializeUpgradeValueToFileIds(StructuredCloneReadInfo& aCloneReadInfo,
+                                   nsAString& aFileIds);
 
   static const JSClass*
   DummyPropClass()
   {
     return &sDummyPropJSClass;
   }
 
   void