Bug 1361330 - Part 4: Core implementation; r=asuth
authorJan Varga <jan.varga@gmail.com>
Mon, 20 Aug 2018 14:33:03 +0200
changeset 487445 b23989158dafee59a3d7c07ae7109a1e6be93670
parent 487444 bf1a68dee63bed087d408fae4df0e1b2671f9338
child 487446 481c13a4221b4533b594954abe01f25ffb3c4e36
push id9719
push userffxbld-merge
push dateFri, 24 Aug 2018 17:49:46 +0000
treeherdermozilla-beta@719ec98fba77 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersasuth
bugs1361330
milestone63.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 1361330 - Part 4: Core implementation; r=asuth
dom/moz.build
dom/quota/ActorsParent.cpp
dom/quota/Client.h
dom/simpledb/ActorsChild.cpp
dom/simpledb/ActorsChild.h
dom/simpledb/ActorsParent.cpp
dom/simpledb/ActorsParent.h
dom/simpledb/PBackgroundSDBConnection.ipdl
dom/simpledb/PBackgroundSDBRequest.ipdl
dom/simpledb/SDBConnection.cpp
dom/simpledb/SDBConnection.h
dom/simpledb/SDBRequest.cpp
dom/simpledb/SDBRequest.h
dom/simpledb/SDBResults.cpp
dom/simpledb/SDBResults.h
dom/simpledb/moz.build
dom/simpledb/nsISDBCallbacks.idl
dom/simpledb/nsISDBConnection.idl
dom/simpledb/nsISDBRequest.idl
dom/simpledb/nsISDBResults.idl
ipc/glue/BackgroundChildImpl.cpp
ipc/glue/BackgroundChildImpl.h
ipc/glue/BackgroundParentImpl.cpp
ipc/glue/BackgroundParentImpl.h
ipc/glue/PBackground.ipdl
layout/build/nsLayoutCID.h
layout/build/nsLayoutModule.cpp
--- a/dom/moz.build
+++ b/dom/moz.build
@@ -96,16 +96,17 @@ DIRS += [
     'performance',
     'webbrowserpersist',
     'xhr',
     'worklet',
     'script',
     'payments',
     'websocket',
     'serviceworkers',
+    'simpledb',
 ]
 
 if CONFIG['OS_ARCH'] == 'WINNT':
     DIRS += ['plugins/ipc/hangui']
 
 DIRS += ['presentation']
 
 TEST_DIRS += [
--- a/dom/quota/ActorsParent.cpp
+++ b/dom/quota/ActorsParent.cpp
@@ -31,16 +31,17 @@
 #include "mozilla/CondVar.h"
 #include "mozilla/dom/PContent.h"
 #include "mozilla/dom/asmjscache/AsmJSCache.h"
 #include "mozilla/dom/cache/QuotaClient.h"
 #include "mozilla/dom/indexedDB/ActorsParent.h"
 #include "mozilla/dom/quota/PQuotaParent.h"
 #include "mozilla/dom/quota/PQuotaRequestParent.h"
 #include "mozilla/dom/quota/PQuotaUsageRequestParent.h"
+#include "mozilla/dom/simpledb/ActorsParent.h"
 #include "mozilla/dom/StorageActivityService.h"
 #include "mozilla/ipc/BackgroundParent.h"
 #include "mozilla/ipc/BackgroundUtils.h"
 #include "mozilla/IntegerRange.h"
 #include "mozilla/Mutex.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/Services.h"
 #include "mozilla/StaticPtr.h"
@@ -3595,26 +3596,31 @@ QuotaManager::Init(const nsAString& aBas
 
   // Make a timer here to avoid potential failures later. We don't actually
   // initialize the timer until shutdown.
   mShutdownTimer = NS_NewTimer();
   if (NS_WARN_IF(!mShutdownTimer)) {
     return NS_ERROR_FAILURE;
   }
 
-  static_assert(Client::IDB == 0 && Client::ASMJS == 1 && Client::DOMCACHE == 2 &&
-                Client::TYPE_MAX == 3, "Fix the registration!");
+  static_assert(Client::IDB == 0 &&
+                Client::ASMJS == 1 &&
+                Client::DOMCACHE == 2 &&
+                Client::SDB == 3 &&
+                Client::TYPE_MAX == 4,
+                "Fix the registration!");
 
   MOZ_ASSERT(mClients.Capacity() == Client::TYPE_MAX,
              "Should be using an auto array with correct capacity!");
 
   // Register clients.
   mClients.AppendElement(indexedDB::CreateQuotaClient());
   mClients.AppendElement(asmjscache::CreateClient());
   mClients.AppendElement(cache::CreateQuotaClient());
+  mClients.AppendElement(simpledb::CreateQuotaClient());
 
   return NS_OK;
 }
 
 void
 QuotaManager::Shutdown()
 {
   AssertIsOnOwningThread();
--- a/dom/quota/Client.h
+++ b/dom/quota/Client.h
@@ -14,16 +14,17 @@
 #include "PersistenceType.h"
 
 class nsIFile;
 class nsIRunnable;
 
 #define IDB_DIRECTORY_NAME "idb"
 #define ASMJSCACHE_DIRECTORY_NAME "asmjs"
 #define DOMCACHE_DIRECTORY_NAME "cache"
+#define SDB_DIRECTORY_NAME "sdb"
 #define LS_DIRECTORY_NAME "ls"
 
 BEGIN_QUOTA_NAMESPACE
 
 class QuotaManager;
 class UsageInfo;
 
 // An abstract interface for quota manager clients.
@@ -37,16 +38,17 @@ public:
   NS_INLINE_DECL_PURE_VIRTUAL_REFCOUNTING
 
   enum Type {
     IDB = 0,
     //LS,
     //APPCACHE,
     ASMJS,
     DOMCACHE,
+    SDB,
     TYPE_MAX
   };
 
   virtual Type
   GetType() = 0;
 
   static nsresult
   TypeToText(Type aType, nsAString& aText)
@@ -59,16 +61,20 @@ public:
       case ASMJS:
         aText.AssignLiteral(ASMJSCACHE_DIRECTORY_NAME);
         break;
 
       case DOMCACHE:
         aText.AssignLiteral(DOMCACHE_DIRECTORY_NAME);
         break;
 
+      case SDB:
+        aText.AssignLiteral(SDB_DIRECTORY_NAME);
+        break;
+
       case TYPE_MAX:
       default:
         MOZ_ASSERT_UNREACHABLE("Bad id value!");
         return NS_ERROR_UNEXPECTED;
     }
 
     return NS_OK;
   }
@@ -80,16 +86,19 @@ public:
       aType = IDB;
     }
     else if (aText.EqualsLiteral(ASMJSCACHE_DIRECTORY_NAME)) {
       aType = ASMJS;
     }
     else if (aText.EqualsLiteral(DOMCACHE_DIRECTORY_NAME)) {
       aType = DOMCACHE;
     }
+    else if (aText.EqualsLiteral(SDB_DIRECTORY_NAME)) {
+      aType = SDB;
+    }
     else {
       return NS_ERROR_FAILURE;
     }
 
     return NS_OK;
   }
 
   // Methods which are called on the IO thread.
new file mode 100644
--- /dev/null
+++ b/dom/simpledb/ActorsChild.cpp
@@ -0,0 +1,243 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=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 "ActorsChild.h"
+
+#include "nsVariant.h"
+#include "SDBConnection.h"
+#include "SDBRequest.h"
+#include "SDBResults.h"
+
+namespace mozilla {
+namespace dom {
+
+/*******************************************************************************
+ * SDBConnectionChild
+ ******************************************************************************/
+
+SDBConnectionChild::SDBConnectionChild(SDBConnection* aConnection)
+  : mConnection(aConnection)
+{
+  AssertIsOnOwningThread();
+  MOZ_ASSERT(aConnection);
+
+  MOZ_COUNT_CTOR(SDBConnectionChild);
+}
+
+SDBConnectionChild::~SDBConnectionChild()
+{
+  AssertIsOnOwningThread();
+
+  MOZ_COUNT_DTOR(SDBConnectionChild);
+}
+
+void
+SDBConnectionChild::SendDeleteMeInternal()
+{
+  AssertIsOnOwningThread();
+
+  if (mConnection) {
+    mConnection->ClearBackgroundActor();
+    mConnection = nullptr;
+
+    MOZ_ALWAYS_TRUE(PBackgroundSDBConnectionChild::SendDeleteMe());
+  }
+}
+
+void
+SDBConnectionChild::ActorDestroy(ActorDestroyReason aWhy)
+{
+  AssertIsOnOwningThread();
+
+  if (mConnection) {
+    mConnection->ClearBackgroundActor();
+#ifdef DEBUG
+    mConnection = nullptr;
+#endif
+  }
+}
+
+PBackgroundSDBRequestChild*
+SDBConnectionChild::AllocPBackgroundSDBRequestChild(
+                                                const SDBRequestParams& aParams)
+{
+  AssertIsOnOwningThread();
+
+  MOZ_CRASH("PBackgroundSDBRequestChild actors should be manually "
+            "constructed!");
+}
+
+bool
+SDBConnectionChild::DeallocPBackgroundSDBRequestChild(
+                                             PBackgroundSDBRequestChild* aActor)
+{
+  AssertIsOnOwningThread();
+  MOZ_ASSERT(aActor);
+
+  delete static_cast<SDBRequestChild*>(aActor);
+  return true;
+}
+
+mozilla::ipc::IPCResult
+SDBConnectionChild::RecvAllowToClose()
+{
+  AssertIsOnOwningThread();
+
+  if (mConnection) {
+    mConnection->AllowToClose();
+  }
+
+  return IPC_OK();
+}
+
+mozilla::ipc::IPCResult
+SDBConnectionChild::RecvClosed()
+{
+  AssertIsOnOwningThread();
+
+  if (mConnection) {
+    mConnection->OnClose(/* aAbnormal */ true);
+  }
+
+  return IPC_OK();
+}
+
+/*******************************************************************************
+ * SDBRequestChild
+ ******************************************************************************/
+
+SDBRequestChild::SDBRequestChild(SDBRequest* aRequest)
+  : mConnection(aRequest->GetConnection())
+  , mRequest(aRequest)
+{
+  AssertIsOnOwningThread();
+  MOZ_ASSERT(aRequest);
+
+  MOZ_COUNT_CTOR(SDBRequestChild);
+}
+
+SDBRequestChild::~SDBRequestChild()
+{
+  AssertIsOnOwningThread();
+
+  MOZ_COUNT_DTOR(SDBRequestChild);
+}
+
+#ifdef DEBUG
+
+void
+SDBRequestChild::AssertIsOnOwningThread() const
+{
+  MOZ_ASSERT(mRequest);
+  mRequest->AssertIsOnOwningThread();
+}
+
+#endif // DEBUG
+
+void
+SDBRequestChild::HandleResponse(nsresult aResponse)
+{
+  AssertIsOnOwningThread();
+  MOZ_ASSERT(NS_FAILED(aResponse));
+  MOZ_ASSERT(mRequest);
+
+  mRequest->SetError(aResponse);
+}
+
+void
+SDBRequestChild::HandleResponse()
+{
+  AssertIsOnOwningThread();
+  MOZ_ASSERT(mRequest);
+
+  RefPtr<nsVariant> variant = new nsVariant();
+  variant->SetAsVoid();
+
+  mRequest->SetResult(variant);
+}
+
+void
+SDBRequestChild::HandleResponse(const nsCString& aResponse)
+{
+  AssertIsOnOwningThread();
+  MOZ_ASSERT(mRequest);
+
+  RefPtr<SDBResult> result = new SDBResult(aResponse);
+
+  RefPtr<nsVariant> variant = new nsVariant();
+  variant->SetAsInterface(NS_GET_IID(nsISDBResult), result);
+
+  mRequest->SetResult(variant);
+}
+
+void
+SDBRequestChild::ActorDestroy(ActorDestroyReason aWhy)
+{
+  AssertIsOnOwningThread();
+
+  if (mConnection) {
+    mConnection->AssertIsOnOwningThread();
+
+    mConnection->OnRequestFinished();
+#ifdef DEBUG
+    mConnection = nullptr;
+#endif
+  }
+}
+
+mozilla::ipc::IPCResult
+SDBRequestChild::Recv__delete__(const SDBRequestResponse& aResponse)
+{
+  AssertIsOnOwningThread();
+  MOZ_ASSERT(mRequest);
+  MOZ_ASSERT(mConnection);
+
+  switch (aResponse.type()) {
+    case SDBRequestResponse::Tnsresult:
+      HandleResponse(aResponse.get_nsresult());
+      break;
+
+    case SDBRequestResponse::TSDBRequestOpenResponse:
+      HandleResponse();
+
+      mConnection->OnOpen();
+
+      break;
+
+    case SDBRequestResponse::TSDBRequestSeekResponse:
+      HandleResponse();
+      break;
+
+    case SDBRequestResponse::TSDBRequestReadResponse:
+      HandleResponse(aResponse.get_SDBRequestReadResponse().data());
+      break;
+
+    case SDBRequestResponse::TSDBRequestWriteResponse:
+      HandleResponse();
+      break;
+
+    case SDBRequestResponse::TSDBRequestCloseResponse:
+      HandleResponse();
+
+      mConnection->OnClose(/* aAbnormal */ false);
+
+      break;
+
+    default:
+      MOZ_CRASH("Unknown response type!");
+  }
+
+  mConnection->OnRequestFinished();
+
+  // Null this out so that we don't try to call OnRequestFinished() again in
+  // ActorDestroy.
+  mConnection = nullptr;
+
+  return IPC_OK();
+}
+
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/simpledb/ActorsChild.h
@@ -0,0 +1,115 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=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_simpledb_ActorsChild_h
+#define mozilla_dom_simpledb_ActorsChild_h
+
+#include "mozilla/dom/PBackgroundSDBConnectionChild.h"
+#include "mozilla/dom/PBackgroundSDBRequestChild.h"
+
+namespace mozilla {
+namespace ipc {
+
+class BackgroundChildImpl;
+
+} // namespace ipc
+
+namespace dom {
+
+class SDBConnection;
+class SDBRequest;
+
+class SDBConnectionChild final
+  : public PBackgroundSDBConnectionChild
+{
+  friend class mozilla::ipc::BackgroundChildImpl;
+  friend class SDBConnection;
+
+  SDBConnection* mConnection;
+
+  NS_DECL_OWNINGTHREAD
+
+public:
+  void
+  AssertIsOnOwningThread() const
+  {
+    NS_ASSERT_OWNINGTHREAD(SDBConnectionChild);
+  }
+
+private:
+  // Only created by SDBConnection.
+  explicit SDBConnectionChild(SDBConnection* aConnection);
+
+  // Only destroyed by mozilla::ipc::BackgroundChildImpl.
+  ~SDBConnectionChild();
+
+  void
+  SendDeleteMeInternal();
+
+  // IPDL methods are only called by IPDL.
+  virtual void
+  ActorDestroy(ActorDestroyReason aWhy) override;
+
+  virtual PBackgroundSDBRequestChild*
+  AllocPBackgroundSDBRequestChild(const SDBRequestParams& aParams) override;
+
+  virtual bool
+  DeallocPBackgroundSDBRequestChild(PBackgroundSDBRequestChild* aActor)
+                                    override;
+
+  virtual mozilla::ipc::IPCResult
+  RecvAllowToClose() override;
+
+  virtual mozilla::ipc::IPCResult
+  RecvClosed() override;
+};
+
+class SDBRequestChild final
+  : public PBackgroundSDBRequestChild
+{
+  friend class SDBConnectionChild;
+  friend class SDBConnection;
+
+  RefPtr<SDBConnection> mConnection;
+  RefPtr<SDBRequest> mRequest;
+
+public:
+  void
+  AssertIsOnOwningThread() const
+#ifdef DEBUG
+  ;
+#else
+  { }
+#endif
+
+private:
+  // Only created by SDBConnection.
+  explicit SDBRequestChild(SDBRequest* aRequest);
+
+  // Only destroyed by SDBConnectionChild.
+  ~SDBRequestChild();
+
+  void
+  HandleResponse(nsresult aResponse);
+
+  void
+  HandleResponse();
+
+  void
+  HandleResponse(const nsCString& aResponse);
+
+  // IPDL methods are only called by IPDL.
+  virtual void
+  ActorDestroy(ActorDestroyReason aWhy) override;
+
+  virtual mozilla::ipc::IPCResult
+  Recv__delete__(const SDBRequestResponse& aResponse) override;
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_simpledb_ActorsChild_h
new file mode 100644
--- /dev/null
+++ b/dom/simpledb/ActorsParent.cpp
@@ -0,0 +1,2005 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=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 "ActorsParent.h"
+
+#include "mozilla/Unused.h"
+#include "mozilla/dom/PBackgroundSDBConnectionParent.h"
+#include "mozilla/dom/PBackgroundSDBRequestParent.h"
+#include "mozilla/dom/quota/Client.h"
+#include "mozilla/dom/quota/FileStreams.h"
+#include "mozilla/dom/quota/MemoryOutputStream.h"
+#include "mozilla/dom/quota/QuotaManager.h"
+#include "mozilla/dom/quota/UsageInfo.h"
+#include "mozilla/ipc/BackgroundParent.h"
+#include "mozilla/ipc/PBackgroundParent.h"
+#include "mozilla/ipc/PBackgroundSharedTypes.h"
+#include "nsIFileStreams.h"
+#include "nsISimpleEnumerator.h"
+#include "nsStringStream.h"
+#include "prio.h"
+
+#define DISABLE_ASSERTS_FOR_FUZZING 0
+
+#if DISABLE_ASSERTS_FOR_FUZZING
+#define ASSERT_UNLESS_FUZZING(...) do { } while (0)
+#else
+#define ASSERT_UNLESS_FUZZING(...) MOZ_ASSERT(false, __VA_ARGS__)
+#endif
+
+namespace mozilla {
+namespace dom {
+
+using namespace mozilla::dom::quota;
+using namespace mozilla::ipc;
+
+namespace {
+
+/*******************************************************************************
+ * Constants
+ ******************************************************************************/
+
+const uint32_t kCopyBufferSize = 32768;
+
+/*******************************************************************************
+ * Actor class declarations
+ ******************************************************************************/
+
+class StreamHelper final
+  : public Runnable
+{
+  nsCOMPtr<nsIEventTarget> mOwningEventTarget;
+  nsCOMPtr<nsIFileStream> mFileStream;
+  nsCOMPtr<nsIRunnable> mCallback;
+
+public:
+  StreamHelper(nsIFileStream* aFileStream,
+               nsIRunnable* aCallback);
+
+  void
+  AsyncClose();
+
+private:
+  ~StreamHelper() override;
+
+  void
+  RunOnBackgroundThread();
+
+  void
+  RunOnIOThread();
+
+  NS_DECL_NSIRUNNABLE
+};
+
+class Connection final
+  : public PBackgroundSDBConnectionParent
+{
+  RefPtr<DirectoryLock> mDirectoryLock;
+  nsCOMPtr<nsIFileStream> mFileStream;
+  const PrincipalInfo mPrincipalInfo;
+  nsCString mOrigin;
+  nsString mName;
+
+  bool mRunningRequest;
+  bool mOpen;
+  bool mAllowedToClose;
+  bool mActorDestroyed;
+
+public:
+  explicit Connection(const PrincipalInfo& aPrincipalInfo);
+
+  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(mozilla::dom::Connection)
+
+  nsIFileStream*
+  GetFileStream() const
+  {
+    AssertIsOnIOThread();
+
+    return mFileStream;
+  }
+
+  const PrincipalInfo&
+  GetPrincipalInfo() const
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+
+    return mPrincipalInfo;
+  }
+
+  const nsCString&
+  Origin() const
+  {
+    AssertIsOnBackgroundThread();
+    MOZ_ASSERT(!mOrigin.IsEmpty());
+
+    return mOrigin;
+  }
+
+  const nsString&
+  Name() const
+  {
+    AssertIsOnBackgroundThread();
+    MOZ_ASSERT(!mName.IsEmpty());
+
+    return mName;
+  }
+
+  void
+  OnNewRequest();
+
+  void
+  OnRequestFinished();
+
+  void
+  OnOpen(const nsACString& aOrigin,
+         const nsAString& aName,
+         already_AddRefed<DirectoryLock> aDirectoryLock,
+         already_AddRefed<nsIFileStream> aFileStream);
+
+  void
+  OnClose();
+
+  void
+  AllowToClose();
+
+private:
+  ~Connection();
+
+  void
+  MaybeCloseStream();
+
+  bool
+  VerifyRequestParams(const SDBRequestParams& aParams) const;
+
+  // IPDL methods.
+  virtual void
+  ActorDestroy(ActorDestroyReason aWhy) override;
+
+  mozilla::ipc::IPCResult
+  RecvDeleteMe() override;
+
+  virtual PBackgroundSDBRequestParent*
+  AllocPBackgroundSDBRequestParent(const SDBRequestParams& aParams) override;
+
+  virtual mozilla::ipc::IPCResult
+  RecvPBackgroundSDBRequestConstructor(PBackgroundSDBRequestParent* aActor,
+                                       const SDBRequestParams& aParams)
+                                       override;
+
+  virtual bool
+  DeallocPBackgroundSDBRequestParent(PBackgroundSDBRequestParent* aActor)
+                                     override;
+};
+
+class ConnectionOperationBase
+  : public Runnable
+  , public PBackgroundSDBRequestParent
+{
+  nsCOMPtr<nsIEventTarget> mOwningEventTarget;
+  RefPtr<Connection> mConnection;
+  nsresult mResultCode;
+  Atomic<bool> mOperationMayProceed;
+  bool mActorDestroyed;
+
+public:
+  nsIEventTarget*
+  OwningEventTarget() const
+  {
+    MOZ_ASSERT(mOwningEventTarget);
+
+    return mOwningEventTarget;
+  }
+
+  bool
+  IsOnOwningThread() const
+  {
+    MOZ_ASSERT(mOwningEventTarget);
+
+    bool current;
+    return
+      NS_SUCCEEDED(mOwningEventTarget->IsOnCurrentThread(&current)) && current;
+  }
+
+  void
+  AssertIsOnOwningThread() const
+  {
+    MOZ_ASSERT(IsOnBackgroundThread());
+    MOZ_ASSERT(IsOnOwningThread());
+  }
+
+  Connection*
+  GetConnection() const
+  {
+    MOZ_ASSERT(mConnection);
+
+    return mConnection;
+  }
+
+  nsresult
+  ResultCode() const
+  {
+    return mResultCode;
+  }
+
+  void
+  MaybeSetFailureCode(nsresult aErrorCode)
+  {
+    MOZ_ASSERT(NS_FAILED(aErrorCode));
+
+    if (NS_SUCCEEDED(mResultCode)) {
+      mResultCode = aErrorCode;
+    }
+  }
+
+  // May be called on any thread, but you should call IsActorDestroyed() if
+  // you know you're on the background thread because it is slightly faster.
+  bool
+  OperationMayProceed() const
+  {
+    return mOperationMayProceed;
+  }
+
+  bool
+  IsActorDestroyed() const
+  {
+    AssertIsOnOwningThread();
+
+    return mActorDestroyed;
+  }
+
+  // May be overridden by subclasses if they need to perform work on the
+  // background thread before being dispatched but must always call the base
+  // class implementation. Returning false will kill the child actors and
+  // prevent dispatch.
+  virtual bool
+  Init();
+
+  virtual nsresult
+  Dispatch();
+
+  // This callback will be called on the background thread before releasing the
+  // final reference to this request object. Subclasses may perform any
+  // additional cleanup here but must always call the base class implementation.
+  virtual void
+  Cleanup();
+
+protected:
+  ConnectionOperationBase(Connection* aConnection)
+    : Runnable("dom::ConnectionOperationBase")
+    , mOwningEventTarget(GetCurrentThreadEventTarget())
+    , mConnection(aConnection)
+    , mResultCode(NS_OK)
+    , mOperationMayProceed(true)
+    , mActorDestroyed(false)
+  {
+    AssertIsOnOwningThread();
+  }
+
+  ~ConnectionOperationBase() override;
+
+  void
+  SendResults();
+
+  void
+  DatabaseWork();
+
+  // Methods that subclasses must implement.
+  virtual nsresult
+  DoDatabaseWork(nsIFileStream* aFileStream) = 0;
+
+  // Subclasses use this override to set the IPDL response value.
+  virtual void
+  GetResponse(SDBRequestResponse& aResponse) = 0;
+
+  // A method that subclasses may implement.
+  virtual void
+  OnSuccess();
+
+private:
+  NS_IMETHOD
+  Run() override;
+
+  // IPDL methods.
+  void
+  ActorDestroy(ActorDestroyReason aWhy) override;
+};
+
+class OpenOp final
+  : public ConnectionOperationBase
+  , public OpenDirectoryListener
+{
+  enum class State
+  {
+    // Just created on the PBackground thread, dispatched to the main thread.
+    // Next step is FinishOpen.
+    Initial,
+
+    // Opening directory or initializing quota manager on the PBackground
+    // thread. Next step is either DirectoryOpenPending if quota manager is
+    // already initialized or QuotaManagerPending if quota manager needs to be
+    // initialized.
+    FinishOpen,
+
+    // Waiting for quota manager initialization to complete on the PBackground
+    // thread. Next step is either SendingResults if initialization failed or
+    // DirectoryOpenPending if initialization succeeded.
+    QuotaManagerPending,
+
+    // Waiting for directory open allowed on the PBackground thread. The next
+    // step is either SendingResults if directory lock failed to acquire, or
+    // DatabaseWorkOpen if directory lock is acquired.
+    DirectoryOpenPending,
+
+    // Waiting to do/doing work on the QuotaManager IO thread. Its next step is
+    // SendingResults.
+    DatabaseWorkOpen,
+
+    // Waiting to send/sending results on the PBackground thread. Next step is
+    // Completed.
+    SendingResults,
+
+    // All done.
+    Completed
+  };
+
+  const SDBRequestOpenParams mParams;
+  RefPtr<DirectoryLock> mDirectoryLock;
+  nsCOMPtr<nsIFileStream> mFileStream;
+  nsCString mSuffix;
+  nsCString mGroup;
+  nsCString mOrigin;
+  State mState;
+  bool mFileStreamOpen;
+
+public:
+  OpenOp(Connection* aConnection, const SDBRequestParams& aParams);
+
+  nsresult
+  Dispatch() override;
+
+private:
+  ~OpenOp() override;
+
+  nsresult
+  Open();
+
+  nsresult
+  FinishOpen();
+
+  nsresult
+  QuotaManagerOpen();
+
+  nsresult
+  OpenDirectory();
+
+  nsresult
+  SendToIOThread();
+
+  nsresult
+  DatabaseWork();
+
+  void
+  StreamClosedCallback();
+
+  // ConnectionOperationBase overrides
+  nsresult
+  DoDatabaseWork(nsIFileStream* aFileStream) override;
+
+  void
+  GetResponse(SDBRequestResponse& aResponse) override;
+
+  void
+  OnSuccess() override;
+
+  void
+  Cleanup() override;
+
+  NS_DECL_ISUPPORTS_INHERITED
+
+  NS_IMETHOD
+  Run() override;
+
+  // OpenDirectoryListener overrides.
+  void
+  DirectoryLockAcquired(DirectoryLock* aLock) override;
+
+  void
+  DirectoryLockFailed() override;
+};
+
+class SeekOp final
+  : public ConnectionOperationBase
+{
+  const SDBRequestSeekParams mParams;
+
+public:
+  SeekOp(Connection* aConnection,
+         const SDBRequestParams& aParams);
+
+private:
+  ~SeekOp() override = default;
+
+  nsresult
+  DoDatabaseWork(nsIFileStream* aFileStream) override;
+
+  void
+  GetResponse(SDBRequestResponse& aResponse) override;
+};
+
+class ReadOp final
+  : public ConnectionOperationBase
+{
+  const SDBRequestReadParams mParams;
+
+  RefPtr<MemoryOutputStream> mOutputStream;
+
+public:
+  ReadOp(Connection* aConnection,
+         const SDBRequestParams& aParams);
+
+  bool
+  Init() override;
+
+private:
+  ~ReadOp() override = default;
+
+  nsresult
+  DoDatabaseWork(nsIFileStream* aFileStream) override;
+
+  void
+  GetResponse(SDBRequestResponse& aResponse) override;
+};
+
+class WriteOp final
+  : public ConnectionOperationBase
+{
+  const SDBRequestWriteParams mParams;
+
+  nsCOMPtr<nsIInputStream> mInputStream;
+
+  uint64_t mSize;
+
+public:
+  WriteOp(Connection* aConnection,
+          const SDBRequestParams& aParams);
+
+  bool
+  Init() override;
+
+private:
+  ~WriteOp() override = default;
+
+  nsresult
+  DoDatabaseWork(nsIFileStream* aFileStream) override;
+
+  void
+  GetResponse(SDBRequestResponse& aResponse) override;
+};
+
+class CloseOp final
+  : public ConnectionOperationBase
+{
+public:
+  explicit CloseOp(Connection* aConnection);
+
+private:
+  ~CloseOp() override = default;
+
+  nsresult
+  DoDatabaseWork(nsIFileStream* aFileStream) override;
+
+  void
+  GetResponse(SDBRequestResponse& aResponse) override;
+
+  void
+  OnSuccess() override;
+};
+
+/*******************************************************************************
+ * Other class declarations
+ ******************************************************************************/
+
+class QuotaClient final
+  : public mozilla::dom::quota::Client
+{
+  static QuotaClient* sInstance;
+
+  bool mShutdownRequested;
+
+public:
+  QuotaClient();
+
+  static bool
+  IsShuttingDownOnBackgroundThread()
+  {
+    AssertIsOnBackgroundThread();
+
+    if (sInstance) {
+      return sInstance->IsShuttingDown();
+    }
+
+    return QuotaManager::IsShuttingDown();
+  }
+
+  static bool
+  IsShuttingDownOnNonBackgroundThread()
+  {
+    MOZ_ASSERT(!IsOnBackgroundThread());
+
+    return QuotaManager::IsShuttingDown();
+  }
+
+  bool
+  IsShuttingDown() const
+  {
+    AssertIsOnBackgroundThread();
+
+    return mShutdownRequested;
+  }
+
+  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(QuotaClient, override)
+
+  Type
+  GetType() override;
+
+  nsresult
+  InitOrigin(PersistenceType aPersistenceType,
+             const nsACString& aGroup,
+             const nsACString& aOrigin,
+             const AtomicBool& aCanceled,
+             UsageInfo* aUsageInfo) override;
+
+  nsresult
+  GetUsageForOrigin(PersistenceType aPersistenceType,
+                    const nsACString& aGroup,
+                    const nsACString& aOrigin,
+                    const AtomicBool& aCanceled,
+                    UsageInfo* aUsageInfo) override;
+
+  void
+  OnOriginClearCompleted(PersistenceType aPersistenceType,
+                         const nsACString& aOrigin)
+                         override;
+
+  void
+  ReleaseIOThreadObjects() override;
+
+  void
+  AbortOperations(const nsACString& aOrigin) override;
+
+  void
+  AbortOperationsForProcess(ContentParentId aContentParentId) override;
+
+  void
+  StartIdleMaintenance() override;
+
+  void
+  StopIdleMaintenance() override;
+
+  void
+  ShutdownWorkThreads() override;
+
+private:
+  ~QuotaClient() override;
+};
+
+/*******************************************************************************
+ * Globals
+ ******************************************************************************/
+
+typedef nsTArray<RefPtr<Connection>> ConnectionArray;
+
+StaticAutoPtr<ConnectionArray> gOpenConnections;
+
+} // namespace
+
+/*******************************************************************************
+ * Exported functions
+ ******************************************************************************/
+
+PBackgroundSDBConnectionParent*
+AllocPBackgroundSDBConnectionParent(const PrincipalInfo& aPrincipalInfo)
+{
+  AssertIsOnBackgroundThread();
+
+  if (NS_WARN_IF(QuotaClient::IsShuttingDownOnBackgroundThread())) {
+    return nullptr;
+  }
+
+  if (NS_WARN_IF(aPrincipalInfo.type() == PrincipalInfo::TNullPrincipalInfo)) {
+    ASSERT_UNLESS_FUZZING();
+    return nullptr;
+  }
+
+  RefPtr<Connection> actor = new Connection(aPrincipalInfo);
+
+  return actor.forget().take();
+}
+
+bool
+RecvPBackgroundSDBConnectionConstructor(PBackgroundSDBConnectionParent* aActor,
+                                        const PrincipalInfo& aPrincipalInfo)
+{
+  AssertIsOnBackgroundThread();
+  MOZ_ASSERT(aActor);
+  MOZ_ASSERT(!QuotaClient::IsShuttingDownOnBackgroundThread());
+
+  return true;
+}
+
+bool
+DeallocPBackgroundSDBConnectionParent(PBackgroundSDBConnectionParent* aActor)
+{
+  AssertIsOnBackgroundThread();
+  MOZ_ASSERT(aActor);
+
+  RefPtr<Connection> actor = dont_AddRef(static_cast<Connection*>(aActor));
+  return true;
+}
+
+namespace simpledb {
+
+already_AddRefed<mozilla::dom::quota::Client>
+CreateQuotaClient()
+{
+  AssertIsOnBackgroundThread();
+
+  RefPtr<QuotaClient> client = new QuotaClient();
+  return client.forget();
+}
+
+} // namespace simpledb
+
+/*******************************************************************************
+ * StreamHelper
+ ******************************************************************************/
+
+StreamHelper::StreamHelper(nsIFileStream* aFileStream,
+                           nsIRunnable* aCallback)
+  : Runnable("dom::StreamHelper")
+  , mOwningEventTarget(GetCurrentThreadEventTarget())
+  , mFileStream(aFileStream)
+  , mCallback(aCallback)
+{
+  AssertIsOnBackgroundThread();
+  MOZ_ASSERT(aFileStream);
+  MOZ_ASSERT(aCallback);
+}
+
+StreamHelper::~StreamHelper()
+{
+  MOZ_ASSERT(!mFileStream);
+  MOZ_ASSERT(!mCallback);
+}
+
+void
+StreamHelper::AsyncClose()
+{
+  AssertIsOnBackgroundThread();
+
+  QuotaManager* quotaManager = QuotaManager::Get();
+  MOZ_ASSERT(quotaManager);
+
+  MOZ_ALWAYS_SUCCEEDS(
+    quotaManager->IOThread()->Dispatch(this, NS_DISPATCH_NORMAL));
+}
+
+void
+StreamHelper::RunOnBackgroundThread()
+{
+  AssertIsOnBackgroundThread();
+
+  nsCOMPtr<nsIFileStream> fileStream;
+  mFileStream.swap(fileStream);
+
+  nsCOMPtr<nsIRunnable> callback;
+  mCallback.swap(callback);
+
+  callback->Run();
+}
+
+void
+StreamHelper::RunOnIOThread()
+{
+  AssertIsOnIOThread();
+  MOZ_ASSERT(mFileStream);
+
+  nsCOMPtr<nsIInputStream> inputStream = do_QueryInterface(mFileStream);
+  MOZ_ASSERT(inputStream);
+
+  nsresult rv = inputStream->Close();
+  Unused << NS_WARN_IF(NS_FAILED(rv));
+
+  MOZ_ALWAYS_SUCCEEDS(mOwningEventTarget->Dispatch(this, NS_DISPATCH_NORMAL));
+}
+
+NS_IMETHODIMP
+StreamHelper::Run()
+{
+  MOZ_ASSERT(mCallback);
+
+  if (IsOnBackgroundThread()) {
+    RunOnBackgroundThread();
+  } else {
+    RunOnIOThread();
+  }
+
+  return NS_OK;
+}
+
+/*******************************************************************************
+ * Connection
+ ******************************************************************************/
+
+Connection::Connection(const PrincipalInfo& aPrincipalInfo)
+  : mPrincipalInfo(aPrincipalInfo)
+  , mRunningRequest(false)
+  , mOpen(false)
+  , mAllowedToClose(false)
+  , mActorDestroyed(false)
+{
+  AssertIsOnBackgroundThread();
+  MOZ_ASSERT(!QuotaClient::IsShuttingDownOnBackgroundThread());
+}
+
+Connection::~Connection()
+{
+  MOZ_ASSERT(!mRunningRequest);
+  MOZ_ASSERT(!mOpen);
+  MOZ_ASSERT(mActorDestroyed);
+}
+
+void
+Connection::OnNewRequest()
+{
+  AssertIsOnBackgroundThread();
+  MOZ_ASSERT(!mRunningRequest);
+
+  mRunningRequest = true;
+}
+
+void
+Connection::OnRequestFinished()
+{
+  AssertIsOnBackgroundThread();
+  MOZ_ASSERT(mRunningRequest);
+
+  mRunningRequest = false;
+
+  MaybeCloseStream();
+}
+
+void
+Connection::OnOpen(const nsACString& aOrigin,
+                   const nsAString& aName,
+                   already_AddRefed<DirectoryLock> aDirectoryLock,
+                   already_AddRefed<nsIFileStream> aFileStream)
+{
+  AssertIsOnBackgroundThread();
+  MOZ_ASSERT(!aOrigin.IsEmpty());
+  MOZ_ASSERT(!aName.IsEmpty());
+  MOZ_ASSERT(mOrigin.IsEmpty());
+  MOZ_ASSERT(mName.IsEmpty());
+  MOZ_ASSERT(!mDirectoryLock);
+  MOZ_ASSERT(!mFileStream);
+  MOZ_ASSERT(!mOpen);
+
+  mOrigin = aOrigin;
+  mName = aName;
+  mDirectoryLock = aDirectoryLock;
+  mFileStream = aFileStream;
+  mOpen = true;
+
+  if (!gOpenConnections) {
+    gOpenConnections = new ConnectionArray();
+  }
+
+  gOpenConnections->AppendElement(this);
+}
+
+void
+Connection::OnClose()
+{
+  AssertIsOnBackgroundThread();
+  MOZ_ASSERT(!mOrigin.IsEmpty());
+  MOZ_ASSERT(mDirectoryLock);
+  MOZ_ASSERT(mFileStream);
+  MOZ_ASSERT(mOpen);
+
+  mOrigin.Truncate();
+  mName.Truncate();
+  mDirectoryLock = nullptr;
+  mFileStream = nullptr;
+  mOpen = false;
+
+  MOZ_ASSERT(gOpenConnections);
+  gOpenConnections->RemoveElement(this);
+
+  if (gOpenConnections->IsEmpty()) {
+    gOpenConnections = nullptr;
+  }
+
+  if (mAllowedToClose && !mActorDestroyed) {
+    Unused << SendClosed();
+  }
+}
+
+void
+Connection::AllowToClose()
+{
+  AssertIsOnBackgroundThread();
+
+  if (mAllowedToClose) {
+    return;
+  }
+
+  mAllowedToClose = true;
+
+  if (!mActorDestroyed) {
+    Unused << SendAllowToClose();
+  }
+
+  MaybeCloseStream();
+}
+
+void
+Connection::MaybeCloseStream()
+{
+  AssertIsOnBackgroundThread();
+
+  if (!mRunningRequest &&
+      mOpen &&
+      mAllowedToClose) {
+    nsCOMPtr<nsIRunnable> callback =
+      NewRunnableMethod("dom::Connection::OnClose",
+                        this,
+                        &Connection::OnClose);
+
+    RefPtr<StreamHelper> helper = new StreamHelper(mFileStream, callback);
+    helper->AsyncClose();
+  }
+}
+
+bool
+Connection::VerifyRequestParams(const SDBRequestParams& aParams) const
+{
+  AssertIsOnBackgroundThread();
+  MOZ_ASSERT(aParams.type() != SDBRequestParams::T__None);
+
+  switch (aParams.type()) {
+    case SDBRequestParams::TSDBRequestOpenParams: {
+      if (NS_WARN_IF(mOpen)) {
+        ASSERT_UNLESS_FUZZING();
+        return false;
+      }
+
+      break;
+    }
+
+    case SDBRequestParams::TSDBRequestSeekParams:
+    case SDBRequestParams::TSDBRequestReadParams:
+    case SDBRequestParams::TSDBRequestWriteParams:
+    case SDBRequestParams::TSDBRequestCloseParams: {
+      if (NS_WARN_IF(!mOpen)) {
+        ASSERT_UNLESS_FUZZING();
+        return false;
+      }
+
+      break;
+    }
+
+    default:
+      MOZ_CRASH("Should never get here!");
+  }
+
+  return true;
+}
+
+void
+Connection::ActorDestroy(ActorDestroyReason aWhy)
+{
+  AssertIsOnBackgroundThread();
+  MOZ_ASSERT(!mActorDestroyed);
+
+  mActorDestroyed = true;
+
+  AllowToClose();
+}
+
+mozilla::ipc::IPCResult
+Connection::RecvDeleteMe()
+{
+  AssertIsOnBackgroundThread();
+  MOZ_ASSERT(!mActorDestroyed);
+
+  IProtocol* mgr = Manager();
+  if (!PBackgroundSDBConnectionParent::Send__delete__(this)) {
+    return IPC_FAIL_NO_REASON(mgr);
+  }
+
+  return IPC_OK();
+}
+
+PBackgroundSDBRequestParent*
+Connection::AllocPBackgroundSDBRequestParent(const SDBRequestParams& aParams)
+{
+  AssertIsOnBackgroundThread();
+  MOZ_ASSERT(aParams.type() != SDBRequestParams::T__None);
+
+  if (aParams.type() == SDBRequestParams::TSDBRequestOpenParams &&
+      NS_WARN_IF(QuotaClient::IsShuttingDownOnBackgroundThread())) {
+    return nullptr;
+  }
+
+  if (mAllowedToClose) {
+    return nullptr;
+  }
+
+#ifdef DEBUG
+  // Always verify parameters in DEBUG builds!
+  bool trustParams = false;
+#else
+  PBackgroundParent* backgroundActor = Manager();
+  MOZ_ASSERT(backgroundActor);
+
+  bool trustParams = !BackgroundParent::IsOtherProcessActor(backgroundActor);
+#endif
+
+  if (NS_WARN_IF(!trustParams && !VerifyRequestParams(aParams))) {
+    ASSERT_UNLESS_FUZZING();
+    return nullptr;
+  }
+
+  if (NS_WARN_IF(mRunningRequest)) {
+    ASSERT_UNLESS_FUZZING();
+    return nullptr;
+  }
+
+  RefPtr<ConnectionOperationBase> actor;
+
+  switch (aParams.type()) {
+    case SDBRequestParams::TSDBRequestOpenParams:
+      actor = new OpenOp(this, aParams);
+      break;
+
+    case SDBRequestParams::TSDBRequestSeekParams:
+      actor = new SeekOp(this, aParams);
+      break;
+
+    case SDBRequestParams::TSDBRequestReadParams:
+      actor = new ReadOp(this, aParams);
+      break;
+
+    case SDBRequestParams::TSDBRequestWriteParams:
+      actor = new WriteOp(this, aParams);
+      break;
+
+    case SDBRequestParams::TSDBRequestCloseParams:
+      actor = new CloseOp(this);
+      break;
+
+    default:
+      MOZ_CRASH("Should never get here!");
+  }
+
+  // Transfer ownership to IPDL.
+  return actor.forget().take();
+}
+
+mozilla::ipc::IPCResult
+Connection::RecvPBackgroundSDBRequestConstructor(
+                                            PBackgroundSDBRequestParent* aActor,
+                                            const SDBRequestParams& aParams)
+{
+  AssertIsOnBackgroundThread();
+  MOZ_ASSERT(aActor);
+  MOZ_ASSERT(aParams.type() != SDBRequestParams::T__None);
+  MOZ_ASSERT_IF(aParams.type() == SDBRequestParams::TSDBRequestOpenParams,
+                !QuotaClient::IsShuttingDownOnBackgroundThread());
+  MOZ_ASSERT(!mAllowedToClose);
+  MOZ_ASSERT(!mRunningRequest);
+
+  auto* op = static_cast<ConnectionOperationBase*>(aActor);
+
+  if (NS_WARN_IF(!op->Init())) {
+    op->Cleanup();
+    return IPC_FAIL_NO_REASON(this);
+  }
+
+  if (NS_WARN_IF(NS_FAILED(op->Dispatch()))) {
+    op->Cleanup();
+    return IPC_FAIL_NO_REASON(this);
+  }
+
+  return IPC_OK();
+}
+
+bool
+Connection::DeallocPBackgroundSDBRequestParent(
+                                            PBackgroundSDBRequestParent* aActor)
+{
+  AssertIsOnBackgroundThread();
+  MOZ_ASSERT(aActor);
+
+  // Transfer ownership back from IPDL.
+  RefPtr<ConnectionOperationBase> actor =
+    dont_AddRef(static_cast<ConnectionOperationBase*>(aActor));
+  return true;
+}
+
+/*******************************************************************************
+ * ConnectionOperationBase
+ ******************************************************************************/
+
+ConnectionOperationBase::~ConnectionOperationBase()
+{
+  MOZ_ASSERT(!mConnection,
+             "ConnectionOperationBase::Cleanup() was not called by a subclass!");
+  MOZ_ASSERT(mActorDestroyed);
+}
+
+bool
+ConnectionOperationBase::Init()
+{
+  AssertIsOnBackgroundThread();
+  MOZ_ASSERT(mConnection);
+
+  mConnection->OnNewRequest();
+
+  return true;
+}
+
+nsresult
+ConnectionOperationBase::Dispatch()
+{
+  if (NS_WARN_IF(QuotaClient::IsShuttingDownOnBackgroundThread()) ||
+      IsActorDestroyed()) {
+    return NS_ERROR_FAILURE;
+  }
+
+  QuotaManager* quotaManager = QuotaManager::Get();
+  MOZ_ASSERT(quotaManager);
+
+  nsresult rv = quotaManager->IOThread()->Dispatch(this, NS_DISPATCH_NORMAL);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  return NS_OK;
+}
+
+void
+ConnectionOperationBase::Cleanup()
+{
+  AssertIsOnOwningThread();
+  MOZ_ASSERT(mConnection);
+
+  mConnection->OnRequestFinished();
+
+  mConnection = nullptr;
+}
+
+void
+ConnectionOperationBase::SendResults()
+{
+  AssertIsOnOwningThread();
+
+  if (IsActorDestroyed()) {
+    MaybeSetFailureCode(NS_ERROR_FAILURE);
+  } else {
+    SDBRequestResponse response;
+
+    if (NS_SUCCEEDED(mResultCode)) {
+      GetResponse(response);
+
+      MOZ_ASSERT(response.type() != SDBRequestResponse::T__None);
+      MOZ_ASSERT(response.type() != SDBRequestResponse::Tnsresult);
+    } else {
+      response = mResultCode;
+    }
+
+    Unused <<
+      PBackgroundSDBRequestParent::Send__delete__(this, response);
+
+    if (NS_SUCCEEDED(mResultCode)) {
+      OnSuccess();
+    }
+  }
+
+  Cleanup();
+}
+
+void
+ConnectionOperationBase::DatabaseWork()
+{
+  AssertIsOnIOThread();
+  MOZ_ASSERT(NS_SUCCEEDED(mResultCode));
+
+  if (!OperationMayProceed()) {
+    // The operation was canceled in some way, likely because the child process
+    // has crashed.
+    mResultCode = NS_ERROR_FAILURE;
+  } else {
+    nsIFileStream* fileStream = mConnection->GetFileStream();
+    MOZ_ASSERT(fileStream);
+
+    nsresult rv = DoDatabaseWork(fileStream);
+    if (NS_FAILED(rv)) {
+      mResultCode = rv;
+    }
+  }
+
+  MOZ_ALWAYS_SUCCEEDS(mOwningEventTarget->Dispatch(this, NS_DISPATCH_NORMAL));
+}
+
+void
+ConnectionOperationBase::OnSuccess()
+{
+  AssertIsOnOwningThread();
+}
+
+NS_IMETHODIMP
+ConnectionOperationBase::Run()
+{
+  if (IsOnBackgroundThread()) {
+    SendResults();
+  } else {
+    DatabaseWork();
+  }
+
+  return NS_OK;
+}
+
+void
+ConnectionOperationBase::ActorDestroy(ActorDestroyReason aWhy)
+{
+  AssertIsOnBackgroundThread();
+
+  mOperationMayProceed = false;
+  mActorDestroyed = true;
+}
+
+OpenOp::OpenOp(Connection* aConnection, const SDBRequestParams& aParams)
+  : ConnectionOperationBase(aConnection)
+  , mParams(aParams.get_SDBRequestOpenParams())
+  , mState(State::Initial)
+  , mFileStreamOpen(false)
+{
+  MOZ_ASSERT(aParams.type() == SDBRequestParams::TSDBRequestOpenParams);
+}
+
+OpenOp::~OpenOp()
+{
+  MOZ_ASSERT(!mDirectoryLock);
+  MOZ_ASSERT(!mFileStream);
+  MOZ_ASSERT(!mFileStreamOpen);
+  MOZ_ASSERT_IF(OperationMayProceed(),
+                mState == State::Initial || mState == State::Completed);
+}
+
+nsresult
+OpenOp::Dispatch()
+{
+  MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(this));
+
+  return NS_OK;
+}
+
+nsresult
+OpenOp::Open()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(mState == State::Initial);
+
+  if (NS_WARN_IF(QuotaClient::IsShuttingDownOnNonBackgroundThread()) ||
+      !OperationMayProceed()) {
+    return NS_ERROR_FAILURE;
+  }
+
+  const PrincipalInfo& principalInfo = GetConnection()->GetPrincipalInfo();
+
+  if (principalInfo.type() == PrincipalInfo::TSystemPrincipalInfo) {
+    QuotaManager::GetInfoForChrome(&mSuffix, &mGroup, &mOrigin);
+  } else {
+    MOZ_ASSERT(principalInfo.type() == PrincipalInfo::TContentPrincipalInfo);
+
+    nsresult rv;
+    nsCOMPtr<nsIPrincipal> principal =
+      PrincipalInfoToPrincipal(principalInfo, &rv);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+
+    rv = QuotaManager::GetInfoFromPrincipal(principal,
+                                            &mSuffix,
+                                            &mGroup,
+                                            &mOrigin);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+  }
+
+  mState = State::FinishOpen;
+  MOZ_ALWAYS_SUCCEEDS(OwningEventTarget()->Dispatch(this, NS_DISPATCH_NORMAL));
+
+  return NS_OK;
+}
+
+nsresult
+OpenOp::FinishOpen()
+{
+  AssertIsOnOwningThread();
+  MOZ_ASSERT(mState == State::FinishOpen);
+
+  if (gOpenConnections) {
+    for (Connection* connection : *gOpenConnections) {
+      if (connection->Origin() == mOrigin &&
+          connection->Name() == mParams.name()) {
+        return NS_ERROR_STORAGE_BUSY;
+      }
+    }
+  }
+
+  if (QuotaManager::Get()) {
+    nsresult rv = OpenDirectory();
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+
+    return NS_OK;
+  }
+
+  mState = State::QuotaManagerPending;
+  QuotaManager::GetOrCreate(this);
+
+  return NS_OK;
+}
+
+nsresult
+OpenOp::QuotaManagerOpen()
+{
+  AssertIsOnOwningThread();
+  MOZ_ASSERT(mState == State::QuotaManagerPending);
+
+  if (NS_WARN_IF(!QuotaManager::Get())) {
+    return NS_ERROR_FAILURE;
+  }
+
+  nsresult rv = OpenDirectory();
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  return NS_OK;
+}
+
+nsresult
+OpenOp::OpenDirectory()
+{
+  AssertIsOnOwningThread();
+  MOZ_ASSERT(mState == State::FinishOpen ||
+             mState == State::QuotaManagerPending);
+  MOZ_ASSERT(!mOrigin.IsEmpty());
+  MOZ_ASSERT(!mDirectoryLock);
+  MOZ_ASSERT(!QuotaClient::IsShuttingDownOnBackgroundThread());
+  MOZ_ASSERT(QuotaManager::Get());
+
+  mState = State::DirectoryOpenPending;
+  QuotaManager::Get()->OpenDirectory(PERSISTENCE_TYPE_DEFAULT,
+                                     mGroup,
+                                     mOrigin,
+                                     mozilla::dom::quota::Client::SDB,
+                                     /* aExclusive */ false,
+                                     this);
+
+  return NS_OK;
+}
+
+nsresult
+OpenOp::SendToIOThread()
+{
+  AssertIsOnOwningThread();
+  MOZ_ASSERT(mState == State::DirectoryOpenPending);
+
+  if (NS_WARN_IF(QuotaClient::IsShuttingDownOnBackgroundThread()) ||
+      IsActorDestroyed()) {
+    return NS_ERROR_FAILURE;
+  }
+
+  mFileStream = new FileStream(PERSISTENCE_TYPE_DEFAULT, mGroup, mOrigin);
+
+  QuotaManager* quotaManager = QuotaManager::Get();
+  MOZ_ASSERT(quotaManager);
+
+  // Must set this before dispatching otherwise we will race with the IO thread.
+  mState = State::DatabaseWorkOpen;
+
+  nsresult rv = quotaManager->IOThread()->Dispatch(this, NS_DISPATCH_NORMAL);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  return NS_OK;
+}
+
+nsresult
+OpenOp::DatabaseWork()
+{
+  AssertIsOnIOThread();
+  MOZ_ASSERT(mState == State::DatabaseWorkOpen);
+  MOZ_ASSERT(mFileStream);
+  MOZ_ASSERT(!mFileStreamOpen);
+
+  if (NS_WARN_IF(QuotaClient::IsShuttingDownOnNonBackgroundThread()) ||
+      !OperationMayProceed()) {
+    return NS_ERROR_FAILURE;
+  }
+
+  QuotaManager* quotaManager = QuotaManager::Get();
+  MOZ_ASSERT(quotaManager);
+
+  nsCOMPtr<nsIFile> dbDirectory;
+  nsresult rv =
+    quotaManager->EnsureOriginIsInitialized(PERSISTENCE_TYPE_DEFAULT,
+                                            mSuffix,
+                                            mGroup,
+                                            mOrigin,
+                                            getter_AddRefs(dbDirectory));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = dbDirectory->Append(NS_LITERAL_STRING(SDB_DIRECTORY_NAME));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  bool exists;
+  rv = dbDirectory->Exists(&exists);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  if (!exists) {
+    rv = dbDirectory->Create(nsIFile::DIRECTORY_TYPE, 0755);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+  }
+#ifdef DEBUG
+  else {
+    bool isDirectory;
+    MOZ_ASSERT(NS_SUCCEEDED(dbDirectory->IsDirectory(&isDirectory)));
+    MOZ_ASSERT(isDirectory);
+  }
+#endif
+
+  nsCOMPtr<nsIFile> dbFile;
+  rv = dbDirectory->Clone(getter_AddRefs(dbFile));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = dbFile->Append(mParams.name());
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  nsString databaseFilePath;
+  rv = dbFile->GetPath(databaseFilePath);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = mFileStream->Init(dbFile, PR_RDWR | PR_CREATE_FILE, 0644, 0);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  mFileStreamOpen = true;
+
+  rv = DoDatabaseWork(mFileStream);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  // Must set mState before dispatching otherwise we will race with the owning
+  // thread.
+  mState = State::SendingResults;
+
+  rv = OwningEventTarget()->Dispatch(this, NS_DISPATCH_NORMAL);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  return NS_OK;
+}
+
+void
+OpenOp::StreamClosedCallback()
+{
+  AssertIsOnOwningThread();
+  MOZ_ASSERT(NS_FAILED(ResultCode()));
+  MOZ_ASSERT(mDirectoryLock);
+  MOZ_ASSERT(mFileStream);
+  MOZ_ASSERT(mFileStreamOpen);
+
+  mDirectoryLock = nullptr;
+  mFileStream = nullptr;
+  mFileStreamOpen = false;
+}
+
+nsresult
+OpenOp::DoDatabaseWork(nsIFileStream* aFileStream)
+{
+  AssertIsOnIOThread();
+
+  return NS_OK;
+}
+
+void
+OpenOp::GetResponse(SDBRequestResponse& aResponse)
+{
+  AssertIsOnOwningThread();
+
+  aResponse = SDBRequestOpenResponse();
+}
+
+void
+OpenOp::OnSuccess()
+{
+  AssertIsOnOwningThread();
+  MOZ_ASSERT(NS_SUCCEEDED(ResultCode()));
+  MOZ_ASSERT(!mOrigin.IsEmpty());
+  MOZ_ASSERT(mDirectoryLock);
+  MOZ_ASSERT(mFileStream);
+  MOZ_ASSERT(mFileStreamOpen);
+
+  RefPtr<DirectoryLock> directoryLock;
+  nsCOMPtr<nsIFileStream> fileStream;
+
+  mDirectoryLock.swap(directoryLock);
+  mFileStream.swap(fileStream);
+  mFileStreamOpen = false;
+
+  GetConnection()->OnOpen(mOrigin,
+                          mParams.name(),
+                          directoryLock.forget(),
+                          fileStream.forget());
+}
+
+void
+OpenOp::Cleanup()
+{
+  AssertIsOnOwningThread();
+  MOZ_ASSERT_IF(mFileStreamOpen, mFileStream);
+
+  if (mFileStream && mFileStreamOpen) {
+    // If we have an initialized file stream then the operation must have failed
+    // and there must be a directory lock too.
+    MOZ_ASSERT(NS_FAILED(ResultCode()));
+    MOZ_ASSERT(mDirectoryLock);
+
+    // We must close the stream on the I/O thread before releasing it on this
+    // thread. The directory lock can't be released either.
+    nsCOMPtr<nsIRunnable> callback =
+      NewRunnableMethod("dom::OpenOp::StreamClosedCallback",
+                        this,
+                        &OpenOp::StreamClosedCallback);
+
+    RefPtr<StreamHelper> helper = new StreamHelper(mFileStream, callback);
+    helper->AsyncClose();
+  } else {
+    MOZ_ASSERT(!mFileStreamOpen);
+
+    mDirectoryLock = nullptr;
+    mFileStream = nullptr;
+  }
+
+  ConnectionOperationBase::Cleanup();
+}
+
+NS_IMPL_ISUPPORTS_INHERITED0(OpenOp, ConnectionOperationBase)
+
+NS_IMETHODIMP
+OpenOp::Run()
+{
+  nsresult rv;
+
+  switch (mState) {
+    case State::Initial:
+      rv = Open();
+      break;
+
+    case State::FinishOpen:
+      rv = FinishOpen();
+      break;
+
+    case State::QuotaManagerPending:
+      rv = QuotaManagerOpen();
+      break;
+
+    case State::DatabaseWorkOpen:
+      rv = DatabaseWork();
+      break;
+
+    case State::SendingResults:
+      SendResults();
+      return NS_OK;
+
+    default:
+      MOZ_CRASH("Bad state!");
+  }
+
+  if (NS_WARN_IF(NS_FAILED(rv)) && mState != State::SendingResults) {
+    MaybeSetFailureCode(rv);
+
+    // Must set mState before dispatching otherwise we will race with the owning
+    // thread.
+    mState = State::SendingResults;
+
+    if (IsOnOwningThread()) {
+      SendResults();
+    } else {
+      MOZ_ALWAYS_SUCCEEDS(
+        OwningEventTarget()->Dispatch(this, NS_DISPATCH_NORMAL));
+    }
+  }
+
+  return NS_OK;
+}
+
+void
+OpenOp::DirectoryLockAcquired(DirectoryLock* aLock)
+{
+  AssertIsOnOwningThread();
+  MOZ_ASSERT(mState == State::DirectoryOpenPending);
+  MOZ_ASSERT(!mDirectoryLock);
+
+  mDirectoryLock = aLock;
+
+  nsresult rv = SendToIOThread();
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    MaybeSetFailureCode(rv);
+
+    // The caller holds a strong reference to us, no need for a self reference
+    // before calling Run().
+
+    mState = State::SendingResults;
+    MOZ_ALWAYS_SUCCEEDS(Run());
+
+    return;
+  }
+}
+
+void
+OpenOp::DirectoryLockFailed()
+{
+  AssertIsOnOwningThread();
+  MOZ_ASSERT(mState == State::DirectoryOpenPending);
+  MOZ_ASSERT(!mDirectoryLock);
+
+  MaybeSetFailureCode(NS_ERROR_FAILURE);
+
+  // The caller holds a strong reference to us, no need for a self reference
+  // before calling Run().
+
+  mState = State::SendingResults;
+  MOZ_ALWAYS_SUCCEEDS(Run());
+}
+
+SeekOp::SeekOp(Connection* aConnection,
+               const SDBRequestParams& aParams)
+  : ConnectionOperationBase(aConnection)
+  , mParams(aParams.get_SDBRequestSeekParams())
+{
+  MOZ_ASSERT(aParams.type() == SDBRequestParams::TSDBRequestSeekParams);
+}
+
+nsresult
+SeekOp::DoDatabaseWork(nsIFileStream* aFileStream)
+{
+  AssertIsOnIOThread();
+  MOZ_ASSERT(aFileStream);
+
+  nsCOMPtr<nsISeekableStream> seekableStream = do_QueryInterface(aFileStream);
+  MOZ_ASSERT(seekableStream);
+
+  nsresult rv = seekableStream->Seek(nsISeekableStream::NS_SEEK_SET,
+                                     mParams.offset());
+
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  return NS_OK;
+}
+
+void
+SeekOp::GetResponse(SDBRequestResponse& aResponse)
+{
+  aResponse = SDBRequestSeekResponse();
+}
+
+ReadOp::ReadOp(Connection* aConnection,
+               const SDBRequestParams& aParams)
+  : ConnectionOperationBase(aConnection)
+  , mParams(aParams.get_SDBRequestReadParams())
+{
+  MOZ_ASSERT(aParams.type() == SDBRequestParams::TSDBRequestReadParams);
+}
+
+bool
+ReadOp::Init()
+{
+  AssertIsOnOwningThread();
+
+  if (NS_WARN_IF(!ConnectionOperationBase::Init())) {
+    return false;
+  }
+
+  mOutputStream = MemoryOutputStream::Create(mParams.size());
+  if (NS_WARN_IF(!mOutputStream)) {
+    return false;
+  }
+
+  return true;
+}
+
+nsresult
+ReadOp::DoDatabaseWork(nsIFileStream* aFileStream)
+{
+  AssertIsOnIOThread();
+  MOZ_ASSERT(aFileStream);
+
+  nsCOMPtr<nsIInputStream> inputStream = do_QueryInterface(aFileStream);
+  MOZ_ASSERT(inputStream);
+
+  nsresult rv;
+
+  uint64_t offset = 0;
+
+  do {
+    char copyBuffer[kCopyBufferSize];
+
+    uint64_t max = mParams.size() - offset;
+    if (max == 0) {
+      break;
+    }
+
+    uint32_t count = sizeof(copyBuffer);
+    if (count > max) {
+      count = max;
+    }
+
+    uint32_t numRead;
+    rv = inputStream->Read(copyBuffer, count, &numRead);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+
+    if (!numRead) {
+      break;
+    }
+
+    uint32_t numWrite;
+    rv = mOutputStream->Write(copyBuffer, numRead, &numWrite);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+
+    if (NS_WARN_IF(numWrite != numRead)) {
+      return NS_ERROR_FAILURE;
+    }
+
+    offset += numWrite;
+  } while (true);
+
+  MOZ_ASSERT(offset == mParams.size());
+
+  MOZ_ALWAYS_SUCCEEDS(mOutputStream->Close());
+
+  return NS_OK;
+}
+
+void
+ReadOp::GetResponse(SDBRequestResponse& aResponse)
+{
+  aResponse = SDBRequestReadResponse(mOutputStream->Data());
+}
+
+WriteOp::WriteOp(Connection* aConnection,
+                 const SDBRequestParams& aParams)
+  : ConnectionOperationBase(aConnection)
+  , mParams(aParams.get_SDBRequestWriteParams())
+  , mSize(0)
+{
+  MOZ_ASSERT(aParams.type() == SDBRequestParams::TSDBRequestWriteParams);
+}
+
+bool
+WriteOp::Init()
+{
+  AssertIsOnOwningThread();
+
+  if (NS_WARN_IF(!ConnectionOperationBase::Init())) {
+    return false;
+  }
+
+  const nsCString& string = mParams.data();
+
+  nsCOMPtr<nsIInputStream> inputStream;
+  nsresult rv =
+    NS_NewCStringInputStream(getter_AddRefs(inputStream), string);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return false;
+  }
+
+  mInputStream = std::move(inputStream);
+  mSize = string.Length();
+
+  return true;
+}
+
+nsresult
+WriteOp::DoDatabaseWork(nsIFileStream* aFileStream)
+{
+  AssertIsOnIOThread();
+  MOZ_ASSERT(aFileStream);
+
+  nsCOMPtr<nsIOutputStream> outputStream = do_QueryInterface(aFileStream);
+  MOZ_ASSERT(outputStream);
+
+  nsresult rv;
+
+  do {
+    char copyBuffer[kCopyBufferSize];
+
+    uint32_t numRead;
+    rv = mInputStream->Read(copyBuffer, sizeof(copyBuffer), &numRead);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      break;
+    }
+
+    if (!numRead) {
+      break;
+    }
+
+    uint32_t numWrite;
+    rv = outputStream->Write(copyBuffer, numRead, &numWrite);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+
+    if (NS_WARN_IF(numWrite != numRead)) {
+      return NS_ERROR_FAILURE;
+    }
+  } while (true);
+
+  MOZ_ALWAYS_SUCCEEDS(mInputStream->Close());
+
+  return NS_OK;
+}
+
+void
+WriteOp::GetResponse(SDBRequestResponse& aResponse)
+{
+  aResponse = SDBRequestWriteResponse();
+}
+
+CloseOp::CloseOp(Connection* aConnection)
+  : ConnectionOperationBase(aConnection)
+{ }
+
+nsresult
+CloseOp::DoDatabaseWork(nsIFileStream* aFileStream)
+{
+  AssertIsOnIOThread();
+  MOZ_ASSERT(aFileStream);
+
+  nsCOMPtr<nsIInputStream> inputStream = do_QueryInterface(aFileStream);
+  MOZ_ASSERT(inputStream);
+
+  nsresult rv = inputStream->Close();
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  return NS_OK;
+}
+
+void
+CloseOp::GetResponse(SDBRequestResponse& aResponse)
+{
+  aResponse = SDBRequestCloseResponse();
+}
+
+void
+CloseOp::OnSuccess()
+{
+  AssertIsOnOwningThread();
+
+  GetConnection()->OnClose();
+}
+
+/*******************************************************************************
+ * QuotaClient
+ ******************************************************************************/
+
+QuotaClient* QuotaClient::sInstance = nullptr;
+
+QuotaClient::QuotaClient()
+  : mShutdownRequested(false)
+{
+  AssertIsOnBackgroundThread();
+  MOZ_ASSERT(!sInstance, "We expect this to be a singleton!");
+
+  sInstance = this;
+}
+
+QuotaClient::~QuotaClient()
+{
+  AssertIsOnBackgroundThread();
+  MOZ_ASSERT(sInstance == this, "We expect this to be a singleton!");
+
+  sInstance = nullptr;
+}
+
+mozilla::dom::quota::Client::Type
+QuotaClient::GetType()
+{
+  return QuotaClient::SDB;
+}
+
+nsresult
+QuotaClient::InitOrigin(PersistenceType aPersistenceType,
+                        const nsACString& aGroup,
+                        const nsACString& aOrigin,
+                        const AtomicBool& aCanceled,
+                        UsageInfo* aUsageInfo)
+{
+  AssertIsOnIOThread();
+
+  if (!aUsageInfo) {
+    return NS_OK;
+  }
+
+  return GetUsageForOrigin(aPersistenceType,
+                           aGroup,
+                           aOrigin,
+                           aCanceled,
+                           aUsageInfo);
+}
+
+nsresult
+QuotaClient::GetUsageForOrigin(PersistenceType aPersistenceType,
+                               const nsACString& aGroup,
+                               const nsACString& aOrigin,
+                               const AtomicBool& aCanceled,
+                               UsageInfo* aUsageInfo)
+{
+  AssertIsOnIOThread();
+  MOZ_ASSERT(aUsageInfo);
+
+  QuotaManager* quotaManager = QuotaManager::Get();
+  MOZ_ASSERT(quotaManager);
+
+  nsCOMPtr<nsIFile> directory;
+  nsresult rv = quotaManager->GetDirectoryForOrigin(aPersistenceType, aOrigin,
+                                                    getter_AddRefs(directory));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  MOZ_ASSERT(directory);
+
+  rv = directory->Append(NS_LITERAL_STRING(SDB_DIRECTORY_NAME));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  DebugOnly<bool> exists;
+  MOZ_ASSERT(NS_SUCCEEDED(directory->Exists(&exists)) && exists);
+
+  nsCOMPtr<nsISimpleEnumerator> entries;
+  rv = directory->GetDirectoryEntries(getter_AddRefs(entries));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  bool hasMore;
+  while (NS_SUCCEEDED((rv = entries->HasMoreElements(&hasMore))) &&
+         hasMore && !aCanceled) {
+    nsCOMPtr<nsISupports> entry;
+    rv = entries->GetNext(getter_AddRefs(entry));
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+
+    nsCOMPtr<nsIFile> file = do_QueryInterface(entry);
+    MOZ_ASSERT(file);
+
+    int64_t fileSize;
+    rv = file->GetFileSize(&fileSize);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+
+    MOZ_ASSERT(fileSize >= 0);
+
+    aUsageInfo->AppendToDatabaseUsage(uint64_t(fileSize));
+  }
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  return NS_OK;
+}
+
+void
+QuotaClient::OnOriginClearCompleted(PersistenceType aPersistenceType,
+                                    const nsACString& aOrigin)
+{
+  AssertIsOnIOThread();
+}
+
+void
+QuotaClient::ReleaseIOThreadObjects()
+{
+  AssertIsOnIOThread();
+}
+
+void
+QuotaClient::AbortOperations(const nsACString& aOrigin)
+{
+  AssertIsOnBackgroundThread();
+
+  if (gOpenConnections) {
+    for (Connection* connection : *gOpenConnections) {
+      if (aOrigin.IsVoid() || connection->Origin() == aOrigin) {
+        connection->AllowToClose();
+      }
+    }
+  }
+}
+
+void
+QuotaClient::AbortOperationsForProcess(ContentParentId aContentParentId)
+{
+  AssertIsOnBackgroundThread();
+}
+
+void
+QuotaClient::StartIdleMaintenance()
+{
+  AssertIsOnBackgroundThread();
+}
+
+void
+QuotaClient::StopIdleMaintenance()
+{
+  AssertIsOnBackgroundThread();
+}
+
+void
+QuotaClient::ShutdownWorkThreads()
+{
+  AssertIsOnBackgroundThread();
+  MOZ_ASSERT(!mShutdownRequested);
+
+  mShutdownRequested = true;
+
+  if (gOpenConnections) {
+    for (Connection* connection : *gOpenConnections) {
+      connection->AllowToClose();
+    }
+
+    MOZ_ALWAYS_TRUE(SpinEventLoopUntil([&]() { return !gOpenConnections; }));
+  }
+}
+
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/simpledb/ActorsParent.h
@@ -0,0 +1,52 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=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_simpledb_ActorsParent_h
+#define mozilla_dom_simpledb_ActorsParent_h
+
+template <class> struct already_AddRefed;
+
+namespace mozilla {
+
+namespace ipc {
+
+class PrincipalInfo;
+
+} // namespace ipc
+
+namespace dom {
+
+class PBackgroundSDBConnectionParent;
+
+namespace quota {
+
+class Client;
+
+} // namespace quota
+
+PBackgroundSDBConnectionParent*
+AllocPBackgroundSDBConnectionParent(
+                             const mozilla::ipc::PrincipalInfo& aPrincipalInfo);
+
+bool
+RecvPBackgroundSDBConnectionConstructor(
+                             PBackgroundSDBConnectionParent* aActor,
+                             const mozilla::ipc::PrincipalInfo& aPrincipalInfo);
+
+bool
+DeallocPBackgroundSDBConnectionParent(PBackgroundSDBConnectionParent* aActor);
+
+namespace simpledb {
+
+already_AddRefed<mozilla::dom::quota::Client>
+CreateQuotaClient();
+
+} // namespace simpledb
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_simpledb_ActorsParent_h
new file mode 100644
--- /dev/null
+++ b/dom/simpledb/PBackgroundSDBConnection.ipdl
@@ -0,0 +1,64 @@
+/* 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 protocol PBackground;
+include protocol PBackgroundSDBRequest;
+
+namespace mozilla {
+namespace dom {
+
+struct SDBRequestOpenParams
+{
+  nsString name;
+};
+
+struct SDBRequestSeekParams
+{
+  uint64_t offset;
+};
+
+struct SDBRequestReadParams
+{
+  uint64_t size;
+};
+
+struct SDBRequestWriteParams
+{
+  nsCString data;
+};
+
+struct SDBRequestCloseParams
+{
+};
+
+union SDBRequestParams
+{
+  SDBRequestOpenParams;
+  SDBRequestSeekParams;
+  SDBRequestReadParams;
+  SDBRequestWriteParams;
+  SDBRequestCloseParams;
+};
+
+protocol PBackgroundSDBConnection
+{
+  manager PBackground;
+
+  manages PBackgroundSDBRequest;
+
+parent:
+  async DeleteMe();
+
+  async PBackgroundSDBRequest(SDBRequestParams params);
+
+child:
+  async __delete__();
+
+  async AllowToClose();
+
+  async Closed();
+};
+
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/simpledb/PBackgroundSDBRequest.ipdl
@@ -0,0 +1,50 @@
+/* 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 protocol PBackgroundSDBConnection;
+
+namespace mozilla {
+namespace dom {
+
+struct SDBRequestOpenResponse
+{
+};
+
+struct SDBRequestSeekResponse
+{
+};
+
+struct SDBRequestReadResponse
+{
+  nsCString data;
+};
+
+struct SDBRequestWriteResponse
+{
+};
+
+struct SDBRequestCloseResponse
+{
+};
+
+union SDBRequestResponse
+{
+  nsresult;
+  SDBRequestOpenResponse;
+  SDBRequestSeekResponse;
+  SDBRequestReadResponse;
+  SDBRequestWriteResponse;
+  SDBRequestCloseResponse;
+};
+
+protocol PBackgroundSDBRequest
+{
+  manager PBackgroundSDBConnection;
+
+child:
+  async __delete__(SDBRequestResponse response);
+};
+
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/simpledb/SDBConnection.cpp
@@ -0,0 +1,403 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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 "SDBConnection.h"
+
+#include "ActorsChild.h"
+#include "jsfriendapi.h"
+#include "mozilla/ipc/BackgroundChild.h"
+#include "mozilla/ipc/BackgroundParent.h"
+#include "mozilla/ipc/BackgroundUtils.h"
+#include "mozilla/ipc/PBackgroundChild.h"
+#include "nsISDBCallbacks.h"
+#include "SDBRequest.h"
+
+namespace mozilla {
+namespace dom {
+
+using namespace mozilla::ipc;
+
+namespace {
+
+nsresult
+GetWriteData(JSContext* aCx,
+             JS::Handle<JS::Value> aValue,
+             nsCString& aData)
+{
+  if (aValue.isObject()) {
+    JS::Rooted<JSObject*> obj(aCx, &aValue.toObject());
+
+    bool isView = false;
+    if (JS_IsArrayBufferObject(obj) ||
+        (isView = JS_IsArrayBufferViewObject(obj))) {
+      uint8_t* data;
+      uint32_t length;
+      bool unused;
+      if (isView) {
+        JS_GetObjectAsArrayBufferView(obj, &length, &unused, &data);
+      } else {
+        JS_GetObjectAsArrayBuffer(obj, &length, &data);
+      }
+
+      if (NS_WARN_IF(!aData.Assign(reinterpret_cast<char*>(data),
+                                   length,
+                                   fallible_t()))) {
+        return NS_ERROR_OUT_OF_MEMORY;
+      }
+
+      return NS_OK;
+    }
+  }
+
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+} // namespace
+
+SDBConnection::SDBConnection()
+  : mBackgroundActor(nullptr)
+  , mRunningRequest(false)
+  , mOpen(false)
+  , mAllowedToClose(false)
+{
+  AssertIsOnOwningThread();
+}
+
+SDBConnection::~SDBConnection()
+{
+  AssertIsOnOwningThread();
+
+  if (mBackgroundActor) {
+    mBackgroundActor->SendDeleteMeInternal();
+    MOZ_ASSERT(!mBackgroundActor, "SendDeleteMeInternal should have cleared!");
+  }
+}
+
+void
+SDBConnection::ClearBackgroundActor()
+{
+  AssertIsOnOwningThread();
+
+  mBackgroundActor = nullptr;
+}
+
+void
+SDBConnection::OnNewRequest()
+{
+  AssertIsOnOwningThread();
+  MOZ_ASSERT(!mRunningRequest);
+
+  mRunningRequest = true;
+}
+
+void
+SDBConnection::OnRequestFinished()
+{
+  AssertIsOnOwningThread();
+  MOZ_ASSERT(mRunningRequest);
+
+  mRunningRequest = false;
+}
+
+void
+SDBConnection::OnOpen()
+{
+  AssertIsOnOwningThread();
+  MOZ_ASSERT(!mOpen);
+
+  mOpen = true;
+}
+
+void
+SDBConnection::OnClose(bool aAbnormal)
+{
+  AssertIsOnOwningThread();
+  MOZ_ASSERT(mOpen);
+
+  mOpen = false;
+
+  if (aAbnormal) {
+    MOZ_ASSERT(mAllowedToClose);
+
+    if (mCloseCallback) {
+      mCloseCallback->OnClose(this);
+    }
+  }
+}
+
+void
+SDBConnection::AllowToClose()
+{
+  AssertIsOnOwningThread();
+
+  mAllowedToClose = true;
+}
+
+nsresult
+SDBConnection::CheckState()
+{
+  AssertIsOnOwningThread();
+
+  if (mAllowedToClose) {
+    return NS_ERROR_ABORT;
+  }
+
+  if (mRunningRequest) {
+    return NS_ERROR_NOT_AVAILABLE;
+  }
+
+  return NS_OK;
+}
+
+nsresult
+SDBConnection::EnsureBackgroundActor()
+{
+  AssertIsOnOwningThread();
+
+  if (mBackgroundActor) {
+    return NS_OK;
+  }
+
+  PBackgroundChild* backgroundActor =
+    BackgroundChild::GetOrCreateForCurrentThread();
+  if (NS_WARN_IF(!backgroundActor)) {
+    return NS_ERROR_FAILURE;
+  }
+
+  SDBConnectionChild* actor = new SDBConnectionChild(this);
+
+  mBackgroundActor =
+    static_cast<SDBConnectionChild*>(
+      backgroundActor->SendPBackgroundSDBConnectionConstructor(
+                                                              actor,
+                                                              *mPrincipalInfo));
+  if (NS_WARN_IF(!mBackgroundActor)) {
+    return NS_ERROR_FAILURE;
+  }
+
+  return NS_OK;
+}
+
+nsresult
+SDBConnection::InitiateRequest(SDBRequest* aRequest,
+                               const SDBRequestParams& aParams)
+{
+  AssertIsOnOwningThread();
+  MOZ_ASSERT(aRequest);
+  MOZ_ASSERT(mBackgroundActor);
+
+  auto actor = new SDBRequestChild(aRequest);
+
+  if (!mBackgroundActor->SendPBackgroundSDBRequestConstructor(actor, aParams)) {
+    return NS_ERROR_FAILURE;
+  }
+
+  // Balanced in SDBRequestChild::Recv__delete__().
+  OnNewRequest();
+
+  return NS_OK;
+}
+
+NS_IMPL_ISUPPORTS(SDBConnection, nsISDBConnection)
+
+NS_IMETHODIMP
+SDBConnection::Init(nsIPrincipal *aPrincipal)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(aPrincipal);
+
+  nsAutoPtr<PrincipalInfo> principalInfo(new PrincipalInfo());
+  nsresult rv = PrincipalToPrincipalInfo(aPrincipal, principalInfo);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  if (principalInfo->type() != PrincipalInfo::TContentPrincipalInfo &&
+      principalInfo->type() != PrincipalInfo::TSystemPrincipalInfo) {
+    NS_WARNING("Simpledb not allowed for this principal!");
+    return NS_ERROR_INVALID_ARG;
+  }
+
+  mPrincipalInfo = std::move(principalInfo);
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+SDBConnection::Open(const nsAString& aName, nsISDBRequest** _retval)
+{
+  AssertIsOnOwningThread();
+
+  nsresult rv = CheckState();
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  if (mOpen) {
+    return NS_ERROR_ALREADY_INITIALIZED;
+  }
+
+  SDBRequestOpenParams params;
+  params.name() = aName;
+
+  RefPtr<SDBRequest> request = new SDBRequest(this);
+
+  rv = EnsureBackgroundActor();
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = InitiateRequest(request, params);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  request.forget(_retval);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+SDBConnection::Seek(uint64_t aOffset, nsISDBRequest** _retval)
+{
+  AssertIsOnOwningThread();
+
+  nsresult rv = CheckState();
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  if (!mOpen) {
+    return NS_BASE_STREAM_CLOSED;
+  }
+
+  SDBRequestSeekParams params;
+  params.offset() = aOffset;
+
+  RefPtr<SDBRequest> request = new SDBRequest(this);
+
+  rv = InitiateRequest(request, params);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  request.forget(_retval);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+SDBConnection::Read(uint64_t aSize, nsISDBRequest** _retval)
+{
+  AssertIsOnOwningThread();
+
+  nsresult rv = CheckState();
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  if (!mOpen) {
+    return NS_BASE_STREAM_CLOSED;
+  }
+
+  SDBRequestReadParams params;
+  params.size() = aSize;
+
+  RefPtr<SDBRequest> request = new SDBRequest(this);
+
+  rv = InitiateRequest(request, params);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  request.forget(_retval);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+SDBConnection::Write(JS::HandleValue aValue,
+                     JSContext* aCx,
+                     nsISDBRequest** _retval)
+{
+  AssertIsOnOwningThread();
+
+  nsresult rv = CheckState();
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  if (!mOpen) {
+    return NS_BASE_STREAM_CLOSED;
+  }
+
+  JS::Rooted<JS::Value> value(aCx, aValue);
+
+  nsCString data;
+  rv = GetWriteData(aCx, value, data);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  SDBRequestWriteParams params;
+  params.data() = data;
+
+  RefPtr<SDBRequest> request = new SDBRequest(this);
+
+  rv = InitiateRequest(request, params);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  request.forget(_retval);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+SDBConnection::Close(nsISDBRequest** _retval)
+{
+  AssertIsOnOwningThread();
+
+  nsresult rv = CheckState();
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  if (!mOpen) {
+    return NS_BASE_STREAM_CLOSED;
+  }
+
+  SDBRequestCloseParams params;
+
+  RefPtr<SDBRequest> request = new SDBRequest(this);
+
+  rv = InitiateRequest(request, params);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  request.forget(_retval);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+SDBConnection::GetCloseCallback(nsISDBCloseCallback** aCloseCallback)
+{
+  AssertIsOnOwningThread();
+  MOZ_ASSERT(aCloseCallback);
+
+  NS_IF_ADDREF(*aCloseCallback = mCloseCallback);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+SDBConnection::SetCloseCallback(nsISDBCloseCallback* aCloseCallback)
+{
+  AssertIsOnOwningThread();
+
+  mCloseCallback = aCloseCallback;
+  return NS_OK;
+}
+
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/simpledb/SDBConnection.h
@@ -0,0 +1,97 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=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_simpledb_SDBConnection_h
+#define mozilla_dom_simpledb_SDBConnection_h
+
+#include "nsAutoPtr.h"
+#include "nsISDBConnection.h"
+#include "nsTArray.h"
+
+#define NS_SDBCONNECTION_CONTRACTID \
+  "@mozilla.org/dom/sdb-connection;1"
+
+class nsISDBCloseCallback;
+
+namespace mozilla {
+
+namespace ipc {
+
+class PBackgroundChild;
+class PrincipalInfo;
+
+} // namespace ipc
+
+namespace dom {
+
+class SDBConnectionChild;
+class SDBRequest;
+class SDBRequestParams;
+
+class SDBConnection final
+  : public nsISDBConnection
+{
+  typedef mozilla::ipc::PBackgroundChild PBackgroundChild;
+  typedef mozilla::ipc::PrincipalInfo PrincipalInfo;
+
+  nsCOMPtr<nsISDBCloseCallback> mCloseCallback;
+
+  nsAutoPtr<PrincipalInfo> mPrincipalInfo;
+
+  SDBConnectionChild* mBackgroundActor;
+
+  bool mRunningRequest;
+  bool mOpen;
+  bool mAllowedToClose;
+
+public:
+  SDBConnection();
+
+  void
+  AssertIsOnOwningThread() const
+  {
+    NS_ASSERT_OWNINGTHREAD(SDBConnection);
+  }
+
+  void
+  ClearBackgroundActor();
+
+  void
+  OnNewRequest();
+
+  void
+  OnRequestFinished();
+
+  void
+  OnOpen();
+
+  void
+  OnClose(bool aAbnormal);
+
+  void
+  AllowToClose();
+
+private:
+  ~SDBConnection();
+
+  nsresult
+  CheckState();
+
+  nsresult
+  EnsureBackgroundActor();
+
+  nsresult
+  InitiateRequest(SDBRequest* aRequest,
+                  const SDBRequestParams& aParams);
+
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSISDBCONNECTION
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif /* mozilla_dom_simpledb_SDBConnection_h */
new file mode 100644
--- /dev/null
+++ b/dom/simpledb/SDBRequest.cpp
@@ -0,0 +1,134 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=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 "SDBRequest.h"
+
+#include "nsISDBCallbacks.h"
+#include "nsThreadUtils.h"
+#include "SDBConnection.h"
+
+namespace mozilla {
+namespace dom {
+
+SDBRequest::SDBRequest(SDBConnection* aConnection)
+  : mConnection(aConnection)
+  , mResultCode(NS_OK)
+  , mHaveResultOrErrorCode(false)
+{
+  AssertIsOnOwningThread();
+  MOZ_ASSERT(aConnection);
+}
+
+SDBRequest::~SDBRequest()
+{
+  AssertIsOnOwningThread();
+}
+
+void
+SDBRequest::SetResult(nsIVariant* aResult)
+{
+  AssertIsOnOwningThread();
+  MOZ_ASSERT(aResult);
+  MOZ_ASSERT(mConnection);
+  MOZ_ASSERT(!mHaveResultOrErrorCode);
+
+  mResult = aResult;
+  mHaveResultOrErrorCode = true;
+
+  FireCallback();
+}
+
+void
+SDBRequest::SetError(nsresult aRv)
+{
+  AssertIsOnOwningThread();
+  MOZ_ASSERT(mConnection);
+  MOZ_ASSERT(mResultCode == NS_OK);
+  MOZ_ASSERT(!mHaveResultOrErrorCode);
+
+  mResultCode = aRv;
+  mHaveResultOrErrorCode = true;
+
+  FireCallback();
+}
+
+void
+SDBRequest::FireCallback()
+{
+  AssertIsOnOwningThread();
+
+  if (mCallback) {
+    nsCOMPtr<nsISDBCallback> callback;
+    callback.swap(mCallback);
+
+    MOZ_ALWAYS_SUCCEEDS(NS_DispatchToCurrentThread(
+      NewRunnableMethod<RefPtr<SDBRequest>>(
+        "nsISDBCallback::OnComplete",
+        callback,
+        &nsISDBCallback::OnComplete,
+        this)));
+  }
+}
+
+NS_IMPL_CYCLE_COLLECTING_ADDREF(SDBRequest)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(SDBRequest)
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(SDBRequest)
+  NS_INTERFACE_MAP_ENTRY(nsISupports)
+  NS_INTERFACE_MAP_ENTRY(nsISDBRequest)
+NS_INTERFACE_MAP_END
+
+NS_IMPL_CYCLE_COLLECTION(SDBRequest, mCallback, mResult)
+
+NS_IMETHODIMP
+SDBRequest::GetResult(nsIVariant** aResult)
+{
+  AssertIsOnOwningThread();
+  MOZ_ASSERT(aResult);
+
+  if (!mHaveResultOrErrorCode) {
+    return NS_ERROR_NOT_AVAILABLE;
+  }
+
+  NS_IF_ADDREF(*aResult = mResult);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+SDBRequest::GetResultCode(nsresult* aResultCode)
+{
+  AssertIsOnOwningThread();
+  MOZ_ASSERT(aResultCode);
+
+  if (!mHaveResultOrErrorCode) {
+    return NS_ERROR_NOT_AVAILABLE;
+  }
+
+  *aResultCode = mResultCode;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+SDBRequest::GetCallback(nsISDBCallback** aCallback)
+{
+  AssertIsOnOwningThread();
+  MOZ_ASSERT(aCallback);
+
+  NS_IF_ADDREF(*aCallback = mCallback);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+SDBRequest::SetCallback(nsISDBCallback* aCallback)
+{
+  AssertIsOnOwningThread();
+
+  mCallback = aCallback;
+  return NS_OK;
+}
+
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/simpledb/SDBRequest.h
@@ -0,0 +1,70 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=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_simpledb_SDBRequest_h
+#define mozilla_dom_simpledb_SDBRequest_h
+
+#include "nsCOMPtr.h"
+#include "nsCycleCollectionParticipant.h"
+#include "nsISDBRequest.h"
+#include "nsIVariant.h"
+
+class nsISDBCallback;
+
+namespace mozilla {
+namespace dom {
+
+class SDBConnection;
+
+class SDBRequest final
+  : public nsISDBRequest
+{
+  RefPtr<SDBConnection> mConnection;
+
+  nsCOMPtr<nsIVariant> mResult;
+  nsCOMPtr<nsISDBCallback> mCallback;
+
+  nsresult mResultCode;
+  bool mHaveResultOrErrorCode;
+
+public:
+  explicit SDBRequest(SDBConnection* aConnection);
+
+  void
+  AssertIsOnOwningThread() const
+  {
+    NS_ASSERT_OWNINGTHREAD(SDBRequest);
+  }
+
+  SDBConnection*
+  GetConnection() const
+  {
+    AssertIsOnOwningThread();
+
+    return mConnection;
+  }
+
+  void
+  SetResult(nsIVariant* aResult);
+
+  void
+  SetError(nsresult aRv);
+
+private:
+  ~SDBRequest();
+
+  void
+  FireCallback();
+
+  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+  NS_DECL_NSISDBREQUEST
+  NS_DECL_CYCLE_COLLECTION_CLASS(SDBRequest)
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_simpledb_SDBRequest_h
new file mode 100644
--- /dev/null
+++ b/dom/simpledb/SDBResults.cpp
@@ -0,0 +1,63 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=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 "SDBResults.h"
+
+#include "nsContentUtils.h"
+
+namespace mozilla {
+namespace dom {
+
+SDBResult::SDBResult(const nsACString& aData)
+  : mData(aData)
+{
+}
+
+NS_IMPL_ISUPPORTS(SDBResult,
+                  nsISDBResult)
+
+NS_IMETHODIMP
+SDBResult::GetAsArray(uint32_t* aDataLen, uint8_t** aData)
+{
+  MOZ_ASSERT(aDataLen);
+  MOZ_ASSERT(aData);
+
+  if (mData.IsEmpty()) {
+    *aDataLen = 0;
+    *aData = nullptr;
+    return NS_OK;
+  }
+
+  uint32_t length = mData.Length();
+
+  uint8_t* data = static_cast<uint8_t*>(moz_xmalloc(length * sizeof(uint8_t)));
+  if (!data) {
+    return NS_ERROR_OUT_OF_MEMORY;
+  }
+
+  memcpy(data, mData.BeginReading(), length * sizeof(uint8_t));
+
+  *aDataLen = length;
+  *aData = data;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+SDBResult::GetAsArrayBuffer(JSContext* aCx, JS::MutableHandleValue _retval)
+{
+  JS::Rooted<JSObject*> arrayBuffer(aCx);
+  nsresult rv =
+    nsContentUtils::CreateArrayBuffer(aCx, mData, arrayBuffer.address());
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  _retval.setObject(*arrayBuffer);
+  return NS_OK;
+}
+
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/simpledb/SDBResults.h
@@ -0,0 +1,35 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=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_simpledb_SDBResults_h
+#define mozilla_dom_simpledb_SDBResults_h
+
+#include "nsISDBResults.h"
+#include "nsString.h"
+
+namespace mozilla {
+namespace dom {
+
+class SDBResult
+  : public nsISDBResult
+{
+  nsCString mData;
+
+public:
+  explicit SDBResult(const nsACString& aData);
+
+private:
+  virtual ~SDBResult()
+  { }
+
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSISDBRESULT
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_simpledb_SDBResults_h
new file mode 100644
--- /dev/null
+++ b/dom/simpledb/moz.build
@@ -0,0 +1,42 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+XPIDL_SOURCES += [
+    'nsISDBCallbacks.idl',
+    'nsISDBConnection.idl',
+    'nsISDBRequest.idl',
+    'nsISDBResults.idl',
+]
+
+XPIDL_MODULE = 'dom_simpledb'
+
+EXPORTS.mozilla.dom.simpledb += [
+    'ActorsParent.h',
+]
+
+EXPORTS.mozilla.dom += [
+    'SDBConnection.h',
+]
+
+UNIFIED_SOURCES += [
+    'ActorsChild.cpp',
+    'ActorsParent.cpp',
+    'SDBConnection.cpp',
+    'SDBRequest.cpp',
+    'SDBResults.cpp',
+]
+
+IPDL_SOURCES += [
+    'PBackgroundSDBConnection.ipdl',
+    'PBackgroundSDBRequest.ipdl',
+]
+
+include('/ipc/chromium/chromium-config.mozbuild')
+
+FINAL_LIBRARY = 'xul'
+
+if CONFIG['GNU_CXX']:
+    CXXFLAGS += ['-Wno-error=shadow']
new file mode 100644
--- /dev/null
+++ b/dom/simpledb/nsISDBCallbacks.idl
@@ -0,0 +1,22 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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 "nsISupports.idl"
+
+interface nsISDBConnection;
+interface nsISDBRequest;
+
+[scriptable, function, uuid(8cbd576c-c6bf-42fd-96ee-3b824dafe1d4)]
+interface nsISDBCallback : nsISupports
+{
+  void onComplete(in nsISDBRequest aRequest);
+};
+
+[scriptable, function, uuid(e0821d43-62b9-40fe-99f8-ff9ab3184cbf)]
+interface nsISDBCloseCallback : nsISupports
+{
+  void onClose(in nsISDBConnection aConnection);
+};
new file mode 100644
--- /dev/null
+++ b/dom/simpledb/nsISDBConnection.idl
@@ -0,0 +1,35 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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 "nsISupports.idl"
+
+interface nsIPrincipal;
+interface nsISDBCloseCallback;
+interface nsISDBRequest;
+
+[scriptable, builtinclass, uuid(ea420fdd-548f-44f9-9286-59aad6a40f01)]
+interface nsISDBConnection : nsISupports
+{
+  [must_use] void
+  init(in nsIPrincipal aPrincipal);
+
+  [must_use] nsISDBRequest
+  open(in AString aName);
+
+  [must_use] nsISDBRequest
+  seek(in unsigned long long offset);
+
+  [must_use] nsISDBRequest
+  read(in unsigned long long size);
+
+  [must_use, implicit_jscontext] nsISDBRequest
+  write(in jsval value);
+
+  [must_use] nsISDBRequest
+  close();
+
+  attribute nsISDBCloseCallback closeCallback;
+};
new file mode 100644
--- /dev/null
+++ b/dom/simpledb/nsISDBRequest.idl
@@ -0,0 +1,20 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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 "nsISupports.idl"
+
+interface nsISDBCallback;
+interface nsIVariant;
+
+[scriptable, uuid(13f05bcf-715c-427e-aac8-df9b2c1ec1e3)]
+interface nsISDBRequest : nsISupports
+{
+  [must_use] readonly attribute nsIVariant result;
+
+  [must_use] readonly attribute nsresult resultCode;
+
+  attribute nsISDBCallback callback;
+};
new file mode 100644
--- /dev/null
+++ b/dom/simpledb/nsISDBResults.idl
@@ -0,0 +1,18 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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 "nsISupports.idl"
+
+[scriptable, uuid(bca19e01-b34e-4a48-8875-2f4cb871febf)]
+interface nsISDBResult : nsISupports
+{
+  [must_use] void
+  getAsArray([optional] out uint32_t dataLen,
+             [array, retval, size_is(dataLen)] out uint8_t data);
+
+  [must_use, implicit_jscontext] jsval
+  getAsArrayBuffer();
+};
--- a/ipc/glue/BackgroundChildImpl.cpp
+++ b/ipc/glue/BackgroundChildImpl.cpp
@@ -11,16 +11,17 @@
 #include "FileDescriptorSetChild.h"
 #ifdef MOZ_WEBRTC
 #include "CamerasChild.h"
 #endif
 #include "mozilla/media/MediaChild.h"
 #include "mozilla/Assertions.h"
 #include "mozilla/SchedulerGroup.h"
 #include "mozilla/dom/ClientManagerActors.h"
+#include "mozilla/dom/PBackgroundSDBConnectionChild.h"
 #include "mozilla/dom/PFileSystemRequestChild.h"
 #include "mozilla/dom/FileSystemTaskBase.h"
 #include "mozilla/dom/asmjscache/AsmJSCache.h"
 #include "mozilla/dom/cache/ActorUtils.h"
 #include "mozilla/dom/indexedDB/PBackgroundIDBFactoryChild.h"
 #include "mozilla/dom/indexedDB/PBackgroundIndexedDBUtilsChild.h"
 #include "mozilla/dom/ipc/IPCBlobInputStreamChild.h"
 #include "mozilla/dom/ipc/PendingIPCBlobChild.h"
@@ -234,16 +235,34 @@ BackgroundChildImpl::DeallocPBackgroundL
                                       PBackgroundLocalStorageCacheChild* aActor)
 {
   MOZ_ASSERT(aActor);
 
   delete aActor;
   return true;
 }
 
+BackgroundChildImpl::PBackgroundSDBConnectionChild*
+BackgroundChildImpl::AllocPBackgroundSDBConnectionChild(
+                                            const PrincipalInfo& aPrincipalInfo)
+{
+  MOZ_CRASH("PBackgroundSDBConnectionChild actor should be manually "
+            "constructed!");
+}
+
+bool
+BackgroundChildImpl::DeallocPBackgroundSDBConnectionChild(
+                                          PBackgroundSDBConnectionChild* aActor)
+{
+  MOZ_ASSERT(aActor);
+
+  delete aActor;
+  return true;
+}
+
 BackgroundChildImpl::PBackgroundStorageChild*
 BackgroundChildImpl::AllocPBackgroundStorageChild(const nsString& aProfilePath)
 {
   MOZ_CRASH("PBackgroundStorageChild actors should be manually constructed!");
 }
 
 bool
 BackgroundChildImpl::DeallocPBackgroundStorageChild(
--- a/ipc/glue/BackgroundChildImpl.h
+++ b/ipc/glue/BackgroundChildImpl.h
@@ -65,16 +65,24 @@ protected:
 
   virtual PBackgroundIndexedDBUtilsChild*
   AllocPBackgroundIndexedDBUtilsChild() override;
 
   virtual bool
   DeallocPBackgroundIndexedDBUtilsChild(PBackgroundIndexedDBUtilsChild* aActor)
                                         override;
 
+  virtual PBackgroundSDBConnectionChild*
+  AllocPBackgroundSDBConnectionChild(const PrincipalInfo& aPrincipalInfo)
+                                     override;
+
+  virtual bool
+  DeallocPBackgroundSDBConnectionChild(PBackgroundSDBConnectionChild* aActor)
+                                       override;
+
   virtual PBackgroundLocalStorageCacheChild*
   AllocPBackgroundLocalStorageCacheChild(const PrincipalInfo& aPrincipalInfo,
                                          const nsCString& aOriginKey,
                                          const uint32_t& aPrivateBrowsingId)
                                          override;
 
   virtual bool
   DeallocPBackgroundLocalStorageCacheChild(
--- a/ipc/glue/BackgroundParentImpl.cpp
+++ b/ipc/glue/BackgroundParentImpl.cpp
@@ -29,16 +29,17 @@
 #include "mozilla/dom/StorageActivityService.h"
 #include "mozilla/dom/asmjscache/AsmJSCache.h"
 #include "mozilla/dom/cache/ActorUtils.h"
 #include "mozilla/dom/indexedDB/ActorsParent.h"
 #include "mozilla/dom/ipc/IPCBlobInputStreamParent.h"
 #include "mozilla/dom/ipc/PendingIPCBlobParent.h"
 #include "mozilla/dom/ipc/TemporaryIPCBlobParent.h"
 #include "mozilla/dom/quota/ActorsParent.h"
+#include "mozilla/dom/simpledb/ActorsParent.h"
 #include "mozilla/dom/StorageIPC.h"
 #include "mozilla/dom/MIDIManagerParent.h"
 #include "mozilla/dom/MIDIPortParent.h"
 #include "mozilla/dom/MIDIPlatformService.h"
 #include "mozilla/ipc/BackgroundParent.h"
 #include "mozilla/ipc/BackgroundUtils.h"
 #include "mozilla/ipc/IPCStreamAlloc.h"
 #include "mozilla/ipc/PBackgroundSharedTypes.h"
@@ -247,16 +248,53 @@ BackgroundParentImpl::RecvFlushPendingFi
   AssertIsOnBackgroundThread();
 
   if (!mozilla::dom::indexedDB::RecvFlushPendingFileDeletions()) {
     return IPC_FAIL_NO_REASON(this);
   }
   return IPC_OK();
 }
 
+BackgroundParentImpl::PBackgroundSDBConnectionParent*
+BackgroundParentImpl::AllocPBackgroundSDBConnectionParent(
+                                            const PrincipalInfo& aPrincipalInfo)
+{
+  AssertIsInMainProcess();
+  AssertIsOnBackgroundThread();
+
+  return mozilla::dom::AllocPBackgroundSDBConnectionParent(aPrincipalInfo);
+}
+
+mozilla::ipc::IPCResult
+BackgroundParentImpl::RecvPBackgroundSDBConnectionConstructor(
+                                         PBackgroundSDBConnectionParent* aActor,
+                                         const PrincipalInfo& aPrincipalInfo)
+{
+  AssertIsInMainProcess();
+  AssertIsOnBackgroundThread();
+  MOZ_ASSERT(aActor);
+
+  if (!mozilla::dom::RecvPBackgroundSDBConnectionConstructor(aActor,
+                                                             aPrincipalInfo)) {
+    return IPC_FAIL_NO_REASON(this);
+  }
+  return IPC_OK();
+}
+
+bool
+BackgroundParentImpl::DeallocPBackgroundSDBConnectionParent(
+                                         PBackgroundSDBConnectionParent* aActor)
+{
+  AssertIsInMainProcess();
+  AssertIsOnBackgroundThread();
+  MOZ_ASSERT(aActor);
+
+  return mozilla::dom::DeallocPBackgroundSDBConnectionParent(aActor);
+}
+
 BackgroundParentImpl::PBackgroundLocalStorageCacheParent*
 BackgroundParentImpl::AllocPBackgroundLocalStorageCacheParent(
                                             const PrincipalInfo& aPrincipalInfo,
                                             const nsCString& aOriginKey,
                                             const uint32_t& aPrivateBrowsingId)
 {
   AssertIsInMainProcess();
   AssertIsOnBackgroundThread();
--- a/ipc/glue/BackgroundParentImpl.h
+++ b/ipc/glue/BackgroundParentImpl.h
@@ -58,16 +58,30 @@ protected:
   virtual bool
   DeallocPBackgroundIndexedDBUtilsParent(
                                         PBackgroundIndexedDBUtilsParent* aActor)
                                         override;
 
   virtual mozilla::ipc::IPCResult
   RecvFlushPendingFileDeletions() override;
 
+  virtual PBackgroundSDBConnectionParent*
+  AllocPBackgroundSDBConnectionParent(const PrincipalInfo& aPrincipalInfo)
+                                      override;
+
+  virtual mozilla::ipc::IPCResult
+  RecvPBackgroundSDBConnectionConstructor(
+                                         PBackgroundSDBConnectionParent* aActor,
+                                         const PrincipalInfo& aPrincipalInfo)
+                                         override;
+
+  virtual bool
+  DeallocPBackgroundSDBConnectionParent(PBackgroundSDBConnectionParent* aActor)
+                                        override;
+
   virtual PBackgroundLocalStorageCacheParent*
   AllocPBackgroundLocalStorageCacheParent(const PrincipalInfo& aPrincipalInfo,
                                           const nsCString& aOriginKey,
                                           const uint32_t& aPrivateBrowsingId)
                                           override;
 
   virtual mozilla::ipc::IPCResult
   RecvPBackgroundLocalStorageCacheConstructor(
--- a/ipc/glue/PBackground.ipdl
+++ b/ipc/glue/PBackground.ipdl
@@ -1,15 +1,16 @@
 /* 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 protocol PAsmJSCacheEntry;
 include protocol PBackgroundIDBFactory;
 include protocol PBackgroundIndexedDBUtils;
+include protocol PBackgroundSDBConnection;
 include protocol PBackgroundLocalStorageCache;
 include protocol PBackgroundStorage;
 include protocol PBackgroundTest;
 include protocol PBroadcastChannel;
 include protocol PCache;
 include protocol PCacheStorage;
 include protocol PCacheStreamControl;
 include protocol PClientManager;
@@ -61,16 +62,17 @@ using mozilla::dom::asmjscache::WritePar
 namespace mozilla {
 namespace ipc {
 
 sync protocol PBackground
 {
   manages PAsmJSCacheEntry;
   manages PBackgroundIDBFactory;
   manages PBackgroundIndexedDBUtils;
+  manages PBackgroundSDBConnection;
   manages PBackgroundLocalStorageCache;
   manages PBackgroundStorage;
   manages PBackgroundTest;
   manages PBroadcastChannel;
   manages PCache;
   manages PCacheStorage;
   manages PCacheStreamControl;
   manages PClientManager;
@@ -103,16 +105,18 @@ parent:
 
   async PBackgroundIDBFactory(LoggingInfo loggingInfo);
 
   async PBackgroundIndexedDBUtils();
 
   // Use only for testing!
   async FlushPendingFileDeletions();
 
+  async PBackgroundSDBConnection(PrincipalInfo principalInfo);
+
   async PBackgroundLocalStorageCache(PrincipalInfo principalInfo,
                                      nsCString originKey,
                                      uint32_t privateBrowsingId);
 
   async PBackgroundStorage(nsString profilePath);
 
   async PVsync();
 
--- a/layout/build/nsLayoutCID.h
+++ b/layout/build/nsLayoutCID.h
@@ -26,16 +26,20 @@
 // {3B581FD4-3497-426c-8F61-3658B971CB80}
 #define NS_TREEBOXOBJECT_CID \
 { 0x3b581fd4, 0x3497, 0x426c, { 0x8f, 0x61, 0x36, 0x58, 0xb9, 0x71, 0xcb, 0x80 } }
 
 // {2fe88332-31c6-4829-b247-a07d8a73e80f}
 #define NS_CANVASRENDERINGCONTEXTWEBGL_CID \
 { 0x2fe88332, 0x31c6, 0x4829, { 0xb2, 0x47, 0xa0, 0x7d, 0x8a, 0x7e, 0xe8, 0x0fe } }
 
+// {ae2793c0-2ba3-4adb-9c5e-c23525812c64}
+#define NS_SDBCONNECTION_CID \
+{ 0xae2793c0, 0x2ba3, 0x4adb, { 0x9c, 0x5e, 0xc2, 0x35, 0x25, 0x81, 0x2c, 0x64 } }
+
 // {A746DECD-AE74-4d86-8E75-4FDA81A9BE90}
 #define NS_DOMSESSIONSTORAGEMANAGER_CID               \
 { 0xa746decd, 0xae74, 0x4d86, { 0x8e, 0x75, 0x4f, 0xda, 0x81, 0xa9, 0xbe, 0x90 } }
 
 // {656DB07C-AA80-49e4-BCE8-E431BAAE697D}
 #define NS_DOMLOCALSTORAGEMANAGER_CID               \
 { 0x656db07c, 0xaa80, 0x49e4, { 0xbc, 0xe8, 0xe4, 0x31, 0xba, 0xae, 0x69, 0x7d } }
 
--- a/layout/build/nsLayoutModule.cpp
+++ b/layout/build/nsLayoutModule.cpp
@@ -61,16 +61,17 @@
 #include "nsGlobalWindowCommands.h"
 #include "nsIControllerCommandTable.h"
 #include "nsJSProtocolHandler.h"
 #include "nsIControllerContext.h"
 #include "nsZipArchive.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/dom/BlobURL.h"
 #include "mozilla/dom/DOMRequest.h"
+#include "mozilla/dom/SDBConnection.h"
 #include "mozilla/dom/LocalStorageManager.h"
 #include "mozilla/dom/network/UDPSocketChild.h"
 #include "mozilla/dom/quota/QuotaManagerService.h"
 #include "mozilla/dom/ServiceWorkerManager.h"
 #include "mozilla/dom/SessionStorageManager.h"
 #include "mozilla/dom/StorageActivityService.h"
 #include "mozilla/dom/WorkerDebuggerManager.h"
 #include "mozilla/dom/Notification.h"
@@ -192,16 +193,17 @@ NS_GENERIC_FACTORY_CONSTRUCTOR(HTMLEdito
 #define PRESENTATION_TCP_SESSION_TRANSPORT_CID \
 { 0xc9d023f4, 0x6228, 0x4c07, { 0x8b, 0x1d, 0x9c, 0x19, 0x57, 0x3f, 0xaa, 0x27 } }
 
 already_AddRefed<nsIPresentationService> NS_CreatePresentationService();
 
 // Factory Constructor
 typedef mozilla::dom::BlobURL::Mutator BlobURLMutator;
 NS_GENERIC_FACTORY_CONSTRUCTOR(BlobURLMutator)
+NS_GENERIC_FACTORY_CONSTRUCTOR(SDBConnection)
 NS_GENERIC_FACTORY_CONSTRUCTOR(LocalStorageManager)
 NS_GENERIC_FACTORY_CONSTRUCTOR(SessionStorageManager)
 NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(DOMRequestService,
                                          DOMRequestService::FactoryCreate)
 NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(QuotaManagerService,
                                          QuotaManagerService::FactoryCreate)
 NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(ServiceWorkerManager,
                                          ServiceWorkerManager::GetInstance)
@@ -529,16 +531,17 @@ NS_DEFINE_NAMED_CID(NS_JSURIMUTATOR_CID)
 NS_DEFINE_NAMED_CID(NS_WINDOWCOMMANDTABLE_CID);
 NS_DEFINE_NAMED_CID(NS_WINDOWCONTROLLER_CID);
 NS_DEFINE_NAMED_CID(NS_PLUGINDOCLOADERFACTORY_CID);
 NS_DEFINE_NAMED_CID(NS_PLUGINDOCUMENT_CID);
 NS_DEFINE_NAMED_CID(NS_VIDEODOCUMENT_CID);
 NS_DEFINE_NAMED_CID(NS_STYLESHEETSERVICE_CID);
 NS_DEFINE_NAMED_CID(NS_HOSTOBJECTURI_CID);
 NS_DEFINE_NAMED_CID(NS_HOSTOBJECTURIMUTATOR_CID);
+NS_DEFINE_NAMED_CID(NS_SDBCONNECTION_CID);
 NS_DEFINE_NAMED_CID(NS_DOMSESSIONSTORAGEMANAGER_CID);
 NS_DEFINE_NAMED_CID(NS_DOMLOCALSTORAGEMANAGER_CID);
 NS_DEFINE_NAMED_CID(NS_TEXTEDITOR_CID);
 NS_DEFINE_NAMED_CID(DOMREQUEST_SERVICE_CID);
 NS_DEFINE_NAMED_CID(QUOTAMANAGER_SERVICE_CID);
 NS_DEFINE_NAMED_CID(SERVICEWORKERMANAGER_CID);
 NS_DEFINE_NAMED_CID(STORAGEACTIVITYSERVICE_CID);
 NS_DEFINE_NAMED_CID(NOTIFICATIONTELEMETRYSERVICE_CID);
@@ -766,16 +769,17 @@ static const mozilla::Module::CIDEntry k
   { &kNS_WINDOWCOMMANDTABLE_CID, false, nullptr, CreateWindowCommandTableConstructor },
   { &kNS_WINDOWCONTROLLER_CID, false, nullptr, CreateWindowControllerWithSingletonCommandTable },
   { &kNS_PLUGINDOCLOADERFACTORY_CID, false, nullptr, CreateContentDLF },
   { &kNS_PLUGINDOCUMENT_CID, false, nullptr, CreatePluginDocument },
   { &kNS_VIDEODOCUMENT_CID, false, nullptr, CreateVideoDocument },
   { &kNS_STYLESHEETSERVICE_CID, false, nullptr, nsStyleSheetServiceConstructor },
   { &kNS_HOSTOBJECTURI_CID, false, nullptr, BlobURLMutatorConstructor }, // do_CreateInstance returns mutator
   { &kNS_HOSTOBJECTURIMUTATOR_CID, false, nullptr, BlobURLMutatorConstructor },
+  { &kNS_SDBCONNECTION_CID, false, nullptr, SDBConnectionConstructor },
   { &kNS_DOMSESSIONSTORAGEMANAGER_CID, false, nullptr, SessionStorageManagerConstructor },
   { &kNS_DOMLOCALSTORAGEMANAGER_CID, false, nullptr, LocalStorageManagerConstructor },
   { &kNS_TEXTEDITOR_CID, false, nullptr, TextEditorConstructor },
   { &kDOMREQUEST_SERVICE_CID, false, nullptr, DOMRequestServiceConstructor },
   { &kQUOTAMANAGER_SERVICE_CID, false, nullptr, QuotaManagerServiceConstructor },
   { &kSERVICEWORKERMANAGER_CID, false, nullptr, ServiceWorkerManagerConstructor },
   { &kSTORAGEACTIVITYSERVICE_CID, false, nullptr, StorageActivityServiceConstructor },
   { &kNOTIFICATIONTELEMETRYSERVICE_CID, false, nullptr, NotificationTelemetryServiceConstructor },
@@ -866,16 +870,17 @@ static const mozilla::Module::ContractID
 #ifdef MOZ_XUL
   { "@mozilla.org/xul/xul-sort-service;1", &kNS_XULSORTSERVICE_CID },
 #endif
   { CONTENT_DLF_CONTRACTID, &kNS_CONTENT_DOCUMENT_LOADER_FACTORY_CID },
   { NS_JSPROTOCOLHANDLER_CONTRACTID, &kNS_JSPROTOCOLHANDLER_CID },
   { NS_WINDOWCONTROLLER_CONTRACTID, &kNS_WINDOWCONTROLLER_CID },
   { PLUGIN_DLF_CONTRACTID, &kNS_PLUGINDOCLOADERFACTORY_CID },
   { NS_STYLESHEETSERVICE_CONTRACTID, &kNS_STYLESHEETSERVICE_CID },
+  { NS_SDBCONNECTION_CONTRACTID, &kNS_SDBCONNECTION_CID },
   { "@mozilla.org/dom/localStorage-manager;1", &kNS_DOMLOCALSTORAGEMANAGER_CID },
   // Keeping the old ContractID for backward compatibility
   { "@mozilla.org/dom/storagemanager;1", &kNS_DOMLOCALSTORAGEMANAGER_CID },
   { "@mozilla.org/dom/sessionStorage-manager;1", &kNS_DOMSESSIONSTORAGEMANAGER_CID },
   { "@mozilla.org/editor/texteditor;1", &kNS_TEXTEDITOR_CID },
   { DOMREQUEST_SERVICE_CONTRACTID, &kDOMREQUEST_SERVICE_CID },
   { QUOTAMANAGER_SERVICE_CONTRACTID, &kQUOTAMANAGER_SERVICE_CID },
   { SERVICEWORKERMANAGER_CONTRACTID, &kSERVICEWORKERMANAGER_CID },