Bug 961049 - Part 4: QuotaManager on PBackground core changes; r=baku
authorJan Varga <jan.varga@gmail.com>
Sun, 22 Nov 2015 10:43:55 +0100
changeset 273639 27a67b5cc5916ac57ca7b64808da50799e0407f0
parent 273638 950f004242c5453c629ae619af47cbc9f3edafb1
child 273640 c53f530ef89cf0a4e169326b8467e6c86796375a
push id68341
push userJan.Varga@gmail.com
push dateSun, 22 Nov 2015 09:46:11 +0000
treeherdermozilla-inbound@8ff7083db0ea [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbaku
bugs961049
milestone45.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 961049 - Part 4: QuotaManager on PBackground core changes; r=baku
browser/base/content/pageinfo/permissions.js
dom/ipc/ContentParent.cpp
dom/quota/ActorsChild.cpp
dom/quota/ActorsChild.h
dom/quota/ActorsParent.cpp
dom/quota/ActorsParent.h
dom/quota/Client.h
dom/quota/OriginScope.h
dom/quota/PQuota.ipdl
dom/quota/PQuotaRequest.ipdl
dom/quota/PQuotaUsageRequest.ipdl
dom/quota/QuotaCommon.h
dom/quota/QuotaManager.cpp
dom/quota/QuotaManager.h
dom/quota/QuotaManagerService.cpp
dom/quota/QuotaManagerService.h
dom/quota/QuotaRequests.cpp
dom/quota/QuotaRequests.h
dom/quota/moz.build
dom/quota/nsIQuotaCallbacks.idl
dom/quota/nsIQuotaManager.idl
dom/quota/nsIQuotaManagerService.idl
dom/quota/nsIQuotaRequest.idl
dom/quota/nsIQuotaRequests.idl
dom/quota/nsIUsageCallback.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
testing/specialpowers/content/SpecialPowersObserverAPI.js
testing/specialpowers/content/specialpowers.js
testing/specialpowers/content/specialpowersAPI.js
toolkit/forgetaboutsite/ForgetAboutSite.jsm
toolkit/modules/Services.jsm
--- a/browser/base/content/pageinfo/permissions.js
+++ b/browser/base/content/pageinfo/permissions.js
@@ -1,16 +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/. */
 
 Components.utils.import("resource:///modules/SitePermissions.jsm");
 Components.utils.import("resource://gre/modules/BrowserUtils.jsm");
 
-const nsIQuotaManager = Components.interfaces.nsIQuotaManager;
+const nsIQuotaManagerService = Components.interfaces.nsIQuotaManagerService;
 
 var gPermURI;
 var gUsageRequest;
 
 var gPermissions = SitePermissions.listPermissions();
 gPermissions.push("plugins");
 
 var permissionObserver = {
@@ -181,68 +181,70 @@ function setRadioState(aPartId, aValue)
 
 function initIndexedDBRow()
 {
   let row = document.getElementById("perm-indexedDB-row");
   let extras = document.getElementById("perm-indexedDB-extras");
 
   row.appendChild(extras);
 
-  var quotaManager = Components.classes["@mozilla.org/dom/quota/manager;1"]
-                               .getService(nsIQuotaManager);
+  var quotaManagerService =
+    Components.classes["@mozilla.org/dom/quota-manager-service;1"]
+              .getService(nsIQuotaManagerService);
   let principal = Components.classes["@mozilla.org/scriptsecuritymanager;1"]
                             .getService(Components.interfaces.nsIScriptSecurityManager)
                             .createCodebasePrincipal(gPermURI, {});
   gUsageRequest =
-    quotaManager.getUsageForPrincipal(principal, onIndexedDBUsageCallback);
+    quotaManagerService.getUsageForPrincipal(principal,
+                                             onIndexedDBUsageCallback);
 
   var status = document.getElementById("indexedDBStatus");
   var button = document.getElementById("indexedDBClear");
 
   status.value = "";
   status.setAttribute("hidden", "true");
   button.setAttribute("hidden", "true");
 }
 
 function onIndexedDBClear()
 {
   let principal = Components.classes["@mozilla.org/scriptsecuritymanager;1"]
                             .getService(Components.interfaces.nsIScriptSecurityManager)
                             .createCodebasePrincipal(gPermURI, {});
 
-  Components.classes["@mozilla.org/dom/quota/manager;1"]
-            .getService(nsIQuotaManager)
+  Components.classes["@mozilla.org/dom/quota-manager-service;1"]
+            .getService(nsIQuotaManagerService)
             .clearStoragesForPrincipal(principal);
 
   Components.classes["@mozilla.org/serviceworkers/manager;1"]
             .getService(Components.interfaces.nsIServiceWorkerManager)
             .removeAndPropagate(gPermURI.host);
 
   SitePermissions.remove(gPermURI, "indexedDB");
   initIndexedDBRow();
 }
 
-function onIndexedDBUsageCallback(principal, usage, fileUsage)
+function onIndexedDBUsageCallback(request)
 {
-  let uri = principal.URI;
+  let uri = request.principal.URI;
   if (!uri.equals(gPermURI)) {
     throw new Error("Callback received for bad URI: " + uri);
   }
 
-  if (usage) {
+  if (request.usage) {
     if (!("DownloadUtils" in window)) {
       Components.utils.import("resource://gre/modules/DownloadUtils.jsm");
     }
 
     var status = document.getElementById("indexedDBStatus");
     var button = document.getElementById("indexedDBClear");
 
     status.value =
       gBundle.getFormattedString("indexedDBUsage",
-                                 DownloadUtils.convertByteUnits(usage));
+                                 DownloadUtils.convertByteUnits(request.usage));
     status.removeAttribute("hidden");
     button.removeAttribute("hidden");
   }
 }
 
 function fillInPluginPermissionTemplate(aPluginName, aPermissionString) {
   let permPluginTemplate = document.getElementById("permPluginTemplate").cloneNode(true);
   permPluginTemplate.setAttribute("permString", aPermissionString);
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -64,17 +64,17 @@
 #include "mozilla/dom/devicestorage/DeviceStorageRequestParent.h"
 #include "mozilla/dom/icc/IccParent.h"
 #include "mozilla/dom/indexedDB/IndexedDatabaseManager.h"
 #include "mozilla/dom/mobileconnection/MobileConnectionParent.h"
 #include "mozilla/dom/mobilemessage/SmsParent.h"
 #include "mozilla/dom/power/PowerManagerService.h"
 #include "mozilla/dom/PresentationParent.h"
 #include "mozilla/dom/PPresentationParent.h"
-#include "mozilla/dom/quota/QuotaManager.h"
+#include "mozilla/dom/quota/QuotaManagerService.h"
 #include "mozilla/dom/telephony/TelephonyParent.h"
 #include "mozilla/dom/time/DateCacheCleaner.h"
 #include "mozilla/dom/voicemail/VoicemailParent.h"
 #include "mozilla/embedding/printingui/PrintingParent.h"
 #include "mozilla/hal_sandbox/PHalParent.h"
 #include "mozilla/ipc/BackgroundChild.h"
 #include "mozilla/ipc/BackgroundParent.h"
 #include "mozilla/ipc/FileDescriptorSetParent.h"
@@ -1790,20 +1790,20 @@ ContentParent::ShutDownProcess(ShutDownM
             StartForceKillTimer();
         }
 
         // If call was not successful, the channel must have been broken
         // somehow, and we will clean up the error in ActorDestroy.
         return;
     }
 
-    using mozilla::dom::quota::QuotaManager;
-
-    if (QuotaManager* quotaManager = QuotaManager::Get()) {
-        quotaManager->AbortOperationsForProcess(mChildID);
+    using mozilla::dom::quota::QuotaManagerService;
+
+    if (QuotaManagerService* quotaManagerService = QuotaManagerService::Get()) {
+        quotaManagerService->AbortOperationsForProcess(mChildID);
     }
 
     // If Close() fails with an error, we'll end up back in this function, but
     // with aMethod = CLOSE_CHANNEL_WITH_ERROR.  It's important that we call
     // CloseWithError() in this case; see bug 895204.
 
     if (aMethod == CLOSE_CHANNEL && !mCalledClose) {
         // Close() can only be called once: It kicks off the destruction
new file mode 100644
--- /dev/null
+++ b/dom/quota/ActorsChild.cpp
@@ -0,0 +1,268 @@
+/* -*- 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 "QuotaManagerService.h"
+#include "QuotaRequests.h"
+
+namespace mozilla {
+namespace dom {
+namespace quota {
+
+/*******************************************************************************
+ * QuotaChild
+ ******************************************************************************/
+
+QuotaChild::QuotaChild(QuotaManagerService* aService)
+  : mService(aService)
+#ifdef DEBUG
+  , mOwningThread(NS_GetCurrentThread())
+#endif
+{
+  AssertIsOnOwningThread();
+  MOZ_ASSERT(aService);
+
+  MOZ_COUNT_CTOR(quota::QuotaChild);
+}
+
+QuotaChild::~QuotaChild()
+{
+  AssertIsOnOwningThread();
+
+  MOZ_COUNT_DTOR(quota::QuotaChild);
+}
+
+#ifdef DEBUG
+
+void
+QuotaChild::AssertIsOnOwningThread() const
+{
+  MOZ_ASSERT(mOwningThread);
+
+  bool current;
+  MOZ_ASSERT(NS_SUCCEEDED(mOwningThread->IsOnCurrentThread(&current)));
+  MOZ_ASSERT(current);
+}
+
+#endif // DEBUG
+
+void
+QuotaChild::ActorDestroy(ActorDestroyReason aWhy)
+{
+  AssertIsOnOwningThread();
+
+  if (mService) {
+    mService->ClearBackgroundActor();
+#ifdef DEBUG
+    mService = nullptr;
+#endif
+  }
+}
+
+PQuotaUsageRequestChild*
+QuotaChild::AllocPQuotaUsageRequestChild(const UsageRequestParams& aParams)
+{
+  AssertIsOnOwningThread();
+
+  MOZ_CRASH("PQuotaUsageRequestChild actors should be manually constructed!");
+}
+
+bool
+QuotaChild::DeallocPQuotaUsageRequestChild(PQuotaUsageRequestChild* aActor)
+{
+  AssertIsOnOwningThread();
+  MOZ_ASSERT(aActor);
+
+  delete static_cast<QuotaUsageRequestChild*>(aActor);
+  return true;
+}
+
+PQuotaRequestChild*
+QuotaChild::AllocPQuotaRequestChild(const RequestParams& aParams)
+{
+  AssertIsOnOwningThread();
+
+  MOZ_CRASH("PQuotaRequestChild actors should be manually constructed!");
+}
+
+bool
+QuotaChild::DeallocPQuotaRequestChild(PQuotaRequestChild* aActor)
+{
+  AssertIsOnOwningThread();
+  MOZ_ASSERT(aActor);
+
+  delete static_cast<QuotaRequestChild*>(aActor);
+  return true;
+}
+
+/*******************************************************************************
+ * QuotaUsageRequestChild
+ ******************************************************************************/
+
+QuotaUsageRequestChild::QuotaUsageRequestChild(UsageRequest* aRequest)
+  : mRequest(aRequest)
+{
+  AssertIsOnOwningThread();
+
+  MOZ_COUNT_CTOR(quota::QuotaUsageRequestChild);
+}
+
+QuotaUsageRequestChild::~QuotaUsageRequestChild()
+{
+  // Can't assert owning thread here because the request is cleared.
+
+  MOZ_COUNT_DTOR(quota::QuotaUsageRequestChild);
+}
+
+#ifdef DEBUG
+
+void
+QuotaUsageRequestChild::AssertIsOnOwningThread() const
+{
+  MOZ_ASSERT(mRequest);
+  mRequest->AssertIsOnOwningThread();
+}
+
+#endif // DEBUG
+
+void
+QuotaUsageRequestChild::HandleResponse(nsresult aResponse)
+{
+  AssertIsOnOwningThread();
+  MOZ_ASSERT(NS_FAILED(aResponse));
+  MOZ_ASSERT(mRequest);
+
+  mRequest->SetError(aResponse);
+}
+
+void
+QuotaUsageRequestChild::HandleResponse(const UsageResponse& aResponse)
+{
+  AssertIsOnOwningThread();
+  MOZ_ASSERT(mRequest);
+
+  mRequest->SetResult(aResponse.usage(), aResponse.fileUsage());
+}
+
+void
+QuotaUsageRequestChild::ActorDestroy(ActorDestroyReason aWhy)
+{
+  AssertIsOnOwningThread();
+
+  if (mRequest) {
+    mRequest->ClearBackgroundActor();
+#ifdef DEBUG
+    mRequest = nullptr;
+#endif
+  }
+}
+
+bool
+QuotaUsageRequestChild::Recv__delete__(const UsageRequestResponse& aResponse)
+{
+  AssertIsOnOwningThread();
+  MOZ_ASSERT(mRequest);
+
+  switch (aResponse.type()) {
+    case UsageRequestResponse::Tnsresult:
+      HandleResponse(aResponse.get_nsresult());
+      break;
+
+    case UsageRequestResponse::TUsageResponse:
+      HandleResponse(aResponse.get_UsageResponse());
+      break;
+
+    default:
+      MOZ_CRASH("Unknown response type!");
+  }
+
+  return true;
+}
+
+/*******************************************************************************
+ * QuotaRequestChild
+ ******************************************************************************/
+
+QuotaRequestChild::QuotaRequestChild(Request* aRequest)
+  : mRequest(aRequest)
+{
+  AssertIsOnOwningThread();
+
+  MOZ_COUNT_CTOR(quota::QuotaRequestChild);
+}
+
+QuotaRequestChild::~QuotaRequestChild()
+{
+  AssertIsOnOwningThread();
+
+  MOZ_COUNT_DTOR(quota::QuotaRequestChild);
+}
+
+#ifdef DEBUG
+
+void
+QuotaRequestChild::AssertIsOnOwningThread() const
+{
+  MOZ_ASSERT(mRequest);
+  mRequest->AssertIsOnOwningThread();
+}
+
+#endif // DEBUG
+
+void
+QuotaRequestChild::HandleResponse(nsresult aResponse)
+{
+  AssertIsOnOwningThread();
+  MOZ_ASSERT(NS_FAILED(aResponse));
+  MOZ_ASSERT(mRequest);
+
+  mRequest->SetError(aResponse);
+}
+
+void
+QuotaRequestChild::HandleResponse()
+{
+  AssertIsOnOwningThread();
+  MOZ_ASSERT(mRequest);
+
+  mRequest->SetResult();
+}
+
+void
+QuotaRequestChild::ActorDestroy(ActorDestroyReason aWhy)
+{
+  AssertIsOnOwningThread();
+}
+
+bool
+QuotaRequestChild::Recv__delete__(const RequestResponse& aResponse)
+{
+  AssertIsOnOwningThread();
+  MOZ_ASSERT(mRequest);
+
+  switch (aResponse.type()) {
+    case RequestResponse::Tnsresult:
+      HandleResponse(aResponse.get_nsresult());
+      break;
+
+    case RequestResponse::TClearOriginResponse:
+    case RequestResponse::TClearAppResponse:
+    case RequestResponse::TClearAllResponse:
+    case RequestResponse::TResetAllResponse:
+      HandleResponse();
+      break;
+
+    default:
+      MOZ_CRASH("Unknown response type!");
+  }
+
+  return true;
+}
+
+} // namespace quota
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/quota/ActorsChild.h
@@ -0,0 +1,153 @@
+/* -*- 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_quota_ActorsChild_h
+#define mozilla_dom_quota_ActorsChild_h
+
+#include "mozilla/dom/quota/PQuotaChild.h"
+#include "mozilla/dom/quota/PQuotaRequestChild.h"
+#include "mozilla/dom/quota/PQuotaUsageRequestChild.h"
+
+namespace mozilla {
+namespace ipc {
+
+class BackgroundChildImpl;
+
+} // namespace ipc
+
+namespace dom {
+namespace quota {
+
+class QuotaManagerService;
+class Request;
+class UsageRequest;
+
+class QuotaChild final
+  : public PQuotaChild
+{
+  friend class mozilla::ipc::BackgroundChildImpl;
+  friend class QuotaManagerService;
+
+  QuotaManagerService* mService;
+
+#ifdef DEBUG
+  nsCOMPtr<nsIEventTarget> mOwningThread;
+#endif
+
+public:
+  void
+  AssertIsOnOwningThread() const
+#ifdef DEBUG
+  ;
+#else
+  { }
+#endif
+
+private:
+  // Only created by QuotaManagerService.
+  explicit QuotaChild(QuotaManagerService* aService);
+
+  // Only destroyed by mozilla::ipc::BackgroundChildImpl.
+  ~QuotaChild();
+
+  // IPDL methods are only called by IPDL.
+  virtual void
+  ActorDestroy(ActorDestroyReason aWhy) override;
+
+  virtual PQuotaUsageRequestChild*
+  AllocPQuotaUsageRequestChild(const UsageRequestParams& aParams) override;
+
+  virtual bool
+  DeallocPQuotaUsageRequestChild(PQuotaUsageRequestChild* aActor) override;
+
+  virtual PQuotaRequestChild*
+  AllocPQuotaRequestChild(const RequestParams& aParams) override;
+
+  virtual bool
+  DeallocPQuotaRequestChild(PQuotaRequestChild* aActor) override;
+};
+
+class QuotaUsageRequestChild final
+  : public PQuotaUsageRequestChild
+{
+  friend class QuotaChild;
+  friend class QuotaManagerService;
+
+  RefPtr<UsageRequest> mRequest;
+
+public:
+  void
+  AssertIsOnOwningThread() const
+#ifdef DEBUG
+  ;
+#else
+  { }
+#endif
+
+private:
+  // Only created by QuotaManagerService.
+  explicit QuotaUsageRequestChild(UsageRequest* aRequest);
+
+  // Only destroyed by QuotaChild.
+  ~QuotaUsageRequestChild();
+
+  void
+  HandleResponse(nsresult aResponse);
+
+  void
+  HandleResponse(const UsageResponse& aResponse);
+
+  // IPDL methods are only called by IPDL.
+  virtual void
+  ActorDestroy(ActorDestroyReason aWhy) override;
+
+  virtual bool
+  Recv__delete__(const UsageRequestResponse& aResponse) override;
+};
+
+class QuotaRequestChild final
+  : public PQuotaRequestChild
+{
+  friend class QuotaChild;
+  friend class QuotaManagerService;
+
+  RefPtr<Request> mRequest;
+
+public:
+  void
+  AssertIsOnOwningThread() const
+#ifdef DEBUG
+  ;
+#else
+  { }
+#endif
+
+private:
+  // Only created by QuotaManagerService.
+  explicit QuotaRequestChild(Request* aRequest);
+
+  // Only destroyed by QuotaChild.
+  ~QuotaRequestChild();
+
+  void
+  HandleResponse(nsresult aResponse);
+
+  void
+  HandleResponse();
+
+  // IPDL methods are only called by IPDL.
+  virtual void
+  ActorDestroy(ActorDestroyReason aWhy) override;
+
+  virtual bool
+  Recv__delete__(const RequestResponse& aResponse) override;
+};
+
+} // namespace quota
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_quota_ActorsChild_h
rename from dom/quota/QuotaManager.cpp
rename to dom/quota/ActorsParent.cpp
--- a/dom/quota/QuotaManager.cpp
+++ b/dom/quota/ActorsParent.cpp
@@ -1,68 +1,83 @@
 /* -*- 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 "QuotaManager.h"
-
-#include "mozIApplicationClearPrivateDataParams.h"
+#include "ActorsParent.h"
+
 #include "nsIBinaryInputStream.h"
 #include "nsIBinaryOutputStream.h"
 #include "nsIFile.h"
-#include "nsIIdleService.h"
+#include "nsIFileStreams.h"
 #include "nsIObserverService.h"
 #include "nsIPermissionManager.h"
 #include "nsIPrincipal.h"
-#include "nsIQuotaRequest.h"
 #include "nsIRunnable.h"
 #include "nsISimpleEnumerator.h"
 #include "nsIScriptObjectPrincipal.h"
 #include "nsIScriptSecurityManager.h"
 #include "nsITimer.h"
 #include "nsIURI.h"
-#include "nsIUsageCallback.h"
 #include "nsPIDOMWindow.h"
 
 #include <algorithm>
 #include "GeckoProfiler.h"
 #include "mozilla/Atomics.h"
 #include "mozilla/BasePrincipal.h"
 #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/ipc/BackgroundParent.h"
+#include "mozilla/ipc/BackgroundUtils.h"
 #include "mozilla/IntegerRange.h"
 #include "mozilla/Mutex.h"
 #include "mozilla/LazyIdleThread.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/Services.h"
+#include "mozilla/StaticPtr.h"
 #include "mozilla/TypeTraits.h"
+#include "mozilla/unused.h"
 #include "nsAppDirectoryServiceDefs.h"
 #include "nsComponentManagerUtils.h"
 #include "nsAboutProtocolUtils.h"
 #include "nsCharSeparatedTokenizer.h"
 #include "nsContentUtils.h"
 #include "nsCRTGlue.h"
 #include "nsDirectoryServiceUtils.h"
 #include "nsEscape.h"
 #include "nsNetUtil.h"
 #include "nsPrintfCString.h"
 #include "nsScriptSecurityManager.h"
 #include "nsThreadUtils.h"
 #include "nsXULAppAPI.h"
+#include "prio.h"
 #include "xpcpublic.h"
 
 #include "OriginScope.h"
+#include "QuotaManager.h"
+#include "QuotaManagerService.h"
 #include "QuotaObject.h"
 #include "UsageInfo.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
+
 // The amount of time, in milliseconds, that our IO thread will stay alive
 // after the last event it processes.
 #define DEFAULT_THREAD_TIMEOUT_MS 30000
 
 // The amount of time, in milliseconds, that we will wait for active storage
 // transactions on shutdown before aborting them.
 #define DEFAULT_SHUTDOWN_TIMER_MS 30000
 
@@ -86,16 +101,26 @@
 #define KB * 1024ULL
 #define MB * 1024ULL KB
 #define GB * 1024ULL MB
 
 namespace mozilla {
 namespace dom {
 namespace quota {
 
+using namespace mozilla::ipc;
+
+const bool QuotaManager::kRunningXPCShellTests =
+#ifdef ENABLE_TESTS
+  !!PR_GetEnv("XPCSHELL_TEST_PROFILE_DIR")
+#else
+  false
+#endif
+  ;
+
 // We want profiles to be platform-independent so we always need to replace
 // the same characters on every platform. Windows has the most extensive set
 // of illegal characters so we use its FILE_ILLEGAL_CHARACTERS and
 // FILE_PATH_SEPARATOR.
 const char QuotaManager::kReplaceChars[] = CONTROL_CHARACTERS "/:*?\"<>|\\";
 
 namespace {
 
@@ -173,16 +198,24 @@ public:
                     bool aExclusive,
                     bool aInternal,
                     OpenDirectoryListener* aOpenListener);
 
   static bool
   MatchOriginScopes(const OriginScope& aOriginScope1,
                     const OriginScope& aOriginScope2);
 
+  void
+  AssertIsOnOwningThread() const
+#ifdef DEBUG
+  ;
+#else
+  { }
+#endif
+
   const Nullable<PersistenceType>&
   GetPersistenceType() const
   {
     return mPersistenceType;
   }
 
   const nsACString&
   GetGroup() const
@@ -223,61 +256,162 @@ public:
 
   // Test whether this DirectoryLock needs to wait for the given lock.
   bool
   MustWaitFor(const DirectoryLockImpl& aLock);
 
   void
   AddBlockingLock(DirectoryLockImpl* aLock)
   {
-    MOZ_ASSERT(NS_IsMainThread());
+    AssertIsOnOwningThread();
 
     mBlocking.AppendElement(aLock);
   }
 
   const nsTArray<DirectoryLockImpl*>&
   GetBlockedOnLocks()
   {
     return mBlockedOn;
   }
 
   void
   AddBlockedOnLock(DirectoryLockImpl* aLock)
   {
-    MOZ_ASSERT(NS_IsMainThread());
+    AssertIsOnOwningThread();
 
     mBlockedOn.AppendElement(aLock);
   }
 
   void
   MaybeUnblock(DirectoryLockImpl* aLock)
   {
-    MOZ_ASSERT(NS_IsMainThread());
+    AssertIsOnOwningThread();
 
     mBlockedOn.RemoveElement(aLock);
     if (mBlockedOn.IsEmpty()) {
       NotifyOpenListener();
     }
   }
 
   void
   NotifyOpenListener();
 
   void
   Invalidate()
   {
-    MOZ_ASSERT(NS_IsMainThread());
+    AssertIsOnOwningThread();
 
     mInvalidated = true;
   }
 
+  NS_INLINE_DECL_REFCOUNTING(DirectoryLockImpl)
+
+private:
+  ~DirectoryLockImpl();
+};
+
+class QuotaManager::CreateRunnable final
+  : public BackgroundThreadObject
+  , public nsRunnable
+{
+  nsTArray<nsCOMPtr<nsIRunnable>> mCallbacks;
+  nsString mBaseDirPath;
+  RefPtr<QuotaManager> mManager;
+  nsresult mResultCode;
+
+  enum class State
+  {
+    Initial,
+    CreatingManager,
+    RegisteringObserver,
+    CallingCallbacks,
+    Completed
+  };
+
+  State mState;
+
+public:
+  CreateRunnable()
+    : mResultCode(NS_OK)
+    , mState(State::Initial)
+  {
+    AssertIsOnBackgroundThread();
+  }
+
+  void
+  AddCallback(nsIRunnable* aCallback)
+  {
+    AssertIsOnOwningThread();
+    MOZ_ASSERT(aCallback);
+
+    mCallbacks.AppendElement(aCallback);
+  }
+
 private:
-  ~DirectoryLockImpl();
+  ~CreateRunnable()
+  { }
+
+  nsresult
+  Init();
+
+  nsresult
+  CreateManager();
+
+  nsresult
+  RegisterObserver();
+
+  void
+  CallCallbacks();
+
+  State
+  GetNextState(nsCOMPtr<nsIEventTarget>& aThread);
+
+  NS_DECL_NSIRUNNABLE
+};
+
+class QuotaManager::ShutdownRunnable final
+  : public nsRunnable
+{
+  // Only touched on the main thread.
+  bool& mDone;
+
+public:
+  explicit ShutdownRunnable(bool& aDone)
+    : mDone(aDone)
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+  }
+
+private:
+  ~ShutdownRunnable()
+  { }
+
+  NS_DECL_NSIRUNNABLE
+};
+
+class QuotaManager::ShutdownObserver final
+  : public nsIObserver
+{
+  nsCOMPtr<nsIEventTarget> mBackgroundThread;
+
+public:
+  explicit ShutdownObserver(nsIEventTarget* aBackgroundThread)
+    : mBackgroundThread(aBackgroundThread)
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+  }
 
   NS_DECL_ISUPPORTS
+  NS_DECL_NSIOBSERVER
+
+private:
+  ~ShutdownObserver()
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+  }
 };
 
 namespace {
 
 /*******************************************************************************
  * Local class declarations
  ******************************************************************************/
 
@@ -501,55 +635,118 @@ private:
   ~CollectOriginsHelper()
   { }
 
   NS_IMETHOD
   Run();
 };
 
 class OriginOperationBase
-  : public nsRunnable
+  : public BackgroundThreadObject
+  , public nsRunnable
 {
 protected:
+  nsresult mResultCode;
+
   enum State {
     // Not yet run.
     State_Initial,
 
-    // Running on the main thread in the listener for OpenDirectory.
+    // Running initialization on the main thread.
+    State_Initializing,
+
+    // Running initialization on the owning thread.
+    State_FinishingInit,
+
+    // Running quota manager initialization on the owning thread.
+    State_CreatingQuotaManager,
+
+    // Running on the owning thread in the listener for OpenDirectory.
     State_DirectoryOpenPending,
 
     // Running on the IO thread.
     State_DirectoryWorkOpen,
 
-    // Running on the main thread after all work is done.
+    // Running on the owning thread after all work is done.
     State_UnblockingOpen,
 
+    // All done.
     State_Complete
   };
 
+private:
   State mState;
-  nsresult mResultCode;
+  bool mActorDestroyed;
 
 protected:
-  OriginOperationBase()
-    : mState(State_Initial)
+  bool mNeedsMainThreadInit;
+  bool mNeedsQuotaManagerInit;
+
+public:
+  void
+  NoteActorDestroyed()
+  {
+    AssertIsOnOwningThread();
+
+    mActorDestroyed = true;
+  }
+
+  bool
+  IsActorDestroyed() const
+  {
+    AssertIsOnOwningThread();
+
+    return mActorDestroyed;
+  }
+
+protected:
+  explicit OriginOperationBase(
+                          nsIEventTarget* aOwningThread = NS_GetCurrentThread())
+    : BackgroundThreadObject(aOwningThread)
     , mResultCode(NS_OK)
+    , mState(State_Initial)
+    , mActorDestroyed(false)
+    , mNeedsMainThreadInit(false)
+    , mNeedsQuotaManagerInit(false)
   { }
 
   // Reference counted.
   virtual ~OriginOperationBase()
   {
     MOZ_ASSERT(mState == State_Complete);
+    MOZ_ASSERT(mActorDestroyed);
+  }
+
+  State
+  GetState() const
+  {
+    return mState;
+  }
+
+  void
+  SetState(State aState)
+  {
+    MOZ_ASSERT(mState == State_Initial);
+    mState = aState;
   }
 
   void
   AdvanceState()
   {
     switch (mState) {
       case State_Initial:
+        mState = State_Initializing;
+        return;
+      case State_Initializing:
+        mState = State_FinishingInit;
+        return;
+      case State_FinishingInit:
+        mState = State_CreatingQuotaManager;
+        return;
+      case State_CreatingQuotaManager:
         mState = State_DirectoryOpenPending;
         return;
       case State_DirectoryOpenPending:
         mState = State_DirectoryWorkOpen;
         return;
       case State_DirectoryWorkOpen:
         mState = State_UnblockingOpen;
         return;
@@ -560,73 +757,126 @@ protected:
         MOZ_CRASH("Bad state!");
     }
   }
 
   NS_IMETHOD
   Run();
 
   virtual nsresult
+  DoInitOnMainThread()
+  {
+    return NS_OK;
+  }
+
+  virtual void
   Open() = 0;
 
   nsresult
   DirectoryOpen();
 
   virtual nsresult
   DoDirectoryWork(QuotaManager* aQuotaManager) = 0;
 
   void
   Finish(nsresult aResult);
 
   virtual void
   UnblockOpen() = 0;
 
 private:
   nsresult
+  Init();
+
+  nsresult
+  InitOnMainThread();
+
+  nsresult
+  FinishInit();
+
+  nsresult
+  QuotaManagerOpen();
+
+  nsresult
   DirectoryWork();
 };
 
+class FinalizeOriginEvictionOp
+  : public OriginOperationBase
+{
+  nsTArray<RefPtr<DirectoryLockImpl>> mLocks;
+
+public:
+  FinalizeOriginEvictionOp(nsIEventTarget* aBackgroundThread,
+                           nsTArray<RefPtr<DirectoryLockImpl>>& aLocks)
+    : OriginOperationBase(aBackgroundThread)
+  {
+    MOZ_ASSERT(!NS_IsMainThread());
+
+    mLocks.SwapElements(aLocks);
+  }
+
+  void
+  Dispatch();
+
+  void
+  RunOnIOThreadImmediately();
+
+private:
+  ~FinalizeOriginEvictionOp()
+  { }
+
+  virtual void
+  Open() override;
+
+  virtual nsresult
+  DoDirectoryWork(QuotaManager* aQuotaManager) override;
+
+  virtual void
+  UnblockOpen() override;
+};
+
 class NormalOriginOperationBase
   : public OriginOperationBase
   , public OpenDirectoryListener
 {
   RefPtr<DirectoryLock> mDirectoryLock;
 
 protected:
   Nullable<PersistenceType> mPersistenceType;
   OriginScope mOriginScope;
   const bool mExclusive;
 
 public:
   void
   RunImmediately()
   {
-    MOZ_ASSERT(mState == State_Initial);
+    MOZ_ASSERT(GetState() == State_Initial);
 
     MOZ_ALWAYS_TRUE(NS_SUCCEEDED(this->Run()));
   }
 
 protected:
   NormalOriginOperationBase(Nullable<PersistenceType> aPersistenceType,
                             const OriginScope& aOriginScope,
                             bool aExclusive)
     : mPersistenceType(aPersistenceType)
     , mOriginScope(aOriginScope)
     , mExclusive(aExclusive)
   {
-    MOZ_ASSERT(NS_IsMainThread());
+    AssertIsOnOwningThread();
   }
 
   ~NormalOriginOperationBase()
   { }
 
 private:
   NS_DECL_ISUPPORTS_INHERITED
 
-  virtual nsresult
+  virtual void
   Open() override;
 
   virtual void
   UnblockOpen() override;
 
   // OpenDirectoryListener overrides.
   virtual void
   DirectoryLockAcquired(DirectoryLock* aLock) override;
@@ -648,158 +898,207 @@ public:
   SaveOriginAccessTimeOp(PersistenceType aPersistenceType,
                          const nsACString& aOrigin,
                          int64_t aTimestamp)
     : NormalOriginOperationBase(Nullable<PersistenceType>(aPersistenceType),
                                 OriginScope::FromOrigin(aOrigin),
                                 /* aExclusive */ false)
     , mTimestamp(aTimestamp)
   {
-    MOZ_ASSERT(NS_IsMainThread());
+    AssertIsOnOwningThread();
   }
 
 private:
   ~SaveOriginAccessTimeOp()
   { }
 
   virtual nsresult
   DoDirectoryWork(QuotaManager* aQuotaManager) override;
 
   virtual void
-  SendResults() override
-  { }
+  SendResults() override;
 };
 
-class GetUsageOp
+/*******************************************************************************
+ * Actor class declarations
+ ******************************************************************************/
+
+class Quota final
+  : public PQuotaParent
+{
+  DebugOnly<bool> mActorDestroyed;
+
+public:
+  Quota();
+
+  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(mozilla::dom::quota::Quota)
+
+private:
+  ~Quota();
+
+  void
+  StartIdleMaintenance();
+
+  // IPDL methods.
+  virtual void
+  ActorDestroy(ActorDestroyReason aWhy) override;
+
+  virtual PQuotaUsageRequestParent*
+  AllocPQuotaUsageRequestParent(const UsageRequestParams& aParams) override;
+
+  virtual bool
+  RecvPQuotaUsageRequestConstructor(PQuotaUsageRequestParent* aActor,
+                                    const UsageRequestParams& aParams) override;
+
+  virtual bool
+  DeallocPQuotaUsageRequestParent(PQuotaUsageRequestParent* aActor) override;
+
+  virtual PQuotaRequestParent*
+  AllocPQuotaRequestParent(const RequestParams& aParams) override;
+
+  virtual bool
+  RecvPQuotaRequestConstructor(PQuotaRequestParent* aActor,
+                               const RequestParams& aParams) override;
+
+  virtual bool
+  DeallocPQuotaRequestParent(PQuotaRequestParent* aActor) override;
+
+  virtual bool
+  RecvStartIdleMaintenance() override;
+
+  virtual bool
+  RecvStopIdleMaintenance() override;
+};
+
+class GetUsageOp final
   : public NormalOriginOperationBase
-  , public nsIQuotaRequest
+  , public PQuotaUsageRequestParent
 {
   UsageInfo mUsageInfo;
 
-  const nsCString mGroup;
-  nsCOMPtr<nsIPrincipal> mPrincipal;
-  nsCOMPtr<nsIUsageCallback> mCallback;
-  const bool mIsApp;
+  const UsageParams mParams;
+  nsCString mGroup;
+  bool mIsApp;
 
 public:
-  GetUsageOp(const nsACString& aGroup,
-             const nsACString& aOrigin,
-             bool aIsApp,
-             nsIPrincipal* aPrincipal,
-             nsIUsageCallback* aCallback);
+  explicit GetUsageOp(const UsageRequestParams& aParams);
+
+  bool
+  Init(Quota* aQuota);
 
 private:
   ~GetUsageOp()
   { }
 
+  virtual nsresult
+  DoInitOnMainThread() override;
+
   nsresult
   AddToUsage(QuotaManager* aQuotaManager,
              PersistenceType aPersistenceType);
 
   virtual nsresult
   DoDirectoryWork(QuotaManager* aQuotaManager) override;
 
   virtual void
   SendResults() override;
 
-  NS_DECL_ISUPPORTS_INHERITED
-  NS_DECL_NSIQUOTAREQUEST
+  // IPDL methods.
+  virtual void
+  ActorDestroy(ActorDestroyReason aWhy) override;
+
+  virtual bool
+  RecvCancel() override;
 };
 
-class ResetOrClearOp
+class QuotaRequestBase
   : public NormalOriginOperationBase
-{
-  bool mClear;
+  , public PQuotaRequestParent
+{
+public:
+  // May be overridden by subclasses if they need to perform work on the
+  // background thread before being run.
+  virtual bool
+  Init(Quota* aQuota);
+
+protected:
+  explicit QuotaRequestBase(bool aExclusive)
+    : NormalOriginOperationBase(Nullable<PersistenceType>(),
+                                OriginScope::FromNull(),
+                                aExclusive)
+  { }
+
+  // Subclasses use this override to set the IPDL response value.
+  virtual void
+  GetResponse(RequestResponse& aResponse) = 0;
+
+private:
+  virtual void
+  SendResults() override;
+
+  // IPDL methods.
+  virtual void
+  ActorDestroy(ActorDestroyReason aWhy) override;
+};
+
+class ResetOrClearOp final
+  : public QuotaRequestBase
+{
+  const bool mClear;
 
 public:
   explicit ResetOrClearOp(bool aClear)
-    : NormalOriginOperationBase(Nullable<PersistenceType>(),
-                                OriginScope::FromNull(),
-                                /* aExclusive */ true)
+    : QuotaRequestBase(/* aExclusive */ true)
     , mClear(aClear)
   {
-    MOZ_ASSERT(NS_IsMainThread());
+    AssertIsOnOwningThread();
   }
 
 private:
   ~ResetOrClearOp()
   { }
 
   void
   DeleteFiles(QuotaManager* aQuotaManager);
 
   virtual nsresult
   DoDirectoryWork(QuotaManager* aQuotaManager) override;
 
   virtual void
-  SendResults() override
-  { }
+  GetResponse(RequestResponse& aResponse) override;
 };
 
-class OriginClearOp
-  : public NormalOriginOperationBase
-{
+class OriginClearOp final
+  : public QuotaRequestBase
+{
+  const RequestParams mParams;
+  const bool mApp;
+
 public:
-  OriginClearOp(Nullable<PersistenceType> aPersistenceType,
-                const OriginScope& aOriginScope)
-    : NormalOriginOperationBase(aPersistenceType,
-                                aOriginScope,
-                                /* aExclusive */ true)
-  {
-    MOZ_ASSERT(NS_IsMainThread());
-  }
+  explicit OriginClearOp(const RequestParams& aParams);
+
+  virtual bool
+  Init(Quota* aQuota) override;
 
 private:
   ~OriginClearOp()
   { }
 
+  virtual nsresult
+  DoInitOnMainThread() override;
+
   void
   DeleteFiles(QuotaManager* aQuotaManager,
               PersistenceType aPersistenceType);
 
   virtual nsresult
   DoDirectoryWork(QuotaManager* aQuotaManager) override;
 
   virtual void
-  SendResults() override
-  { }
-};
-
-class FinalizeOriginEvictionOp
-  : public OriginOperationBase
-{
-  nsTArray<RefPtr<DirectoryLockImpl>> mLocks;
-
-public:
-  explicit FinalizeOriginEvictionOp(
-                                  nsTArray<RefPtr<DirectoryLockImpl>>& aLocks)
-  {
-    MOZ_ASSERT(!NS_IsMainThread());
-
-    mLocks.SwapElements(aLocks);
-  }
-
-  void
-  Dispatch();
-
-  void
-  RunOnIOThreadImmediately();
-
-private:
-  ~FinalizeOriginEvictionOp()
-  { }
-
-  virtual nsresult
-  Open() override;
-
-  virtual nsresult
-  DoDirectoryWork(QuotaManager* aQuotaManager) override;
-
-  virtual void
-  UnblockOpen() override;
+  GetResponse(RequestResponse& aResponse) override;
 };
 
 /*******************************************************************************
  * Helper Functions
  ******************************************************************************/
 
 template <typename T, bool = mozilla::IsUnsigned<T>::value>
 struct IntChecker
@@ -845,16 +1144,48 @@ PatternMatchesOrigin(const nsACString& a
                      const nsACString& aOrigin)
 {
   // Aren't we smart!
   return StringBeginsWith(aOrigin, aPatternString);
 }
 
 } // namespace
 
+BackgroundThreadObject::BackgroundThreadObject()
+  : mOwningThread(NS_GetCurrentThread())
+{
+  AssertIsOnOwningThread();
+}
+
+BackgroundThreadObject::BackgroundThreadObject(nsIEventTarget* aOwningThread)
+  : mOwningThread(aOwningThread)
+{
+}
+
+#ifdef DEBUG
+
+void
+BackgroundThreadObject::AssertIsOnOwningThread() const
+{
+  AssertIsOnBackgroundThread();
+  MOZ_ASSERT(mOwningThread);
+  bool current;
+  MOZ_ASSERT(NS_SUCCEEDED(mOwningThread->IsOnCurrentThread(&current)));
+  MOZ_ASSERT(current);
+}
+
+#endif // DEBUG
+
+nsIEventTarget*
+BackgroundThreadObject::OwningThread() const
+{
+  MOZ_ASSERT(mOwningThread);
+  return mOwningThread;
+}
+
 bool
 IsOnIOThread()
 {
   QuotaManager* quotaManager = QuotaManager::Get();
   NS_ASSERTION(quotaManager, "Must have a manager here!");
 
   bool currentThread;
   return NS_SUCCEEDED(quotaManager->IOThread()->
@@ -891,17 +1222,19 @@ ReportInternalError(const char* aFile, u
   nsContentUtils::LogSimpleConsoleError(
     NS_ConvertUTF8toUTF16(nsPrintfCString(
                           "Quota %s: %s:%lu", aStr, aFile, aLine)),
     "quota");
 }
 
 namespace {
 
-QuotaManager* gInstance = nullptr;
+StaticRefPtr<QuotaManager> gInstance;
+bool gCreateFailed = false;
+StaticRefPtr<QuotaManager::CreateRunnable> gCreateRunnable;
 mozilla::Atomic<bool> gShutdown(false);
 
 // Constants for temporary storage limit computing.
 static const int32_t kDefaultFixedLimitKB = -1;
 static const uint32_t kDefaultChunkSizeKB = 10 * 1024;
 int32_t gFixedLimitKB = kDefaultFixedLimitKB;
 uint32_t gChunkSizeKB = kDefaultChunkSizeKB;
 
@@ -1605,16 +1938,44 @@ GetTemporaryStorageLimit(nsIFile* aDirec
 
   *aLimit = resultKB * 1024;
   return NS_OK;
 }
 
 } // namespace
 
 /*******************************************************************************
+ * Exported functions
+ ******************************************************************************/
+
+PQuotaParent*
+AllocPQuotaParent()
+{
+  AssertIsOnBackgroundThread();
+
+  if (NS_WARN_IF(QuotaManager::IsShuttingDown())) {
+    return nullptr;
+  }
+
+  RefPtr<Quota> actor = new Quota();
+
+  return actor.forget().take();
+}
+
+bool
+DeallocPQuotaParent(PQuotaParent* aActor)
+{
+  AssertIsOnBackgroundThread();
+  MOZ_ASSERT(aActor);
+
+  RefPtr<Quota> actor = dont_AddRef(static_cast<Quota*>(aActor));
+  return true;
+}
+
+/*******************************************************************************
  * Directory lock
  ******************************************************************************/
 
 DirectoryLockImpl::DirectoryLockImpl(QuotaManager* aQuotaManager,
                                      Nullable<PersistenceType> aPersistenceType,
                                      const nsACString& aGroup,
                                      const OriginScope& aOriginScope,
                                      Nullable<bool> aIsApp,
@@ -1628,51 +1989,51 @@ DirectoryLockImpl::DirectoryLockImpl(Quo
   , mOriginScope(aOriginScope)
   , mIsApp(aIsApp)
   , mClientType(aClientType)
   , mOpenListener(aOpenListener)
   , mExclusive(aExclusive)
   , mInternal(aInternal)
   , mInvalidated(false)
 {
-  MOZ_ASSERT(NS_IsMainThread());
+  AssertIsOnOwningThread();
   MOZ_ASSERT(aQuotaManager);
   MOZ_ASSERT_IF(!aInternal, !aPersistenceType.IsNull());
   MOZ_ASSERT_IF(!aInternal,
                 aPersistenceType.Value() != PERSISTENCE_TYPE_INVALID);
   MOZ_ASSERT_IF(!aInternal, !aGroup.IsEmpty());
   MOZ_ASSERT_IF(aInternal, !aOriginScope.IsEmpty() || aOriginScope.IsNull());
   MOZ_ASSERT_IF(!aInternal, aOriginScope.IsOrigin());
   MOZ_ASSERT_IF(!aInternal, !aOriginScope.IsEmpty());
   MOZ_ASSERT_IF(!aInternal, !aIsApp.IsNull());
   MOZ_ASSERT_IF(!aInternal, !aClientType.IsNull());
   MOZ_ASSERT_IF(!aInternal, aClientType.Value() != Client::TYPE_MAX);
   MOZ_ASSERT_IF(!aInternal, aOpenListener);
 }
 
 DirectoryLockImpl::~DirectoryLockImpl()
 {
-  MOZ_ASSERT(NS_IsMainThread());
+  AssertIsOnOwningThread();
   MOZ_ASSERT(mQuotaManager);
 
   for (DirectoryLockImpl* blockingLock : mBlocking) {
     blockingLock->MaybeUnblock(this);
   }
 
   mBlocking.Clear();
 
   mQuotaManager->UnregisterDirectoryLock(this);
 }
 
 // static
 bool
 DirectoryLockImpl::MatchOriginScopes(const OriginScope& aOriginScope1,
                                      const OriginScope& aOriginScope2)
 {
-  MOZ_ASSERT(NS_IsMainThread());
+  AssertIsOnBackgroundThread();
 
   bool match;
 
   if (aOriginScope2.IsNull() || aOriginScope1.IsNull()) {
     match = true;
   } else if (aOriginScope2.IsOrigin()) {
     if (aOriginScope1.IsOrigin()) {
       match = aOriginScope2.Equals(aOriginScope1);
@@ -1684,20 +2045,31 @@ DirectoryLockImpl::MatchOriginScopes(con
   } else {
     match = PatternMatchesOrigin(aOriginScope1, aOriginScope2) ||
             PatternMatchesOrigin(aOriginScope2, aOriginScope1);
   }
 
   return match;
 }
 
+#ifdef DEBUG
+
+void
+DirectoryLockImpl::AssertIsOnOwningThread() const
+{
+  MOZ_ASSERT(mQuotaManager);
+  mQuotaManager->AssertIsOnOwningThread();
+}
+
+#endif // DEBUG
+
 bool
 DirectoryLockImpl::MustWaitFor(const DirectoryLockImpl& aExistingLock)
 {
-  MOZ_ASSERT(NS_IsMainThread());
+  AssertIsOnOwningThread();
 
   // Waiting is never required if the ops in comparison represent shared locks.
   if (!aExistingLock.mExclusive && !mExclusive) {
     return false;
   }
 
   // If the persistence types don't overlap, the op can proceed.
   if (!aExistingLock.mPersistenceType.IsNull() && !mPersistenceType.IsNull() &&
@@ -1720,32 +2092,271 @@ DirectoryLockImpl::MustWaitFor(const Dir
   // Otherwise, when all attributes overlap (persistence type, origin scope and
   // client type) the op must wait.
   return true;
 }
 
 void
 DirectoryLockImpl::NotifyOpenListener()
 {
-  MOZ_ASSERT(NS_IsMainThread());
+  AssertIsOnOwningThread();
   MOZ_ASSERT(mQuotaManager);
   MOZ_ASSERT(mOpenListener);
 
   if (mInvalidated) {
     mOpenListener->DirectoryLockFailed();
   } else {
     mOpenListener->DirectoryLockAcquired(this);
   }
 
   mOpenListener = nullptr;
 
   mQuotaManager->RemovePendingDirectoryLock(this);
 }
 
-NS_IMPL_ISUPPORTS0(DirectoryLockImpl);
+nsresult
+QuotaManager::
+CreateRunnable::Init()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(mState == State::Initial);
+
+  nsresult rv;
+
+  nsCOMPtr<nsIFile> baseDir;
+  rv = NS_GetSpecialDirectory(NS_APP_INDEXEDDB_PARENT_DIR,
+                              getter_AddRefs(baseDir));
+  if (NS_FAILED(rv)) {
+    rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR,
+                                getter_AddRefs(baseDir));
+  }
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = baseDir->GetPath(mBaseDirPath);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  return NS_OK;
+}
+
+nsresult
+QuotaManager::
+CreateRunnable::CreateManager()
+{
+  AssertIsOnOwningThread();
+  MOZ_ASSERT(mState == State::CreatingManager);
+
+  mManager = new QuotaManager();
+
+  nsresult rv = mManager->Init(mBaseDirPath);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  return NS_OK;
+}
+
+nsresult
+QuotaManager::
+CreateRunnable::RegisterObserver()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(mState == State::RegisteringObserver);
+
+  if (NS_FAILED(Preferences::AddIntVarCache(&gFixedLimitKB, PREF_FIXED_LIMIT,
+                                            kDefaultFixedLimitKB)) ||
+      NS_FAILED(Preferences::AddUintVarCache(&gChunkSizeKB,
+                                             PREF_CHUNK_SIZE,
+                                             kDefaultChunkSizeKB))) {
+    NS_WARNING("Unable to respond to temp storage pref changes!");
+  }
+
+  if (NS_FAILED(Preferences::AddBoolVarCache(&gTestingEnabled,
+                                             PREF_TESTING_FEATURES, false))) {
+    NS_WARNING("Unable to respond to testing pref changes!");
+  }
+
+  nsCOMPtr<nsIObserverService> observerService =
+    mozilla::services::GetObserverService();
+  if (NS_WARN_IF(!observerService)) {
+    return NS_ERROR_FAILURE;
+  }
+
+  nsCOMPtr<nsIObserver> observer = new ShutdownObserver(mOwningThread);
+
+  nsresult rv = observerService->AddObserver(observer,
+                                             PROFILE_BEFORE_CHANGE_OBSERVER_ID,
+                                             false);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  QuotaManagerService* qms = QuotaManagerService::GetOrCreate();
+  if (NS_WARN_IF(!qms)) {
+    return rv;
+  }
+
+  qms->NoteLiveManager(mManager);
+
+  return NS_OK;
+}
+
+void
+QuotaManager::
+CreateRunnable::CallCallbacks()
+{
+  AssertIsOnOwningThread();
+  MOZ_ASSERT(mState == State::CallingCallbacks);
+
+  gCreateRunnable = nullptr;
+
+  if (NS_FAILED(mResultCode)) {
+    gCreateFailed = true;
+  } else {
+    gInstance = mManager;
+  }
+
+  mManager = nullptr;
+
+  nsTArray<nsCOMPtr<nsIRunnable>> callbacks;
+  mCallbacks.SwapElements(callbacks);
+
+  for (nsCOMPtr<nsIRunnable>& callback : callbacks) {
+    Unused << callback->Run();
+  }
+}
+
+auto
+QuotaManager::
+CreateRunnable::GetNextState(nsCOMPtr<nsIEventTarget>& aThread) -> State
+{
+  switch (mState) {
+    case State::Initial:
+      aThread = mOwningThread;
+      return State::CreatingManager;
+    case State::CreatingManager:
+      aThread = do_GetMainThread();
+      return State::RegisteringObserver;
+    case State::RegisteringObserver:
+      aThread = mOwningThread;
+      return State::CallingCallbacks;
+    case State::CallingCallbacks:
+      aThread = nullptr;
+      return State::Completed;
+    default:
+      MOZ_CRASH("Bad state!");
+  }
+}
+
+NS_IMETHODIMP
+QuotaManager::
+CreateRunnable::Run()
+{
+  nsresult rv;
+
+  switch (mState) {
+    case State::Initial:
+      rv = Init();
+      break;
+
+    case State::CreatingManager:
+      rv = CreateManager();
+      break;
+
+    case State::RegisteringObserver:
+      rv = RegisterObserver();
+      break;
+
+    case State::CallingCallbacks:
+      CallCallbacks();
+      rv = NS_OK;
+      break;
+
+    case State::Completed:
+    default:
+      MOZ_CRASH("Bad state!");
+  }
+
+  nsCOMPtr<nsIEventTarget> thread;
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    if (NS_SUCCEEDED(mResultCode)) {
+      mResultCode = rv;
+    }
+
+    mState = State::CallingCallbacks;
+    thread = mOwningThread;
+  } else {
+    mState = GetNextState(thread);
+  }
+
+  if (thread) {
+    MOZ_ALWAYS_TRUE(NS_SUCCEEDED(thread->Dispatch(this, NS_DISPATCH_NORMAL)));
+  }
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+QuotaManager::
+ShutdownRunnable::Run()
+{
+  if (NS_IsMainThread()) {
+    QuotaManagerService* qms = QuotaManagerService::Get();
+    MOZ_ASSERT(qms);
+
+    qms->NoteFinishedManager();
+
+    mDone = true;
+
+    return NS_OK;
+  }
+
+  AssertIsOnBackgroundThread();
+
+  RefPtr<QuotaManager> quotaManager = gInstance.get();
+  if (quotaManager) {
+    quotaManager->Shutdown();
+
+    gInstance = nullptr;
+  }
+
+  MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(this)));
+
+  return NS_OK;
+}
+
+NS_IMPL_ISUPPORTS(QuotaManager::ShutdownObserver, nsIObserver)
+
+NS_IMETHODIMP
+QuotaManager::
+ShutdownObserver::Observe(nsISupports* aSubject,
+                          const char* aTopic,
+                          const char16_t* aData)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(!strcmp(aTopic, PROFILE_BEFORE_CHANGE_OBSERVER_ID));
+
+  bool done = false;
+
+  RefPtr<ShutdownRunnable> shutdownRunnable = new ShutdownRunnable(done);
+  MOZ_ALWAYS_TRUE(NS_SUCCEEDED(
+    mBackgroundThread->Dispatch(shutdownRunnable, NS_DISPATCH_NORMAL)));
+
+  nsIThread* currentThread = NS_GetCurrentThread();
+  MOZ_ASSERT(currentThread);
+
+  while (!done) {
+    MOZ_ALWAYS_TRUE(NS_ProcessNextEvent(currentThread));
+  }
+
+  return NS_OK;
+}
 
 /*******************************************************************************
  * Quota object
  ******************************************************************************/
 
 void
 QuotaObject::AddRef()
 {
@@ -1985,78 +2596,62 @@ QuotaObject::MaybeUpdateSize(int64_t aSi
 
 QuotaManager::QuotaManager()
 : mQuotaMutex("QuotaManager.mQuotaMutex"),
   mTemporaryStorageLimit(0),
   mTemporaryStorageUsage(0),
   mTemporaryStorageInitialized(false),
   mStorageAreaInitialized(false)
 {
-  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
-  NS_ASSERTION(!gInstance, "More than one instance!");
+  AssertIsOnOwningThread();
+  MOZ_ASSERT(!gInstance);
 }
 
 QuotaManager::~QuotaManager()
 {
-  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
-  NS_ASSERTION(!gInstance || gInstance == this, "Different instances!");
-  gInstance = nullptr;
-}
-
-// static
-QuotaManager*
-QuotaManager::GetOrCreate()
-{
-  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+  AssertIsOnOwningThread();
+  MOZ_ASSERT(!gInstance || gInstance == this);
+}
+
+void
+QuotaManager::GetOrCreate(nsIRunnable* aCallback)
+{
+  AssertIsOnBackgroundThread();
 
   if (IsShuttingDown()) {
-    NS_ERROR("Calling GetOrCreate() after shutdown!");
-    return nullptr;
-  }
-
-  if (!gInstance) {
-    RefPtr<QuotaManager> instance(new QuotaManager());
-
-    nsresult rv = instance->Init();
-    NS_ENSURE_SUCCESS(rv, nullptr);
-
-    nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
-    NS_ENSURE_TRUE(obs, nullptr);
-
-    // We need this callback to know when to shut down all our threads.
-    rv = obs->AddObserver(instance, PROFILE_BEFORE_CHANGE_OBSERVER_ID, false);
-    NS_ENSURE_SUCCESS(rv, nullptr);
-
-    // The observer service will hold our last reference, don't AddRef here.
-    gInstance = instance;
-  }
-
-  return gInstance;
+    MOZ_ASSERT(false, "Calling GetOrCreate() after shutdown!");
+    return;
+  }
+
+  if (gInstance || gCreateFailed) {
+    MOZ_ASSERT(!gCreateRunnable);
+    MOZ_ASSERT_IF(gCreateFailed, !gInstance);
+
+    MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToCurrentThread(aCallback)));
+  } else {
+    if (!gCreateRunnable) {
+      gCreateRunnable = new CreateRunnable();
+      MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(gCreateRunnable)));
+    }
+
+    gCreateRunnable->AddCallback(aCallback);
+  }
 }
 
 // static
 QuotaManager*
 QuotaManager::Get()
 {
+  MOZ_ASSERT(!NS_IsMainThread());
+
   // Does not return an owning reference.
   return gInstance;
 }
 
 // static
-QuotaManager*
-QuotaManager::FactoryCreate()
-{
-  // Returns a raw pointer that carries an owning reference! Lame, but the
-  // singleton factory macros force this.
-  QuotaManager* quotaManager = GetOrCreate();
-  NS_IF_ADDREF(quotaManager);
-  return quotaManager;
-}
-
-// static
 bool
 QuotaManager::IsShuttingDown()
 {
   return gShutdown;
 }
 
 auto
 QuotaManager::CreateDirectoryLock(Nullable<PersistenceType> aPersistenceType,
@@ -2064,17 +2659,17 @@ QuotaManager::CreateDirectoryLock(Nullab
                                   const OriginScope& aOriginScope,
                                   Nullable<bool> aIsApp,
                                   Nullable<Client::Type> aClientType,
                                   bool aExclusive,
                                   bool aInternal,
                                   OpenDirectoryListener* aOpenListener)
   -> already_AddRefed<DirectoryLockImpl>
 {
-  MOZ_ASSERT(NS_IsMainThread());
+  AssertIsOnOwningThread();
   MOZ_ASSERT_IF(!aInternal, !aPersistenceType.IsNull());
   MOZ_ASSERT_IF(!aInternal,
                 aPersistenceType.Value() != PERSISTENCE_TYPE_INVALID);
   MOZ_ASSERT_IF(!aInternal, !aGroup.IsEmpty());
   MOZ_ASSERT_IF(aInternal, !aOriginScope.IsEmpty() || aOriginScope.IsNull());
   MOZ_ASSERT_IF(!aInternal, aOriginScope.IsOrigin());
   MOZ_ASSERT_IF(!aInternal, !aOriginScope.IsEmpty());
   MOZ_ASSERT_IF(!aInternal, !aIsApp.IsNull());
@@ -2117,17 +2712,17 @@ QuotaManager::CreateDirectoryLock(Nullab
 
 auto
 QuotaManager::CreateDirectoryLockForEviction(PersistenceType aPersistenceType,
                                              const nsACString& aGroup,
                                              const nsACString& aOrigin,
                                              bool aIsApp)
   -> already_AddRefed<DirectoryLockImpl>
 {
-  MOZ_ASSERT(NS_IsMainThread());
+  AssertIsOnOwningThread();
   MOZ_ASSERT(aPersistenceType != PERSISTENCE_TYPE_INVALID);
   MOZ_ASSERT(!aOrigin.IsEmpty());
 
   RefPtr<DirectoryLockImpl> lock =
     new DirectoryLockImpl(this,
                           Nullable<PersistenceType>(aPersistenceType),
                           aGroup,
                           OriginScope::FromOrigin(aOrigin),
@@ -2147,17 +2742,17 @@ QuotaManager::CreateDirectoryLockForEvic
   RegisterDirectoryLock(lock);
 
   return lock.forget();
 }
 
 void
 QuotaManager::RegisterDirectoryLock(DirectoryLockImpl* aLock)
 {
-  MOZ_ASSERT(NS_IsMainThread());
+  AssertIsOnOwningThread();
   MOZ_ASSERT(aLock);
 
   mDirectoryLocks.AppendElement(aLock);
 
   if (aLock->ShouldUpdateLockTable()) {
     const Nullable<PersistenceType>& persistenceType =
       aLock->GetPersistenceType();
     const OriginScope& originScope = aLock->GetOriginScope();
@@ -2183,17 +2778,17 @@ QuotaManager::RegisterDirectoryLock(Dire
     }
     array->AppendElement(aLock);
   }
 }
 
 void
 QuotaManager::UnregisterDirectoryLock(DirectoryLockImpl* aLock)
 {
-  MOZ_ASSERT(NS_IsMainThread());
+  AssertIsOnOwningThread();
 
   MOZ_ALWAYS_TRUE(mDirectoryLocks.RemoveElement(aLock));
 
   if (aLock->ShouldUpdateLockTable()) {
     const Nullable<PersistenceType>& persistenceType =
       aLock->GetPersistenceType();
     const OriginScope& originScope = aLock->GetOriginScope();
 
@@ -2219,28 +2814,28 @@ QuotaManager::UnregisterDirectoryLock(Di
       }
     }
   }
 }
 
 void
 QuotaManager::RemovePendingDirectoryLock(DirectoryLockImpl* aLock)
 {
-  MOZ_ASSERT(NS_IsMainThread());
+  AssertIsOnOwningThread();
   MOZ_ASSERT(aLock);
 
   MOZ_ALWAYS_TRUE(mPendingDirectoryLocks.RemoveElement(aLock));
 }
 
 uint64_t
 QuotaManager::CollectOriginsForEviction(
                                   uint64_t aMinSizeToBeFreed,
                                   nsTArray<RefPtr<DirectoryLockImpl>>& aLocks)
 {
-  MOZ_ASSERT(NS_IsMainThread());
+  AssertIsOnOwningThread();
   MOZ_ASSERT(aLocks.IsEmpty());
 
   struct MOZ_STACK_CLASS Helper final
   {
     static void
     GetInactiveOriginInfos(nsTArray<RefPtr<OriginInfo>>& aOriginInfos,
                            nsTArray<DirectoryLockImpl*>& aLocks,
                            nsTArray<OriginInfo*>& aInactiveOriginInfos)
@@ -2358,95 +2953,148 @@ QuotaManager::CollectOriginsForEviction(
 
     return sizeToBeFreed;
   }
 
   return 0;
 }
 
 nsresult
-QuotaManager::Init()
+QuotaManager::Init(const nsAString& aBaseDirPath)
 {
   nsresult rv;
-  if (XRE_IsParentProcess()) {
-    nsCOMPtr<nsIFile> baseDir;
-    rv = NS_GetSpecialDirectory(NS_APP_INDEXEDDB_PARENT_DIR,
-                                getter_AddRefs(baseDir));
-    if (NS_FAILED(rv)) {
-      rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR,
-                                  getter_AddRefs(baseDir));
-    }
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    rv = CloneStoragePath(baseDir,
-                          NS_LITERAL_STRING(INDEXEDDB_DIRECTORY_NAME),
-                          mIndexedDBPath);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    rv = baseDir->Append(NS_LITERAL_STRING(STORAGE_DIRECTORY_NAME));
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    rv = baseDir->GetPath(mStoragePath);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    rv = CloneStoragePath(baseDir,
-                          NS_LITERAL_STRING(PERMANENT_DIRECTORY_NAME),
-                          mPermanentStoragePath);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    rv = CloneStoragePath(baseDir,
-                          NS_LITERAL_STRING(TEMPORARY_DIRECTORY_NAME),
-                          mTemporaryStoragePath);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    rv = CloneStoragePath(baseDir,
-                          NS_LITERAL_STRING(DEFAULT_DIRECTORY_NAME),
-                          mDefaultStoragePath);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    // Make a lazy thread for any IO we need (like clearing or enumerating the
-    // contents of storage directories).
-    mIOThread = new LazyIdleThread(DEFAULT_THREAD_TIMEOUT_MS,
-                                   NS_LITERAL_CSTRING("Storage I/O"),
-                                   LazyIdleThread::ManualShutdown);
-
-    // Make a timer here to avoid potential failures later. We don't actually
-    // initialize the timer until shutdown.
-    mShutdownTimer = do_CreateInstance(NS_TIMER_CONTRACTID);
-    NS_ENSURE_TRUE(mShutdownTimer, NS_ERROR_FAILURE);
-  }
-
-  if (NS_FAILED(Preferences::AddIntVarCache(&gFixedLimitKB, PREF_FIXED_LIMIT,
-                                            kDefaultFixedLimitKB)) ||
-      NS_FAILED(Preferences::AddUintVarCache(&gChunkSizeKB,
-                                             PREF_CHUNK_SIZE,
-                                             kDefaultChunkSizeKB))) {
-    NS_WARNING("Unable to respond to temp storage pref changes!");
-  }
-
-  if (NS_FAILED(Preferences::AddBoolVarCache(&gTestingEnabled,
-                                             PREF_TESTING_FEATURES, false))) {
-    NS_WARNING("Unable to respond to testing pref changes!");
+
+  nsCOMPtr<nsIFile> baseDir = do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = baseDir->InitWithPath(aBaseDirPath);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = CloneStoragePath(baseDir,
+                        NS_LITERAL_STRING(INDEXEDDB_DIRECTORY_NAME),
+                        mIndexedDBPath);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = baseDir->Append(NS_LITERAL_STRING(STORAGE_DIRECTORY_NAME));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = baseDir->GetPath(mStoragePath);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = CloneStoragePath(baseDir,
+                        NS_LITERAL_STRING(PERMANENT_DIRECTORY_NAME),
+                        mPermanentStoragePath);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = CloneStoragePath(baseDir,
+                        NS_LITERAL_STRING(TEMPORARY_DIRECTORY_NAME),
+                        mTemporaryStoragePath);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = CloneStoragePath(baseDir,
+                        NS_LITERAL_STRING(DEFAULT_DIRECTORY_NAME),
+                        mDefaultStoragePath);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  // Make a lazy thread for any IO we need (like clearing or enumerating the
+  // contents of storage directories).
+  mIOThread = new LazyIdleThread(DEFAULT_THREAD_TIMEOUT_MS,
+                                 NS_LITERAL_CSTRING("Storage I/O"),
+                                 LazyIdleThread::ManualShutdown);
+
+  // Make a timer here to avoid potential failures later. We don't actually
+  // initialize the timer until shutdown.
+  mShutdownTimer = do_CreateInstance(NS_TIMER_CONTRACTID);
+  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!");
 
-  NS_ASSERTION(mClients.Capacity() == Client::TYPE_MAX,
-               "Should be using an auto array with correct capacity!");
+  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());
 
   return NS_OK;
 }
 
 void
+QuotaManager::Shutdown()
+{
+  AssertIsOnOwningThread();
+
+  // Setting this flag prevents the service from being recreated and prevents
+  // further storagess from being created.
+  if (gShutdown.exchange(true)) {
+    NS_ERROR("Shutdown more than once?!");
+  }
+
+  StopIdleMaintenance();
+
+  // Kick off the shutdown timer.
+  MOZ_ALWAYS_TRUE(NS_SUCCEEDED(
+    mShutdownTimer->InitWithFuncCallback(&ShutdownTimerCallback,
+                                         this,
+                                         DEFAULT_SHUTDOWN_TIMER_MS,
+                                         nsITimer::TYPE_ONE_SHOT)));
+
+  // Each client will spin the event loop while we wait on all the threads
+  // to close. Our timer may fire during that loop.
+  for (uint32_t index = 0; index < Client::TYPE_MAX; index++) {
+    mClients[index]->ShutdownWorkThreads();
+  }
+
+  // Cancel the timer regardless of whether it actually fired.
+  if (NS_FAILED(mShutdownTimer->Cancel())) {
+    NS_WARNING("Failed to cancel shutdown timer!");
+  }
+
+  // Give clients a chance to cleanup IO thread only objects.
+  nsCOMPtr<nsIRunnable> runnable =
+    NS_NewRunnableMethod(this, &QuotaManager::ReleaseIOThreadObjects);
+  if (!runnable) {
+    NS_WARNING("Failed to create runnable!");
+  }
+
+  if (NS_FAILED(mIOThread->Dispatch(runnable, NS_DISPATCH_NORMAL))) {
+    NS_WARNING("Failed to dispatch runnable!");
+  }
+
+  // Make sure to join with our IO thread.
+  if (NS_FAILED(mIOThread->Shutdown())) {
+    NS_WARNING("Failed to shutdown IO thread!");
+  }
+
+  for (RefPtr<DirectoryLockImpl>& lock : mPendingDirectoryLocks) {
+    lock->Invalidate();
+  }
+}
+
+void
 QuotaManager::InitQuotaForOrigin(PersistenceType aPersistenceType,
                                  const nsACString& aGroup,
                                  const nsACString& aOrigin,
                                  bool aIsApp,
                                  uint64_t aUsageBytes,
                                  int64_t aAccessTime)
 {
   AssertIsOnIOThread();
@@ -2499,17 +3147,17 @@ QuotaManager::DecreaseUsageForOrigin(Per
   }
 }
 
 void
 QuotaManager::UpdateOriginAccessTime(PersistenceType aPersistenceType,
                                      const nsACString& aGroup,
                                      const nsACString& aOrigin)
 {
-  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+  AssertIsOnOwningThread();
   MOZ_ASSERT(aPersistenceType != PERSISTENCE_TYPE_PERSISTENT);
 
   MutexAutoLock lock(mQuotaMutex);
 
   GroupInfoPair* pair;
   if (!mGroupInfoPairs.Get(aGroup, &pair)) {
     return;
   }
@@ -2657,17 +3305,17 @@ QuotaManager::GetQuotaObject(Persistence
   NS_ENSURE_SUCCESS(rv, nullptr);
 
   return GetQuotaObject(aPersistenceType, aGroup, aOrigin, file);
 }
 
 void
 QuotaManager::AbortOperationsForProcess(ContentParentId aContentParentId)
 {
-  MOZ_ASSERT(NS_IsMainThread());
+  AssertIsOnOwningThread();
 
   for (RefPtr<Client>& client : mClients) {
     client->AbortOperationsForProcess(aContentParentId);
   }
 }
 
 nsresult
 QuotaManager::GetDirectoryForOrigin(PersistenceType aPersistenceType,
@@ -3115,17 +3763,17 @@ void
 QuotaManager::OpenDirectory(PersistenceType aPersistenceType,
                             const nsACString& aGroup,
                             const nsACString& aOrigin,
                             bool aIsApp,
                             Client::Type aClientType,
                             bool aExclusive,
                             OpenDirectoryListener* aOpenListener)
 {
-  MOZ_ASSERT(NS_IsMainThread());
+  AssertIsOnOwningThread();
 
   RefPtr<DirectoryLockImpl> lock =
     CreateDirectoryLock(Nullable<PersistenceType>(aPersistenceType),
                         aGroup,
                         OriginScope::FromOrigin(aOrigin),
                         Nullable<bool>(aIsApp),
                         Nullable<Client::Type>(aClientType),
                         aExclusive,
@@ -3135,17 +3783,17 @@ QuotaManager::OpenDirectory(PersistenceT
 }
 
 void
 QuotaManager::OpenDirectoryInternal(Nullable<PersistenceType> aPersistenceType,
                                     const OriginScope& aOriginScope,
                                     bool aExclusive,
                                     OpenDirectoryListener* aOpenListener)
 {
-  MOZ_ASSERT(NS_IsMainThread());
+  AssertIsOnOwningThread();
 
   RefPtr<DirectoryLockImpl> lock =
     CreateDirectoryLock(aPersistenceType,
                         EmptyCString(),
                         aOriginScope,
                         Nullable<bool>(),
                         Nullable<Client::Type>(),
                         aExclusive,
@@ -3649,218 +4297,16 @@ QuotaManager::GetDirectoryMetadata(nsIFi
   aGroup = group;
   aOrigin = origin;
   if (aIsApp) {
     *aIsApp = isApp;
   }
   return NS_OK;
 }
 
-NS_IMPL_ISUPPORTS(QuotaManager, nsIQuotaManager, nsIObserver)
-
-NS_IMETHODIMP
-QuotaManager::GetUsageForPrincipal(nsIPrincipal* aPrincipal,
-                                   nsIUsageCallback* aCallback,
-                                   nsIQuotaRequest** _retval)
-{
-  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
-
-  NS_ENSURE_ARG_POINTER(aPrincipal);
-  NS_ENSURE_ARG_POINTER(aCallback);
-
-  // This only works from the main process.
-  NS_ENSURE_TRUE(XRE_IsParentProcess(), NS_ERROR_NOT_AVAILABLE);
-
-  // Figure out which origin we're dealing with.
-  nsCString group;
-  nsCString origin;
-  bool isApp;
-  nsresult rv = GetInfoFromPrincipal(aPrincipal, &group, &origin, &isApp);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  RefPtr<GetUsageOp> op =
-    new GetUsageOp(group, origin, isApp, aPrincipal, aCallback);
-
-  op->RunImmediately();
-
-  op.forget(_retval);
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-QuotaManager::Clear()
-{
-  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
-
-  if (!gTestingEnabled) {
-    NS_WARNING("Testing features are not enabled!");
-    return NS_OK;
-  }
-
-  RefPtr<ResetOrClearOp> op = new ResetOrClearOp(/* aClear */ true);
-
-  op->RunImmediately();
-
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-QuotaManager::ClearStoragesForPrincipal(nsIPrincipal* aPrincipal,
-                                        const nsACString& aPersistenceType)
-{
-  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
-
-  NS_ENSURE_ARG_POINTER(aPrincipal);
-
-  Nullable<PersistenceType> persistenceType;
-  nsresult rv =
-    NullablePersistenceTypeFromText(aPersistenceType, &persistenceType);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return NS_ERROR_INVALID_ARG;
-  }
-
-  // This only works from the main process.
-  NS_ENSURE_TRUE(XRE_IsParentProcess(), NS_ERROR_NOT_AVAILABLE);
-
-  // Figure out which origin we're dealing with.
-  nsCString origin;
-  rv = GetInfoFromPrincipal(aPrincipal, nullptr, &origin, nullptr);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  const mozilla::OriginAttributes& attrs =
-    mozilla::BasePrincipal::Cast(aPrincipal)->OriginAttributesRef();
-
-  nsAutoCString pattern;
-  GetOriginPatternString(attrs.mAppId, attrs.mInBrowser, origin, pattern);
-
-  RefPtr<OriginClearOp> op =
-    new OriginClearOp(persistenceType, OriginScope::FromPattern(pattern));
-
-  op->RunImmediately();
-
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-QuotaManager::Reset()
-{
-  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
-
-  if (!gTestingEnabled) {
-    NS_WARNING("Testing features are not enabled!");
-    return NS_OK;
-  }
-
-  RefPtr<ResetOrClearOp> op = new ResetOrClearOp(/* aClear */ false);
-
-  op->RunImmediately();
-
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-QuotaManager::Observe(nsISupports* aSubject,
-                      const char* aTopic,
-                      const char16_t* aData)
-{
-  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
-
-  if (!strcmp(aTopic, PROFILE_BEFORE_CHANGE_OBSERVER_ID)) {
-    // Setting this flag prevents the service from being recreated and prevents
-    // further storagess from being created.
-    if (gShutdown.exchange(true)) {
-      NS_ERROR("Shutdown more than once?!");
-    }
-
-    if (XRE_IsParentProcess()) {
-      // Kick off the shutdown timer.
-      if (NS_FAILED(mShutdownTimer->Init(this, DEFAULT_SHUTDOWN_TIMER_MS,
-                                         nsITimer::TYPE_ONE_SHOT))) {
-        NS_WARNING("Failed to initialize shutdown timer!");
-      }
-
-      // Each client will spin the event loop while we wait on all the threads
-      // to close. Our timer may fire during that loop.
-      for (uint32_t index = 0; index < Client::TYPE_MAX; index++) {
-        mClients[index]->ShutdownWorkThreads();
-      }
-
-      // Cancel the timer regardless of whether it actually fired.
-      if (NS_FAILED(mShutdownTimer->Cancel())) {
-        NS_WARNING("Failed to cancel shutdown timer!");
-      }
-
-      // Give clients a chance to cleanup IO thread only objects.
-      nsCOMPtr<nsIRunnable> runnable =
-        NS_NewRunnableMethod(this, &QuotaManager::ReleaseIOThreadObjects);
-      if (!runnable) {
-        NS_WARNING("Failed to create runnable!");
-      }
-
-      if (NS_FAILED(mIOThread->Dispatch(runnable, NS_DISPATCH_NORMAL))) {
-        NS_WARNING("Failed to dispatch runnable!");
-      }
-
-      // Make sure to join with our IO thread.
-      if (NS_FAILED(mIOThread->Shutdown())) {
-        NS_WARNING("Failed to shutdown IO thread!");
-      }
-
-      for (RefPtr<DirectoryLockImpl>& lock : mPendingDirectoryLocks) {
-        lock->Invalidate();
-      }
-    }
-
-    return NS_OK;
-  }
-
-  if (!strcmp(aTopic, NS_TIMER_CALLBACK_TOPIC)) {
-    NS_ASSERTION(XRE_IsParentProcess(), "Should only happen in the main process!");
-
-    NS_WARNING("Some storage operations are taking longer than expected "
-               "during shutdown and will be aborted!");
-
-    // Abort all operations.
-    for (RefPtr<Client>& client : mClients) {
-      client->AbortOperations(NullCString());
-    }
-
-    return NS_OK;
-  }
-
-  if (!strcmp(aTopic, TOPIC_WEB_APP_CLEAR_DATA)) {
-    nsCOMPtr<mozIApplicationClearPrivateDataParams> params =
-      do_QueryInterface(aSubject);
-    NS_ENSURE_TRUE(params, NS_ERROR_UNEXPECTED);
-
-    uint32_t appId;
-    nsresult rv = params->GetAppId(&appId);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    bool browserOnly;
-    rv = params->GetBrowserOnly(&browserOnly);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    rv = ClearStoragesForApp(appId, browserOnly);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    return NS_OK;
-  }
-
-  if (!strcmp(aTopic, OBSERVER_TOPIC_IDLE_DAILY)) {
-    for (auto& client : mClients) {
-      client->PerformIdleMaintenance();
-    }
-    return NS_OK;
-  }
-
-  NS_NOTREACHED("Unknown topic!");
-  return NS_ERROR_UNEXPECTED;
-}
-
 uint64_t
 QuotaManager::LockedCollectOriginsForEviction(
                                   uint64_t aMinSizeToBeFreed,
                                   nsTArray<RefPtr<DirectoryLockImpl>>& aLocks)
 {
   mQuotaMutex.AssertCurrentThreadOwns();
 
   RefPtr<CollectOriginsHelper> helper =
@@ -3868,17 +4314,18 @@ QuotaManager::LockedCollectOriginsForEvi
 
   // Unlock while calling out to XPCOM (code behind the dispatch method needs
   // to acquire its own lock which can potentially lead to a deadlock and it
   // also calls an observer that can do various stuff like IO, so it's better
   // to not hold our mutex while that happens).
   {
     MutexAutoUnlock autoUnlock(mQuotaMutex);
 
-    MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(helper)));
+    MOZ_ALWAYS_TRUE(NS_SUCCEEDED(mOwningThread->Dispatch(helper,
+                                                         NS_DISPATCH_NORMAL)));
   }
 
   return helper->BlockAndReturnOriginsForEviction(aLocks);
 }
 
 void
 QuotaManager::LockedRemoveQuotaForOrigin(PersistenceType aPersistenceType,
                                          const nsACString& aGroup,
@@ -3903,37 +4350,16 @@ QuotaManager::LockedRemoveQuotaForOrigin
 
       if (!pair->LockedHasGroupInfos()) {
         mGroupInfoPairs.Remove(aGroup);
       }
     }
   }
 }
 
-nsresult
-QuotaManager::ClearStoragesForApp(uint32_t aAppId, bool aBrowserOnly)
-{
-  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
-  NS_ASSERTION(aAppId != kUnknownAppId, "Bad appId!");
-
-  // This only works from the main process.
-  NS_ENSURE_TRUE(XRE_IsParentProcess(), NS_ERROR_NOT_AVAILABLE);
-
-  nsAutoCString pattern;
-  GetOriginPatternStringMaybeIgnoreBrowser(aAppId, aBrowserOnly, pattern);
-
-  RefPtr<OriginClearOp> op =
-    new OriginClearOp(Nullable<PersistenceType>(),
-                      OriginScope::FromPattern(pattern));
-
-  op->RunImmediately();
-
-  return NS_OK;
-}
-
 void
 QuotaManager::CheckTemporaryStorageLimits()
 {
   AssertIsOnIOThread();
 
   nsTArray<OriginInfo*> doomedOriginInfos;
   {
     MutexAutoLock lock(mQuotaMutex);
@@ -4089,17 +4515,17 @@ QuotaManager::DeleteFilesForOrigin(Persi
 
 void
 QuotaManager::FinalizeOriginEviction(
                                   nsTArray<RefPtr<DirectoryLockImpl>>& aLocks)
 {
   NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
 
   RefPtr<FinalizeOriginEvictionOp> op =
-    new FinalizeOriginEvictionOp(aLocks);
+    new FinalizeOriginEvictionOp(mOwningThread, aLocks);
 
   if (IsOnIOThread()) {
     op->RunOnIOThreadImmediately();
   } else {
     op->Dispatch();
   }
 }
 
@@ -4140,16 +4566,33 @@ QuotaManager::GetOriginPatternString(uin
     NS_ASSERTION(PatternMatchesOrigin(pattern, aOrigin),
                  "Origin doesn't match parameters!");
   }
 #endif
 
   _retval = aOrigin;
 }
 
+void
+QuotaManager::ShutdownTimerCallback(nsITimer* aTimer, void* aClosure)
+{
+  AssertIsOnBackgroundThread();
+
+  auto quotaManager = static_cast<QuotaManager*>(aClosure);
+  MOZ_ASSERT(quotaManager);
+
+  NS_WARNING("Some storage operations are taking longer than expected "
+             "during shutdown and will be aborted!");
+
+  // Abort all operations.
+  for (RefPtr<Client>& client : quotaManager->mClients) {
+    client->AbortOperations(NullCString());
+  }
+}
+
 auto
 QuotaManager::GetDirectoryLockTable(PersistenceType aPersistenceType)
   -> DirectoryLockTable&
 {
   switch (aPersistenceType) {
     case PERSISTENCE_TYPE_TEMPORARY:
       return mTemporaryDirectoryLockTable;
     case PERSISTENCE_TYPE_DEFAULT:
@@ -4304,17 +4747,17 @@ CollectOriginsHelper::BlockAndReturnOrig
 
   mLocks.SwapElements(aLocks);
   return mSizeToBeFreed;
 }
 
 NS_IMETHODIMP
 CollectOriginsHelper::Run()
 {
-  MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
+  AssertIsOnBackgroundThread();
 
   QuotaManager* quotaManager = QuotaManager::Get();
   NS_ASSERTION(quotaManager, "Shouldn't be null!");
 
   // We use extra stack vars here to avoid race detector warnings (the same
   // memory accessed with and without the lock held).
   nsTArray<RefPtr<DirectoryLockImpl>> locks;
   uint64_t sizeToBeFreed =
@@ -4327,24 +4770,43 @@ CollectOriginsHelper::Run()
   mLocks.SwapElements(locks);
   mSizeToBeFreed = sizeToBeFreed;
   mWaiting = false;
   mCondVar.Notify();
 
   return NS_OK;
 }
 
+/*******************************************************************************
+ * OriginOperationBase
+ ******************************************************************************/
+
 NS_IMETHODIMP
 OriginOperationBase::Run()
 {
   nsresult rv;
 
   switch (mState) {
     case State_Initial: {
-      rv = Open();
+      rv = Init();
+      break;
+    }
+
+    case State_Initializing: {
+      rv = InitOnMainThread();
+      break;
+    }
+
+    case State_FinishingInit: {
+      rv = FinishInit();
+      break;
+    }
+
+    case State_CreatingQuotaManager: {
+      rv = QuotaManagerOpen();
       break;
     }
 
     case State_DirectoryOpenPending: {
       rv = DirectoryOpen();
       break;
     }
 
@@ -4367,17 +4829,17 @@ OriginOperationBase::Run()
   }
 
   return NS_OK;
 }
 
 nsresult
 OriginOperationBase::DirectoryOpen()
 {
-  MOZ_ASSERT(NS_IsMainThread());
+  AssertIsOnOwningThread();
   MOZ_ASSERT(mState == State_DirectoryOpenPending);
 
   QuotaManager* quotaManager = QuotaManager::Get();
   if (NS_WARN_IF(!quotaManager)) {
     return NS_ERROR_FAILURE;
   }
 
   // Must set this before dispatching otherwise we will race with the IO thread.
@@ -4397,17 +4859,91 @@ OriginOperationBase::Finish(nsresult aRe
   if (NS_SUCCEEDED(mResultCode)) {
     mResultCode = aResult;
   }
 
   // Must set mState before dispatching otherwise we will race with the main
   // thread.
   mState = State_UnblockingOpen;
 
-  MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(this)));
+  MOZ_ALWAYS_TRUE(NS_SUCCEEDED(mOwningThread->Dispatch(this,
+                                                       NS_DISPATCH_NORMAL)));
+}
+
+nsresult
+OriginOperationBase::Init()
+{
+  AssertIsOnOwningThread();
+  MOZ_ASSERT(mState == State_Initial);
+
+  AdvanceState();
+
+  if (mNeedsMainThreadInit) {
+    MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(this)));
+  } else {
+    AdvanceState();
+    MOZ_ALWAYS_TRUE(NS_SUCCEEDED(Run()));
+  }
+
+  return NS_OK;
+}
+
+nsresult
+OriginOperationBase::InitOnMainThread()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(mState == State_Initializing);
+
+  nsresult rv = DoInitOnMainThread();
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  AdvanceState();
+
+  MOZ_ALWAYS_TRUE(NS_SUCCEEDED(mOwningThread->Dispatch(this,
+                                                       NS_DISPATCH_NORMAL)));
+
+  return NS_OK;
+}
+
+nsresult
+OriginOperationBase::FinishInit()
+{
+  AssertIsOnOwningThread();
+  MOZ_ASSERT(mState == State_FinishingInit);
+
+  if (QuotaManager::IsShuttingDown()) {
+    return NS_ERROR_FAILURE;
+  }
+
+  AdvanceState();
+
+  if (mNeedsQuotaManagerInit && !QuotaManager::Get()) {
+    QuotaManager::GetOrCreate(this);
+  } else {
+    Open();
+  }
+
+  return NS_OK;
+}
+
+nsresult
+OriginOperationBase::QuotaManagerOpen()
+{
+  AssertIsOnOwningThread();
+  MOZ_ASSERT(mState == State_CreatingQuotaManager);
+
+  if (NS_WARN_IF(!QuotaManager::Get())) {
+    return NS_ERROR_FAILURE;
+  }
+
+  Open();
+
+  return NS_OK;
 }
 
 nsresult
 OriginOperationBase::DirectoryWork()
 {
   AssertIsOnIOThread();
   MOZ_ASSERT(mState == State_DirectoryWorkOpen);
 
@@ -4416,87 +4952,139 @@ OriginOperationBase::DirectoryWork()
     return NS_ERROR_FAILURE;
   }
 
   nsresult rv = DoDirectoryWork(quotaManager);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
-  // Must set mState before dispatching otherwise we will race with the main
+  // Must set mState before dispatching otherwise we will race with the owning
   // thread.
   AdvanceState();
 
-  MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(this)));
-
-  return NS_OK;
-}
-
-NS_IMPL_ISUPPORTS_INHERITED0(NormalOriginOperationBase, nsRunnable)
-
-nsresult
-NormalOriginOperationBase::Open()
-{
-  MOZ_ASSERT(NS_IsMainThread());
-  MOZ_ASSERT(mState == State_Initial);
-
-  if (QuotaManager::IsShuttingDown()) {
-    return NS_ERROR_FAILURE;
-  }
-
-  QuotaManager* quotaManager = QuotaManager::Get();
-  if (NS_WARN_IF(!quotaManager)) {
-    return NS_ERROR_FAILURE;
-  }
-
-  AdvanceState();
-
-  quotaManager->OpenDirectoryInternal(mPersistenceType,
-                                      mOriginScope,
-                                      mExclusive,
-                                      this);
+  MOZ_ALWAYS_TRUE(NS_SUCCEEDED(mOwningThread->Dispatch(this,
+                                                       NS_DISPATCH_NORMAL)));
 
   return NS_OK;
 }
 
 void
+FinalizeOriginEvictionOp::Dispatch()
+{
+  MOZ_ASSERT(!NS_IsMainThread());
+  MOZ_ASSERT(GetState() == State_Initial);
+
+  SetState(State_DirectoryOpenPending);
+
+  MOZ_ALWAYS_TRUE(NS_SUCCEEDED(mOwningThread->Dispatch(this,
+                                                       NS_DISPATCH_NORMAL)));
+}
+
+void
+FinalizeOriginEvictionOp::RunOnIOThreadImmediately()
+{
+  AssertIsOnIOThread();
+  MOZ_ASSERT(GetState() == State_Initial);
+
+  SetState(State_DirectoryWorkOpen);
+
+  MOZ_ALWAYS_TRUE(NS_SUCCEEDED(this->Run()));
+}
+
+void
+FinalizeOriginEvictionOp::Open()
+{
+  MOZ_CRASH("Shouldn't get here!");
+}
+
+nsresult
+FinalizeOriginEvictionOp::DoDirectoryWork(QuotaManager* aQuotaManager)
+{
+  AssertIsOnIOThread();
+
+  PROFILER_LABEL("Quota", "FinalizeOriginEvictionOp::DoDirectoryWork",
+                 js::ProfileEntry::Category::OTHER);
+
+  for (RefPtr<DirectoryLockImpl>& lock : mLocks) {
+    aQuotaManager->OriginClearCompleted(lock->GetPersistenceType().Value(),
+                                        lock->GetOriginScope(),
+                                        lock->GetIsApp().Value());
+  }
+
+  return NS_OK;
+}
+
+void
+FinalizeOriginEvictionOp::UnblockOpen()
+{
+  AssertIsOnOwningThread();
+  MOZ_ASSERT(GetState() == State_UnblockingOpen);
+
+#ifdef DEBUG
+  NoteActorDestroyed();
+#endif
+
+  mLocks.Clear();
+
+  AdvanceState();
+}
+
+NS_IMPL_ISUPPORTS_INHERITED0(NormalOriginOperationBase, nsRunnable)
+
+void
+NormalOriginOperationBase::Open()
+{
+  AssertIsOnOwningThread();
+  MOZ_ASSERT(GetState() == State_CreatingQuotaManager);
+  MOZ_ASSERT(QuotaManager::Get());
+
+  AdvanceState();
+
+  QuotaManager::Get()->OpenDirectoryInternal(mPersistenceType,
+                                             mOriginScope,
+                                             mExclusive,
+                                             this);
+}
+
+void
 NormalOriginOperationBase::UnblockOpen()
 {
-  MOZ_ASSERT(NS_IsMainThread());
-  MOZ_ASSERT(mState == State_UnblockingOpen);
+  AssertIsOnOwningThread();
+  MOZ_ASSERT(GetState() == State_UnblockingOpen);
 
   SendResults();
 
   mDirectoryLock = nullptr;
 
   AdvanceState();
 }
 
 void
 NormalOriginOperationBase::DirectoryLockAcquired(DirectoryLock* aLock)
 {
-  MOZ_ASSERT(NS_IsMainThread());
+  AssertIsOnOwningThread();
   MOZ_ASSERT(aLock);
-  MOZ_ASSERT(mState == State_DirectoryOpenPending);
+  MOZ_ASSERT(GetState() == State_DirectoryOpenPending);
   MOZ_ASSERT(!mDirectoryLock);
 
   mDirectoryLock = aLock;
 
   nsresult rv = DirectoryOpen();
   if (NS_WARN_IF(NS_FAILED(rv))) {
     Finish(rv);
     return;
   }
 }
 
 void
 NormalOriginOperationBase::DirectoryLockFailed()
 {
-  MOZ_ASSERT(NS_IsMainThread());
-  MOZ_ASSERT(mState == State_DirectoryOpenPending);
+  AssertIsOnOwningThread();
+  MOZ_ASSERT(GetState() == State_DirectoryOpenPending);
   MOZ_ASSERT(!mDirectoryLock);
 
   Finish(NS_ERROR_FAILURE);
 }
 
 nsresult
 SaveOriginAccessTimeOp::DoDirectoryWork(QuotaManager* aQuotaManager)
 {
@@ -4529,34 +5117,278 @@ SaveOriginAccessTimeOp::DoDirectoryWork(
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
   }
 
   return NS_OK;
 }
 
-GetUsageOp::GetUsageOp(const nsACString& aGroup,
-                       const nsACString& aOrigin,
-                       bool aIsApp,
-                       nsIPrincipal* aPrincipal,
-                       nsIUsageCallback* aCallback)
+void
+SaveOriginAccessTimeOp::SendResults()
+{
+#ifdef DEBUG
+  NoteActorDestroyed();
+#endif
+}
+
+/*******************************************************************************
+ * Quota
+ ******************************************************************************/
+
+Quota::Quota()
+  : mActorDestroyed(false)
+{
+}
+
+Quota::~Quota()
+{
+  MOZ_ASSERT(mActorDestroyed);
+}
+
+void
+Quota::StartIdleMaintenance()
+{
+  AssertIsOnBackgroundThread();
+  MOZ_ASSERT(!QuotaManager::IsShuttingDown());
+
+  QuotaManager* quotaManager = QuotaManager::Get();
+  if (NS_WARN_IF(!quotaManager)) {
+    return;
+  }
+
+  quotaManager->StartIdleMaintenance();
+}
+
+void
+Quota::ActorDestroy(ActorDestroyReason aWhy)
+{
+  AssertIsOnBackgroundThread();
+  MOZ_ASSERT(!mActorDestroyed);
+
+  mActorDestroyed = true;
+}
+
+PQuotaUsageRequestParent*
+Quota::AllocPQuotaUsageRequestParent(const UsageRequestParams& aParams)
+{
+  RefPtr<GetUsageOp> actor = new GetUsageOp(aParams);
+
+  // Transfer ownership to IPDL.
+  return actor.forget().take();
+}
+
+bool
+Quota::RecvPQuotaUsageRequestConstructor(PQuotaUsageRequestParent* aActor,
+                                         const UsageRequestParams& aParams)
+{
+  AssertIsOnBackgroundThread();
+  MOZ_ASSERT(aActor);
+  MOZ_ASSERT(aParams.type() != UsageRequestParams::T__None);
+
+  auto* op = static_cast<GetUsageOp*>(aActor);
+
+  if (NS_WARN_IF(!op->Init(this))) {
+    return false;
+  }
+
+  op->RunImmediately();
+  return true;
+}
+
+bool
+Quota::DeallocPQuotaUsageRequestParent(PQuotaUsageRequestParent* aActor)
+{
+  AssertIsOnBackgroundThread();
+  MOZ_ASSERT(aActor);
+
+  // Transfer ownership back from IPDL.
+  RefPtr<GetUsageOp> actor =
+    dont_AddRef(static_cast<GetUsageOp*>(aActor));
+  return true;
+}
+
+PQuotaRequestParent*
+Quota::AllocPQuotaRequestParent(const RequestParams& aParams)
+{
+  AssertIsOnBackgroundThread();
+  MOZ_ASSERT(aParams.type() != RequestParams::T__None);
+
+  if (aParams.type() == RequestParams::TClearAppParams) {
+    PBackgroundParent* actor = Manager();
+    MOZ_ASSERT(actor);
+
+    if (BackgroundParent::IsOtherProcessActor(actor)) {
+      ASSERT_UNLESS_FUZZING();
+      return nullptr;
+    }
+  }
+
+  RefPtr<QuotaRequestBase> actor;
+
+  switch (aParams.type()) {
+    case RequestParams::TClearOriginParams:
+    case RequestParams::TClearAppParams:
+      actor = new OriginClearOp(aParams);
+      break;
+
+    case RequestParams::TClearAllParams:
+      actor = new ResetOrClearOp(/* aClear */ true);
+      break;
+
+    case RequestParams::TResetAllParams:
+      actor = new ResetOrClearOp(/* aClear */ false);
+      break;
+
+    default:
+      MOZ_CRASH("Should never get here!");
+  }
+
+  MOZ_ASSERT(actor);
+
+  // Transfer ownership to IPDL.
+  return actor.forget().take();
+}
+
+bool
+Quota::RecvPQuotaRequestConstructor(PQuotaRequestParent* aActor,
+                                    const RequestParams& aParams)
+{
+  AssertIsOnBackgroundThread();
+  MOZ_ASSERT(aActor);
+  MOZ_ASSERT(aParams.type() != RequestParams::T__None);
+
+  auto* op = static_cast<QuotaRequestBase*>(aActor);
+  if (NS_WARN_IF(!op->Init(this))) {
+    return false;
+  }
+
+  op->RunImmediately();
+  return true;
+}
+
+bool
+Quota::DeallocPQuotaRequestParent(PQuotaRequestParent* aActor)
+{
+  AssertIsOnBackgroundThread();
+  MOZ_ASSERT(aActor);
+
+  // Transfer ownership back from IPDL.
+  RefPtr<QuotaRequestBase> actor =
+    dont_AddRef(static_cast<QuotaRequestBase*>(aActor));
+  return true;
+}
+
+bool
+Quota::RecvStartIdleMaintenance()
+{
+  AssertIsOnBackgroundThread();
+
+  PBackgroundParent* actor = Manager();
+  MOZ_ASSERT(actor);
+
+  if (BackgroundParent::IsOtherProcessActor(actor)) {
+    ASSERT_UNLESS_FUZZING();
+    return false;
+  }
+
+  if (QuotaManager::IsShuttingDown()) {
+    return true;
+  }
+
+  QuotaManager* quotaManager = QuotaManager::Get();
+  if (!quotaManager) {
+    nsCOMPtr<nsIRunnable> callback =
+      NS_NewRunnableMethod(this, &Quota::StartIdleMaintenance);
+
+    QuotaManager::GetOrCreate(callback);
+    return true;
+  }
+
+  quotaManager->StartIdleMaintenance();
+
+  return true;
+}
+
+bool
+Quota::RecvStopIdleMaintenance()
+{
+  AssertIsOnBackgroundThread();
+
+  PBackgroundParent* actor = Manager();
+  MOZ_ASSERT(actor);
+
+  if (BackgroundParent::IsOtherProcessActor(actor)) {
+    ASSERT_UNLESS_FUZZING();
+    return false;
+  }
+
+  if (QuotaManager::IsShuttingDown()) {
+    return true;
+  }
+
+  QuotaManager* quotaManager = QuotaManager::Get();
+  if (!quotaManager) {
+    return true;
+  }
+
+  quotaManager->StopIdleMaintenance();
+
+  return true;
+}
+
+GetUsageOp::GetUsageOp(const UsageRequestParams& aParams)
   : NormalOriginOperationBase(Nullable<PersistenceType>(),
-                              OriginScope::FromOrigin(aOrigin),
+                              OriginScope::FromNull(),
                               /* aExclusive */ false)
-  , mGroup(aGroup)
-  , mPrincipal(aPrincipal)
-  , mCallback(aCallback)
-  , mIsApp(aIsApp)
+  , mParams(aParams.get_UsageParams())
+{
+  AssertIsOnOwningThread();
+  MOZ_ASSERT(aParams.type() == UsageRequestParams::TUsageParams);
+}
+
+bool
+GetUsageOp::Init(Quota* aQuota)
+{
+  AssertIsOnOwningThread();
+  MOZ_ASSERT(aQuota);
+
+  mNeedsMainThreadInit = true;
+  mNeedsQuotaManagerInit = true;
+
+  return true;
+}
+
+nsresult
+GetUsageOp::DoInitOnMainThread()
 {
   MOZ_ASSERT(NS_IsMainThread());
-  MOZ_ASSERT(!aGroup.IsEmpty());
-  MOZ_ASSERT(!aOrigin.IsEmpty());
-  MOZ_ASSERT(aPrincipal);
-  MOZ_ASSERT(aCallback);
+  MOZ_ASSERT(GetState() == State_Initializing);
+  MOZ_ASSERT(mNeedsMainThreadInit);
+
+  const PrincipalInfo& principalInfo = mParams.principalInfo();
+
+  nsresult rv;
+  nsCOMPtr<nsIPrincipal> principal =
+    PrincipalInfoToPrincipal(principalInfo, &rv);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  // Figure out which origin we're dealing with.
+  nsCString origin;
+  rv = QuotaManager::GetInfoFromPrincipal(principal, &mGroup, &origin,
+                                          &mIsApp);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  mOriginScope.SetFromOrigin(origin);
+
+  return NS_OK;
 }
 
 nsresult
 GetUsageOp::AddToUsage(QuotaManager* aQuotaManager,
                        PersistenceType aPersistenceType)
 {
   AssertIsOnIOThread();
 
@@ -4671,41 +5503,102 @@ GetUsageOp::DoDirectoryWork(QuotaManager
   }
 
   return NS_OK;
 }
 
 void
 GetUsageOp::SendResults()
 {
-  MOZ_ASSERT(NS_IsMainThread());
-
-  // Call the callback unless we were canceled.
-  if (!mUsageInfo.Canceled()) {
-    // XXX Implement better error reporting here, bug 1170019.
-    if (NS_FAILED(mResultCode)) {
-      mUsageInfo.ResetUsage();
+  AssertIsOnOwningThread();
+
+  if (IsActorDestroyed()) {
+    if (NS_SUCCEEDED(mResultCode)) {
+      mResultCode = NS_ERROR_FAILURE;
+    }
+  } else {
+    if (mUsageInfo.Canceled()) {
+      mResultCode = NS_ERROR_FAILURE;
+    }
+
+    UsageRequestResponse response;
+
+    if (NS_SUCCEEDED(mResultCode)) {
+      UsageResponse usageResponse;
+      usageResponse.usage() = mUsageInfo.TotalUsage();
+      usageResponse.fileUsage() = mUsageInfo.FileUsage();
+      response = usageResponse;
+    } else {
+      response = mResultCode;
     }
 
-    mCallback->OnUsageResult(mPrincipal, mUsageInfo.TotalUsage(), mUsageInfo.FileUsage());
-  }
-
-  // Clean up.
-  mPrincipal = nullptr;
-  mCallback = nullptr;
-}
-
-NS_IMPL_ISUPPORTS_INHERITED(GetUsageOp,
-                            NormalOriginOperationBase,
-                            nsIQuotaRequest)
-
-NS_IMETHODIMP
-GetUsageOp::Cancel()
-{
-  return mUsageInfo.Cancel();
+    Unused << PQuotaUsageRequestParent::Send__delete__(this, response);
+  }
+}
+
+void
+GetUsageOp::ActorDestroy(ActorDestroyReason aWhy)
+{
+  AssertIsOnOwningThread();
+
+  NoteActorDestroyed();
+}
+
+bool
+GetUsageOp::RecvCancel()
+{
+  AssertIsOnOwningThread();
+
+  nsresult rv = mUsageInfo.Cancel();
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return false;
+  }
+
+  return true;
+}
+
+bool
+QuotaRequestBase::Init(Quota* aQuota)
+{
+  AssertIsOnOwningThread();
+  MOZ_ASSERT(aQuota);
+
+  mNeedsQuotaManagerInit = true;
+
+  return true;
+}
+
+void
+QuotaRequestBase::SendResults()
+{
+  AssertIsOnOwningThread();
+
+  if (IsActorDestroyed()) {
+    if (NS_SUCCEEDED(mResultCode)) {
+      mResultCode = NS_ERROR_FAILURE;
+    }
+  } else {
+    RequestResponse response;
+
+    if (NS_SUCCEEDED(mResultCode)) {
+      GetResponse(response);
+    } else {
+      response = mResultCode;
+    }
+
+    Unused << PQuotaRequestParent::Send__delete__(this, response);
+  }
+}
+
+void
+QuotaRequestBase::ActorDestroy(ActorDestroyReason aWhy)
+{
+  AssertIsOnOwningThread();
+
+  NoteActorDestroyed();
 }
 
 void
 ResetOrClearOp::DeleteFiles(QuotaManager* aQuotaManager)
 {
   AssertIsOnIOThread();
   NS_ASSERTION(aQuotaManager, "Don't pass me null!");
 
@@ -4742,16 +5635,108 @@ ResetOrClearOp::DoDirectoryWork(QuotaMan
   aQuotaManager->RemoveQuota();
 
   aQuotaManager->ResetOrClearCompleted();
 
   return NS_OK;
 }
 
 void
+ResetOrClearOp::GetResponse(RequestResponse& aResponse)
+{
+  AssertIsOnOwningThread();
+  if (mClear) {
+    aResponse = ClearAllResponse();
+  } else {
+    aResponse = ResetAllResponse();
+  }
+}
+
+OriginClearOp::OriginClearOp(const RequestParams& aParams)
+  : QuotaRequestBase(/* aExclusive */ true)
+  , mParams(aParams)
+  , mApp(aParams.type() == RequestParams::TClearAppParams)
+{
+  MOZ_ASSERT(aParams.type() == RequestParams::TClearOriginParams ||
+             aParams.type() == RequestParams::TClearAppParams);
+}
+
+bool
+OriginClearOp::Init(Quota* aQuota)
+{
+  AssertIsOnOwningThread();
+  MOZ_ASSERT(aQuota);
+
+  if (NS_WARN_IF(!QuotaRequestBase::Init(aQuota))) {
+    return false;
+  }
+
+  if (mApp) {
+    const ClearAppParams& params = mParams.get_ClearAppParams();
+
+    nsAutoCString pattern;
+    QuotaManager::GetOriginPatternStringMaybeIgnoreBrowser(params.appId(),
+                                                           params.browserOnly(),
+                                                           pattern);
+
+    mOriginScope.SetFromPattern(pattern);
+  } else {
+    const ClearOriginParams& params = mParams.get_ClearOriginParams();
+
+    if (params.persistenceTypeIsExplicit()) {
+      MOZ_ASSERT(params.persistenceType() != PERSISTENCE_TYPE_INVALID);
+
+      mPersistenceType.SetValue(params.persistenceType());
+    }
+
+    mNeedsMainThreadInit = true;
+  }
+
+  return true;
+}
+
+nsresult
+OriginClearOp::DoInitOnMainThread()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(GetState() == State_Initializing);
+  MOZ_ASSERT(!mApp);
+  MOZ_ASSERT(mNeedsMainThreadInit);
+
+  const ClearOriginParams& params = mParams.get_ClearOriginParams();
+
+  const PrincipalInfo& principalInfo = params.principalInfo();
+
+  nsresult rv;
+  nsCOMPtr<nsIPrincipal> principal =
+    PrincipalInfoToPrincipal(principalInfo, &rv);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  // Figure out which origin we're dealing with.
+  nsCString origin;
+  rv = QuotaManager::GetInfoFromPrincipal(principal, nullptr, &origin, nullptr);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  const mozilla::OriginAttributes& attrs =
+    mozilla::BasePrincipal::Cast(principal)->OriginAttributesRef();
+
+  nsAutoCString pattern;
+  QuotaManager::GetOriginPatternString(attrs.mAppId, attrs.mInBrowser, origin,
+                                       pattern);
+
+  mOriginScope.SetFromPattern(pattern);
+
+  return NS_OK;
+}
+
+void
 OriginClearOp::DeleteFiles(QuotaManager* aQuotaManager,
                            PersistenceType aPersistenceType)
 {
   AssertIsOnIOThread();
   MOZ_ASSERT(aQuotaManager);
 
   nsresult rv;
 
@@ -4867,69 +5852,25 @@ OriginClearOp::DoDirectoryWork(QuotaMana
   } else {
     DeleteFiles(aQuotaManager, mPersistenceType.Value());
   }
 
   return NS_OK;
 }
 
 void
-FinalizeOriginEvictionOp::Dispatch()
-{
-  MOZ_ASSERT(!NS_IsMainThread());
-  MOZ_ASSERT(mState == State_Initial);
-
-  mState = State_DirectoryOpenPending;
-
-  MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(this)));
-}
-
-void
-FinalizeOriginEvictionOp::RunOnIOThreadImmediately()
-{
-  AssertIsOnIOThread();
-  MOZ_ASSERT(mState == State_Initial);
-
-  mState = State_DirectoryWorkOpen;
-
-  MOZ_ALWAYS_TRUE(NS_SUCCEEDED(this->Run()));
-}
-
-nsresult
-FinalizeOriginEvictionOp::Open()
-{
-  MOZ_CRASH("Shouldn't get here!");
-}
-
-nsresult
-FinalizeOriginEvictionOp::DoDirectoryWork(QuotaManager* aQuotaManager)
-{
-  AssertIsOnIOThread();
-
-  PROFILER_LABEL("Quota", "FinalizeOriginEvictionOp::DoDirectoryWork",
-                 js::ProfileEntry::Category::OTHER);
-
-  for (RefPtr<DirectoryLockImpl>& lock : mLocks) {
-    aQuotaManager->OriginClearCompleted(lock->GetPersistenceType().Value(),
-                                        lock->GetOriginScope(),
-                                        lock->GetIsApp().Value());
-  }
-
-  return NS_OK;
-}
-
-void
-FinalizeOriginEvictionOp::UnblockOpen()
-{
-  MOZ_ASSERT(NS_IsMainThread());
-  MOZ_ASSERT(mState == State_UnblockingOpen);
-
-  mLocks.Clear();
-
-  AdvanceState();
+OriginClearOp::GetResponse(RequestResponse& aResponse)
+{
+  AssertIsOnOwningThread();
+
+  if (mApp) {
+    aResponse = ClearAppResponse();
+  } else {
+    aResponse = ClearOriginResponse();
+  }
 }
 
 nsresult
 StorageDirectoryHelper::CreateOrUpgradeMetadataFiles(bool aCreate)
 {
   AssertIsOnIOThread();
   MOZ_ASSERT_IF(mPersistent, aCreate);
 
new file mode 100644
--- /dev/null
+++ b/dom/quota/ActorsParent.h
@@ -0,0 +1,26 @@
+/* -*- 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_quota_ActorsParent_h
+#define mozilla_dom_quota_ActorsParent_h
+
+namespace mozilla {
+namespace dom {
+namespace quota {
+
+class PQuotaParent;
+
+PQuotaParent*
+AllocPQuotaParent();
+
+bool
+DeallocPQuotaParent(PQuotaParent* aActor);
+
+} // namespace quota
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_quota_ActorsParent_h
--- a/dom/quota/Client.h
+++ b/dom/quota/Client.h
@@ -106,25 +106,28 @@ public:
 
   virtual void
   OnOriginClearCompleted(PersistenceType aPersistenceType,
                          const nsACString& aOrigin) = 0;
 
   virtual void
   ReleaseIOThreadObjects() = 0;
 
-  // Methods which are called on the main thred.
+  // Methods which are called on the background thred.
   virtual void
   AbortOperations(const nsACString& aOrigin) = 0;
 
   virtual void
   AbortOperationsForProcess(ContentParentId aContentParentId) = 0;
 
   virtual void
-  PerformIdleMaintenance() = 0;
+  StartIdleMaintenance() = 0;
+
+  virtual void
+  StopIdleMaintenance() = 0;
 
   virtual void
   ShutdownWorkThreads() = 0;
 
 protected:
   virtual ~Client()
   { }
 };
--- a/dom/quota/OriginScope.h
+++ b/dom/quota/OriginScope.h
@@ -58,22 +58,43 @@ public:
   }
 
   Type
   GetType() const
   {
     return mType;
   }
 
+  void
+  SetFromOrigin(const nsACString& aOrigin)
+  {
+    Assign(aOrigin);
+    mType = eOrigin;
+  }
+
+  void
+  SetFromPattern(const nsACString& aPattern)
+  {
+    Assign(aPattern);
+    mType = ePattern;
+  }
+
+  void
+  SetFromNull()
+  {
+    SetIsVoid(true);
+    mType = eNull;
+  }
+
 private:
   OriginScope(const nsACString& aString, Type aType)
   : nsCString(aString), mType(aType)
   { }
 
   bool
   operator==(const OriginScope& aOther) = delete;
 
-  const Type mType;
+  Type mType;
 };
 
 END_QUOTA_NAMESPACE
 
 #endif // mozilla_dom_quota_originorpatternstring_h__
new file mode 100644
--- /dev/null
+++ b/dom/quota/PQuota.ipdl
@@ -0,0 +1,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 protocol PBackground;
+include protocol PQuotaRequest;
+include protocol PQuotaUsageRequest;
+
+include PBackgroundSharedTypes;
+
+include "mozilla/dom/quota/SerializationHelpers.h";
+
+using mozilla::dom::quota::PersistenceType
+  from "mozilla/dom/quota/PersistenceType.h";
+
+namespace mozilla {
+namespace dom {
+namespace quota {
+
+struct UsageParams
+{
+  PrincipalInfo principalInfo;
+};
+
+union UsageRequestParams
+{
+  UsageParams;
+};
+
+struct ClearOriginParams
+{
+  PrincipalInfo principalInfo;
+  PersistenceType persistenceType;
+  bool persistenceTypeIsExplicit;
+};
+
+struct ClearAppParams
+{
+  uint32_t appId;
+  bool browserOnly;
+};
+
+struct ClearAllParams
+{
+};
+
+struct ResetAllParams
+{
+};
+
+union RequestParams
+{
+  ClearOriginParams;
+  ClearAppParams;
+  ClearAllParams;
+  ResetAllParams;
+};
+
+protocol PQuota
+{
+  manager PBackground;
+
+  manages PQuotaRequest;
+  manages PQuotaUsageRequest;
+
+parent:
+  __delete__();
+
+  PQuotaUsageRequest(UsageRequestParams params);
+
+  PQuotaRequest(RequestParams params);
+
+  StartIdleMaintenance();
+
+  StopIdleMaintenance();
+};
+
+} // namespace quota
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/quota/PQuotaRequest.ipdl
@@ -0,0 +1,46 @@
+/* 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 PQuota;
+
+namespace mozilla {
+namespace dom {
+namespace quota {
+
+struct ClearOriginResponse
+{
+};
+
+struct ClearAppResponse
+{
+};
+
+struct ClearAllResponse
+{
+};
+
+struct ResetAllResponse
+{
+};
+
+union RequestResponse
+{
+  nsresult;
+  ClearOriginResponse;
+  ClearAppResponse;
+  ClearAllResponse;
+  ResetAllResponse;
+};
+
+protocol PQuotaRequest
+{
+  manager PQuota;
+
+child:
+  __delete__(RequestResponse response);
+};
+
+} // namespace quota
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/quota/PQuotaUsageRequest.ipdl
@@ -0,0 +1,36 @@
+/* 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 PQuota;
+
+namespace mozilla {
+namespace dom {
+namespace quota {
+
+struct UsageResponse
+{
+  uint64_t usage;
+  uint64_t fileUsage;
+};
+
+union UsageRequestResponse
+{
+  nsresult;
+  UsageResponse;
+};
+
+protocol PQuotaUsageRequest
+{
+  manager PQuota;
+
+parent:
+  Cancel();
+
+child:
+  __delete__(UsageRequestResponse response);
+};
+
+} // namespace quota
+} // namespace dom
+} // namespace mozilla
--- a/dom/quota/QuotaCommon.h
+++ b/dom/quota/QuotaCommon.h
@@ -25,18 +25,43 @@
 
 #define QM_WARNING(...)                                                        \
   do {                                                                         \
     nsPrintfCString str(__VA_ARGS__);                                          \
     mozilla::dom::quota::ReportInternalError(__FILE__, __LINE__, str.get());   \
     NS_WARNING(str.get());                                                     \
   } while (0)
 
+class nsIEventTarget;
+
 BEGIN_QUOTA_NAMESPACE
 
+class BackgroundThreadObject
+{
+protected:
+  nsCOMPtr<nsIEventTarget> mOwningThread;
+
+public:
+  void
+  AssertIsOnOwningThread() const
+#ifdef DEBUG
+  ;
+#else
+  { }
+#endif
+
+  nsIEventTarget*
+  OwningThread() const;
+
+protected:
+  BackgroundThreadObject();
+
+  explicit BackgroundThreadObject(nsIEventTarget* aOwningThread);
+};
+
 void
 AssertIsOnIOThread();
 
 void
 AssertCurrentThreadOwnsQuotaMutex();
 
 bool
 IsOnIOThread();
--- a/dom/quota/QuotaManager.h
+++ b/dom/quota/QuotaManager.h
@@ -4,44 +4,36 @@
  * 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_quota_quotamanager_h__
 #define mozilla_dom_quota_quotamanager_h__
 
 #include "QuotaCommon.h"
 
-#include "nsIObserver.h"
-#include "nsIQuotaManager.h"
-
 #include "mozilla/dom/Nullable.h"
 #include "mozilla/dom/ipc/IdType.h"
 #include "mozilla/Mutex.h"
 
 #include "nsClassHashtable.h"
 #include "nsRefPtrHashtable.h"
 
 #include "Client.h"
 #include "PersistenceType.h"
 
 #define QUOTA_MANAGER_CONTRACTID "@mozilla.org/dom/quota/manager;1"
 
+class nsIEventTarget;
 class nsIPrincipal;
 class nsIThread;
 class nsITimer;
 class nsIURI;
 class nsPIDOMWindow;
 class nsIRunnable;
 
-namespace mozilla {
-namespace dom {
-class OptionalContentId;
-} // namespace dom
-} // namespace mozilla
-
 BEGIN_QUOTA_NAMESPACE
 
 class DirectoryLockImpl;
 class GroupInfo;
 class GroupInfoPair;
 class OriginInfo;
 class OriginScope;
 class QuotaObject;
@@ -51,20 +43,18 @@ class NS_NO_VTABLE RefCountedObject
 public:
   NS_IMETHOD_(MozExternalRefCountType)
   AddRef() = 0;
 
   NS_IMETHOD_(MozExternalRefCountType)
   Release() = 0;
 };
 
-// nsISupports is needed for nsMainThreadPtrHandle<DirectoryLock>
-// XXX RemoveMe once bug 1164581 gets fixed.
 class DirectoryLock
-  : public nsISupports
+  : public RefCountedObject
 {
   friend class DirectoryLockImpl;
 
 private:
   DirectoryLock()
   { }
 
   ~DirectoryLock()
@@ -96,18 +86,18 @@ struct OriginParams
   , mIsApp(aIsApp)
   { }
 
   nsCString mOrigin;
   PersistenceType mPersistenceType;
   bool mIsApp;
 };
 
-class QuotaManager final : public nsIQuotaManager,
-                           public nsIObserver
+class QuotaManager final
+  : public BackgroundThreadObject
 {
   friend class DirectoryLockImpl;
   friend class GroupInfo;
   friend class OriginInfo;
   friend class QuotaObject;
 
   enum MozBrowserPatternFlag
   {
@@ -115,34 +105,36 @@ class QuotaManager final : public nsIQuo
     NotMozBrowser,
     IgnoreMozBrowser
   };
 
   typedef nsClassHashtable<nsCStringHashKey,
                            nsTArray<DirectoryLockImpl*>> DirectoryLockTable;
 
 public:
-  NS_DECL_ISUPPORTS
-  NS_DECL_NSIQUOTAMANAGER
-  NS_DECL_NSIOBSERVER
+  class CreateRunnable;
+
+private:
+  class ShutdownRunnable;
+  class ShutdownObserver;
+
+public:
+  NS_INLINE_DECL_REFCOUNTING(QuotaManager)
+
+  static const bool kRunningXPCShellTests;
 
   static const char kReplaceChars[];
 
-  // Returns a non-owning reference.
-  static QuotaManager*
-  GetOrCreate();
+  static void
+  GetOrCreate(nsIRunnable* aCallback);
 
   // Returns a non-owning reference.
   static QuotaManager*
   Get();
 
-  // Returns an owning reference! No one should call this but the factory.
-  static QuotaManager*
-  FactoryCreate();
-
   // Returns true if we've begun the shutdown process.
   static bool IsShuttingDown();
 
   bool
   IsOriginInitialized(const nsACString& aOrigin) const
   {
     AssertIsOnIOThread();
 
@@ -257,16 +249,36 @@ public:
   OriginClearCompleted(PersistenceType aPersistenceType,
                        const nsACString& aOrigin,
                        bool aIsApp);
 
   void
   ResetOrClearCompleted();
 
   void
+  StartIdleMaintenance()
+  {
+    AssertIsOnOwningThread();
+
+    for (auto& client : mClients) {
+      client->StartIdleMaintenance();
+    }
+  }
+
+  void
+  StopIdleMaintenance()
+  {
+    AssertIsOnOwningThread();
+
+    for (auto& client : mClients) {
+      client->StopIdleMaintenance();
+    }
+  }
+
+  void
   AssertCurrentThreadOwnsQuotaMutex()
   {
     mQuotaMutex.AssertCurrentThreadOwns();
   }
 
   nsIThread*
   IOThread()
   {
@@ -367,17 +379,20 @@ public:
                        bool* aIsApp);
 
 private:
   QuotaManager();
 
   virtual ~QuotaManager();
 
   nsresult
-  Init();
+  Init(const nsAString& aBaseDirPath);
+
+  void
+  Shutdown();
 
   already_AddRefed<DirectoryLockImpl>
   CreateDirectoryLock(Nullable<PersistenceType> aPersistenceType,
                       const nsACString& aGroup,
                       const OriginScope& aOriginScope,
                       Nullable<bool> aIsApp,
                       Nullable<Client::Type> aClientType,
                       bool aExclusive,
@@ -424,19 +439,16 @@ private:
   nsresult
   InitializeOrigin(PersistenceType aPersistenceType,
                    const nsACString& aGroup,
                    const nsACString& aOrigin,
                    bool aIsApp,
                    int64_t aAccessTime,
                    nsIFile* aDirectory);
 
-  nsresult
-  ClearStoragesForApp(uint32_t aAppId, bool aBrowserOnly);
-
   void
   CheckTemporaryStorageLimits();
 
   void
   DeleteFilesForOrigin(PersistenceType aPersistenceType,
                        const nsACString& aOrigin);
 
   void
@@ -456,16 +468,19 @@ private:
   GetDirectoryLockTable(PersistenceType aPersistenceType);
 
   static void
   GetOriginPatternString(uint32_t aAppId,
                          MozBrowserPatternFlag aBrowserFlag,
                          const nsACString& aOrigin,
                          nsAutoCString& _retval);
 
+  static void
+  ShutdownTimerCallback(nsITimer* aTimer, void* aClosure);
+
   mozilla::Mutex mQuotaMutex;
 
   nsClassHashtable<nsCStringHashKey, GroupInfoPair> mGroupInfoPairs;
 
   // Maintains a list of directory locks that are queued.
   nsTArray<RefPtr<DirectoryLockImpl>> mPendingDirectoryLocks;
 
   // Maintains a list of directory locks that are acquired or queued.
new file mode 100644
--- /dev/null
+++ b/dom/quota/QuotaManagerService.cpp
@@ -0,0 +1,823 @@
+/* -*- 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 "QuotaManagerService.h"
+
+#include "ActorsChild.h"
+#include "mozIApplicationClearPrivateDataParams.h"
+#include "mozilla/BasePrincipal.h"
+#include "mozilla/ClearOnShutdown.h"
+#include "mozilla/Hal.h"
+#include "mozilla/Preferences.h"
+#include "mozilla/unused.h"
+#include "mozilla/ipc/BackgroundChild.h"
+#include "mozilla/ipc/BackgroundParent.h"
+#include "mozilla/ipc/BackgroundUtils.h"
+#include "mozilla/ipc/PBackgroundChild.h"
+#include "nsIIdleService.h"
+#include "nsIIPCBackgroundChildCreateCallback.h"
+#include "nsIObserverService.h"
+#include "nsIScriptSecurityManager.h"
+#include "nsXULAppAPI.h"
+#include "QuotaManager.h"
+#include "QuotaRequests.h"
+
+#define PROFILE_BEFORE_CHANGE_OBSERVER_ID "profile-before-change"
+
+namespace mozilla {
+namespace dom {
+namespace quota {
+
+using namespace mozilla::ipc;
+
+namespace {
+
+// Preference that is used to enable testing features.
+const char kTestingPref[] = "dom.quotaManager.testing";
+
+const char kIdleServiceContractId[] = "@mozilla.org/widget/idleservice;1";
+
+// The number of seconds we will wait after receiving the idle-daily
+// notification before beginning maintenance.
+const uint32_t kIdleObserverTimeSec = 1;
+
+mozilla::StaticRefPtr<QuotaManagerService> gQuotaManagerService;
+
+mozilla::Atomic<bool> gInitialized(false);
+mozilla::Atomic<bool> gClosed(false);
+mozilla::Atomic<bool> gTestingMode(false);
+
+void
+TestingPrefChangedCallback(const char* aPrefName,
+                           void* aClosure)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(!strcmp(aPrefName, kTestingPref));
+  MOZ_ASSERT(!aClosure);
+
+  gTestingMode = Preferences::GetBool(aPrefName);
+}
+
+class AbortOperationsRunnable final
+  : public nsRunnable
+{
+  ContentParentId mContentParentId;
+
+public:
+  explicit AbortOperationsRunnable(ContentParentId aContentParentId)
+    : mContentParentId(aContentParentId)
+  { }
+
+private:
+  NS_DECL_NSIRUNNABLE
+};
+
+} // namespace
+
+class QuotaManagerService::BackgroundCreateCallback final
+  : public nsIIPCBackgroundChildCreateCallback
+{
+  RefPtr<QuotaManagerService> mService;
+
+public:
+  explicit
+  BackgroundCreateCallback(QuotaManagerService* aService)
+    : mService(aService)
+  {
+    MOZ_ASSERT(aService);
+  }
+
+  NS_DECL_ISUPPORTS
+
+private:
+  ~BackgroundCreateCallback()
+  { }
+
+  NS_DECL_NSIIPCBACKGROUNDCHILDCREATECALLBACK
+};
+
+class QuotaManagerService::PendingRequestInfo
+{
+protected:
+  RefPtr<RequestBase> mRequest;
+
+public:
+  explicit PendingRequestInfo(RequestBase* aRequest)
+    : mRequest(aRequest)
+  { }
+
+  virtual ~PendingRequestInfo()
+  { }
+
+  RequestBase*
+  GetRequest() const
+  {
+    return mRequest;
+  }
+
+  virtual nsresult
+  InitiateRequest(QuotaChild* aActor) = 0;
+};
+
+class QuotaManagerService::UsageRequestInfo
+  : public PendingRequestInfo
+{
+  UsageRequestParams mParams;
+
+public:
+  UsageRequestInfo(UsageRequest* aRequest,
+                   const UsageRequestParams& aParams)
+    : PendingRequestInfo(aRequest)
+    , mParams(aParams)
+  {
+    MOZ_ASSERT(aRequest);
+    MOZ_ASSERT(aParams.type() != UsageRequestParams::T__None);
+  }
+
+  virtual nsresult
+  InitiateRequest(QuotaChild* aActor) override;
+};
+
+class QuotaManagerService::RequestInfo
+  : public PendingRequestInfo
+{
+  RequestParams mParams;
+
+public:
+  RequestInfo(Request* aRequest,
+              const RequestParams& aParams)
+    : PendingRequestInfo(aRequest)
+    , mParams(aParams)
+  {
+    MOZ_ASSERT(aRequest);
+    MOZ_ASSERT(aParams.type() != RequestParams::T__None);
+  }
+
+  virtual nsresult
+  InitiateRequest(QuotaChild* aActor) override;
+};
+
+class QuotaManagerService::IdleMaintenanceInfo
+  : public PendingRequestInfo
+{
+  const bool mStart;
+
+public:
+  explicit IdleMaintenanceInfo(bool aStart)
+    : PendingRequestInfo(nullptr)
+    , mStart(aStart)
+  { }
+
+  virtual nsresult
+  InitiateRequest(QuotaChild* aActor) override;
+};
+
+QuotaManagerService::QuotaManagerService()
+  : mBackgroundActor(nullptr)
+  , mBackgroundActorFailed(false)
+  , mIdleObserverRegistered(false)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+}
+
+QuotaManagerService::~QuotaManagerService()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(!mIdleObserverRegistered);
+}
+
+// static
+QuotaManagerService*
+QuotaManagerService::GetOrCreate()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  if (gClosed) {
+    MOZ_ASSERT(false, "Calling GetOrCreate() after shutdown!");
+    return nullptr;
+  }
+
+  if (!gQuotaManagerService) {
+    RefPtr<QuotaManagerService> instance(new QuotaManagerService());
+
+    nsresult rv = instance->Init();
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return nullptr;
+    }
+
+    if (gInitialized.exchange(true)) {
+      MOZ_ASSERT(false, "Initialized more than once?!");
+    }
+
+    gQuotaManagerService = instance;
+
+    ClearOnShutdown(&gQuotaManagerService);
+  }
+
+  return gQuotaManagerService;
+}
+
+// static
+QuotaManagerService*
+QuotaManagerService::Get()
+{
+  // Does not return an owning reference.
+  return gQuotaManagerService;
+}
+
+// static
+QuotaManagerService*
+QuotaManagerService::FactoryCreate()
+{
+  // Returns a raw pointer that carries an owning reference! Lame, but the
+  // singleton factory macros force this.
+  QuotaManagerService* quotaManagerService = GetOrCreate();
+  NS_IF_ADDREF(quotaManagerService);
+  return quotaManagerService;
+}
+
+void
+QuotaManagerService::ClearBackgroundActor()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  mBackgroundActor = nullptr;
+}
+
+void
+QuotaManagerService::NoteLiveManager(QuotaManager* aManager)
+{
+  MOZ_ASSERT(XRE_IsParentProcess());
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(aManager);
+
+  mBackgroundThread = aManager->OwningThread();
+}
+
+void
+QuotaManagerService::NoteFinishedManager()
+{
+  MOZ_ASSERT(XRE_IsParentProcess());
+  MOZ_ASSERT(NS_IsMainThread());
+
+  mBackgroundThread = nullptr;
+}
+
+void
+QuotaManagerService::AbortOperationsForProcess(ContentParentId aContentParentId)
+{
+  MOZ_ASSERT(XRE_IsParentProcess());
+  MOZ_ASSERT(NS_IsMainThread());
+
+  if (!mBackgroundThread) {
+    return;
+  }
+
+  RefPtr<AbortOperationsRunnable> runnable =
+    new AbortOperationsRunnable(aContentParentId);
+
+  MOZ_ALWAYS_TRUE(NS_SUCCEEDED(
+    mBackgroundThread->Dispatch(runnable, NS_DISPATCH_NORMAL)));
+}
+
+nsresult
+QuotaManagerService::Init()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  if (XRE_IsParentProcess()) {
+    nsCOMPtr<nsIObserverService> observerService =
+      mozilla::services::GetObserverService();
+    if (NS_WARN_IF(!observerService)) {
+      return NS_ERROR_FAILURE;
+    }
+
+    nsresult rv = observerService->AddObserver(this,
+                                               PROFILE_BEFORE_CHANGE_OBSERVER_ID,
+                                               false);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+  }
+
+  Preferences::RegisterCallbackAndCall(TestingPrefChangedCallback,
+                                       kTestingPref);
+
+  return NS_OK;
+}
+
+void
+QuotaManagerService::Destroy()
+{
+  // Setting the closed flag prevents the service from being recreated.
+  // Don't set it though if there's no real instance created.
+  if (gInitialized && gClosed.exchange(true)) {
+    MOZ_ASSERT(false, "Shutdown more than once?!");
+  }
+
+  Preferences::UnregisterCallback(TestingPrefChangedCallback, kTestingPref);
+
+  delete this;
+}
+
+nsresult
+QuotaManagerService::InitiateRequest(nsAutoPtr<PendingRequestInfo>& aInfo)
+{
+  // Nothing can be done here if we have previously failed to create a
+  // background actor.
+  if (mBackgroundActorFailed) {
+    return NS_ERROR_FAILURE;
+  }
+
+  if (!mBackgroundActor && mPendingRequests.IsEmpty()) {
+    // We need to start the sequence to create a background actor for this
+    // thread.
+
+    RefPtr<BackgroundCreateCallback> cb = new BackgroundCreateCallback(this);
+    if (NS_WARN_IF(!BackgroundChild::GetOrCreateForCurrentThread(cb))) {
+      return NS_ERROR_FAILURE;
+    }
+  }
+
+  // If we already have a background actor then we can start this request now.
+  if (mBackgroundActor) {
+    nsresult rv = aInfo->InitiateRequest(mBackgroundActor);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+  } else {
+    mPendingRequests.AppendElement(aInfo.forget());
+  }
+
+  return NS_OK;
+}
+
+nsresult
+QuotaManagerService::BackgroundActorCreated(PBackgroundChild* aBackgroundActor)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(aBackgroundActor);
+  MOZ_ASSERT(!mBackgroundActor);
+  MOZ_ASSERT(!mBackgroundActorFailed);
+
+  {
+    QuotaChild* actor = new QuotaChild(this);
+
+    mBackgroundActor =
+      static_cast<QuotaChild*>(aBackgroundActor->SendPQuotaConstructor(actor));
+  }
+
+  if (NS_WARN_IF(!mBackgroundActor)) {
+    BackgroundActorFailed();
+    return NS_ERROR_FAILURE;
+  }
+
+  nsresult rv = NS_OK;
+
+  for (uint32_t index = 0, count = mPendingRequests.Length();
+       index < count;
+       index++) {
+    nsAutoPtr<PendingRequestInfo> info(mPendingRequests[index].forget());
+
+    nsresult rv2 = info->InitiateRequest(mBackgroundActor);
+
+    // Warn for every failure, but just return the first failure if there are
+    // multiple failures.
+    if (NS_WARN_IF(NS_FAILED(rv2)) && NS_SUCCEEDED(rv)) {
+      rv = rv2;
+    }
+  }
+
+  mPendingRequests.Clear();
+
+  return rv;
+}
+
+void
+QuotaManagerService::BackgroundActorFailed()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(!mPendingRequests.IsEmpty());
+  MOZ_ASSERT(!mBackgroundActor);
+  MOZ_ASSERT(!mBackgroundActorFailed);
+
+  mBackgroundActorFailed = true;
+
+  for (uint32_t index = 0, count = mPendingRequests.Length();
+       index < count;
+       index++) {
+    nsAutoPtr<PendingRequestInfo> info(mPendingRequests[index].forget());
+
+    RequestBase* request = info->GetRequest();
+    if (request) {
+      request->SetError(NS_ERROR_FAILURE);
+    }
+  }
+
+  mPendingRequests.Clear();
+}
+
+void
+QuotaManagerService::PerformIdleMaintenance()
+{
+  using namespace mozilla::hal;
+
+  MOZ_ASSERT(XRE_IsParentProcess());
+  MOZ_ASSERT(NS_IsMainThread());
+
+  // If we're running on battery power then skip all idle maintenance since we
+  // would otherwise be doing lots of disk I/O.
+  BatteryInformation batteryInfo;
+
+#ifdef MOZ_WIDGET_ANDROID
+  // Android XPCShell doesn't load the AndroidBridge that is needed to make
+  // GetCurrentBatteryInformation work...
+  if (!QuotaManager::kRunningXPCShellTests)
+#endif
+  {
+    GetCurrentBatteryInformation(&batteryInfo);
+  }
+
+  // If we're running XPCShell because we always want to be able to test this
+  // code so pretend that we're always charging.
+  if (QuotaManager::kRunningXPCShellTests) {
+    batteryInfo.level() = 100;
+    batteryInfo.charging() = true;
+  }
+
+  if (NS_WARN_IF(!batteryInfo.charging())) {
+    return;
+  }
+
+  if (QuotaManager::kRunningXPCShellTests) {
+    // We don't want user activity to impact this code if we're running tests.
+    Unused << Observe(nullptr, OBSERVER_TOPIC_IDLE, nullptr);
+  } else if (!mIdleObserverRegistered) {
+    nsCOMPtr<nsIIdleService> idleService =
+      do_GetService(kIdleServiceContractId);
+    MOZ_ASSERT(idleService);
+
+    MOZ_ALWAYS_TRUE(NS_SUCCEEDED(
+      idleService->AddIdleObserver(this, kIdleObserverTimeSec)));
+
+    mIdleObserverRegistered = true;
+  }
+}
+
+void
+QuotaManagerService::RemoveIdleObserver()
+{
+  MOZ_ASSERT(XRE_IsParentProcess());
+  MOZ_ASSERT(NS_IsMainThread());
+
+  if (mIdleObserverRegistered) {
+    nsCOMPtr<nsIIdleService> idleService =
+      do_GetService(kIdleServiceContractId);
+    MOZ_ASSERT(idleService);
+
+    MOZ_ALWAYS_TRUE(NS_SUCCEEDED(
+      idleService->RemoveIdleObserver(this, kIdleObserverTimeSec)));
+
+    mIdleObserverRegistered = false;
+  }
+}
+
+NS_IMPL_ADDREF(QuotaManagerService)
+NS_IMPL_RELEASE_WITH_DESTROY(QuotaManagerService, Destroy())
+NS_IMPL_QUERY_INTERFACE(QuotaManagerService,
+                        nsIQuotaManagerService,
+                        nsIObserver)
+
+NS_IMETHODIMP
+QuotaManagerService::GetUsageForPrincipal(nsIPrincipal* aPrincipal,
+                                          nsIQuotaUsageCallback* aCallback,
+                                          nsIQuotaUsageRequest** _retval)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(aPrincipal);
+  MOZ_ASSERT(aCallback);
+  MOZ_ASSERT(nsContentUtils::IsCallerChrome());
+
+  RefPtr<UsageRequest> request = new UsageRequest(aPrincipal, aCallback);
+
+  UsageParams params;
+
+  PrincipalInfo& principalInfo = params.principalInfo();
+
+  nsresult rv = PrincipalToPrincipalInfo(aPrincipal, &principalInfo);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  if (principalInfo.type() != PrincipalInfo::TContentPrincipalInfo &&
+      principalInfo.type() != PrincipalInfo::TSystemPrincipalInfo) {
+    return NS_ERROR_UNEXPECTED;
+  }
+
+  nsAutoPtr<PendingRequestInfo> info(new UsageRequestInfo(request, params));
+
+  rv = InitiateRequest(info);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  request.forget(_retval);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+QuotaManagerService::Clear(nsIQuotaRequest** _retval)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(nsContentUtils::IsCallerChrome());
+
+  if (NS_WARN_IF(!gTestingMode)) {
+    return NS_ERROR_UNEXPECTED;
+  }
+
+  RefPtr<Request> request = new Request();
+
+  ClearAllParams params;
+
+  nsAutoPtr<PendingRequestInfo> info(new RequestInfo(request, params));
+
+  nsresult rv = InitiateRequest(info);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  request.forget(_retval);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+QuotaManagerService::ClearStoragesForPrincipal(nsIPrincipal* aPrincipal,
+                                               const nsACString& aPersistenceType,
+                                               nsIQuotaRequest** _retval)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(aPrincipal);
+  MOZ_ASSERT(nsContentUtils::IsCallerChrome());
+
+  RefPtr<Request> request = new Request(aPrincipal);
+
+  ClearOriginParams params;
+
+  PrincipalInfo& principalInfo = params.principalInfo();
+
+  nsresult rv = PrincipalToPrincipalInfo(aPrincipal, &principalInfo);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  if (principalInfo.type() != PrincipalInfo::TContentPrincipalInfo &&
+      principalInfo.type() != PrincipalInfo::TSystemPrincipalInfo) {
+    return NS_ERROR_UNEXPECTED;
+  }
+
+  Nullable<PersistenceType> persistenceType;
+  rv = NullablePersistenceTypeFromText(aPersistenceType, &persistenceType);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return NS_ERROR_INVALID_ARG;
+  }
+
+  if (persistenceType.IsNull()) {
+    params.persistenceTypeIsExplicit() = false;
+  } else {
+    params.persistenceType() = persistenceType.Value();
+    params.persistenceTypeIsExplicit() = true;
+  }
+
+  nsAutoPtr<PendingRequestInfo> info(new RequestInfo(request, params));
+
+  rv = InitiateRequest(info);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  request.forget(_retval);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+QuotaManagerService::Reset(nsIQuotaRequest** _retval)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(nsContentUtils::IsCallerChrome());
+
+  if (NS_WARN_IF(!gTestingMode)) {
+    return NS_ERROR_UNEXPECTED;
+  }
+
+  RefPtr<Request> request = new Request();
+
+  ResetAllParams params;
+
+  nsAutoPtr<PendingRequestInfo> info(new RequestInfo(request, params));
+
+  nsresult rv = InitiateRequest(info);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  request.forget(_retval);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+QuotaManagerService::Observe(nsISupports* aSubject,
+                             const char* aTopic,
+                             const char16_t* aData)
+{
+  MOZ_ASSERT(XRE_IsParentProcess());
+  MOZ_ASSERT(NS_IsMainThread());
+
+  if (!strcmp(aTopic, PROFILE_BEFORE_CHANGE_OBSERVER_ID)) {
+    RemoveIdleObserver();
+    return NS_OK;
+  }
+
+  if (!strcmp(aTopic, TOPIC_WEB_APP_CLEAR_DATA)) {
+    nsCOMPtr<mozIApplicationClearPrivateDataParams> params =
+      do_QueryInterface(aSubject);
+    if (NS_WARN_IF(!params)) {
+      return NS_ERROR_UNEXPECTED;
+    }
+
+    uint32_t appId;
+    nsresult rv = params->GetAppId(&appId);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+
+    bool browserOnly;
+    rv = params->GetBrowserOnly(&browserOnly);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+
+    RefPtr<Request> request = new Request();
+
+    ClearAppParams requestParams;
+    requestParams.appId() = appId;
+    requestParams.browserOnly() = browserOnly;
+
+    nsAutoPtr<PendingRequestInfo> info(new RequestInfo(request, requestParams));
+
+    rv = InitiateRequest(info);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+
+    return NS_OK;
+  }
+
+  if (!strcmp(aTopic, OBSERVER_TOPIC_IDLE_DAILY)) {
+    PerformIdleMaintenance();
+    return NS_OK;
+  }
+
+  if (!strcmp(aTopic, OBSERVER_TOPIC_IDLE)) {
+    nsAutoPtr<PendingRequestInfo> info(
+      new IdleMaintenanceInfo(/* aStart */ true));
+
+    nsresult rv = InitiateRequest(info);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+
+    return NS_OK;
+  }
+
+  if (!strcmp(aTopic, OBSERVER_TOPIC_ACTIVE)) {
+    RemoveIdleObserver();
+
+    nsAutoPtr<PendingRequestInfo> info(
+      new IdleMaintenanceInfo(/* aStart */ false));
+
+    nsresult rv = InitiateRequest(info);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+
+    return NS_OK;
+  }
+
+  MOZ_ASSERT_UNREACHABLE("Should never get here!");
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+AbortOperationsRunnable::Run()
+{
+  AssertIsOnBackgroundThread();
+
+  if (QuotaManager::IsShuttingDown()) {
+    return NS_OK;
+  }
+
+  QuotaManager* quotaManager = QuotaManager::Get();
+  if (!quotaManager) {
+    return NS_OK;
+  }
+
+  quotaManager->AbortOperationsForProcess(mContentParentId);
+
+  return NS_OK;
+}
+
+NS_IMPL_ISUPPORTS(QuotaManagerService::BackgroundCreateCallback,
+                  nsIIPCBackgroundChildCreateCallback)
+
+void
+QuotaManagerService::
+BackgroundCreateCallback::ActorCreated(PBackgroundChild* aActor)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(aActor);
+  MOZ_ASSERT(mService);
+
+  RefPtr<QuotaManagerService> service;
+  mService.swap(service);
+
+  service->BackgroundActorCreated(aActor);
+}
+
+void
+QuotaManagerService::
+BackgroundCreateCallback::ActorFailed()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(mService);
+
+  RefPtr<QuotaManagerService> service;
+  mService.swap(service);
+
+  service->BackgroundActorFailed();
+}
+
+nsresult
+QuotaManagerService::
+UsageRequestInfo::InitiateRequest(QuotaChild* aActor)
+{
+  MOZ_ASSERT(aActor);
+
+  auto request = static_cast<UsageRequest*>(mRequest.get());
+
+  auto actor = new QuotaUsageRequestChild(request);
+
+  if (!aActor->SendPQuotaUsageRequestConstructor(actor, mParams)) {
+    request->SetError(NS_ERROR_FAILURE);
+    return NS_ERROR_FAILURE;
+  }
+
+  request->SetBackgroundActor(actor);
+
+  return NS_OK;
+}
+
+nsresult
+QuotaManagerService::
+RequestInfo::InitiateRequest(QuotaChild* aActor)
+{
+  MOZ_ASSERT(aActor);
+
+  auto request = static_cast<Request*>(mRequest.get());
+
+  auto actor = new QuotaRequestChild(request);
+
+  if (!aActor->SendPQuotaRequestConstructor(actor, mParams)) {
+    request->SetError(NS_ERROR_FAILURE);
+    return NS_ERROR_FAILURE;
+  }
+
+  return NS_OK;
+}
+
+nsresult
+QuotaManagerService::
+IdleMaintenanceInfo::InitiateRequest(QuotaChild* aActor)
+{
+  MOZ_ASSERT(aActor);
+
+  bool result;
+
+  if (mStart) {
+    result = aActor->SendStartIdleMaintenance();
+  } else {
+    result = aActor->SendStopIdleMaintenance();
+  }
+
+  if (!result) {
+    return NS_ERROR_FAILURE;
+  }
+
+  return NS_OK;
+}
+
+} // namespace quota
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/quota/QuotaManagerService.h
@@ -0,0 +1,112 @@
+/* -*- 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_quota_QuotaManagerService_h
+#define mozilla_dom_quota_QuotaManagerService_h
+
+#include "mozilla/dom/ipc/IdType.h"
+#include "nsIObserver.h"
+#include "nsIQuotaManagerService.h"
+
+#define QUOTAMANAGER_SERVICE_CONTRACTID \
+  "@mozilla.org/dom/quota-manager-service;1"
+
+namespace mozilla {
+namespace ipc {
+
+class PBackgroundChild;
+
+} // namespace ipc
+
+namespace dom {
+namespace quota {
+
+class QuotaChild;
+class QuotaManager;
+
+class QuotaManagerService final
+  : public nsIQuotaManagerService
+  , public nsIObserver
+{
+  typedef mozilla::ipc::PBackgroundChild PBackgroundChild;
+
+  class BackgroundCreateCallback;
+  class PendingRequestInfo;
+  class UsageRequestInfo;
+  class RequestInfo;
+  class IdleMaintenanceInfo;
+
+  nsCOMPtr<nsIEventTarget> mBackgroundThread;
+
+  nsTArray<nsAutoPtr<PendingRequestInfo>> mPendingRequests;
+
+  QuotaChild* mBackgroundActor;
+
+  bool mBackgroundActorFailed;
+  bool mIdleObserverRegistered;
+
+public:
+  // Returns a non-owning reference.
+  static QuotaManagerService*
+  GetOrCreate();
+
+  // Returns a non-owning reference.
+  static QuotaManagerService*
+  Get();
+
+  // Returns an owning reference! No one should call this but the factory.
+  static QuotaManagerService*
+  FactoryCreate();
+
+  void
+  ClearBackgroundActor();
+
+  void
+  NoteLiveManager(QuotaManager* aManager);
+
+  void
+  NoteFinishedManager();
+
+  // Called when a process is being shot down. Aborts any running operations
+  // for the given process.
+  void
+  AbortOperationsForProcess(ContentParentId aContentParentId);
+
+private:
+  QuotaManagerService();
+  ~QuotaManagerService();
+
+  nsresult
+  Init();
+
+  void
+  Destroy();
+
+  nsresult
+  InitiateRequest(nsAutoPtr<PendingRequestInfo>& aInfo);
+
+  nsresult
+  BackgroundActorCreated(PBackgroundChild* aBackgroundActor);
+
+  void
+  BackgroundActorFailed();
+
+  void
+  PerformIdleMaintenance();
+
+  void
+  RemoveIdleObserver();
+
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIQUOTAMANAGERSERVICE
+  NS_DECL_NSIOBSERVER
+};
+
+} // namespace quota
+} // namespace dom
+} // namespace mozilla
+
+#endif /* mozilla_dom_quota_QuotaManagerService_h */
new file mode 100644
--- /dev/null
+++ b/dom/quota/QuotaRequests.cpp
@@ -0,0 +1,296 @@
+/* -*- 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 "QuotaRequests.h"
+
+#include "ActorsChild.h"
+#include "nsIQuotaCallbacks.h"
+
+namespace mozilla {
+namespace dom {
+namespace quota {
+
+RequestBase::RequestBase()
+  : mResultCode(NS_OK)
+  , mHaveResultOrErrorCode(false)
+{
+#ifdef DEBUG
+  mOwningThread = PR_GetCurrentThread();
+#endif
+  AssertIsOnOwningThread();
+}
+
+RequestBase::RequestBase(nsIPrincipal* aPrincipal)
+  : mPrincipal(aPrincipal)
+  , mResultCode(NS_OK)
+  , mHaveResultOrErrorCode(false)
+{
+#ifdef DEBUG
+  mOwningThread = PR_GetCurrentThread();
+#endif
+  AssertIsOnOwningThread();
+}
+
+#ifdef DEBUG
+
+void
+RequestBase::AssertIsOnOwningThread() const
+{
+  MOZ_ASSERT(mOwningThread);
+  MOZ_ASSERT(PR_GetCurrentThread() == mOwningThread);
+}
+
+#endif // DEBUG
+
+void
+RequestBase::SetError(nsresult aRv)
+{
+  AssertIsOnOwningThread();
+  MOZ_ASSERT(mResultCode == NS_OK);
+  MOZ_ASSERT(!mHaveResultOrErrorCode);
+
+  mResultCode = aRv;
+  mHaveResultOrErrorCode = true;
+
+  FireCallback();
+}
+
+NS_IMPL_CYCLE_COLLECTION_0(RequestBase)
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(RequestBase)
+  NS_INTERFACE_MAP_ENTRY(nsISupports)
+NS_INTERFACE_MAP_END
+
+NS_IMPL_CYCLE_COLLECTING_ADDREF(RequestBase)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(RequestBase)
+
+NS_IMETHODIMP
+RequestBase::GetPrincipal(nsIPrincipal** aPrincipal)
+{
+  AssertIsOnOwningThread();
+  MOZ_ASSERT(aPrincipal);
+
+  NS_IF_ADDREF(*aPrincipal = mPrincipal);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+RequestBase::GetResultCode(nsresult* aResultCode)
+{
+  AssertIsOnOwningThread();
+  MOZ_ASSERT(aResultCode);
+
+  if (!mHaveResultOrErrorCode) {
+    return NS_ERROR_FAILURE;
+  }
+
+  *aResultCode = mResultCode;
+  return NS_OK;
+}
+
+UsageRequest::UsageRequest(nsIPrincipal* aPrincipal,
+	                         nsIQuotaUsageCallback* aCallback)
+  : RequestBase(aPrincipal)
+  , mCallback(aCallback)
+  , mUsage(0)
+  , mFileUsage(0)
+  , mBackgroundActor(nullptr)
+  , mCanceled(false)
+{
+  AssertIsOnOwningThread();
+  MOZ_ASSERT(aPrincipal);
+  MOZ_ASSERT(aCallback);
+}
+
+UsageRequest::~UsageRequest()
+{
+  AssertIsOnOwningThread();
+}
+
+void
+UsageRequest::SetBackgroundActor(QuotaUsageRequestChild* aBackgroundActor)
+{
+  AssertIsOnOwningThread();
+  MOZ_ASSERT(aBackgroundActor);
+  MOZ_ASSERT(!mBackgroundActor);
+
+  mBackgroundActor = aBackgroundActor;
+
+  if (mCanceled) {
+    mBackgroundActor->SendCancel();
+  }
+}
+
+void
+UsageRequest::SetResult(uint64_t aUsage, uint64_t aFileUsage)
+{
+  AssertIsOnOwningThread();
+  MOZ_ASSERT(!mHaveResultOrErrorCode);
+
+  mUsage = aUsage;
+  mFileUsage = aFileUsage;
+  mHaveResultOrErrorCode = true;
+
+  FireCallback();
+}
+
+NS_IMPL_CYCLE_COLLECTION_INHERITED(UsageRequest, RequestBase, mCallback)
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(UsageRequest)
+  NS_INTERFACE_MAP_ENTRY(nsIQuotaUsageRequest)
+NS_INTERFACE_MAP_END_INHERITING(RequestBase)
+
+NS_IMPL_ADDREF_INHERITED(UsageRequest, RequestBase)
+NS_IMPL_RELEASE_INHERITED(UsageRequest, RequestBase)
+
+NS_IMETHODIMP
+UsageRequest::GetUsage(uint64_t* aUsage)
+{
+  AssertIsOnOwningThread();
+
+  if (!mHaveResultOrErrorCode) {
+    return NS_ERROR_FAILURE;
+  }
+
+  *aUsage = mUsage;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+UsageRequest::GetFileUsage(uint64_t* aFileUsage)
+{
+  AssertIsOnOwningThread();
+  MOZ_ASSERT(aFileUsage);
+
+  if (!mHaveResultOrErrorCode) {
+    return NS_ERROR_FAILURE;
+  }
+
+  *aFileUsage = mFileUsage;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+UsageRequest::GetCallback(nsIQuotaUsageCallback** aCallback)
+{
+  AssertIsOnOwningThread();
+  MOZ_ASSERT(aCallback);
+
+  NS_IF_ADDREF(*aCallback = mCallback);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+UsageRequest::SetCallback(nsIQuotaUsageCallback* aCallback)
+{
+  AssertIsOnOwningThread();
+
+  mCallback = aCallback;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+UsageRequest::Cancel()
+{
+  AssertIsOnOwningThread();
+
+  if (mCanceled) {
+    NS_WARNING("Canceled more than once?!");
+    return NS_ERROR_UNEXPECTED;
+  }
+
+  if (mBackgroundActor) {
+    mBackgroundActor->SendCancel();
+  }
+
+  mCanceled = true;
+
+  return NS_OK;
+}
+
+void
+UsageRequest::FireCallback()
+{
+  AssertIsOnOwningThread();
+  MOZ_ASSERT(mCallback);
+
+  mCallback->OnUsageResult(this);
+
+  // Clean up.
+  mCallback = nullptr;
+}
+
+Request::Request()
+{
+  AssertIsOnOwningThread();
+}
+
+Request::Request(nsIPrincipal* aPrincipal)
+  : RequestBase(aPrincipal)
+{
+  AssertIsOnOwningThread();
+}
+
+Request::~Request()
+{
+  AssertIsOnOwningThread();
+}
+
+void
+Request::SetResult()
+{
+  AssertIsOnOwningThread();
+  MOZ_ASSERT(!mHaveResultOrErrorCode);
+
+  mHaveResultOrErrorCode = true;
+
+  FireCallback();
+}
+
+NS_IMPL_CYCLE_COLLECTION_INHERITED(Request, RequestBase, mCallback)
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(Request)
+  NS_INTERFACE_MAP_ENTRY(nsIQuotaRequest)
+NS_INTERFACE_MAP_END_INHERITING(RequestBase)
+
+NS_IMPL_ADDREF_INHERITED(mozilla::dom::quota::Request, RequestBase)
+NS_IMPL_RELEASE_INHERITED(mozilla::dom::quota::Request, RequestBase)
+
+NS_IMETHODIMP
+Request::GetCallback(nsIQuotaCallback** aCallback)
+{
+  AssertIsOnOwningThread();
+  MOZ_ASSERT(aCallback);
+
+  NS_IF_ADDREF(*aCallback = mCallback);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+Request::SetCallback(nsIQuotaCallback* aCallback)
+{
+  AssertIsOnOwningThread();
+
+  mCallback = aCallback;
+  return NS_OK;
+}
+
+void
+Request::FireCallback()
+{
+  AssertIsOnOwningThread();
+
+  if (mCallback) {
+    mCallback->OnComplete(this);
+
+    // Clean up.
+    mCallback = nullptr;
+  }
+}
+
+} // namespace quota
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/quota/QuotaRequests.h
@@ -0,0 +1,141 @@
+/* -*- 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_quota_UsageRequest_h
+#define mozilla_dom_quota_UsageRequest_h
+
+#include "nsCOMPtr.h"
+#include "nsCycleCollectionParticipant.h"
+#include "nsIQuotaRequests.h"
+
+class nsIPrincipal;
+class nsIQuotaCallback;
+class nsIQuotaUsageCallback;
+struct PRThread;
+
+namespace mozilla {
+namespace dom {
+namespace quota {
+
+class QuotaUsageRequestChild;
+
+class RequestBase
+  : public nsIQuotaRequestBase
+{
+protected:
+#ifdef DEBUG
+  PRThread* mOwningThread;
+#endif
+
+  nsCOMPtr<nsIPrincipal> mPrincipal;
+
+  nsresult mResultCode;
+  bool mHaveResultOrErrorCode;
+
+public:
+  void
+  AssertIsOnOwningThread() const
+#ifdef DEBUG
+  ;
+#else
+  { }
+#endif
+
+  void
+  SetError(nsresult aRv);
+
+  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+  NS_DECL_NSIQUOTAREQUESTBASE
+  NS_DECL_CYCLE_COLLECTION_CLASS(RequestBase)
+
+protected:
+  RequestBase();
+
+  RequestBase(nsIPrincipal* aPrincipal);
+
+  virtual ~RequestBase()
+  {
+    AssertIsOnOwningThread();
+  }
+
+  virtual void
+  FireCallback() = 0;
+};
+
+class UsageRequest final
+  : public RequestBase
+  , public nsIQuotaUsageRequest
+{
+  nsCOMPtr<nsIQuotaUsageCallback> mCallback;
+
+  uint64_t mUsage;
+  uint64_t mFileUsage;
+
+  QuotaUsageRequestChild* mBackgroundActor;
+
+  bool mCanceled;
+
+public:
+  UsageRequest(nsIPrincipal* aPrincipal,
+               nsIQuotaUsageCallback* aCallback);
+
+  void
+  SetBackgroundActor(QuotaUsageRequestChild* aBackgroundActor);
+
+  void
+  ClearBackgroundActor()
+  {
+    AssertIsOnOwningThread();
+
+    mBackgroundActor = nullptr;
+  }
+
+  void
+  SetResult(uint64_t aUsage, uint64_t aFileUsage);
+
+  NS_DECL_ISUPPORTS_INHERITED
+  NS_FORWARD_NSIQUOTAREQUESTBASE(RequestBase::)
+  NS_DECL_NSIQUOTAUSAGEREQUEST
+  NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(UsageRequest, RequestBase)
+
+private:
+  ~UsageRequest();
+
+  virtual void
+  FireCallback() override;
+};
+
+class Request final
+  : public RequestBase
+  , public nsIQuotaRequest
+{
+  nsCOMPtr<nsIQuotaCallback> mCallback;
+
+public:
+  Request();
+
+  explicit Request(nsIPrincipal* aPrincipal);
+
+  void
+  SetResult();
+
+  NS_DECL_ISUPPORTS_INHERITED
+  NS_FORWARD_NSIQUOTAREQUESTBASE(RequestBase::)
+  NS_DECL_NSIQUOTAREQUEST
+  NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(Request, RequestBase)
+
+private:
+  ~Request();
+
+  virtual void
+  FireCallback() override;
+};
+
+} // namespace quota
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_quota_UsageRequest_h
--- a/dom/quota/moz.build
+++ b/dom/quota/moz.build
@@ -1,36 +1,47 @@
 # -*- Mode: python; c-basic-offset: 4; 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 += [
-    'nsIQuotaManager.idl',
-    'nsIQuotaRequest.idl',
-    'nsIUsageCallback.idl',
+    'nsIQuotaCallbacks.idl',
+    'nsIQuotaManagerService.idl',
+    'nsIQuotaRequests.idl',
 ]
 
 XPIDL_MODULE = 'dom_quota'
 
 EXPORTS.mozilla.dom.quota += [
+    'ActorsParent.h',
     'Client.h',
     'FileStreams.h',
     'PersistenceType.h',
     'QuotaCommon.h',
     'QuotaManager.h',
+    'QuotaManagerService.h',
     'QuotaObject.h',
     'SerializationHelpers.h',
     'UsageInfo.h',
 ]
 
 UNIFIED_SOURCES += [
+    'ActorsChild.cpp',
+    'ActorsParent.cpp',
     'FileStreams.cpp',
-    'QuotaManager.cpp',
+    'QuotaManagerService.cpp',
+    'QuotaRequests.cpp',
+]
+
+IPDL_SOURCES += [
+    'PQuota.ipdl',
+    'PQuotaRequest.ipdl',
+    'PQuotaUsageRequest.ipdl',
 ]
 
 include('/ipc/chromium/chromium-config.mozbuild')
 
 FINAL_LIBRARY = 'xul'
 LOCAL_INCLUDES += [
     '/caps',
 ]
rename from dom/quota/nsIUsageCallback.idl
rename to dom/quota/nsIQuotaCallbacks.idl
--- a/dom/quota/nsIUsageCallback.idl
+++ b/dom/quota/nsIQuotaCallbacks.idl
@@ -1,17 +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 nsIPrincipal;
+interface nsIQuotaRequest;
+interface nsIQuotaUsageRequest;
 
-[scriptable, function, uuid(54b9f44f-533f-41ee-8fa8-86cc978125f0)]
-interface nsIUsageCallback : nsISupports
+[scriptable, function, uuid(c8a21a2a-17b9-4b63-ad95-e0fbcff5de18)]
+interface nsIQuotaUsageCallback : nsISupports
 {
-  void onUsageResult(in nsIPrincipal aPrincipal,
-                     in unsigned long long aUsage,
-                     in unsigned long long aFileUsage);
+  void onUsageResult(in nsIQuotaUsageRequest aRequest);
 };
+
+[scriptable, function, uuid(a08a28e2-5a74-4c84-8070-ed45a07eb013)]
+interface nsIQuotaCallback : nsISupports
+{
+  void onComplete(in nsIQuotaRequest aRequest);
+};
rename from dom/quota/nsIQuotaManager.idl
rename to dom/quota/nsIQuotaManagerService.idl
--- a/dom/quota/nsIQuotaManager.idl
+++ b/dom/quota/nsIQuotaManagerService.idl
@@ -1,62 +1,63 @@
 /* -*- 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 nsIQuotaRequest;
-interface nsIPrincipal;
-interface nsIUsageCallback;
+interface nsIQuotaUsageCallback;
+interface nsIQuotaUsageRequest;
 
-[scriptable, builtinclass, uuid(101cf53c-e7f3-4723-9f43-a23a85c8eda0)]
-interface nsIQuotaManager : nsISupports
+[scriptable, builtinclass, uuid(1b3d0a38-8151-4cf9-89fa-4f92c2ef0e7e)]
+interface nsIQuotaManagerService : nsISupports
 {
   /**
    * Schedules an asynchronous callback that will return the total amount of
    * disk space being used by storages for the given origin.
    *
    * @param aPrincipal
    *        A principal for the origin whose usage is being queried.
    * @param aCallback
    *        The callback that will be called when the usage is available.
    */
-  nsIQuotaRequest
+  nsIQuotaUsageRequest
   getUsageForPrincipal(in nsIPrincipal aPrincipal,
-                       in nsIUsageCallback aCallback);
+                       in nsIQuotaUsageCallback aCallback);
 
   /**
    * Removes all storages. The files may not be deleted immediately depending
    * on prohibitive concurrent operations.
    * Be careful, this removes *all* the data that has ever been stored!
    *
    * If the dom.quotaManager.testing preference is not true the call will be
    * a no-op.
    */
-  void
+  nsIQuotaRequest
   clear();
 
   /**
    * Removes all storages stored for the given URI. The files may not be
    * deleted immediately depending on prohibitive concurrent operations.
    *
    * @param aPrincipal
    *        A principal for the origin whose storages are to be cleared.
    */
-  void
+  nsIQuotaRequest
   clearStoragesForPrincipal(in nsIPrincipal aPrincipal,
                             [optional] in ACString aPersistenceType);
 
   /**
    * Resets quota and storage management. This can be used to force
    * reinitialization of the temp storage, for example when the pref for
    * overriding the temp storage limit has changed.
    * Be carefull, this invalidates all live storages!
    *
    * If the dom.quotaManager.testing preference is not true the call will be
    * a no-op.
    */
-  void
+  nsIQuotaRequest
   reset();
 };
rename from dom/quota/nsIQuotaRequest.idl
rename to dom/quota/nsIQuotaRequests.idl
--- a/dom/quota/nsIQuotaRequest.idl
+++ b/dom/quota/nsIQuotaRequests.idl
@@ -1,14 +1,38 @@
 /* -*- 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, function, uuid(d96769ed-63ac-4070-ac5a-4b0e1728618a)]
-interface nsIQuotaRequest : nsISupports
+interface nsIPrincipal;
+interface nsIQuotaCallback;
+interface nsIQuotaUsageCallback;
+
+[scriptable, uuid(9af54222-0407-48fd-a4ab-9457c986fc49)]
+interface nsIQuotaRequestBase : nsISupports
 {
+  readonly attribute nsIPrincipal principal;
+
+  readonly attribute nsresult resultCode;
+};
+
+[scriptable, uuid(166e28e6-cf6d-4927-a6d7-b51bca9d3469)]
+interface nsIQuotaUsageRequest : nsIQuotaRequestBase
+{
+  readonly attribute unsigned long long usage;
+
+  readonly attribute unsigned long long fileUsage;
+
+  attribute nsIQuotaUsageCallback callback;
+
   void
   cancel();
 };
+
+[scriptable, uuid(22890e3e-ff25-4372-9684-d901060e2f6c)]
+interface nsIQuotaRequest : nsIQuotaRequestBase
+{
+  attribute nsIQuotaCallback callback;
+};
--- a/ipc/glue/BackgroundChildImpl.cpp
+++ b/ipc/glue/BackgroundChildImpl.cpp
@@ -13,16 +13,17 @@
 #endif
 #include "mozilla/media/MediaChild.h"
 #include "mozilla/Assertions.h"
 #include "mozilla/dom/PBlobChild.h"
 #include "mozilla/dom/asmjscache/AsmJSCache.h"
 #include "mozilla/dom/cache/ActorUtils.h"
 #include "mozilla/dom/indexedDB/PBackgroundIDBFactoryChild.h"
 #include "mozilla/dom/ipc/BlobChild.h"
+#include "mozilla/dom/quota/PQuotaChild.h"
 #include "mozilla/dom/MessagePortChild.h"
 #include "mozilla/dom/NuwaChild.h"
 #include "mozilla/ipc/PBackgroundTestChild.h"
 #include "mozilla/layout/VsyncChild.h"
 #include "mozilla/net/PUDPSocketChild.h"
 #include "mozilla/dom/network/UDPSocketChild.h"
 #include "nsID.h"
 #include "nsTraceRefcnt.h"
@@ -410,16 +411,31 @@ bool
 BackgroundChildImpl::DeallocPAsmJSCacheEntryChild(PAsmJSCacheEntryChild* aActor)
 {
   MOZ_ASSERT(aActor);
 
   dom::asmjscache::DeallocEntryChild(aActor);
   return true;
 }
 
+BackgroundChildImpl::PQuotaChild*
+BackgroundChildImpl::AllocPQuotaChild()
+{
+  MOZ_CRASH("PQuotaChild actor should be manually constructed!");
+}
+
+bool
+BackgroundChildImpl::DeallocPQuotaChild(PQuotaChild* aActor)
+{
+  MOZ_ASSERT(aActor);
+
+  delete aActor;
+  return true;
+}
+
 } // namespace ipc
 } // namespace mozilla
 
 bool
 TestChild::Recv__delete__(const nsCString& aTestArg)
 {
   MOZ_RELEASE_ASSERT(aTestArg == mTestArg,
                      "BackgroundTest message was corrupted!");
--- a/ipc/glue/BackgroundChildImpl.h
+++ b/ipc/glue/BackgroundChildImpl.h
@@ -140,16 +140,22 @@ protected:
 
   virtual PAsmJSCacheEntryChild*
   AllocPAsmJSCacheEntryChild(const dom::asmjscache::OpenMode& aOpenMode,
                              const dom::asmjscache::WriteParams& aWriteParams,
                              const PrincipalInfo& aPrincipalInfo) override;
 
   virtual bool
   DeallocPAsmJSCacheEntryChild(PAsmJSCacheEntryChild* aActor) override;
+
+  virtual PQuotaChild*
+  AllocPQuotaChild() override;
+
+  virtual bool
+  DeallocPQuotaChild(PQuotaChild* aActor) override;
 };
 
 class BackgroundChildImpl::ThreadLocal final
 {
   friend class nsAutoPtr<ThreadLocal>;
 
 public:
   nsAutoPtr<mozilla::dom::indexedDB::ThreadLocal> mIndexedDBThreadLocal;
--- a/ipc/glue/BackgroundParentImpl.cpp
+++ b/ipc/glue/BackgroundParentImpl.cpp
@@ -16,16 +16,17 @@
 #include "mozilla/dom/NuwaParent.h"
 #include "mozilla/dom/PBlobParent.h"
 #include "mozilla/dom/MessagePortParent.h"
 #include "mozilla/dom/ServiceWorkerRegistrar.h"
 #include "mozilla/dom/asmjscache/AsmJSCache.h"
 #include "mozilla/dom/cache/ActorUtils.h"
 #include "mozilla/dom/indexedDB/ActorsParent.h"
 #include "mozilla/dom/ipc/BlobParent.h"
+#include "mozilla/dom/quota/ActorsParent.h"
 #include "mozilla/ipc/BackgroundParent.h"
 #include "mozilla/ipc/BackgroundUtils.h"
 #include "mozilla/ipc/PBackgroundSharedTypes.h"
 #include "mozilla/ipc/PBackgroundTestParent.h"
 #include "mozilla/layout/VsyncParent.h"
 #include "mozilla/dom/network/UDPSocketParent.h"
 #include "nsIAppsService.h"
 #include "nsNetUtil.h"
@@ -673,16 +674,35 @@ BackgroundParentImpl::DeallocPAsmJSCache
 {
   AssertIsInMainProcess();
   AssertIsOnBackgroundThread();
 
   dom::asmjscache::DeallocEntryParent(aActor);
   return true;
 }
 
+BackgroundParentImpl::PQuotaParent*
+BackgroundParentImpl::AllocPQuotaParent()
+{
+  AssertIsInMainProcess();
+  AssertIsOnBackgroundThread();
+
+  return mozilla::dom::quota::AllocPQuotaParent();
+}
+
+bool
+BackgroundParentImpl::DeallocPQuotaParent(PQuotaParent* aActor)
+{
+  AssertIsInMainProcess();
+  AssertIsOnBackgroundThread();
+  MOZ_ASSERT(aActor);
+
+  return mozilla::dom::quota::DeallocPQuotaParent(aActor);
+}
+
 } // namespace ipc
 } // namespace mozilla
 
 void
 TestParent::ActorDestroy(ActorDestroyReason aWhy)
 {
   AssertIsInMainProcess();
   AssertIsOnBackgroundThread();
--- a/ipc/glue/BackgroundParentImpl.h
+++ b/ipc/glue/BackgroundParentImpl.h
@@ -160,14 +160,20 @@ protected:
 
   virtual PAsmJSCacheEntryParent*
   AllocPAsmJSCacheEntryParent(const dom::asmjscache::OpenMode& aOpenMode,
                               const dom::asmjscache::WriteParams& aWriteParams,
                               const PrincipalInfo& aPrincipalInfo) override;
 
   virtual bool
   DeallocPAsmJSCacheEntryParent(PAsmJSCacheEntryParent* aActor) override;
+
+  virtual PQuotaParent*
+  AllocPQuotaParent() override;
+
+  virtual bool
+  DeallocPQuotaParent(PQuotaParent* aActor) override;
 };
 
 } // namespace ipc
 } // namespace mozilla
 
 #endif // mozilla_ipc_backgroundparentimpl_h__
--- a/ipc/glue/PBackground.ipdl
+++ b/ipc/glue/PBackground.ipdl
@@ -9,16 +9,17 @@ include protocol PBlob;
 include protocol PBroadcastChannel;
 include protocol PCache;
 include protocol PCacheStorage;
 include protocol PCacheStreamControl;
 include protocol PFileDescriptorSet;
 include protocol PMessagePort;
 include protocol PCameras;
 include protocol PNuwa;
+include protocol PQuota;
 include protocol PServiceWorkerManager;
 include protocol PUDPSocket;
 include protocol PVsync;
 
 include DOMTypes;
 include PBackgroundSharedTypes;
 include PBackgroundIDBSharedTypes;
 
@@ -45,16 +46,17 @@ sync protocol PBackground
   manages PBroadcastChannel;
   manages PCache;
   manages PCacheStorage;
   manages PCacheStreamControl;
   manages PFileDescriptorSet;
   manages PMessagePort;
   manages PCameras;
   manages PNuwa;
+  manages PQuota;
   manages PServiceWorkerManager;
   manages PUDPSocket;
   manages PVsync;
 
 parent:
   // Only called at startup during mochitests to check the basic infrastructure.
   PBackgroundTest(nsCString testArg);
 
@@ -79,16 +81,18 @@ parent:
   PNuwa();
 
   MessagePortForceClose(nsID uuid, nsID destinationUuid, uint32_t sequenceId);
 
   PAsmJSCacheEntry(OpenMode openMode,
                    WriteParams write,
                    PrincipalInfo principalInfo);
 
+  PQuota();
+
 child:
   PCache();
   PCacheStreamControl();
 
 both:
   PBlob(BlobConstructorParams params);
 
   PFileDescriptorSet(FileDescriptor fd);
--- a/layout/build/nsLayoutCID.h
+++ b/layout/build/nsLayoutCID.h
@@ -70,16 +70,16 @@
 #define NS_FOCUSMANAGER_CID \
 { 0xcf7fd51f, 0xaba2, 0x44c1, { 0x9f, 0xf0, 0x11, 0xf7, 0x50, 0x8e, 0xfc, 0xd4 } }
 
 // {3160e271-138d-4cc7-9d63-6429f16957c7}
 #define DOMREQUEST_SERVICE_CID \
 { 0x3160e271, 0x138d, 0x4cc7, { 0x9d, 0x63, 0x64, 0x29, 0xf1, 0x69, 0x57, 0xc7 } }
 
 // {5a75c25a-5e7e-4d90-8f7c-07eb15cc0aa8}
-#define QUOTA_MANAGER_CID \
+#define QUOTAMANAGER_SERVICE_CID \
 { 0x5a75c25a, 0x5e7e, 0x4d90, { 0x8f, 0x7c, 0x07, 0xeb, 0x15, 0xcc, 0x0a, 0xa8 } }
 
 // {c74bde32-bcc7-4840-8430-c733351b212a}
 #define SERVICEWORKERMANAGER_CID \
 { 0xc74bde32, 0xbcc7, 0x4840, { 0x84, 0x30, 0xc7, 0x33, 0x35, 0x1b, 0x21, 0x2a } }
 
 #endif /* nsLayoutCID_h__ */
--- a/layout/build/nsLayoutModule.cpp
+++ b/layout/build/nsLayoutModule.cpp
@@ -82,17 +82,17 @@
 #include "DOMStorageManager.h"
 #include "nsJSON.h"
 #include "nsZipArchive.h"
 #include "mozIApplicationClearPrivateDataParams.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/dom/DOMException.h"
 #include "mozilla/dom/DOMRequest.h"
 #include "mozilla/dom/network/UDPSocketChild.h"
-#include "mozilla/dom/quota/QuotaManager.h"
+#include "mozilla/dom/quota/QuotaManagerService.h"
 #include "mozilla/dom/workers/ServiceWorkerManager.h"
 #include "mozilla/dom/workers/WorkerDebuggerManager.h"
 #include "mozilla/OSFileConstants.h"
 #include "mozilla/Services.h"
 
 #ifdef MOZ_WEBSPEECH_TEST_BACKEND
 #include "mozilla/dom/FakeSpeechRecognitionService.h"
 #endif
@@ -264,17 +264,17 @@ static void Shutdown();
 #include "mozilla/dom/PresentationSessionTransport.h"
 
 #include "mozilla/TextInputProcessor.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 using mozilla::dom::alarm::AlarmHalService;
 using mozilla::dom::power::PowerManagerService;
-using mozilla::dom::quota::QuotaManager;
+using mozilla::dom::quota::QuotaManagerService;
 using mozilla::dom::workers::ServiceWorkerManager;
 using mozilla::dom::workers::WorkerDebuggerManager;
 using mozilla::dom::UDPSocketChild;
 using mozilla::dom::time::TimeService;
 using mozilla::net::StreamingProtocolControllerService;
 using mozilla::gmp::GeckoMediaPluginService;
 
 // Transformiix
@@ -309,18 +309,18 @@ NS_GENERIC_FACTORY_CONSTRUCTOR(nsMediaSo
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsFontTableProtocolHandler)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsHostObjectURI)
 NS_GENERIC_FACTORY_CONSTRUCTOR(DOMParser)
 NS_GENERIC_FACTORY_CONSTRUCTOR(Exception)
 NS_GENERIC_FACTORY_CONSTRUCTOR(DOMSessionStorageManager)
 NS_GENERIC_FACTORY_CONSTRUCTOR(DOMLocalStorageManager)
 NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(DOMRequestService,
                                          DOMRequestService::FactoryCreate)
-NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(QuotaManager,
-                                         QuotaManager::FactoryCreate)
+NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(QuotaManagerService,
+                                         QuotaManagerService::FactoryCreate)
 NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(ServiceWorkerManager,
                                          ServiceWorkerManager::GetInstance)
 NS_GENERIC_FACTORY_CONSTRUCTOR(WorkerDebuggerManager)
 
 #ifdef MOZ_WIDGET_GONK
 NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(SystemWorkerManager,
                                          SystemWorkerManager::FactoryCreate)
 #endif
@@ -760,17 +760,17 @@ NS_DEFINE_NAMED_CID(NS_FONTTABLEPROTOCOL
 NS_DEFINE_NAMED_CID(NS_HOSTOBJECTURI_CID);
 NS_DEFINE_NAMED_CID(NS_XMLHTTPREQUEST_CID);
 NS_DEFINE_NAMED_CID(NS_DOMPARSER_CID);
 NS_DEFINE_NAMED_CID(NS_DOMSESSIONSTORAGEMANAGER_CID);
 NS_DEFINE_NAMED_CID(NS_DOMLOCALSTORAGEMANAGER_CID);
 NS_DEFINE_NAMED_CID(NS_DOMJSON_CID);
 NS_DEFINE_NAMED_CID(NS_TEXTEDITOR_CID);
 NS_DEFINE_NAMED_CID(DOMREQUEST_SERVICE_CID);
-NS_DEFINE_NAMED_CID(QUOTA_MANAGER_CID);
+NS_DEFINE_NAMED_CID(QUOTAMANAGER_SERVICE_CID);
 NS_DEFINE_NAMED_CID(SERVICEWORKERMANAGER_CID);
 NS_DEFINE_NAMED_CID(WORKERDEBUGGERMANAGER_CID);
 #ifdef MOZ_WIDGET_GONK
 NS_DEFINE_NAMED_CID(SYSTEMWORKERMANAGER_CID);
 #endif
 #ifdef MOZ_B2G_BT
 NS_DEFINE_NAMED_CID(BLUETOOTHSERVICE_CID);
 #endif
@@ -1069,17 +1069,17 @@ static const mozilla::Module::CIDEntry k
   { &kNS_XMLHTTPREQUEST_CID, false, nullptr, nsXMLHttpRequestConstructor },
   { &kNS_DOMPARSER_CID, false, nullptr, DOMParserConstructor },
   { &kNS_XPCEXCEPTION_CID, false, nullptr, ExceptionConstructor },
   { &kNS_DOMSESSIONSTORAGEMANAGER_CID, false, nullptr, DOMSessionStorageManagerConstructor },
   { &kNS_DOMLOCALSTORAGEMANAGER_CID, false, nullptr, DOMLocalStorageManagerConstructor },
   { &kNS_DOMJSON_CID, false, nullptr, NS_NewJSON },
   { &kNS_TEXTEDITOR_CID, false, nullptr, nsPlaintextEditorConstructor },
   { &kDOMREQUEST_SERVICE_CID, false, nullptr, DOMRequestServiceConstructor },
-  { &kQUOTA_MANAGER_CID, false, nullptr, QuotaManagerConstructor },
+  { &kQUOTAMANAGER_SERVICE_CID, false, nullptr, QuotaManagerServiceConstructor },
   { &kSERVICEWORKERMANAGER_CID, false, nullptr, ServiceWorkerManagerConstructor },
   { &kWORKERDEBUGGERMANAGER_CID, true, nullptr, WorkerDebuggerManagerConstructor },
 #ifdef MOZ_WIDGET_GONK
   { &kSYSTEMWORKERMANAGER_CID, true, nullptr, SystemWorkerManagerConstructor },
 #endif
 #ifdef MOZ_B2G_BT
   { &kBLUETOOTHSERVICE_CID, true, nullptr, BluetoothServiceConstructor },
 #endif
@@ -1238,17 +1238,17 @@ static const mozilla::Module::ContractID
   { XPC_EXCEPTION_CONTRACTID, &kNS_XPCEXCEPTION_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/dom/json;1", &kNS_DOMJSON_CID },
   { "@mozilla.org/editor/texteditor;1", &kNS_TEXTEDITOR_CID },
   { DOMREQUEST_SERVICE_CONTRACTID, &kDOMREQUEST_SERVICE_CID },
-  { QUOTA_MANAGER_CONTRACTID, &kQUOTA_MANAGER_CID },
+  { QUOTAMANAGER_SERVICE_CONTRACTID, &kQUOTAMANAGER_SERVICE_CID },
   { SERVICEWORKERMANAGER_CONTRACTID, &kSERVICEWORKERMANAGER_CID },
   { WORKERDEBUGGERMANAGER_CONTRACTID, &kWORKERDEBUGGERMANAGER_CID },
 #ifdef MOZ_WIDGET_GONK
   { SYSTEMWORKERMANAGER_CONTRACTID, &kSYSTEMWORKERMANAGER_CID },
 #endif
 #ifdef MOZ_B2G_BT
   { BLUETOOTHSERVICE_CONTRACTID, &kBLUETOOTHSERVICE_CID },
 #endif
@@ -1341,18 +1341,18 @@ static const mozilla::Module::CategoryEn
   XPCONNECT_CATEGORIES
   { "content-policy", NS_DATADOCUMENTCONTENTPOLICY_CONTRACTID, NS_DATADOCUMENTCONTENTPOLICY_CONTRACTID },
   { "content-policy", NS_NODATAPROTOCOLCONTENTPOLICY_CONTRACTID, NS_NODATAPROTOCOLCONTENTPOLICY_CONTRACTID },
   { "content-policy", "CSPService", CSPSERVICE_CONTRACTID },
   { "content-policy", NS_MIXEDCONTENTBLOCKER_CONTRACTID, NS_MIXEDCONTENTBLOCKER_CONTRACTID },
   { "net-channel-event-sinks", "CSPService", CSPSERVICE_CONTRACTID },
   { "net-channel-event-sinks", NS_MIXEDCONTENTBLOCKER_CONTRACTID, NS_MIXEDCONTENTBLOCKER_CONTRACTID },
   { "app-startup", "Script Security Manager", "service," NS_SCRIPTSECURITYMANAGER_CONTRACTID },
-  { TOPIC_WEB_APP_CLEAR_DATA, "QuotaManager", "service," QUOTA_MANAGER_CONTRACTID },
-  { OBSERVER_TOPIC_IDLE_DAILY, "QuotaManager", QUOTA_MANAGER_CONTRACTID },
+  { TOPIC_WEB_APP_CLEAR_DATA, "QuotaManagerService", "service," QUOTAMANAGER_SERVICE_CONTRACTID },
+  { OBSERVER_TOPIC_IDLE_DAILY, "QuotaManagerService", QUOTAMANAGER_SERVICE_CONTRACTID },
 #ifdef MOZ_WIDGET_GONK
   { "app-startup", "Volume Service", "service," NS_VOLUMESERVICE_CONTRACTID },
 #endif
   CONTENTDLF_CATEGORIES
 #ifdef MOZ_WIDGET_GONK
   { "profile-after-change", "Gonk System Worker Manager", SYSTEMWORKERMANAGER_CONTRACTID },
 #endif
 #ifdef MOZ_B2G_BT
--- a/testing/specialpowers/content/SpecialPowersObserverAPI.js
+++ b/testing/specialpowers/content/SpecialPowersObserverAPI.js
@@ -486,54 +486,16 @@ SpecialPowersObserverAPI.prototype = {
         let name = aMessage.json.name;
         let message = aMessage.json.message;
         this._chromeScriptListeners
             .filter(o => (o.name == name && o.id == id))
             .forEach(o => o.listener(message));
         return undefined;	// See comment at the beginning of this function.
       }
 
-      case 'SPQuotaManager': {
-        let qm = Cc['@mozilla.org/dom/quota/manager;1']
-                   .getService(Ci.nsIQuotaManager);
-        let mm = aMessage.target
-                         .QueryInterface(Ci.nsIFrameLoaderOwner)
-                         .frameLoader
-                         .messageManager;
-        let msg = aMessage.data;
-        let principal = msg.principal;
-        let op = msg.op;
-
-        if (op != 'clear' && op != 'getUsage' && op != 'reset') {
-          throw new SpecialPowersError('Invalid operation for SPQuotaManager');
-        }
-
-        if (op == 'clear') {
-          qm.clearStoragesForPrincipal(principal);
-        } else if (op == 'reset') {
-          qm.reset();
-        }
-
-        // We always use the getUsageForPrincipal callback even if we're clearing
-        // since we know that clear and getUsageForPrincipal are synchronized by the
-        // QuotaManager.
-        let callback = function(principal, usage, fileUsage) {
-          let reply = { id: msg.id };
-          if (op == 'getUsage') {
-            reply.usage = usage;
-            reply.fileUsage = fileUsage;
-          }
-          mm.sendAsyncMessage(aMessage.name, reply);
-        };
-
-        qm.getUsageForPrincipal(principal, callback);
-
-        return undefined;	// See comment at the beginning of this function.
-      }
-
       case "SPCleanUpSTSData": {
         let origin = aMessage.data.origin;
         let flags = aMessage.data.flags;
         let uri = Services.io.newURI(origin, null, null);
         let sss = Cc["@mozilla.org/ssservice;1"].
                   getService(Ci.nsISiteSecurityService);
         sss.removeState(Ci.nsISiteSecurityService.HEADER_HSTS, uri, flags);
       }
--- a/testing/specialpowers/content/specialpowers.js
+++ b/testing/specialpowers/content/specialpowers.js
@@ -36,17 +36,16 @@ function SpecialPowers(window) {
                            "SPWebAppService",
                            "SPCleanUpSTSData"];
 
   this.SP_ASYNC_MESSAGES = ["SpecialPowers.Focus",
                             "SpecialPowers.Quit",
                             "SpecialPowers.CreateFiles",
                             "SpecialPowers.RemoveFiles",
                             "SPPingService",
-                            "SPQuotaManager",
                             "SPLoadExtension",
                             "SPStartupExtension",
                             "SPUnloadExtension",
                             "SPExtensionMessage"];
   addMessageListener("SPPingService", this._messageListener);
   addMessageListener("SpecialPowers.FilesCreated", this._messageListener);
   addMessageListener("SpecialPowers.FilesError", this._messageListener);
   let self = this;
--- a/testing/specialpowers/content/specialpowersAPI.js
+++ b/testing/specialpowers/content/specialpowersAPI.js
@@ -42,17 +42,16 @@ function SpecialPowersAPI() {
   this._pendingPrefs = [];
   this._applyingPrefs = false;
   this._permissionsUndoStack = [];
   this._pendingPermissions = [];
   this._applyingPermissions = false;
   this._observingPermissions = false;
   this._fm = null;
   this._cb = null;
-  this._quotaManagerCallbackInfos = null;
 }
 
 function bindDOMWindowUtils(aWindow) {
   if (!aWindow)
     return
 
    var util = aWindow.QueryInterface(Ci.nsIInterfaceRequestor)
                      .getInterface(Ci.nsIDOMWindowUtils);
@@ -1927,71 +1926,16 @@ SpecialPowersAPI.prototype = {
     var msg = {
       'op': 'notify',
       'observerTopic': topic,
       'observerData': data
     };
     this._sendSyncMessage('SPObserverService', msg);
   },
 
-  clearStorageForDoc: function(wrappedDocument, callback) {
-    this._quotaManagerRequest('clear', wrappedDocument, callback);
-  },
-
-  getStorageUsageForDoc: function(wrappedDocument, callback) {
-    this._quotaManagerRequest('getUsage', wrappedDocument, callback);
-  },
-
-  resetStorageForDoc: function(wrappedDocument, callback) {
-    this._quotaManagerRequest('reset', wrappedDocument, callback);
-  },
-
-  _quotaManagerRequest: function(op, wrappedDocument, callback) {
-    const messageTopic = "SPQuotaManager";
-    const id = Cc["@mozilla.org/uuid-generator;1"]
-                 .getService(Ci.nsIUUIDGenerator)
-                 .generateUUID()
-                 .toString();
-
-    let callbackInfo = { id: id, callback: callback };
-
-    if (this._quotaManagerCallbackInfos) {
-      callbackInfo.listener = this._quotaManagerCallbackInfos[0].listener;
-      this._quotaManagerCallbackInfos.push(callbackInfo)
-    } else {
-      callbackInfo.listener = function(msg) {
-        msg = msg.data;
-        for (let index in this._quotaManagerCallbackInfos) {
-          let callbackInfo = this._quotaManagerCallbackInfos[index];
-          if (callbackInfo.id == msg.id) {
-            if (this._quotaManagerCallbackInfos.length > 1) {
-              this._quotaManagerCallbackInfos.splice(index, 1);
-            } else {
-              this._quotaManagerCallbackInfos = null;
-              this._removeMessageListener(messageTopic, callbackInfo.listener);
-            }
-
-            if ('usage' in msg) {
-              callbackInfo.callback(msg.usage, msg.fileUsage);
-            } else {
-              callbackInfo.callback();
-            }
-          }
-        }
-      }.bind(this);
-
-      this._addMessageListener(messageTopic, callbackInfo.listener);
-      this._quotaManagerCallbackInfos = [ callbackInfo ];
-    }
-
-    let principal = unwrapIfWrapped(wrappedDocument).nodePrincipal;
-    let msg = { op: op, principal: principal, id: id };
-    this._sendAsyncMessage(messageTopic, msg);
-  },
-
   createDOMFile: function(path, options) {
     return new File(path, options);
   },
 
   removeAllServiceWorkerData: function() {
     this.notifyObserversInParentProcess(null, "browser:purge-session-history", "");
   },
 
--- a/toolkit/forgetaboutsite/ForgetAboutSite.jsm
+++ b/toolkit/forgetaboutsite/ForgetAboutSite.jsm
@@ -145,30 +145,30 @@ this.ForgetAboutSite = {
           pm.removePermission(perm);
         }
       } catch (e) {
         /* Ignore entry */
       }
     }
 
     // Offline Storages
-    let qm = Cc["@mozilla.org/dom/quota/manager;1"].
-             getService(Ci.nsIQuotaManager);
+    let qms = Cc["@mozilla.org/dom/quota-manager-service;1"].
+              getService(Ci.nsIQuotaManagerService);
     // delete data from both HTTP and HTTPS sites
     let caUtils = {};
     let scriptLoader = Cc["@mozilla.org/moz/jssubscript-loader;1"].
                        getService(Ci.mozIJSSubScriptLoader);
     scriptLoader.loadSubScript("chrome://global/content/contentAreaUtils.js",
                                caUtils);
     let httpURI = caUtils.makeURI("http://" + aDomain);
     let httpsURI = caUtils.makeURI("https://" + aDomain);
     let httpPrincipal = Services.scriptSecurityManager.createCodebasePrincipal(httpURI, {});
     let httpsPrincipal = Services.scriptSecurityManager.createCodebasePrincipal(httpsURI, {});
-    qm.clearStoragesForPrincipal(httpPrincipal);
-    qm.clearStoragesForPrincipal(httpsPrincipal);
+    qms.clearStoragesForPrincipal(httpPrincipal);
+    qms.clearStoragesForPrincipal(httpsPrincipal);
 
     function onContentPrefsRemovalFinished() {
       // Everybody else (including extensions)
       Services.obs.notifyObservers(null, "browser:purge-domain-data", aDomain);
     }
 
     // Content Preferences
     let cps2 = Cc["@mozilla.org/content-pref/service;1"].
--- a/toolkit/modules/Services.jsm
+++ b/toolkit/modules/Services.jsm
@@ -97,16 +97,17 @@ var initTable = [
   ["sysinfo", "@mozilla.org/system-info;1", "nsIPropertyBag2"],
   ["clipboard", "@mozilla.org/widget/clipboard;1", "nsIClipboard"],
   ["DOMRequest", "@mozilla.org/dom/dom-request-service;1", "nsIDOMRequestService"],
   ["focus", "@mozilla.org/focus-manager;1", "nsIFocusManager"],
   ["uriFixup", "@mozilla.org/docshell/urifixup;1", "nsIURIFixup"],
   ["blocklist", "@mozilla.org/extensions/blocklist;1", "nsIBlocklistService"],
   ["netUtils", "@mozilla.org/network/util;1", "nsINetUtil"],
   ["loadContextInfo", "@mozilla.org/load-context-info-factory;1", "nsILoadContextInfoFactory"],
+  ["qms", "@mozilla.org/dom/quota-manager-service;1", "nsIQuotaManagerService"],
 ];
 
 initTable.forEach(([name, contract, intf, enabled = true]) => {
   if (enabled) {
     XPCOMUtils.defineLazyServiceGetter(Services, name, contract, intf);
   }
 });