Bug 957086 - patch 2 - DataStoreService in C++, r=ehsan, r=bz, r=janv
☠☠ backed out by ea7edac9664a ☠ ☠
authorAndrea Marchesini <amarchesini@mozilla.com>
Thu, 29 May 2014 16:34:43 +0100
changeset 185623 4cbc8cf0b6199852d1a303acbcb55a9992e0a89c
parent 185622 ef379607cff7b4c6abcd63cee9f366454fa7616b
child 185624 083b9fb75e9a8fff9eab3dddfed568a88d4a2bcf
push id26859
push userkwierso@gmail.com
push dateFri, 30 May 2014 00:35:41 +0000
treeherdermozilla-central@9164a6ab85b2 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersehsan, bz, janv
bugs957086
milestone32.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 957086 - patch 2 - DataStoreService in C++, r=ehsan, r=bz, r=janv
b2g/installer/package-manifest.in
browser/installer/package-manifest.in
dom/base/Navigator.cpp
dom/datastore/DataStore.manifest
dom/datastore/DataStoreCallbacks.h
dom/datastore/DataStoreDB.cpp
dom/datastore/DataStoreDB.h
dom/datastore/DataStoreImpl.js
dom/datastore/DataStoreRevision.cpp
dom/datastore/DataStoreRevision.h
dom/datastore/DataStoreService.cpp
dom/datastore/DataStoreService.h
dom/datastore/DataStoreService.js
dom/datastore/moz.build
dom/datastore/nsIDataStoreService.idl
dom/indexedDB/IDBFactory.cpp
dom/indexedDB/IDBFactory.h
dom/indexedDB/IDBRequest.cpp
dom/indexedDB/IDBRequest.h
dom/webidl/DataStore.webidl
layout/build/nsLayoutModule.cpp
layout/build/nsLayoutStatics.cpp
mobile/android/installer/package-manifest.in
--- a/b2g/installer/package-manifest.in
+++ b/b2g/installer/package-manifest.in
@@ -803,17 +803,16 @@ bin/components/@DLL_PREFIX@nkgnomevfs@DL
 #endif
 
 #ifdef MOZ_SERVICES_FXACCOUNTS
 @BINPATH@/components/FxAccountsUIGlue.js
 @BINPATH@/components/services_fxaccounts.xpt
 #endif
 
 @BINPATH@/components/DataStore.manifest
-@BINPATH@/components/DataStoreService.js
 @BINPATH@/components/DataStoreImpl.js
 @BINPATH@/components/dom_datastore.xpt
 
 #ifdef MOZ_WEBSPEECH
 @BINPATH@/components/dom_webspeechsynth.xpt
 #endif
 
 #ifdef XP_MACOSX
--- a/browser/installer/package-manifest.in
+++ b/browser/installer/package-manifest.in
@@ -856,17 +856,16 @@ bin/libfreebl_32int64_3.so
 @BINPATH@/metro/chrome/pdfjs/*
 #endif
 @BINPATH@/metro/components
 @BINPATH@/metro/defaults
 @BINPATH@/metro/modules
 #endif
 
 @BINPATH@/components/DataStore.manifest
-@BINPATH@/components/DataStoreService.js
 @BINPATH@/components/DataStoreImpl.js
 @BINPATH@/components/dom_datastore.xpt
 
 
 #ifdef MOZ_ASAN
 #ifdef CLANG_CXX
 @BINPATH@/llvm-symbolizer
 #endif
--- a/dom/base/Navigator.cpp
+++ b/dom/base/Navigator.cpp
@@ -77,17 +77,17 @@
 #include "AudioChannelManager.h"
 #endif
 
 #ifdef MOZ_B2G_FM
 #include "mozilla/dom/FMRadio.h"
 #endif
 
 #include "nsIDOMGlobalPropertyInitializer.h"
-#include "nsIDataStoreService.h"
+#include "mozilla/dom/DataStoreService.h"
 #include "nsJSUtils.h"
 
 #include "nsScriptNameSpaceManager.h"
 
 #include "mozilla/dom/NavigatorBinding.h"
 #include "mozilla/dom/Promise.h"
 
 #include "nsIUploadChannel2.h"
@@ -1461,18 +1461,17 @@ Navigator::GetDataStores(nsPIDOMWindow* 
                          const nsAString& aName,
                          ErrorResult& aRv)
 {
   if (!aWindow || !aWindow->GetDocShell()) {
     aRv.Throw(NS_ERROR_UNEXPECTED);
     return nullptr;
   }
 
-  nsCOMPtr<nsIDataStoreService> service =
-    do_GetService("@mozilla.org/datastore-service;1");
+  nsRefPtr<DataStoreService> service = DataStoreService::GetOrCreate();
   if (!service) {
     aRv.Throw(NS_ERROR_FAILURE);
     return nullptr;
   }
 
   nsCOMPtr<nsISupports> promise;
   aRv = service->GetDataStores(aWindow, aName, getter_AddRefs(promise));
 
--- a/dom/datastore/DataStore.manifest
+++ b/dom/datastore/DataStore.manifest
@@ -1,4 +1,2 @@
 component {db5c9602-030f-4bff-a3de-881a8de370f2} DataStoreImpl.js
 contract @mozilla.org/dom/datastore;1 {db5c9602-030f-4bff-a3de-881a8de370f2}
-component {d193d0e2-c677-4a7b-bb0a-19155b470f2e} DataStoreService.js
-contract @mozilla.org/datastore-service;1 {d193d0e2-c677-4a7b-bb0a-19155b470f2e}
new file mode 100644
--- /dev/null
+++ b/dom/datastore/DataStoreCallbacks.h
@@ -0,0 +1,48 @@
+/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_dom_DataStoreCallbacks_h
+#define mozilla_dom_DataStoreCallbacks_h
+
+#include "nsISupports.h"
+
+namespace mozilla {
+namespace dom {
+
+class DataStoreDB;
+
+class DataStoreDBCallback
+{
+public:
+  NS_IMETHOD_(MozExternalRefCountType) AddRef(void) = 0;
+  NS_IMETHOD_(MozExternalRefCountType) Release(void) = 0;
+
+  virtual void Run(DataStoreDB* aDb, bool aSuccess) = 0;
+
+protected:
+  virtual ~DataStoreDBCallback()
+  {
+  }
+};
+
+class DataStoreRevisionCallback
+{
+public:
+  NS_IMETHOD_(MozExternalRefCountType) AddRef(void) = 0;
+  NS_IMETHOD_(MozExternalRefCountType) Release(void) = 0;
+
+  virtual void Run(const nsAString& aRevisionID) = 0;
+
+protected:
+  virtual ~DataStoreRevisionCallback()
+  {
+  }
+};
+
+} // dom namespace
+} // mozilla namespace
+
+#endif // mozilla_dom_DataStoreCallbacks_h
new file mode 100644
--- /dev/null
+++ b/dom/datastore/DataStoreDB.cpp
@@ -0,0 +1,319 @@
+/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "DataStoreDB.h"
+
+#include "DataStoreCallbacks.h"
+#include "mozilla/dom/IDBDatabaseBinding.h"
+#include "mozilla/dom/IDBFactoryBinding.h"
+#include "mozilla/dom/indexedDB/IDBDatabase.h"
+#include "mozilla/dom/indexedDB/IDBFactory.h"
+#include "mozilla/dom/indexedDB/IDBIndex.h"
+#include "mozilla/dom/indexedDB/IDBObjectStore.h"
+#include "mozilla/dom/indexedDB/IDBRequest.h"
+#include "nsIDOMEvent.h"
+
+#define DATASTOREDB_VERSION        1
+#define DATASTOREDB_NAME           "DataStoreDB"
+#define DATASTOREDB_REVISION_INDEX "revisionIndex"
+
+using namespace mozilla::dom::indexedDB;
+
+namespace mozilla {
+namespace dom {
+
+NS_IMPL_ISUPPORTS(DataStoreDB, nsIDOMEventListener)
+
+DataStoreDB::DataStoreDB(const nsAString& aManifestURL, const nsAString& aName)
+  : mState(Inactive)
+{
+  mDatabaseName.Assign(aName);
+  mDatabaseName.AppendASCII("|");
+  mDatabaseName.Append(aManifestURL);
+}
+
+DataStoreDB::~DataStoreDB()
+{
+}
+
+nsresult
+DataStoreDB::CreateFactoryIfNeeded()
+{
+  if (!mFactory) {
+    nsresult rv = IDBFactory::Create(nullptr, getter_AddRefs(mFactory));
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+  }
+
+  return NS_OK;
+}
+
+nsresult
+DataStoreDB::Open(IDBTransactionMode aMode, const Sequence<nsString>& aDbs,
+                  DataStoreDBCallback* aCallback)
+{
+  MOZ_ASSERT(mState == Inactive);
+
+  nsresult rv = CreateFactoryIfNeeded();
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  ErrorResult error;
+  mRequest = mFactory->Open(mDatabaseName, DATASTOREDB_VERSION, error);
+  if (NS_WARN_IF(error.Failed())) {
+    return error.ErrorCode();
+  }
+
+  rv = AddEventListeners();
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  mState = Active;
+  mTransactionMode = aMode;
+  mObjectStores = aDbs;
+  mCallback = aCallback;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+DataStoreDB::HandleEvent(nsIDOMEvent* aEvent)
+{
+  nsString type;
+  nsresult rv = aEvent->GetType(type);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  if (type.EqualsASCII("success")) {
+    RemoveEventListeners();
+    mState = Inactive;
+
+    rv = DatabaseOpened();
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      mCallback->Run(this, false);
+    } else {
+      mCallback->Run(this, true);
+    }
+
+    mRequest = nullptr;
+    return NS_OK;
+  }
+
+  if (type.EqualsASCII("upgradeneeded")) {
+    return UpgradeSchema();
+  }
+
+  if (type.EqualsASCII("error") || type.EqualsASCII("blocked")) {
+    RemoveEventListeners();
+    mState = Inactive;
+    mCallback->Run(this, false);
+    mRequest = nullptr;
+    return NS_OK;
+  }
+
+  MOZ_ASSUME_UNREACHABLE("This should not happen");
+  return NS_OK;
+}
+
+nsresult
+DataStoreDB::UpgradeSchema()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  AutoSafeJSContext cx;
+
+  ErrorResult error;
+  JS::Rooted<JS::Value> result(cx, mRequest->GetResult(error));
+  if (NS_WARN_IF(error.Failed())) {
+    return error.ErrorCode();
+  }
+
+  MOZ_ASSERT(result.isObject());
+
+  IDBDatabase* database = nullptr;
+  nsresult rv = UNWRAP_OBJECT(IDBDatabase, &result.toObject(), database);
+  if (NS_FAILED(rv)) {
+    NS_WARNING("Didn't get the object we expected!");
+    return rv;
+  }
+
+  {
+    IDBObjectStoreParameters params;
+    params.Init(NS_LITERAL_STRING("{ \"autoIncrement\": true }"));
+    nsRefPtr<IDBObjectStore> store =
+      database->CreateObjectStore(cx, NS_LITERAL_STRING(DATASTOREDB_NAME),
+                                  params, error);
+    if (NS_WARN_IF(error.Failed())) {
+      return error.ErrorCode();
+    }
+  }
+
+  nsRefPtr<IDBObjectStore> store;
+
+  {
+    IDBObjectStoreParameters params;
+    params.Init(NS_LITERAL_STRING("{ \"autoIncrement\": true, \"keyPath\": \"internalRevisionId\" }"));
+
+    store =
+      database->CreateObjectStore(cx, NS_LITERAL_STRING(DATASTOREDB_REVISION),
+                                  params, error);
+    if (NS_WARN_IF(error.Failed())) {
+      return error.ErrorCode();
+    }
+  }
+
+  {
+    IDBIndexParameters params;
+    params.Init(NS_LITERAL_STRING("{ \"unique\": true }"));
+    nsRefPtr<IDBIndex> index =
+      store->CreateIndex(cx, NS_LITERAL_STRING(DATASTOREDB_REVISION_INDEX),
+                         NS_LITERAL_STRING("revisionId"), params, error);
+    if (NS_WARN_IF(error.Failed())) {
+      return error.ErrorCode();
+    }
+  }
+
+  return NS_OK;
+}
+
+nsresult
+DataStoreDB::DatabaseOpened()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  AutoSafeJSContext cx;
+
+  ErrorResult error;
+  JS::Rooted<JS::Value> result(cx, mRequest->GetResult(error));
+  if (NS_WARN_IF(error.Failed())) {
+    return error.ErrorCode();
+  }
+
+  MOZ_ASSERT(result.isObject());
+
+  nsresult rv = UNWRAP_OBJECT(IDBDatabase, &result.toObject(), mDatabase);
+  if (NS_FAILED(rv)) {
+    NS_WARNING("Didn't get the object we expected!");
+    return rv;
+  }
+
+  nsRefPtr<IDBTransaction> txn = mDatabase->Transaction(mObjectStores,
+                                                        mTransactionMode,
+                                                        error);
+  if (NS_WARN_IF(error.Failed())) {
+    return error.ErrorCode();
+  }
+
+  mTransaction = txn.forget();
+  return NS_OK;
+}
+
+nsresult
+DataStoreDB::Delete()
+{
+  MOZ_ASSERT(mState == Inactive);
+
+  nsresult rv = CreateFactoryIfNeeded();
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  mTransaction = nullptr;
+
+  if (mDatabase) {
+    rv = mDatabase->Close();
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+
+    mDatabase = nullptr;
+  }
+
+  ErrorResult error;
+  nsRefPtr<IDBOpenDBRequest> request =
+    mFactory->DeleteDatabase(mDatabaseName, IDBOpenDBOptions(), error);
+  if (NS_WARN_IF(error.Failed())) {
+    return error.ErrorCode();
+  }
+
+  return NS_OK;
+}
+
+indexedDB::IDBTransaction*
+DataStoreDB::Transaction() const
+{
+  MOZ_ASSERT(mTransaction);
+  MOZ_ASSERT(mTransaction->IsOpen());
+  return mTransaction;
+}
+
+nsresult
+DataStoreDB::AddEventListeners()
+{
+  nsresult rv;
+  rv = mRequest->EventTarget::AddEventListener(NS_LITERAL_STRING("success"),
+                                               this, false);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = mRequest->EventTarget::AddEventListener(NS_LITERAL_STRING("upgradeneeded"),
+                                               this, false);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = mRequest->EventTarget::AddEventListener(NS_LITERAL_STRING("error"),
+                                               this, false);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = mRequest->EventTarget::AddEventListener(NS_LITERAL_STRING("blocked"),
+                                               this, false);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  return NS_OK;
+}
+
+nsresult
+DataStoreDB::RemoveEventListeners()
+{
+  nsresult rv;
+  rv = mRequest->RemoveEventListener(NS_LITERAL_STRING("success"),
+                                     this, false);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = mRequest->RemoveEventListener(NS_LITERAL_STRING("upgradeneeded"),
+                                     this, false);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = mRequest->RemoveEventListener(NS_LITERAL_STRING("error"),
+                                     this, false);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = mRequest->RemoveEventListener(NS_LITERAL_STRING("blocked"),
+                                     this, false);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  return NS_OK;
+}
+
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/datastore/DataStoreDB.h
@@ -0,0 +1,82 @@
+/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_dom_DataStoreDB_h
+#define mozilla_dom_DataStoreDB_h
+
+#include "mozilla/dom/IDBTransactionBinding.h"
+#include "nsAutoPtr.h"
+#include "nsIDOMEventListener.h"
+#include "nsISupportsImpl.h"
+#include "nsString.h"
+
+#define DATASTOREDB_REVISION       "revision"
+
+namespace mozilla {
+namespace dom {
+
+namespace indexedDB {
+class IDBDatabase;
+class IDBFactory;
+class IDBObjectStore;
+class IDBOpenDBRequest;
+class IDBTransaction;
+}
+
+class DataStoreDBCallback;
+
+class DataStoreDB MOZ_FINAL : public nsIDOMEventListener
+{
+public:
+  NS_DECL_ISUPPORTS
+
+  DataStoreDB(const nsAString& aManifestURL, const nsAString& aName);
+  ~DataStoreDB();
+
+  nsresult Open(IDBTransactionMode aMode, const Sequence<nsString>& aDb,
+                DataStoreDBCallback* aCallback);
+
+  nsresult Delete();
+
+  indexedDB::IDBTransaction* Transaction() const;
+
+  // nsIDOMEventListener
+  NS_IMETHOD HandleEvent(nsIDOMEvent* aEvent);
+
+private:
+  nsresult CreateFactoryIfNeeded();
+
+  nsresult UpgradeSchema();
+
+  nsresult DatabaseOpened();
+
+  nsresult AddEventListeners();
+
+  nsresult RemoveEventListeners();
+
+  nsString mDatabaseName;
+
+  nsRefPtr<indexedDB::IDBFactory> mFactory;
+  nsRefPtr<indexedDB::IDBOpenDBRequest> mRequest;
+  nsRefPtr<indexedDB::IDBDatabase> mDatabase;
+  nsRefPtr<indexedDB::IDBTransaction> mTransaction;
+
+  nsRefPtr<DataStoreDBCallback> mCallback;
+
+  // Internal state to avoid strange use of this class.
+  enum StateType {
+    Inactive,
+    Active
+  } mState;
+
+  IDBTransactionMode mTransactionMode;
+  Sequence<nsString> mObjectStores;
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_DataStoreDB_h
--- a/dom/datastore/DataStoreImpl.js
+++ b/dom/datastore/DataStoreImpl.js
@@ -54,17 +54,16 @@ function validateId(aId) {
 
   aId = parseInt(aId);
   return (!isNaN(aId) && aId > 0);
 }
 
 /* DataStore object */
 function DataStore() {
   debug("DataStore created");
-  this.wrappedJSObject = this;
 }
 
 DataStore.prototype = {
   classDescription: "DataStore XPCOM Component",
   classID: Components.ID("{db5c9602-030f-4bff-a3de-881a8de370f2}"),
   contractID: "@mozilla.org/dom/datastore-impl;1",
   QueryInterface: XPCOMUtils.generateQI([Ci.nsIDataStore, Ci.nsISupports,
                                          Ci.nsIObserver]),
new file mode 100644
--- /dev/null
+++ b/dom/datastore/DataStoreRevision.cpp
@@ -0,0 +1,102 @@
+/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "DataStoreRevision.h"
+
+#include "DataStoreCallbacks.h"
+#include "DataStoreService.h"
+#include "mozilla/dom/DataStoreBinding.h"
+#include "mozilla/dom/indexedDB/IDBObjectStore.h"
+#include "nsIDOMEvent.h"
+
+namespace mozilla {
+namespace dom {
+
+using namespace indexedDB;
+
+NS_IMPL_ISUPPORTS(DataStoreRevision, nsIDOMEventListener)
+
+// Note: this code in it must not assume anything about the compartment cx is
+// in.
+nsresult
+DataStoreRevision::AddRevision(JSContext* aCx,
+                               IDBObjectStore* aStore,
+                               uint32_t aObjectId,
+                               RevisionType aRevisionType,
+                               DataStoreRevisionCallback* aCallback)
+{
+  MOZ_ASSERT(aStore);
+  MOZ_ASSERT(aCallback);
+
+  nsRefPtr<DataStoreService> service = DataStoreService::Get();
+  if (!service) {
+    return NS_ERROR_FAILURE;
+  }
+
+  nsString id;
+  nsresult rv = service->GenerateUUID(mRevisionID);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  DataStoreRevisionData data;
+  data.mRevisionId = mRevisionID;
+  data.mObjectId = aObjectId;
+
+  switch (aRevisionType) {
+    case RevisionVoid:
+      data.mOperation = NS_LITERAL_STRING("void");
+      break;
+
+    default:
+      MOZ_ASSUME_UNREACHABLE("This should not happen");
+      break;
+  }
+
+  JS::Rooted<JS::Value> value(aCx);
+  if (!data.ToObject(aCx, &value)) {
+    return NS_ERROR_FAILURE;
+  }
+
+  ErrorResult error;
+  mRequest = aStore->Put(aCx, value, JS::UndefinedHandleValue, error);
+  if (NS_WARN_IF(error.Failed())) {
+    return error.ErrorCode();
+  }
+
+  rv = mRequest->EventTarget::AddEventListener(NS_LITERAL_STRING("success"),
+                                               this, false);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  mCallback = aCallback;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+DataStoreRevision::HandleEvent(nsIDOMEvent* aEvent)
+{
+  nsString type;
+  nsresult rv = aEvent->GetType(type);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  if (!type.EqualsASCII("success")) {
+    MOZ_ASSUME_UNREACHABLE("This should not happen");
+    return NS_ERROR_FAILURE;
+  }
+
+  mRequest->RemoveEventListener(NS_LITERAL_STRING("success"), this, false);
+  mRequest = nullptr;
+
+  mCallback->Run(mRevisionID);
+  return NS_OK;
+}
+
+} // dom namespace
+} // mozilla namespace
new file mode 100644
--- /dev/null
+++ b/dom/datastore/DataStoreRevision.h
@@ -0,0 +1,52 @@
+/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_dom_DataStoreRevision_h
+#define mozilla_dom_DataStoreRevision_h
+
+#include "nsIDOMEventListener.h"
+#include "nsAutoPtr.h"
+#include "nsString.h"
+#include "jsapi.h"
+
+namespace mozilla {
+namespace dom {
+
+namespace indexedDB {
+class IDBObjectStore;
+class IDBRequest;
+}
+
+class DataStoreRevisionCallback;
+
+class DataStoreRevision MOZ_FINAL : public nsIDOMEventListener
+{
+public:
+  NS_DECL_ISUPPORTS
+
+  enum RevisionType {
+    RevisionVoid
+  };
+
+  nsresult AddRevision(JSContext* aCx,
+                       indexedDB::IDBObjectStore* aStore,
+                       uint32_t aObjectId,
+                       RevisionType aRevisionType,
+                       DataStoreRevisionCallback* aCallback);
+
+  // nsIDOMEventListener
+  NS_IMETHOD HandleEvent(nsIDOMEvent* aEvent);
+
+private:
+  nsRefPtr<DataStoreRevisionCallback> mCallback;
+  nsRefPtr<indexedDB::IDBRequest> mRequest;
+  nsString mRevisionID;
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_DataStoreRevision_h
new file mode 100644
--- /dev/null
+++ b/dom/datastore/DataStoreService.cpp
@@ -0,0 +1,1277 @@
+/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "DataStoreService.h"
+
+#include "DataStoreCallbacks.h"
+#include "DataStoreDB.h"
+#include "DataStoreRevision.h"
+#include "mozilla/dom/DataStore.h"
+#include "nsIDataStore.h"
+
+#include "mozilla/Preferences.h"
+#include "mozilla/Services.h"
+#include "mozilla/StaticPtr.h"
+#include "mozilla/dom/DOMError.h"
+#include "mozilla/dom/Promise.h"
+#include "mozilla/dom/indexedDB/IDBCursor.h"
+#include "mozilla/dom/indexedDB/IDBObjectStore.h"
+#include "mozilla/dom/DataStoreBinding.h"
+#include "mozilla/dom/DataStoreImplBinding.h"
+
+#include "mozIApplication.h"
+#include "mozIApplicationClearPrivateDataParams.h"
+#include "nsIAppsService.h"
+#include "nsIDOMEvent.h"
+#include "nsIDocument.h"
+#include "nsIDOMGlobalPropertyInitializer.h"
+#include "nsIIOService.h"
+#include "nsIObserverService.h"
+#include "nsIPermissionManager.h"
+#include "nsIScriptSecurityManager.h"
+#include "nsIUUIDGenerator.h"
+#include "nsPIDOMWindow.h"
+#include "nsIURI.h"
+
+#include "nsContentUtils.h"
+#include "nsNetCID.h"
+#include "nsServiceManagerUtils.h"
+#include "nsThreadUtils.h"
+#include "nsXULAppAPI.h"
+
+#define ASSERT_PARENT_PROCESS()                                             \
+  AssertIsInMainProcess();                                                  \
+  if (NS_WARN_IF(XRE_GetProcessType() != GeckoProcessType_Default)) {       \
+    return NS_ERROR_FAILURE;                                                \
+  }
+
+namespace mozilla {
+namespace dom {
+
+using namespace indexedDB;
+
+// This class contains all the information about a DataStore.
+class DataStoreInfo
+{
+public:
+  DataStoreInfo(const nsAString& aName,
+                const nsAString& aOriginURL,
+                const nsAString& aManifestURL,
+                bool aReadOnly,
+                bool aEnabled)
+    : mName(aName)
+    , mOriginURL(aOriginURL)
+    , mManifestURL(aManifestURL)
+    , mReadOnly(aReadOnly)
+    , mEnabled(aEnabled)
+  {}
+
+  void Update(const nsAString& aName,
+              const nsAString& aOriginURL,
+              const nsAString& aManifestURL,
+              bool aReadOnly)
+  {
+    mName = aName;
+    mOriginURL = aOriginURL;
+    mManifestURL = aManifestURL;
+    mReadOnly = aReadOnly;
+  }
+
+  void Enable()
+  {
+    mEnabled = true;
+  }
+
+  nsString mName;
+  nsString mOriginURL;
+  nsString mManifestURL;
+  bool mReadOnly;
+
+  // A DataStore is enabled when it has its first revision.
+  bool mEnabled;
+};
+
+namespace {
+
+// Singleton for DataStoreService.
+StaticRefPtr<DataStoreService> gDataStoreService;
+static uint64_t gCounterID = 0;
+
+typedef nsClassHashtable<nsUint32HashKey, DataStoreInfo> HashApp;
+
+bool
+IsMainProcess()
+{
+  static const bool isMainProcess =
+    XRE_GetProcessType() == GeckoProcessType_Default;
+  return isMainProcess;
+}
+
+void
+AssertIsInMainProcess()
+{
+  MOZ_ASSERT(IsMainProcess());
+}
+
+void
+RejectPromise(nsPIDOMWindow* aWindow, Promise* aPromise, nsresult aRv)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(NS_FAILED(aRv));
+
+  nsRefPtr<DOMError> error;
+  if (aRv == NS_ERROR_DOM_SECURITY_ERR) {
+    error = new DOMError(aWindow, NS_LITERAL_STRING("SecurityError"),
+                         NS_LITERAL_STRING("Access denied"));
+  } else {
+    error = new DOMError(aWindow, NS_LITERAL_STRING("InternalError"),
+                         NS_LITERAL_STRING("An error occurred"));
+  }
+
+  aPromise->MaybeReject(error);
+}
+
+void
+DeleteDatabase(const nsAString& aName,
+               const nsAString& aManifestURL)
+{
+  AssertIsInMainProcess();
+  MOZ_ASSERT(NS_IsMainThread());
+
+  nsRefPtr<DataStoreDB> db = new DataStoreDB(aManifestURL, aName);
+  db->Delete();
+}
+
+PLDHashOperator
+DeleteDataStoresAppEnumerator(
+                             const uint32_t& aAppId,
+                             nsAutoPtr<DataStoreInfo>& aInfo,
+                             void* aUserData)
+{
+  AssertIsInMainProcess();
+  MOZ_ASSERT(NS_IsMainThread());
+
+  auto* appId = static_cast<uint32_t*>(aUserData);
+  if (*appId != aAppId) {
+    return PL_DHASH_NEXT;
+  }
+
+  DeleteDatabase(aInfo->mName, aInfo->mManifestURL);
+  return PL_DHASH_REMOVE;
+}
+
+PLDHashOperator
+DeleteDataStoresEnumerator(const nsAString& aName,
+                           nsAutoPtr<HashApp>& aApps,
+                           void* aUserData)
+{
+  AssertIsInMainProcess();
+  MOZ_ASSERT(NS_IsMainThread());
+
+  aApps->Enumerate(DeleteDataStoresAppEnumerator, aUserData);
+  return aApps->Count() ? PL_DHASH_NEXT : PL_DHASH_REMOVE;
+}
+
+void
+GeneratePermissionName(nsAString& aPermission,
+                       const nsAString& aName,
+                       const nsAString& aManifestURL)
+{
+  aPermission.AssignASCII("indexedDB-chrome-");
+  aPermission.Append(aName);
+  aPermission.AppendASCII("|");
+  aPermission.Append(aManifestURL);
+}
+
+nsresult
+ResetPermission(uint32_t aAppId, const nsAString& aOriginURL,
+                const nsAString& aManifestURL,
+                const nsAString& aPermission,
+                bool aReadOnly)
+{
+  AssertIsInMainProcess();
+  MOZ_ASSERT(NS_IsMainThread());
+
+  nsresult rv;
+  nsCOMPtr<nsIIOService> ioService(do_GetService(NS_IOSERVICE_CONTRACTID, &rv));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  nsCOMPtr<nsIURI> uri;
+  rv = ioService->NewURI(NS_ConvertUTF16toUTF8(aOriginURL), nullptr, nullptr,
+                         getter_AddRefs(uri));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
+  if (!ssm) {
+    return NS_ERROR_FAILURE;
+  }
+
+  nsCOMPtr<nsIPrincipal> principal;
+  rv = ssm->GetAppCodebasePrincipal(uri, aAppId, false,
+                                    getter_AddRefs(principal));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  nsCOMPtr<nsIPermissionManager> pm =
+    do_GetService(NS_PERMISSIONMANAGER_CONTRACTID);
+  if (!pm) {
+    return NS_ERROR_FAILURE;
+  }
+
+  nsCString basePermission;
+  basePermission.Append(NS_ConvertUTF16toUTF8(aPermission));
+
+  // Write permission
+  {
+    nsCString permission;
+    permission.Append(basePermission);
+    permission.AppendASCII("-write");
+
+    uint32_t perm = nsIPermissionManager::UNKNOWN_ACTION;
+    rv = pm->TestExactPermissionFromPrincipal(principal, permission.get(),
+                                              &perm);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+
+    if (aReadOnly && perm == nsIPermissionManager::ALLOW_ACTION) {
+      rv = pm->RemoveFromPrincipal(principal, permission.get());
+    }
+    else if (!aReadOnly && perm != nsIPermissionManager::ALLOW_ACTION) {
+      rv = pm->AddFromPrincipal(principal, permission.get(),
+                                nsIPermissionManager::ALLOW_ACTION,
+                                nsIPermissionManager::EXPIRE_NEVER, 0);
+    }
+
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+  }
+
+  // Read permission
+  {
+    nsCString permission;
+    permission.Append(basePermission);
+    permission.AppendASCII("-read");
+
+    uint32_t perm = nsIPermissionManager::UNKNOWN_ACTION;
+    rv = pm->TestExactPermissionFromPrincipal(principal, permission.get(),
+                                              &perm);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+
+    if (perm != nsIPermissionManager::ALLOW_ACTION) {
+      rv = pm->AddFromPrincipal(principal, permission.get(),
+                                nsIPermissionManager::ALLOW_ACTION,
+                                nsIPermissionManager::EXPIRE_NEVER, 0);
+      if (NS_WARN_IF(NS_FAILED(rv))) {
+        return rv;
+      }
+    }
+  }
+
+  // Generic permission
+  uint32_t perm = nsIPermissionManager::UNKNOWN_ACTION;
+  rv = pm->TestExactPermissionFromPrincipal(principal, basePermission.get(),
+                                            &perm);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  if (perm != nsIPermissionManager::ALLOW_ACTION) {
+    rv = pm->AddFromPrincipal(principal, basePermission.get(),
+                              nsIPermissionManager::ALLOW_ACTION,
+                              nsIPermissionManager::EXPIRE_NEVER, 0);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+  }
+
+  return NS_OK;
+}
+
+class MOZ_STACK_CLASS GetDataStoreInfosData
+{
+public:
+  GetDataStoreInfosData(nsClassHashtable<nsStringHashKey, HashApp>& aAccessStores,
+                        const nsAString& aName, uint32_t aAppId,
+                        nsTArray<DataStoreInfo>& aStores)
+    : mAccessStores(aAccessStores)
+    , mName(aName)
+    , mAppId(aAppId)
+    , mStores(aStores)
+  {}
+
+  nsClassHashtable<nsStringHashKey, HashApp>& mAccessStores;
+  nsString mName;
+  uint32_t mAppId;
+  nsTArray<DataStoreInfo>& mStores;
+};
+
+PLDHashOperator
+GetDataStoreInfosEnumerator(const uint32_t& aAppId,
+                            DataStoreInfo* aInfo,
+                            void* aUserData)
+{
+  AssertIsInMainProcess();
+  MOZ_ASSERT(NS_IsMainThread());
+
+  auto* data = static_cast<GetDataStoreInfosData*>(aUserData);
+  if (aAppId == data->mAppId) {
+    return PL_DHASH_NEXT;
+  }
+
+  HashApp* apps;
+  if (!data->mAccessStores.Get(data->mName, &apps)) {
+    return PL_DHASH_NEXT;
+  }
+
+  DataStoreInfo* accessInfo = nullptr;
+  if (!apps->Get(data->mAppId, &accessInfo)) {
+    return PL_DHASH_NEXT;
+  }
+
+  bool readOnly = aInfo->mReadOnly || accessInfo->mReadOnly;
+  DataStoreInfo accessStore(aInfo->mName, aInfo->mOriginURL,
+                            aInfo->mManifestURL, readOnly,
+                            aInfo->mEnabled);
+  data->mStores.AppendElement(accessStore);
+
+  return PL_DHASH_NEXT;
+}
+
+// This class is useful to enumerate the add permissions for each app.
+class MOZ_STACK_CLASS AddPermissionsData
+{
+public:
+  AddPermissionsData(const nsAString& aPermission, bool aReadOnly)
+    : mPermission(aPermission)
+    , mReadOnly(aReadOnly)
+    , mResult(NS_OK)
+  {}
+
+  nsString mPermission;
+  bool mReadOnly;
+  nsresult mResult;
+};
+
+PLDHashOperator
+AddPermissionsEnumerator(const uint32_t& aAppId,
+                         DataStoreInfo* aInfo,
+                         void* userData)
+{
+  AssertIsInMainProcess();
+  MOZ_ASSERT(NS_IsMainThread());
+
+  auto* data = static_cast<AddPermissionsData*>(userData);
+
+  // ReadOnly is decided by the owner first.
+  bool readOnly = data->mReadOnly || aInfo->mReadOnly;
+
+  data->mResult = ResetPermission(aAppId, aInfo->mOriginURL,
+                                  aInfo->mManifestURL,
+                                  data->mPermission,
+                                  readOnly);
+  return NS_FAILED(data->mResult) ? PL_DHASH_STOP : PL_DHASH_NEXT;
+}
+
+// This class is useful to enumerate the add permissions for each app.
+class MOZ_STACK_CLASS AddAccessPermissionsData
+{
+public:
+  AddAccessPermissionsData(const nsAString& aName, bool aReadOnly)
+    : mName(aName)
+    , mReadOnly(aReadOnly)
+    , mResult(NS_OK)
+  {}
+
+  nsString mName;
+  bool mReadOnly;
+  nsresult mResult;
+};
+
+PLDHashOperator
+AddAccessPermissionsEnumerator(const uint32_t& aAppId,
+                               DataStoreInfo* aInfo,
+                               void* userData)
+{
+  AssertIsInMainProcess();
+  MOZ_ASSERT(NS_IsMainThread());
+
+  auto* data = static_cast<AddAccessPermissionsData*>(userData);
+
+  nsString permission;
+  GeneratePermissionName(permission, data->mName, aInfo->mManifestURL);
+
+  // ReadOnly is decided by the owner first.
+  bool readOnly = aInfo->mReadOnly || data->mReadOnly;
+
+  data->mResult = ResetPermission(aAppId, aInfo->mOriginURL,
+                                  aInfo->mManifestURL,
+                                  permission, readOnly);
+  return NS_FAILED(data->mResult) ? PL_DHASH_STOP : PL_DHASH_NEXT;
+}
+
+} /* anonymous namespace */
+
+// A PendingRequest is created when a content code wants a list of DataStores
+// but some of them are not enabled yet.
+class PendingRequest
+{
+public:
+  PendingRequest(nsPIDOMWindow* aWindow,
+                 Promise* aPromise,
+                 const nsTArray<DataStoreInfo>& aStores,
+                 const nsTArray<nsString>& aPendingDataStores)
+    : mWindow(aWindow)
+    , mPromise(aPromise)
+    , mStores(aStores)
+    , mPendingDataStores(aPendingDataStores)
+  {
+  }
+
+  nsCOMPtr<nsPIDOMWindow> mWindow;
+  nsRefPtr<Promise> mPromise;
+  nsTArray<DataStoreInfo> mStores;
+
+  // This array contains the list of manifestURLs of the DataStores that are
+  // not enabled yet.
+  nsTArray<nsString> mPendingDataStores;
+};
+
+// This callback is used to enable a DataStore when its first revisionID is
+// created.
+class RevisionAddedEnableStoreCallback MOZ_FINAL :
+  public DataStoreRevisionCallback
+{
+public:
+  NS_INLINE_DECL_REFCOUNTING(RevisionAddedEnableStoreCallback);
+
+  RevisionAddedEnableStoreCallback(uint32_t aAppId,
+                                   const nsAString& aName,
+                                   const nsAString& aManifestURL)
+    : mAppId(aAppId)
+    , mName(aName)
+    , mManifestURL(aManifestURL)
+  {
+    AssertIsInMainProcess();
+    MOZ_ASSERT(NS_IsMainThread());
+  }
+
+  void
+  Run(const nsAString& aRevisionId)
+  {
+    AssertIsInMainProcess();
+    MOZ_ASSERT(NS_IsMainThread());
+
+    nsRefPtr<DataStoreService> service = DataStoreService::Get();
+    MOZ_ASSERT(service);
+
+    service->EnableDataStore(mAppId, mName, mManifestURL);
+  }
+
+private:
+  uint32_t mAppId;
+  nsString mName;
+  nsString mManifestURL;
+};
+
+// This DataStoreDBCallback is called when DataStoreDB opens the DataStore DB.
+// Then the first revision will be created if it doesn't exist yet.
+class FirstRevisionIdCallback MOZ_FINAL : public DataStoreDBCallback
+                                        , public nsIDOMEventListener
+{
+public:
+  NS_DECL_ISUPPORTS
+
+  FirstRevisionIdCallback(uint32_t aAppId, const nsAString& aName,
+                          const nsAString& aManifestURL)
+    : mAppId(aAppId)
+    , mName(aName)
+    , mManifestURL(aManifestURL)
+  {
+    AssertIsInMainProcess();
+    MOZ_ASSERT(NS_IsMainThread());
+  }
+
+  void
+  Run(DataStoreDB* aDb, bool aSuccess)
+  {
+    AssertIsInMainProcess();
+    MOZ_ASSERT(NS_IsMainThread());
+    MOZ_ASSERT(aDb);
+
+    if (!aSuccess) {
+      NS_WARNING("Failed to create the first revision.");
+      return;
+    }
+
+    mTxn = aDb->Transaction();
+
+    ErrorResult rv;
+    nsRefPtr<IDBObjectStore> store =
+      mTxn->ObjectStore(NS_LITERAL_STRING(DATASTOREDB_REVISION), rv);
+    if (NS_WARN_IF(rv.Failed())) {
+      return;
+    }
+
+    // a Null JSContext is ok because OpenCursor ignores it if the range is
+    // undefined.
+    mRequest = store->OpenCursor(nullptr, JS::UndefinedHandleValue,
+                                 IDBCursorDirection::Prev, rv);
+    if (NS_WARN_IF(rv.Failed())) {
+      return;
+    }
+
+    nsresult res;
+    res = mRequest->EventTarget::AddEventListener(NS_LITERAL_STRING("success"),
+                                                  this, false);
+    if (NS_WARN_IF(NS_FAILED(res))) {
+      return;
+    }
+  }
+
+  // nsIDOMEventListener
+  NS_IMETHOD
+  HandleEvent(nsIDOMEvent* aEvent)
+  {
+    AssertIsInMainProcess();
+    MOZ_ASSERT(NS_IsMainThread());
+
+    nsString type;
+    nsresult rv = aEvent->GetType(type);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+
+    if (!type.EqualsASCII("success")) {
+      return NS_ERROR_FAILURE;
+    }
+
+    mRequest->RemoveEventListener(NS_LITERAL_STRING("success"), this, false);
+
+    // Note: this cx is only used for rooting and AddRevision, neither of which
+    // actually care which compartment we're in.
+    AutoSafeJSContext cx;
+
+    ErrorResult error;
+    JS::Rooted<JS::Value> result(cx, mRequest->GetResult(error));
+    if (NS_WARN_IF(error.Failed())) {
+      return error.ErrorCode();
+    }
+
+    // This means that the content is a IDBCursor, so the first revision already
+    // exists.
+    if (result.isObject()) {
+      nsRefPtr<DataStoreService> service = DataStoreService::Get();
+      MOZ_ASSERT(service);
+
+      return service->EnableDataStore(mAppId, mName, mManifestURL);
+    }
+
+    MOZ_ASSERT(mTxn);
+    nsRefPtr<IDBObjectStore> store =
+      mTxn->ObjectStore(NS_LITERAL_STRING(DATASTOREDB_REVISION), error);
+    if (NS_WARN_IF(error.Failed())) {
+      return error.ErrorCode();
+    }
+    MOZ_ASSERT(store);
+
+    nsRefPtr<RevisionAddedEnableStoreCallback> callback =
+      new RevisionAddedEnableStoreCallback(mAppId, mName, mManifestURL);
+
+    // If the revision doesn't exist, let's create it.
+    nsRefPtr<DataStoreRevision> mRevision = new DataStoreRevision();
+    return mRevision->AddRevision(cx, store, 0, DataStoreRevision::RevisionVoid,
+                                  callback);
+  }
+
+private:
+  nsRefPtr<IDBRequest> mRequest;
+
+  nsRefPtr<IDBTransaction> mTxn;
+  nsRefPtr<DataStoreRevision> mRevision;
+
+  uint32_t mAppId;
+  nsString mName;
+  nsString mManifestURL;
+};
+
+NS_IMPL_ISUPPORTS(FirstRevisionIdCallback, nsIDOMEventListener)
+
+// This class calls the 'retrieveRevisionId' method of the DataStore object for
+// any DataStore in the 'mResults' array. When all of them are called, the
+// promise is resolved with 'mResults'.
+// The reson why this has to be done is because DataStore are object that can be
+// created in any thread and in any process. The first revision has been
+// created, but they don't know its value yet.
+class RetrieveRevisionsCounter
+{
+public:
+  NS_INLINE_DECL_REFCOUNTING(RetrieveRevisionsCounter);
+
+  RetrieveRevisionsCounter(uint32_t aId, Promise* aPromise, uint32_t aCount)
+    : mPromise(aPromise)
+    , mId(aId)
+    , mCount(aCount)
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+  }
+
+  void
+  AppendDataStore(JSContext* aCx, DataStore* aDataStore,
+                  nsIDataStore* aDataStoreIf)
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+
+    mResults.AppendElement(aDataStore);
+
+    // DataStore will run this callback when the revisionID is retrieved.
+    JSFunction* func = js::NewFunctionWithReserved(aCx, JSCallback,
+                                                   0 /* nargs */, 0 /* flags */,
+                                                   nullptr, nullptr);
+    if (!func) {
+      return;
+    }
+
+    JS::Rooted<JSObject*> obj(aCx, JS_GetFunctionObject(func));
+    if (!obj) {
+      return;
+    }
+
+    // We use the ID to know which counter is this. The service keeps all of
+    // these counters alive with their own IDs in an hashtable.
+    js::SetFunctionNativeReserved(obj, 0, JS::Int32Value(mId));
+
+    JS::Rooted<JS::Value> value(aCx, JS::ObjectValue(*obj));
+    nsresult rv = aDataStoreIf->RetrieveRevisionId(value);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return;
+    }
+  }
+
+private:
+  static bool
+  JSCallback(JSContext* aCx, unsigned aArgc, JS::Value* aVp)
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+
+    JS::CallArgs args = CallArgsFromVp(aArgc, aVp);
+
+    JS::Rooted<JS::Value> value(aCx,
+                                js::GetFunctionNativeReserved(&args.callee(), 0));
+    uint32_t id = value.toInt32();
+
+    nsRefPtr<DataStoreService> service = DataStoreService::Get();
+    MOZ_ASSERT(service);
+
+    nsRefPtr<RetrieveRevisionsCounter> counter = service->GetCounter(id);
+    MOZ_ASSERT(counter);
+
+    // When all the callbacks are called, we can resolve the promise and remove
+    // the counter from the service.
+    --counter->mCount;
+    if (!counter->mCount) {
+      service->RemoveCounter(id);
+      counter->mPromise->MaybeResolve(counter->mResults);
+    }
+
+    return true;
+  }
+
+  nsRefPtr<Promise> mPromise;
+  nsTArray<nsRefPtr<DataStore>> mResults;
+
+  uint32_t mId;
+  uint32_t mCount;
+};
+
+/* static */ already_AddRefed<DataStoreService>
+DataStoreService::GetOrCreate()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  if (!gDataStoreService) {
+    nsRefPtr<DataStoreService> service = new DataStoreService();
+    if (NS_WARN_IF(NS_FAILED(service->Init()))) {
+      return nullptr;
+    }
+
+    gDataStoreService = service;
+  }
+
+  nsRefPtr<DataStoreService> service = gDataStoreService.get();
+  return service.forget();
+}
+
+/* static */ already_AddRefed<DataStoreService>
+DataStoreService::Get()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  nsRefPtr<DataStoreService> service = gDataStoreService.get();
+  return service.forget();
+}
+
+/* static */ void
+DataStoreService::Shutdown()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  if (gDataStoreService) {
+    if (IsMainProcess()) {
+      nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
+      if (obs) {
+        obs->RemoveObserver(gDataStoreService, "webapps-clear-data");
+      }
+    }
+
+    gDataStoreService = nullptr;
+  }
+}
+
+NS_INTERFACE_MAP_BEGIN(DataStoreService)
+  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDataStoreService)
+  NS_INTERFACE_MAP_ENTRY(nsIDataStoreService)
+  NS_INTERFACE_MAP_ENTRY(nsIObserver)
+NS_INTERFACE_MAP_END
+
+NS_IMPL_ADDREF(DataStoreService)
+NS_IMPL_RELEASE(DataStoreService)
+
+DataStoreService::DataStoreService()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+}
+
+DataStoreService::~DataStoreService()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+}
+
+nsresult
+DataStoreService::Init()
+{
+  if (!IsMainProcess()) {
+    return NS_OK;
+  }
+
+  nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
+  if (!obs) {
+    return NS_ERROR_FAILURE;
+  }
+
+  nsresult rv = obs->AddObserver(this, "webapps-clear-data", false);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+DataStoreService::InstallDataStore(uint32_t aAppId,
+                                   const nsAString& aName,
+                                   const nsAString& aOriginURL,
+                                   const nsAString& aManifestURL,
+                                   bool aReadOnly)
+{
+  ASSERT_PARENT_PROCESS()
+  MOZ_ASSERT(NS_IsMainThread());
+
+  HashApp* apps = nullptr;
+  if (!mStores.Get(aName, &apps)) {
+    apps = new HashApp();
+    mStores.Put(aName, apps);
+  }
+
+  DataStoreInfo* info = nullptr;
+  if (!apps->Get(aAppId, &info)) {
+    info = new DataStoreInfo(aName, aOriginURL, aManifestURL, aReadOnly, false);
+    apps->Put(aAppId, info);
+  } else {
+    info->Update(aName, aOriginURL, aManifestURL, aReadOnly);
+  }
+
+  nsresult rv = AddPermissions(aAppId, aName, aOriginURL, aManifestURL,
+                               aReadOnly);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  // Immediately create the first revision.
+  return CreateFirstRevisionId(aAppId, aName, aManifestURL);
+}
+
+NS_IMETHODIMP
+DataStoreService::InstallAccessDataStore(uint32_t aAppId,
+                                         const nsAString& aName,
+                                         const nsAString& aOriginURL,
+                                         const nsAString& aManifestURL,
+                                         bool aReadOnly)
+{
+  ASSERT_PARENT_PROCESS()
+  MOZ_ASSERT(NS_IsMainThread());
+
+  HashApp* apps = nullptr;
+  if (!mAccessStores.Get(aName, &apps)) {
+    apps = new HashApp();
+    mAccessStores.Put(aName, apps);
+  }
+
+  DataStoreInfo* info = nullptr;
+  if (!apps->Get(aAppId, &info)) {
+    info = new DataStoreInfo(aName, aOriginURL, aManifestURL, aReadOnly, false);
+    apps->Put(aAppId, info);
+  } else {
+    info->Update(aName, aOriginURL, aManifestURL, aReadOnly);
+  }
+
+  return AddAccessPermissions(aAppId, aName, aOriginURL, aManifestURL,
+                              aReadOnly);
+}
+
+NS_IMETHODIMP
+DataStoreService::GetDataStores(nsIDOMWindow* aWindow,
+                                const nsAString& aName,
+                                nsISupports** aDataStores)
+{
+  // FIXME This will be a thread-safe method.
+  MOZ_ASSERT(NS_IsMainThread());
+
+  nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aWindow);
+  if (!window) {
+    return NS_ERROR_FAILURE;
+  }
+
+  nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(window);
+  nsRefPtr<Promise> promise = new Promise(global);
+
+  // If this request comes from the main process, we have access to the
+  // window, so we can skip the ipc communication.
+  if (IsMainProcess()) {
+    nsCOMPtr<nsIDocument> document = window->GetDoc();
+    MOZ_ASSERT(document);
+
+    nsCOMPtr<nsIPrincipal> principal = document->NodePrincipal();
+    MOZ_ASSERT(principal);
+
+    uint32_t appId;
+    nsresult rv = principal->GetAppId(&appId);
+    if (NS_FAILED(rv)) {
+      RejectPromise(window, promise, rv);
+      promise.forget(aDataStores);
+      return NS_OK;
+    }
+
+    nsTArray<DataStoreInfo> stores;
+    rv = GetDataStoreInfos(aName, appId, stores);
+    if (NS_FAILED(rv)) {
+      RejectPromise(window, promise, rv);
+      promise.forget(aDataStores);
+      return NS_OK;
+    }
+
+    GetDataStoresCreate(window, promise, stores);
+    promise.forget(aDataStores);
+    return NS_OK;
+  }
+
+  else {
+    // This method can be called in the child so we need to send a request
+    // to the parent and create DataStore object here.
+    // TODO
+  }
+
+  promise.forget(aDataStores);
+  return NS_OK;
+}
+
+void
+DataStoreService::GetDataStoresCreate(nsPIDOMWindow* aWindow, Promise* aPromise,
+                                      const nsTArray<DataStoreInfo>& aStores)
+{
+  AssertIsInMainProcess();
+  MOZ_ASSERT(NS_IsMainThread());
+
+  if (!aStores.Length()) {
+    GetDataStoresResolve(aWindow, aPromise, aStores);
+    return;
+  }
+
+  nsTArray<nsString> pendingDataStores;
+  for (uint32_t i = 0; i < aStores.Length(); ++i) {
+    if (!aStores[i].mEnabled) {
+      pendingDataStores.AppendElement(aStores[i].mManifestURL);
+    }
+  }
+
+  if (!pendingDataStores.Length()) {
+    GetDataStoresResolve(aWindow, aPromise, aStores);
+    return;
+  }
+
+  PendingRequests* requests;
+  if (!mPendingRequests.Get(aStores[0].mName, &requests)) {
+    requests = new PendingRequests();
+    mPendingRequests.Put(aStores[0].mName, requests);
+  }
+
+  PendingRequest request(aWindow, aPromise, aStores, pendingDataStores);
+  requests->AppendElement(request);
+}
+
+void
+DataStoreService::GetDataStoresResolve(nsPIDOMWindow* aWindow,
+                                       Promise* aPromise,
+                                       const nsTArray<DataStoreInfo>& aStores)
+{
+  AssertIsInMainProcess();
+  MOZ_ASSERT(NS_IsMainThread());
+
+  if (!aStores.Length()) {
+    nsTArray<nsRefPtr<DataStore>> results;
+    aPromise->MaybeResolve(results);
+    return;
+  }
+
+  AutoSafeJSContext cx;
+
+  // The counter will finish this task once all the DataStores will know their
+  // first revision Ids.
+  nsRefPtr<RetrieveRevisionsCounter> counter =
+    new RetrieveRevisionsCounter(++gCounterID, aPromise, aStores.Length());
+  mPendingCounters.Put(gCounterID, counter);
+
+  for (uint32_t i = 0; i < aStores.Length(); ++i) {
+    nsCOMPtr<nsIDataStore> dataStore =
+      do_CreateInstance("@mozilla.org/dom/datastore;1");
+    if (NS_WARN_IF(!dataStore)) {
+      return;
+    }
+
+    nsresult rv = dataStore->Init(aWindow, aStores[i].mName,
+                                  aStores[i].mManifestURL,
+                                  aStores[i].mReadOnly);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return;
+    }
+
+    nsCOMPtr<nsIXPConnectWrappedJS> xpcwrappedjs = do_QueryInterface(dataStore);
+    if (NS_WARN_IF(!xpcwrappedjs)) {
+      return;
+    }
+
+    JS::Rooted<JSObject*> dataStoreJS(cx, xpcwrappedjs->GetJSObject());
+    if (NS_WARN_IF(!dataStoreJS)) {
+      return;
+    }
+
+    JSAutoCompartment ac(cx, dataStoreJS);
+    nsRefPtr<DataStoreImpl> dataStoreObj = new DataStoreImpl(dataStoreJS,
+                                                             aWindow);
+
+    nsRefPtr<DataStore> exposedStore = new DataStore(aWindow);
+
+    ErrorResult error;
+    exposedStore->SetDataStoreImpl(*dataStoreObj, error);
+    if (error.Failed()) {
+      return;
+    }
+
+    JS::Rooted<JSObject*> obj(cx, exposedStore->WrapObject(cx));
+    MOZ_ASSERT(obj);
+
+    JS::Rooted<JS::Value> exposedObject(cx, JS::ObjectValue(*obj));
+    dataStore->SetExposedObject(exposedObject);
+
+    counter->AppendDataStore(cx, exposedStore, dataStore);
+  }
+}
+
+// Thie method populates 'aStores' with the list of DataStores with 'aName' as
+// name and available for this 'aAppId'.
+nsresult
+DataStoreService::GetDataStoreInfos(const nsAString& aName,
+                                    uint32_t aAppId,
+                                    nsTArray<DataStoreInfo>& aStores)
+{
+  AssertIsInMainProcess();
+  MOZ_ASSERT(NS_IsMainThread());
+
+  nsCOMPtr<nsIAppsService> appsService =
+    do_GetService("@mozilla.org/AppsService;1");
+  if (NS_WARN_IF(!appsService)) {
+    return NS_ERROR_FAILURE;
+  }
+
+  nsCOMPtr<mozIApplication> app;
+  nsresult rv = appsService->GetAppByLocalId(aAppId, getter_AddRefs(app));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  if (!app) {
+    return NS_ERROR_DOM_SECURITY_ERR;
+  }
+
+  uint16_t status;
+  rv = app->GetAppStatus(&status);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  if (status != nsIPrincipal::APP_STATUS_CERTIFIED &&
+      !Preferences::GetBool("dom.testing.datastore_enabled_for_hosted_apps",
+                            false)) {
+    return NS_ERROR_DOM_SECURITY_ERR;
+  }
+
+  aStores.Clear();
+
+  HashApp* apps = nullptr;
+  if (!mStores.Get(aName, &apps)) {
+    return NS_OK;
+  }
+
+  DataStoreInfo* info = nullptr;
+  if (apps->Get(aAppId, &info)) {
+    DataStoreInfo owned(info->mName, info->mOriginURL, info->mManifestURL,
+                        false, info->mEnabled);
+    aStores.AppendElement(owned);
+  }
+
+  GetDataStoreInfosData data(mAccessStores, aName, aAppId, aStores);
+  apps->EnumerateRead(GetDataStoreInfosEnumerator, &data);
+  return NS_OK;
+}
+
+// This method is called when an app with DataStores is deleted.
+void
+DataStoreService::DeleteDataStores(uint32_t aAppId)
+{
+  AssertIsInMainProcess();
+  MOZ_ASSERT(NS_IsMainThread());
+
+  mStores.Enumerate(DeleteDataStoresEnumerator, &aAppId);
+  mAccessStores.Enumerate(DeleteDataStoresEnumerator, &aAppId);
+}
+
+NS_IMETHODIMP
+DataStoreService::Observe(nsISupports* aSubject,
+                          const char* aTopic,
+                          const char16_t* aData)
+{
+  AssertIsInMainProcess();
+  MOZ_ASSERT(NS_IsMainThread());
+
+  if (strcmp(aTopic, "webapps-clear-data")) {
+    return NS_OK;
+  }
+
+  nsCOMPtr<mozIApplicationClearPrivateDataParams> params =
+    do_QueryInterface(aSubject);
+  MOZ_ASSERT(params);
+
+  // DataStore is explosed to apps, not browser content.
+  bool browserOnly;
+  nsresult rv = params->GetBrowserOnly(&browserOnly);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  if (browserOnly) {
+    return NS_OK;
+  }
+
+  uint32_t appId;
+  rv = params->GetAppId(&appId);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  DeleteDataStores(appId);
+
+  return NS_OK;
+}
+
+nsresult
+DataStoreService::AddPermissions(uint32_t aAppId,
+                                 const nsAString& aName,
+                                 const nsAString& aOriginURL,
+                                 const nsAString& aManifestURL,
+                                 bool aReadOnly)
+{
+  AssertIsInMainProcess();
+  MOZ_ASSERT(NS_IsMainThread());
+
+  // This is the permission name.
+  nsString permission;
+  GeneratePermissionName(permission, aName, aManifestURL);
+
+  // When a new DataStore is installed, the permissions must be set for the
+  // owner app.
+  nsresult rv = ResetPermission(aAppId, aOriginURL, aManifestURL, permission,
+                                aReadOnly);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  // For any app that wants to have access to this DataStore we add the
+  // permissions.
+  HashApp* apps;
+  if (!mAccessStores.Get(aName, &apps)) {
+    return NS_OK;
+  }
+
+  AddPermissionsData data(permission, aReadOnly);
+  apps->EnumerateRead(AddPermissionsEnumerator, &data);
+  return data.mResult;
+}
+
+nsresult
+DataStoreService::AddAccessPermissions(uint32_t aAppId, const nsAString& aName,
+                                       const nsAString& aOriginURL,
+                                       const nsAString& aManifestURL,
+                                       bool aReadOnly)
+{
+  AssertIsInMainProcess();
+  MOZ_ASSERT(NS_IsMainThread());
+
+  // When an app wants to have access to a DataStore, the permissions must be
+  // set.
+  HashApp* apps = nullptr;
+  if (!mStores.Get(aName, &apps)) {
+    return NS_OK;
+  }
+
+  AddAccessPermissionsData data(aName, aReadOnly);
+  apps->EnumerateRead(AddAccessPermissionsEnumerator, &data);
+  return data.mResult;
+}
+
+// This method starts the operation to create the first revision for a DataStore
+// if needed.
+nsresult
+DataStoreService::CreateFirstRevisionId(uint32_t aAppId,
+                                        const nsAString& aName,
+                                        const nsAString& aManifestURL)
+{
+  AssertIsInMainProcess();
+  MOZ_ASSERT(NS_IsMainThread());
+
+  nsRefPtr<DataStoreDB> db = new DataStoreDB(aManifestURL, aName);
+
+  nsRefPtr<FirstRevisionIdCallback> callback =
+    new FirstRevisionIdCallback(aAppId, aName, aManifestURL);
+
+  Sequence<nsString> dbs;
+  dbs.AppendElement(NS_LITERAL_STRING(DATASTOREDB_REVISION));
+
+  return db->Open(IDBTransactionMode::Readwrite, dbs, callback);
+}
+
+nsresult
+DataStoreService::EnableDataStore(uint32_t aAppId, const nsAString& aName,
+                                  const nsAString& aManifestURL)
+{
+  AssertIsInMainProcess();
+  MOZ_ASSERT(NS_IsMainThread());
+
+  {
+    HashApp* apps = nullptr;
+    DataStoreInfo* info = nullptr;
+    if (mStores.Get(aName, &apps) && apps->Get(aAppId, &info)) {
+      info->Enable();
+    }
+  }
+
+  // Maybe we have some pending request waiting for this DataStore.
+  PendingRequests* requests;
+  if (!mPendingRequests.Get(aName, &requests)) {
+    return NS_OK;
+  }
+
+  for (uint32_t i = 0; i < requests->Length();) {
+    PendingRequest& request = requests->ElementAt(i);
+    nsTArray<nsString>::index_type pos =
+      request.mPendingDataStores.IndexOf(aManifestURL);
+    if (pos != request.mPendingDataStores.NoIndex) {
+      request.mPendingDataStores.RemoveElementAt(pos);
+
+      // No other pending dataStores.
+      if (request.mPendingDataStores.IsEmpty()) {
+        GetDataStoresResolve(request.mWindow, request.mPromise,
+                             request.mStores);
+        requests->RemoveElementAt(i);
+        continue;
+      }
+    }
+
+    ++i;
+  }
+
+  // No other pending requests for this name.
+  if (requests->IsEmpty()) {
+    mPendingRequests.Remove(aName);
+  }
+
+  return NS_OK;
+}
+
+already_AddRefed<RetrieveRevisionsCounter>
+DataStoreService::GetCounter(uint32_t aId) const
+{
+  AssertIsInMainProcess();
+  MOZ_ASSERT(NS_IsMainThread());
+
+  nsRefPtr<RetrieveRevisionsCounter> counter;
+  return mPendingCounters.Get(aId, getter_AddRefs(counter))
+           ? counter.forget() : nullptr;
+}
+
+void
+DataStoreService::RemoveCounter(uint32_t aId)
+{
+  AssertIsInMainProcess();
+  MOZ_ASSERT(NS_IsMainThread());
+
+  mPendingCounters.Remove(aId);
+}
+
+nsresult
+DataStoreService::GenerateUUID(nsAString& aID)
+{
+  nsresult rv;
+
+  if (!mUUIDGenerator) {
+    mUUIDGenerator = do_GetService("@mozilla.org/uuid-generator;1", &rv);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+  }
+
+  nsID id;
+  rv = mUUIDGenerator->GenerateUUIDInPlace(&id);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  char chars[NSID_LENGTH];
+  id.ToProvidedString(chars);
+  CopyASCIItoUTF16(chars, aID);
+
+  return NS_OK;
+}
+
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/datastore/DataStoreService.h
@@ -0,0 +1,103 @@
+/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_dom_DataStoreService_h
+#define mozilla_dom_DataStoreService_h
+
+#include "nsClassHashtable.h"
+#include "nsIDataStoreService.h"
+#include "nsIObserver.h"
+#include "nsRefPtrHashtable.h"
+
+class nsIUUIDGenerator;
+class nsPIDOMWindow;
+
+namespace mozilla {
+namespace dom {
+
+class DataStoreInfo;
+class FirstRevisionIdCallback;
+class PendingRequest;
+class Promise;
+class RetrieveRevisionsCounter;
+class RevisionAddedEnableStoreCallback;
+
+class DataStoreService MOZ_FINAL : public nsIDataStoreService
+                                 , public nsIObserver
+{
+  friend class FirstRevisionIdCallback;
+  friend class RetrieveRevisionsCounter;
+  friend class RevisionAddedEnableStoreCallback;
+
+public:
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIOBSERVER
+  NS_DECL_NSIDATASTORESERVICE
+
+  // Returns the DataStoreService singleton. Only to be called from main
+  // thread.
+  static already_AddRefed<DataStoreService> GetOrCreate();
+
+  static already_AddRefed<DataStoreService> Get();
+
+  static void Shutdown();
+
+  nsresult GenerateUUID(nsAString& aID);
+
+private:
+  DataStoreService();
+  ~DataStoreService();
+
+  nsresult Init();
+
+  typedef nsClassHashtable<nsUint32HashKey, DataStoreInfo> HashApp;
+
+  nsresult AddPermissions(uint32_t aAppId, const nsAString& aName,
+                          const nsAString& aOriginURL,
+                          const nsAString& aManifestURL,
+                          bool aReadOnly);
+
+  nsresult AddAccessPermissions(uint32_t aAppId, const nsAString& aName,
+                                const nsAString& aOriginURL,
+                                const nsAString& aManifestURL,
+                                bool aReadOnly);
+
+  nsresult CreateFirstRevisionId(uint32_t aAppId, const nsAString& aName,
+                                 const nsAString& aManifestURL);
+
+  void GetDataStoresCreate(nsPIDOMWindow* aWindow, Promise* aPromise,
+                           const nsTArray<DataStoreInfo>& aStores);
+
+  void GetDataStoresResolve(nsPIDOMWindow* aWindow, Promise* aPromise,
+                            const nsTArray<DataStoreInfo>& aStores);
+
+  nsresult GetDataStoreInfos(const nsAString& aName, uint32_t aAppId,
+                             nsTArray<DataStoreInfo>& aStores);
+
+  void DeleteDataStores(uint32_t aAppId);
+
+  nsresult EnableDataStore(uint32_t aAppId, const nsAString& aName,
+                           const nsAString& aManifestURL);
+
+  already_AddRefed<RetrieveRevisionsCounter> GetCounter(uint32_t aId) const;
+
+  void RemoveCounter(uint32_t aId);
+
+  nsClassHashtable<nsStringHashKey, HashApp> mStores;
+  nsClassHashtable<nsStringHashKey, HashApp> mAccessStores;
+
+  typedef nsTArray<PendingRequest> PendingRequests;
+  nsClassHashtable<nsStringHashKey, PendingRequests> mPendingRequests;
+
+  nsRefPtrHashtable<nsUint32HashKey, RetrieveRevisionsCounter> mPendingCounters;
+
+  nsCOMPtr<nsIUUIDGenerator> mUUIDGenerator;
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_DataStoreService_h
deleted file mode 100644
--- a/dom/datastore/DataStoreService.js
+++ /dev/null
@@ -1,527 +0,0 @@
-/* -*- Mode: js2; js2-basic-offset: 2; indent-tabs-mode: nil; -*- */
-/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-'use strict'
-
-/* static functions */
-
-function debug(s) {
-  //dump('DEBUG DataStoreService: ' + s + '\n');
-}
-
-const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
-
-Cu.import('resource://gre/modules/XPCOMUtils.jsm');
-Cu.import('resource://gre/modules/Services.jsm');
-Cu.import("resource://gre/modules/DataStoreDB.jsm");
-Cu.import("resource://gre/modules/DOMRequestHelper.jsm");
-
-XPCOMUtils.defineLazyServiceGetter(this, "cpmm",
-                                   "@mozilla.org/childprocessmessagemanager;1",
-                                   "nsIMessageSender");
-
-XPCOMUtils.defineLazyServiceGetter(this, "ppmm",
-                                   "@mozilla.org/parentprocessmessagemanager;1",
-                                   "nsIMessageBroadcaster");
-
-XPCOMUtils.defineLazyServiceGetter(this, "permissionManager",
-                                   "@mozilla.org/permissionmanager;1",
-                                   "nsIPermissionManager");
-
-XPCOMUtils.defineLazyServiceGetter(this, "secMan",
-                                   "@mozilla.org/scriptsecuritymanager;1",
-                                   "nsIScriptSecurityManager");
-
-/* DataStoreService */
-
-const DATASTORESERVICE_CID = Components.ID('{d193d0e2-c677-4a7b-bb0a-19155b470f2e}');
-const REVISION_VOID = "void";
-
-function DataStoreService() {
-  debug('DataStoreService Constructor');
-
-  this.inParent = Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULRuntime)
-                    .processType == Ci.nsIXULRuntime.PROCESS_TYPE_DEFAULT;
-
-  if (this.inParent) {
-    let obs = Services.obs;
-    if (!obs) {
-      debug("DataStore Error: observer-service is null!");
-      return;
-    }
-
-    obs.addObserver(this, 'webapps-clear-data', false);
-  }
-
-  let self = this;
-  cpmm.addMessageListener("datastore-first-revision-created",
-                          function(aMsg) { self.receiveMessage(aMsg); });
-}
-
-DataStoreService.prototype = {
-  inParent: false,
-
-  // Hash of DataStores
-  stores: {},
-  accessStores: {},
-  pendingRequests: {},
-
-  installDataStore: function(aAppId, aName, aOrigin, aOwner, aReadOnly) {
-    debug('installDataStore - appId: ' + aAppId + ', aName: ' +
-          aName + ', aOrigin: ' + aOrigin + ', aOwner:' + aOwner +
-          ', aReadOnly: ' + aReadOnly);
-
-    this.checkIfInParent();
-
-    if (aName in this.stores && aAppId in this.stores[aName]) {
-      debug('This should not happen');
-      return;
-    }
-
-    if (!(aName in this.stores)) {
-      this.stores[aName] = {};
-    }
-
-    // A DataStore is enabled when it has a first valid revision.
-    this.stores[aName][aAppId] = { origin: aOrigin, owner: aOwner,
-                                   readOnly: aReadOnly, enabled: false };
-
-    this.addPermissions(aAppId, aName, aOrigin, aOwner, aReadOnly);
-
-    this.createFirstRevisionId(aAppId, aName, aOwner);
-  },
-
-  installAccessDataStore: function(aAppId, aName, aOrigin, aOwner, aReadOnly) {
-    debug('installAccessDataStore - appId: ' + aAppId + ', aName: ' +
-          aName + ', aOrigin: ' + aOrigin + ', aOwner:' + aOwner +
-          ', aReadOnly: ' + aReadOnly);
-
-    this.checkIfInParent();
-
-    if (aName in this.accessStores && aAppId in this.accessStores[aName]) {
-      debug('This should not happen');
-      return;
-    }
-
-    if (!(aName in this.accessStores)) {
-      this.accessStores[aName] = {};
-    }
-
-    this.accessStores[aName][aAppId] = { origin: aOrigin, owner: aOwner,
-                                         readOnly: aReadOnly };
-    this.addAccessPermissions(aAppId, aName, aOrigin, aOwner, aReadOnly);
-  },
-
-  checkIfInParent: function() {
-    if (!this.inParent) {
-      throw "DataStore can execute this operation just in the parent process";
-    }
-  },
-
-  createFirstRevisionId: function(aAppId, aName, aOwner) {
-    debug("createFirstRevisionId database: " + aName);
-
-    let self = this;
-    let db = new DataStoreDB();
-    db.init(aOwner, aName);
-    db.revisionTxn(
-      'readwrite',
-      function(aTxn, aRevisionStore) {
-        debug("createFirstRevisionId - transaction success");
-
-        let request = aRevisionStore.openCursor(null, 'prev');
-        request.onsuccess = function(aEvent) {
-          let cursor = aEvent.target.result;
-          if (cursor) {
-            debug("First revision already created.");
-            self.enableDataStore(aAppId, aName, aOwner);
-          } else {
-            // If the revision doesn't exist, let's create the first one.
-            db.addRevision(aRevisionStore, 0, REVISION_VOID, function() {
-              debug("First revision created.");
-              self.enableDataStore(aAppId, aName, aOwner);
-            });
-          }
-        };
-      }
-    );
-  },
-
-  enableDataStore: function(aAppId, aName, aOwner) {
-    if (aName in this.stores && aAppId in this.stores[aName]) {
-      this.stores[aName][aAppId].enabled = true;
-      ppmm.broadcastAsyncMessage('datastore-first-revision-created',
-                                 { name: aName, owner: aOwner });
-    }
-  },
-
-  addPermissions: function(aAppId, aName, aOrigin, aOwner, aReadOnly) {
-    // When a new DataStore is installed, the permissions must be set for the
-    // owner app.
-    let permission = "indexedDB-chrome-" + aName + '|' + aOwner;
-    this.resetPermissions(aAppId, aOrigin, aOwner, permission, aReadOnly);
-
-    // For any app that wants to have access to this DataStore we add the
-    // permissions.
-    if (aName in this.accessStores) {
-      for (let appId in this.accessStores[aName]) {
-        // ReadOnly is decided by the owner first.
-        let readOnly = aReadOnly || this.accessStores[aName][appId].readOnly;
-        this.resetPermissions(appId, this.accessStores[aName][appId].origin,
-                              this.accessStores[aName][appId].owner,
-                              permission, readOnly);
-      }
-    }
-  },
-
-  addAccessPermissions: function(aAppId, aName, aOrigin, aOwner, aReadOnly) {
-    // When an app wants to have access to a DataStore, the permissions must be
-    // set.
-    if (!(aName in this.stores)) {
-      return;
-    }
-
-    for (let appId in this.stores[aName]) {
-      let permission = "indexedDB-chrome-" + aName + '|' + this.stores[aName][appId].owner;
-      // The ReadOnly is decied by the owenr first.
-      let readOnly = this.stores[aName][appId].readOnly || aReadOnly;
-      this.resetPermissions(aAppId, aOrigin, aOwner, permission, readOnly);
-    }
-  },
-
-  resetPermissions: function(aAppId, aOrigin, aOwner, aPermission, aReadOnly) {
-    debug("ResetPermissions - appId: " + aAppId + " - origin: " + aOrigin +
-          " - owner: " + aOwner + " - permissions: " + aPermission +
-          " - readOnly: " + aReadOnly);
-
-    let uri = Services.io.newURI(aOrigin, null, null);
-    let principal = secMan.getAppCodebasePrincipal(uri, aAppId, false);
-
-    let result = permissionManager.testExactPermissionFromPrincipal(principal,
-                                                                    aPermission + '-write');
-
-    if (aReadOnly && result == Ci.nsIPermissionManager.ALLOW_ACTION) {
-      debug("Write permission removed");
-      permissionManager.removeFromPrincipal(principal, aPermission + '-write');
-    } else if (!aReadOnly && result != Ci.nsIPermissionManager.ALLOW_ACTION) {
-      debug("Write permission added");
-      permissionManager.addFromPrincipal(principal, aPermission + '-write',
-                                         Ci.nsIPermissionManager.ALLOW_ACTION);
-    }
-
-    result = permissionManager.testExactPermissionFromPrincipal(principal,
-                                                                aPermission + '-read');
-    if (result != Ci.nsIPermissionManager.ALLOW_ACTION) {
-      debug("Read permission added");
-      permissionManager.addFromPrincipal(principal, aPermission + '-read',
-                                         Ci.nsIPermissionManager.ALLOW_ACTION);
-    }
-
-    result = permissionManager.testExactPermissionFromPrincipal(principal, aPermission);
-    if (result != Ci.nsIPermissionManager.ALLOW_ACTION) {
-      debug("Generic permission added");
-      permissionManager.addFromPrincipal(principal, aPermission,
-                                         Ci.nsIPermissionManager.ALLOW_ACTION);
-    }
-  },
-
-  getDataStores: function(aWindow, aName) {
-    debug('getDataStores - aName: ' + aName);
-
-    let self = this;
-    return new aWindow.Promise(function(resolve, reject) {
-      // If this request comes from the main process, we have access to the
-      // window, so we can skip the ipc communication.
-      if (self.inParent) {
-        let stores = self.getDataStoresInfo(aName, aWindow.document.nodePrincipal.appId);
-        if (stores === null) {
-          reject(new aWindow.DOMError("SecurityError", "Access denied"));
-          return;
-        }
-        self.getDataStoreCreate(aWindow, resolve, stores);
-      } else {
-        // This method can be called in the child so we need to send a request
-        // to the parent and create DataStore object here.
-        new DataStoreServiceChild(aWindow, aName, function(aStores) {
-          debug("DataStoreServiceChild success callback!");
-          self.getDataStoreCreate(aWindow, resolve, aStores);
-        }, function() {
-          debug("DataStoreServiceChild error callback!");
-          reject(new aWindow.DOMError("SecurityError", "Access denied"));
-        });
-      }
-    });
-  },
-
-  getDataStoresInfo: function(aName, aAppId) {
-    debug('GetDataStoresInfo');
-
-    let appsService = Cc["@mozilla.org/AppsService;1"]
-                        .getService(Ci.nsIAppsService);
-    let app = appsService.getAppByLocalId(aAppId);
-    if (!app) {
-      return null;
-    }
-
-    let prefName = "dom.testing.datastore_enabled_for_hosted_apps";
-    if (app.appStatus != Ci.nsIPrincipal.APP_STATUS_CERTIFIED &&
-        (Services.prefs.getPrefType(prefName) == Services.prefs.PREF_INVALID ||
-          !Services.prefs.getBoolPref(prefName))) {
-      return null;
-    }
-
-    let results = [];
-
-    if (aName in this.stores) {
-      if (aAppId in this.stores[aName]) {
-        results.push({ name: aName,
-                       owner: this.stores[aName][aAppId].owner,
-                       readOnly: false,
-                       enabled: this.stores[aName][aAppId].enabled });
-      }
-
-      for (var i in this.stores[aName]) {
-        if (i == aAppId) {
-          continue;
-        }
-
-        let access = this.getDataStoreAccess(aName, aAppId);
-        if (!access) {
-          continue;
-        }
-
-        let readOnly = this.stores[aName][i].readOnly || access.readOnly;
-        results.push({ name: aName,
-                       owner: this.stores[aName][i].owner,
-                       readOnly: readOnly,
-                       enabled: this.stores[aName][i].enabled });
-      }
-    }
-
-    return results;
-  },
-
-  getDataStoreCreate: function(aWindow, aResolve, aStores) {
-    debug("GetDataStoreCreate");
-
-    let results = new aWindow.Array();
-
-    if (!aStores.length) {
-      aResolve(results);
-      return;
-    }
-
-    let pendingDataStores = [];
-
-    for (let i = 0; i < aStores.length; ++i) {
-      if (!aStores[i].enabled) {
-        pendingDataStores.push(aStores[i].owner);
-      }
-    }
-
-    if (!pendingDataStores.length) {
-      this.getDataStoreResolve(aWindow, aResolve, aStores);
-      return;
-    }
-
-    if (!(aStores[0].name in this.pendingRequests)) {
-      this.pendingRequests[aStores[0].name] = [];
-    }
-
-    this.pendingRequests[aStores[0].name].push({ window: aWindow,
-                                                 resolve: aResolve,
-                                                 stores: aStores,
-                                                 pendingDataStores: pendingDataStores });
-  },
-
-  getDataStoreResolve: function(aWindow, aResolve, aStores) {
-    debug("GetDataStoreResolve");
-
-    let callbackPending = aStores.length;
-    let results = new aWindow.Array();
-
-    if (!callbackPending) {
-      aResolve(results);
-      return;
-    }
-
-    for (let i = 0; i < aStores.length; ++i) {
-      let obj = Cc["@mozilla.org/dom/datastore;1"]
-                  .createInstance(Ci.nsIDataStore);
-      if (!obj || !obj.wrappedJSObject) {
-        dump("Failed to create a DataStore object.\n");
-        return;
-      }
-
-      obj.init(aWindow, aStores[i].name, aStores[i].owner, aStores[i].readOnly);
-
-      let storeImpl = aWindow.DataStoreImpl._create(aWindow, obj.wrappedJSObject);
-
-      let exposedStore = new aWindow.DataStore();
-      exposedStore.setDataStoreImpl(storeImpl);
-
-      obj.exposedObject = exposedStore;
-
-      results.push(exposedStore);
-
-      obj.retrieveRevisionId(
-        function() {
-          --callbackPending;
-          if (!callbackPending) {
-            aResolve(results);
-          }
-        }
-      );
-    }
-  },
-
-  getDataStoreAccess: function(aName, aAppId) {
-    if (!(aName in this.accessStores) ||
-        !(aAppId in this.accessStores[aName])) {
-      return null;
-    }
-
-    return this.accessStores[aName][aAppId];
-  },
-
-  observe: function observe(aSubject, aTopic, aData) {
-    debug('observe - aTopic: ' + aTopic);
-    if (aTopic != 'webapps-clear-data') {
-      return;
-    }
-
-    let params =
-      aSubject.QueryInterface(Ci.mozIApplicationClearPrivateDataParams);
-
-    // DataStore is explosed to apps, not browser content.
-    if (params.browserOnly) {
-      return;
-    }
-
-    function isEmpty(aMap) {
-      for (var key in aMap) {
-        if (aMap.hasOwnProperty(key)) {
-          return false;
-        }
-      }
-      return true;
-    }
-
-    for (let key in this.stores) {
-      if (params.appId in this.stores[key]) {
-        this.deleteDatabase(key, this.stores[key][params.appId].owner);
-        delete this.stores[key][params.appId];
-      }
-
-      if (isEmpty(this.stores[key])) {
-        delete this.stores[key];
-      }
-    }
-
-    for (let key in this.accessStores) {
-      if (params.appId in this.accessStores[key]) {
-        delete this.accessStores[key][params.appId];
-      }
-
-      if (isEmpty(this.accessStores[key])) {
-        delete this.accessStores[key];
-      }
-    }
-  },
-
-  deleteDatabase: function(aName, aOwner) {
-    debug("delete database: " + aName);
-
-    let db = new DataStoreDB();
-    db.init(aOwner, aName);
-    db.delete();
-  },
-
-  receiveMessage: function(aMsg) {
-    debug("receiveMessage");
-    let data = aMsg.json;
-
-    if (!(data.name in this.pendingRequests)) {
-      return;
-    }
-
-    for (let i = 0; i < this.pendingRequests[data.name].length;) {
-      let pos = this.pendingRequests[data.name][i].pendingDataStores.indexOf(data.owner);
-      if (pos != -1) {
-        this.pendingRequests[data.name][i].pendingDataStores.splice(pos, 1);
-        if (!this.pendingRequests[data.name][i].pendingDataStores.length) {
-          this.getDataStoreResolve(this.pendingRequests[data.name][i].window,
-                                   this.pendingRequests[data.name][i].resolve,
-                                   this.pendingRequests[data.name][i].stores);
-          this.pendingRequests[data.name].splice(i, 1);
-          continue;
-        }
-      }
-
-      ++i;
-    }
-
-    if (!this.pendingRequests[data.name].length) {
-      delete this.pendingRequests[data.name];
-    }
-  },
-
-  classID : DATASTORESERVICE_CID,
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIDataStoreService,
-                                         Ci.nsIObserver]),
-  classInfo: XPCOMUtils.generateCI({
-    classID: DATASTORESERVICE_CID,
-    contractID: '@mozilla.org/datastore-service;1',
-    interfaces: [Ci.nsIDataStoreService, Ci.nsIObserver],
-    flags: Ci.nsIClassInfo.SINGLETON
-  })
-};
-
-/* DataStoreServiceChild */
-
-function DataStoreServiceChild(aWindow, aName, aSuccessCb, aErrorCb) {
-  debug("DataStoreServiceChild created");
-  this.init(aWindow, aName, aSuccessCb, aErrorCb);
-}
-
-DataStoreServiceChild.prototype = {
-  __proto__: DOMRequestIpcHelper.prototype,
-
-  init: function(aWindow, aName, aSuccessCb, aErrorCb) {
-    debug("DataStoreServiceChild init");
-    this._successCb = aSuccessCb;
-    this._errorCb = aErrorCb;
-    this._name = aName;
-
-    this.initDOMRequestHelper(aWindow, [ "DataStore:Get:Return:OK",
-                                         "DataStore:Get:Return:KO" ]);
-
-    cpmm.sendAsyncMessage("DataStore:Get",
-                          { name: aName }, null, aWindow.document.nodePrincipal );
-  },
-
-  receiveMessage: function(aMessage) {
-    debug("DataStoreServiceChild receiveMessage");
-
-    if (aMessage.data.name != this._name) {
-      return;
-    }
-
-    switch (aMessage.name) {
-      case 'DataStore:Get:Return:OK':
-        this.destroyDOMRequestHelper();
-        this._successCb(aMessage.data.stores);
-        break;
-
-      case 'DataStore:Get:Return:KO':
-        this.destroyDOMRequestHelper();
-        this._errorCb();
-        break;
-    }
-  }
-}
-
-this.NSGetFactory = XPCOMUtils.generateNSGetFactory([DataStoreService]);
--- a/dom/datastore/moz.build
+++ b/dom/datastore/moz.build
@@ -9,35 +9,41 @@ XPIDL_SOURCES += [
     'nsIDataStoreService.idl',
 ]
 
 XPIDL_MODULE = 'dom_datastore'
 
 EXPORTS.mozilla.dom += [
     'DataStore.h',
     'DataStoreCursor.h',
+    'DataStoreService.h',
 ]
 
 SOURCES += [
     'DataStore.cpp',
     'DataStoreCursor.cpp',
+    'DataStoreDB.cpp',
+    'DataStoreRevision.cpp',
+    'DataStoreService.cpp',
 ]
 
 LOCAL_INCLUDES += [
     '/js/xpconnect/wrappers',
 ]
 
 EXTRA_COMPONENTS += [
     'DataStore.manifest',
     'DataStoreImpl.js',
-    'DataStoreService.js',
 ]
 
 EXTRA_JS_MODULES += [
     'DataStoreChangeNotifier.jsm',
     'DataStoreCursorImpl.jsm',
     'DataStoreDB.jsm',
     'DataStoreServiceInternal.jsm',
 ]
 
 MOCHITEST_MANIFESTS += ['tests/mochitest.ini']
 
+include('/ipc/chromium/chromium-config.mozbuild')
+
 FINAL_LIBRARY = 'xul'
+FAIL_ON_WARNINGS = True
--- a/dom/datastore/nsIDataStoreService.idl
+++ b/dom/datastore/nsIDataStoreService.idl
@@ -2,34 +2,26 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsISupports.idl"
 
 interface nsIDOMWindow;
 
-[scriptable, uuid(bd02d09c-41ab-47b7-9319-57aa8e5059b0)]
+[scriptable, uuid(0a050c4f-d292-4a14-8712-09bc1019840a)]
 interface nsIDataStoreService : nsISupports
 {
   void installDataStore(in unsigned long appId,
                         in DOMString name,
                         in DOMString originURL,
                         in DOMString manifestURL,
                         in boolean readOnly);
 
   void installAccessDataStore(in unsigned long appId,
                               in DOMString name,
                               in DOMString originURL,
                               in DOMString manifestURL,
                               in boolean readOnly);
 
   nsISupports getDataStores(in nsIDOMWindow window,
                             in DOMString name);
-
-  // This is an array of objects composed by:
-  // - readOnly: boolean
-  // - name: DOMString
-  // - owner: DOMString
-  // - enabled: true/false - true if this dataStore is ready to be used.
-  jsval getDataStoresInfo(in DOMString name,
-                          in unsigned long appId);
 };
--- a/dom/indexedDB/IDBFactory.cpp
+++ b/dom/indexedDB/IDBFactory.cpp
@@ -230,19 +230,16 @@ IDBFactory::Create(JSContext* aCx,
 // static
 nsresult
 IDBFactory::Create(ContentParent* aContentParent,
                    IDBFactory** aFactory)
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
   NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
   NS_ASSERTION(nsContentUtils::IsCallerChrome(), "Only for chrome!");
-  NS_ASSERTION(aContentParent, "Null ContentParent!");
-
-  NS_ASSERTION(!nsContentUtils::GetCurrentJSContext(), "Should be called from C++");
 
   // We need to get this information before we push a null principal to avoid
   // IsCallerChrome() assertion in quota manager.
   nsCString group;
   nsCString origin;
   StoragePrivilege privilege;
   PersistenceType defaultPersistenceType;
   QuotaManager::GetInfoForChrome(&group, &origin, &privilege,
--- a/dom/indexedDB/IDBFactory.h
+++ b/dom/indexedDB/IDBFactory.h
@@ -70,17 +70,17 @@ public:
   // Called when using IndexedDB from a JS component or a JSM in the current
   // process.
   static nsresult Create(JSContext* aCx,
                          JS::Handle<JSObject*> aOwningObject,
                          ContentParent* aContentParent,
                          IDBFactory** aFactory);
 
   // Called when using IndexedDB from a JS component or a JSM in a different
-  // process.
+  // process or from a C++ component.
   static nsresult Create(ContentParent* aContentParent,
                          IDBFactory** aFactory);
 
   static already_AddRefed<nsIFileURL>
   GetDatabaseFileURL(nsIFile* aDatabaseFile,
                      PersistenceType aPersistenceType,
                      const nsACString& aGroup,
                      const nsACString& aOrigin);
--- a/dom/indexedDB/IDBRequest.cpp
+++ b/dom/indexedDB/IDBRequest.cpp
@@ -322,17 +322,17 @@ IDBRequest::ReadyState() const
 
 JSObject*
 IDBRequest::WrapObject(JSContext* aCx)
 {
   return IDBRequestBinding::Wrap(aCx, this);
 }
 
 JS::Value
-IDBRequest::GetResult(JSContext* aCx, mozilla::ErrorResult& aRv) const
+IDBRequest::GetResult(mozilla::ErrorResult& aRv) const
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
   if (!mHaveResultOrErrorCode) {
     // XXX Need a real error code here.
     aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR);
   }
 
--- a/dom/indexedDB/IDBRequest.h
+++ b/dom/indexedDB/IDBRequest.h
@@ -127,17 +127,23 @@ public:
   // WebIDL
   nsPIDOMWindow*
   GetParentObject() const
   {
     return GetOwner();
   }
 
   JS::Value
-  GetResult(JSContext* aCx, ErrorResult& aRv) const;
+  GetResult(ErrorResult& aRv) const;
+
+  JS::Value
+  GetResult(JSContext* aCx, ErrorResult& aRv) const
+  {
+    return GetResult(aRv);
+  }
 
   IDBTransaction*
   GetTransaction() const
   {
     NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
     return mTransaction;
   }
 
--- a/dom/webidl/DataStore.webidl
+++ b/dom/webidl/DataStore.webidl
@@ -103,8 +103,15 @@ dictionary DataStoreTask {
   DOMString revisionId;
 
   DataStoreOperation operation;
 
   // When |operation| is "clear" or "done", this must return null.
   DataStoreKey? id;
   any data;
 };
+
+// For internal use.
+dictionary DataStoreRevisionData {
+  DOMString revisionId = "";
+  unsigned long objectId = 0;
+  DOMString operation = "";
+};
--- a/layout/build/nsLayoutModule.cpp
+++ b/layout/build/nsLayoutModule.cpp
@@ -224,16 +224,18 @@ static void Shutdown();
 #include "mozilla/dom/mobilemessage/SmsServicesFactory.h"
 #include "nsIPowerManagerService.h"
 #include "nsIAlarmHalService.h"
 #include "nsIMediaManager.h"
 #include "nsMixedContentBlocker.h"
 
 #include "AudioChannelService.h"
 
+#include "mozilla/dom/DataStoreService.h"
+
 #include "mozilla/dom/power/PowerManagerService.h"
 #include "mozilla/dom/alarm/AlarmHalService.h"
 #include "mozilla/dom/time/TimeService.h"
 #include "StreamingProtocolService.h"
 
 #include "mozilla/dom/telephony/TelephonyFactory.h"
 #include "nsITelephonyProvider.h"
 
@@ -579,20 +581,25 @@ NS_GENERIC_FACTORY_CONSTRUCTOR(nsDOMScri
 NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(Geolocation, Init)
 
 #define NS_GEOLOCATION_SERVICE_CID \
   { 0x404d02a, 0x1CA, 0xAAAB, { 0x47, 0x62, 0x94, 0x4b, 0x1b, 0xf2, 0xf7, 0xb5 } }
 
 #define NS_AUDIOCHANNEL_SERVICE_CID \
   { 0xf712e983, 0x048a, 0x443f, { 0x88, 0x02, 0xfc, 0xc3, 0xd9, 0x27, 0xce, 0xac }}
 
+#define NS_DATASTORE_SERVICE_CID \
+  { 0x0d4285fe, 0xf1b3, 0x49fa, { 0xbc, 0x51, 0xa4, 0xa8, 0x3f, 0x0a, 0xaf, 0x85 }}
+
 NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(nsGeolocationService, nsGeolocationService::GetGeolocationService)
 
 NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(AudioChannelService, AudioChannelService::GetAudioChannelService)
 
+NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(DataStoreService, DataStoreService::GetOrCreate)
+
 #ifdef MOZ_WEBSPEECH
 NS_GENERIC_FACTORY_CONSTRUCTOR(FakeSpeechRecognitionService)
 #endif
 
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsCSPContext)
 NS_GENERIC_FACTORY_CONSTRUCTOR(CSPService)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsMixedContentBlocker)
 
@@ -732,16 +739,17 @@ NS_DEFINE_NAMED_CID(NS_HTMLEDITOR_CID);
 NS_DEFINE_NAMED_CID(NS_EDITORCONTROLLER_CID);
 NS_DEFINE_NAMED_CID(NS_EDITINGCONTROLLER_CID);
 NS_DEFINE_NAMED_CID(NS_EDITORCOMMANDTABLE_CID);
 NS_DEFINE_NAMED_CID(NS_EDITINGCOMMANDTABLE_CID);
 NS_DEFINE_NAMED_CID(NS_TEXTSERVICESDOCUMENT_CID);
 NS_DEFINE_NAMED_CID(NS_GEOLOCATION_SERVICE_CID);
 NS_DEFINE_NAMED_CID(NS_GEOLOCATION_CID);
 NS_DEFINE_NAMED_CID(NS_AUDIOCHANNEL_SERVICE_CID);
+NS_DEFINE_NAMED_CID(NS_DATASTORE_SERVICE_CID);
 NS_DEFINE_NAMED_CID(NS_FOCUSMANAGER_CID);
 NS_DEFINE_NAMED_CID(CSPSERVICE_CID);
 NS_DEFINE_NAMED_CID(NS_CSPCONTEXT_CID);
 NS_DEFINE_NAMED_CID(NS_MIXEDCONTENTBLOCKER_CID);
 NS_DEFINE_NAMED_CID(NS_EVENTLISTENERSERVICE_CID);
 NS_DEFINE_NAMED_CID(NS_GLOBALMESSAGEMANAGER_CID);
 NS_DEFINE_NAMED_CID(NS_PARENTPROCESSMESSAGEMANAGER_CID);
 NS_DEFINE_NAMED_CID(NS_CHILDPROCESSMESSAGEMANAGER_CID);
@@ -1020,16 +1028,17 @@ static const mozilla::Module::CIDEntry k
   { &kNS_EDITORCONTROLLER_CID, false, nullptr, nsEditorControllerConstructor },
   { &kNS_EDITINGCONTROLLER_CID, false, nullptr, nsEditingControllerConstructor },
   { &kNS_EDITORCOMMANDTABLE_CID, false, nullptr, nsEditorCommandTableConstructor },
   { &kNS_EDITINGCOMMANDTABLE_CID, false, nullptr, nsEditingCommandTableConstructor },
   { &kNS_TEXTSERVICESDOCUMENT_CID, false, nullptr, nsTextServicesDocumentConstructor },
   { &kNS_GEOLOCATION_SERVICE_CID, false, nullptr, nsGeolocationServiceConstructor },
   { &kNS_GEOLOCATION_CID, false, nullptr, GeolocationConstructor },
   { &kNS_AUDIOCHANNEL_SERVICE_CID, false, nullptr, AudioChannelServiceConstructor },
+  { &kNS_DATASTORE_SERVICE_CID, false, nullptr, DataStoreServiceConstructor },
   { &kNS_FOCUSMANAGER_CID, false, nullptr, CreateFocusManager },
 #ifdef MOZ_WEBSPEECH
   { &kNS_FAKE_SPEECH_RECOGNITION_SERVICE_CID, false, nullptr, FakeSpeechRecognitionServiceConstructor },
   { &kNS_SYNTHVOICEREGISTRY_CID, true, nullptr, nsSynthVoiceRegistryConstructor },
 #endif
   { &kCSPSERVICE_CID, false, nullptr, CSPServiceConstructor },
   { &kNS_CSPCONTEXT_CID, false, nullptr, nsCSPContextConstructor },
   { &kNS_MIXEDCONTENTBLOCKER_CID, false, nullptr, nsMixedContentBlockerConstructor },
@@ -1175,16 +1184,17 @@ static const mozilla::Module::ContractID
   { NS_AUDIOCHANNELAGENT_CONTRACTID, &kNS_AUDIOCHANNELAGENT_CID },
   { "@mozilla.org/editor/htmleditor;1", &kNS_HTMLEDITOR_CID },
   { "@mozilla.org/editor/editorcontroller;1", &kNS_EDITORCONTROLLER_CID },
   { "@mozilla.org/editor/editingcontroller;1", &kNS_EDITINGCONTROLLER_CID },
   { "@mozilla.org/textservices/textservicesdocument;1", &kNS_TEXTSERVICESDOCUMENT_CID },
   { "@mozilla.org/geolocation/service;1", &kNS_GEOLOCATION_SERVICE_CID },
   { "@mozilla.org/geolocation;1", &kNS_GEOLOCATION_CID },
   { "@mozilla.org/audiochannel/service;1", &kNS_AUDIOCHANNEL_SERVICE_CID },
+  { "@mozilla.org/datastore-service;1", &kNS_DATASTORE_SERVICE_CID },
   { "@mozilla.org/focus-manager;1", &kNS_FOCUSMANAGER_CID },
 #ifdef MOZ_WEBSPEECH
   { NS_SPEECH_RECOGNITION_SERVICE_CONTRACTID_PREFIX "fake", &kNS_FAKE_SPEECH_RECOGNITION_SERVICE_CID },
   { NS_SYNTHVOICEREGISTRY_CONTRACTID, &kNS_SYNTHVOICEREGISTRY_CID },
 #endif
   { CSPSERVICE_CONTRACTID, &kCSPSERVICE_CID },
   { NS_CSPCONTEXT_CONTRACTID, &kNS_CSPCONTEXT_CID },
   { NS_MIXEDCONTENTBLOCKER_CONTRACTID, &kNS_MIXEDCONTENTBLOCKER_CID },
--- a/layout/build/nsLayoutStatics.cpp
+++ b/layout/build/nsLayoutStatics.cpp
@@ -60,16 +60,17 @@
 #include "nsMathMLOperators.h"
 #include "Navigator.h"
 #include "DOMStorageObserver.h"
 #include "CacheObserver.h"
 #include "DisplayItemClip.h"
 #include "ActiveLayerTracker.h"
 
 #include "AudioChannelService.h"
+#include "mozilla/dom/DataStoreService.h"
 
 #ifdef MOZ_XUL
 #include "nsXULPopupManager.h"
 #include "nsXULContentUtils.h"
 #include "nsXULPrototypeCache.h"
 #include "nsXULTooltipListener.h"
 
 #include "inDOMView.h"
@@ -406,16 +407,18 @@ nsLayoutStatics::Shutdown()
 
   nsLayoutUtils::Shutdown();
 
   nsHyphenationManager::Shutdown();
   nsDOMMutationObserver::Shutdown();
 
   AudioChannelService::Shutdown();
 
+  DataStoreService::Shutdown();
+
   ContentParent::ShutDown();
 
   nsRefreshDriver::Shutdown();
 
   DisplayItemClip::Shutdown();
 
   nsDocument::XPCOMShutdown();
 
--- a/mobile/android/installer/package-manifest.in
+++ b/mobile/android/installer/package-manifest.in
@@ -619,11 +619,10 @@ bin/components/@DLL_PREFIX@nkgnomevfs@DL
 @BINPATH@/components/marionettecomponent.js
 #endif
 
 #ifdef MOZ_ANDROID_SYNTHAPKS
 @BINPATH@/components/WebappsUpdateTimer.js
 #endif
 
 @BINPATH@/components/DataStore.manifest
-@BINPATH@/components/DataStoreService.js
 @BINPATH@/components/DataStoreImpl.js
 @BINPATH@/components/dom_datastore.xpt