Bug 961049 - Part 6: Quota Manager on PBackground cache changes; r=bkelly
authorJan Varga <jan.varga@gmail.com>
Sun, 22 Nov 2015 10:44:16 +0100
changeset 273710 a1d1731150f61bfb414c7f4c0411130883b4bfbf
parent 273709 c53f530ef89cf0a4e169326b8467e6c86796375a
child 273711 100da64f36d95a6f13a48ec9e377701f3c328f82
push id29710
push usercbook@mozilla.com
push dateMon, 23 Nov 2015 13:09:07 +0000
treeherdermozilla-central@d3d286102ba7 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbkelly
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 6: Quota Manager on PBackground cache changes; r=bkelly
dom/cache/Context.cpp
dom/cache/Context.h
dom/cache/Manager.cpp
dom/cache/Manager.h
dom/cache/QuotaClient.cpp
dom/cache/test/mochitest/driver.js
dom/cache/test/mochitest/test_cache_orphaned_body.html
dom/cache/test/mochitest/test_cache_orphaned_cache.html
dom/cache/test/mochitest/test_cache_restart.html
dom/cache/test/mochitest/test_cache_shrink.html
dom/cache/test/xpcshell/make_profile.js
--- a/dom/cache/Context.cpp
+++ b/dom/cache/Context.cpp
@@ -127,33 +127,35 @@ public:
     MOZ_ASSERT(mInitAction);
   }
 
   nsresult Dispatch()
   {
     NS_ASSERT_OWNINGTHREAD(QuotaInitRunnable);
     MOZ_ASSERT(mState == STATE_INIT);
 
-    mState = STATE_OPEN_DIRECTORY;
+    mState = STATE_GET_INFO;
     nsresult rv = NS_DispatchToMainThread(this, nsIThread::DISPATCH_NORMAL);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       mState = STATE_COMPLETE;
       Clear();
     }
     return rv;
   }
 
   void Cancel()
   {
     NS_ASSERT_OWNINGTHREAD(QuotaInitRunnable);
     MOZ_ASSERT(!mCanceled);
     mCanceled = true;
     mInitAction->CancelOnInitiatingThread();
   }
 
+  void OpenDirectory();
+
   // OpenDirectoryListener methods
   virtual void
   DirectoryLockAcquired(DirectoryLock* aLock) override;
 
   virtual void
   DirectoryLockFailed() override;
 
 private:
@@ -190,16 +192,18 @@ private:
     MOZ_ASSERT(mState == STATE_COMPLETE);
     MOZ_ASSERT(!mContext);
     MOZ_ASSERT(!mInitAction);
   }
 
   enum State
   {
     STATE_INIT,
+    STATE_GET_INFO,
+    STATE_CREATE_QUOTA_MANAGER,
     STATE_OPEN_DIRECTORY,
     STATE_WAIT_FOR_DIRECTORY_LOCK,
     STATE_ENSURE_ORIGIN_INITIALIZED,
     STATE_RUN_ON_TARGET,
     STATE_RUNNING,
     STATE_COMPLETING,
     STATE_COMPLETE
   };
@@ -229,33 +233,54 @@ private:
   RefPtr<ThreadsafeHandle> mThreadsafeHandle;
   RefPtr<Manager> mManager;
   RefPtr<Data> mData;
   nsCOMPtr<nsIThread> mTarget;
   RefPtr<Action> mInitAction;
   nsCOMPtr<nsIThread> mInitiatingThread;
   nsresult mResult;
   QuotaInfo mQuotaInfo;
-  nsMainThreadPtrHandle<DirectoryLock> mDirectoryLock;
+  RefPtr<DirectoryLock> mDirectoryLock;
   State mState;
   Atomic<bool> mCanceled;
 
 public:
   NS_DECL_THREADSAFE_ISUPPORTS
   NS_DECL_NSIRUNNABLE
 };
 
 void
+Context::QuotaInitRunnable::OpenDirectory()
+{
+  NS_ASSERT_OWNINGTHREAD(QuotaInitRunnable);
+  MOZ_ASSERT(mState == STATE_CREATE_QUOTA_MANAGER ||
+             mState == STATE_OPEN_DIRECTORY);
+  MOZ_ASSERT(QuotaManager::Get());
+
+  // QuotaManager::OpenDirectory() will hold a reference to us as
+  // a listener.  We will then get DirectoryLockAcquired() on the owning
+  // thread when it is safe to access our storage directory.
+  mState = STATE_WAIT_FOR_DIRECTORY_LOCK;
+  QuotaManager::Get()->OpenDirectory(PERSISTENCE_TYPE_DEFAULT,
+                                     mQuotaInfo.mGroup,
+                                     mQuotaInfo.mOrigin,
+                                     mQuotaInfo.mIsApp,
+                                     quota::Client::DOMCACHE,
+                                     /* aExclusive */ false,
+                                     this);
+}
+
+void
 Context::QuotaInitRunnable::DirectoryLockAcquired(DirectoryLock* aLock)
 {
-  MOZ_ASSERT(NS_IsMainThread());
+  NS_ASSERT_OWNINGTHREAD(QuotaInitRunnable);
   MOZ_ASSERT(mState == STATE_WAIT_FOR_DIRECTORY_LOCK);
   MOZ_ASSERT(!mDirectoryLock);
 
-  mDirectoryLock = new nsMainThreadPtrHolder<DirectoryLock>(aLock);
+  mDirectoryLock = aLock;
 
   if (mCanceled) {
     Complete(NS_ERROR_ABORT);
     return;
   }
 
   QuotaManager* qm = QuotaManager::Get();
   MOZ_ASSERT(qm);
@@ -266,17 +291,17 @@ Context::QuotaInitRunnable::DirectoryLoc
     Complete(rv);
     return;
   }
 }
 
 void
 Context::QuotaInitRunnable::DirectoryLockFailed()
 {
-  MOZ_ASSERT(NS_IsMainThread());
+  NS_ASSERT_OWNINGTHREAD(QuotaInitRunnable);
   MOZ_ASSERT(mState == STATE_WAIT_FOR_DIRECTORY_LOCK);
   MOZ_ASSERT(!mDirectoryLock);
 
   NS_WARNING("Failed to acquire a directory lock!");
 
   Complete(NS_ERROR_FAILURE);
 }
 
@@ -285,23 +310,33 @@ NS_IMPL_ISUPPORTS(mozilla::dom::cache::C
 // The QuotaManager init state machine is represented in the following diagram:
 //
 //    +---------------+
 //    |     Start     |      Resolve(error)
 //    | (Orig Thread) +---------------------+
 //    +-------+-------+                     |
 //            |                             |
 // +----------v-----------+                 |
-// |    OpenDirectory     |  Resolve(error) |
+// |       GetInfo        |  Resolve(error) |
 // |    (Main Thread)     +-----------------+
 // +----------+-----------+                 |
 //            |                             |
 // +----------v-----------+                 |
+// |  CreateQuotaManager  |  Resolve(error) |
+// |    (Orig Thread)     +-----------------+
+// +----------+-----------+                 |
+//            |                             |
+// +----------v-----------+                 |
+// |    OpenDirectory     |  Resolve(error) |
+// |    (Orig Thread)     +-----------------+
+// +----------+-----------+                 |
+//            |                             |
+// +----------v-----------+                 |
 // | WaitForDirectoryLock |  Resolve(error) |
-// |    (Main Thread)     +-----------------+
+// |    (Orig Thread)     +-----------------+
 // +----------+-----------+                 |
 //            |                             |
 // +----------v------------+                |
 // |EnsureOriginInitialized| Resolve(error) |
 // |   (Quota IO Thread)   +----------------+
 // +----------+------------+                |
 //            |                             |
 // +----------v------------+                |
@@ -325,53 +360,71 @@ Context::QuotaInitRunnable::Run()
 {
   // May run on different threads depending on the state.  See individual
   // state cases for thread assertions.
 
   RefPtr<SyncResolver> resolver = new SyncResolver();
 
   switch(mState) {
     // -----------------------------------
-    case STATE_OPEN_DIRECTORY:
+    case STATE_GET_INFO:
     {
       MOZ_ASSERT(NS_IsMainThread());
 
+      if (mCanceled) {
+        resolver->Resolve(NS_ERROR_ABORT);
+        break;
+      }
+
+      RefPtr<ManagerId> managerId = mManager->GetManagerId();
+      nsCOMPtr<nsIPrincipal> principal = managerId->Principal();
+      nsresult rv = QuotaManager::GetInfoFromPrincipal(principal,
+                                                       &mQuotaInfo.mGroup,
+                                                       &mQuotaInfo.mOrigin,
+                                                       &mQuotaInfo.mIsApp);
+      if (NS_WARN_IF(NS_FAILED(rv))) {
+        resolver->Resolve(rv);
+        break;
+      }
+
+      mState = STATE_CREATE_QUOTA_MANAGER;
+      MOZ_ALWAYS_TRUE(NS_SUCCEEDED(
+        mInitiatingThread->Dispatch(this, nsIThread::DISPATCH_NORMAL)));
+      break;
+    }
+    // ----------------------------------
+    case STATE_CREATE_QUOTA_MANAGER:
+    {
+      NS_ASSERT_OWNINGTHREAD(QuotaInitRunnable);
+
       if (mCanceled || QuotaManager::IsShuttingDown()) {
         resolver->Resolve(NS_ERROR_ABORT);
         break;
       }
 
-      QuotaManager* qm = QuotaManager::GetOrCreate();
-      if (!qm) {
+      if (QuotaManager::Get()) {
+        OpenDirectory();
+        return NS_OK;
+      }
+
+      mState = STATE_OPEN_DIRECTORY;
+      QuotaManager::GetOrCreate(this);
+      break;
+    }
+    // ----------------------------------
+    case STATE_OPEN_DIRECTORY:
+    {
+      NS_ASSERT_OWNINGTHREAD(QuotaInitRunnable);
+
+      if (NS_WARN_IF(!QuotaManager::Get())) {
         resolver->Resolve(NS_ERROR_FAILURE);
         break;
       }
 
-      RefPtr<ManagerId> managerId = mManager->GetManagerId();
-      nsCOMPtr<nsIPrincipal> principal = managerId->Principal();
-      nsresult rv = qm->GetInfoFromPrincipal(principal,
-                                             &mQuotaInfo.mGroup,
-                                             &mQuotaInfo.mOrigin,
-                                             &mQuotaInfo.mIsApp);
-      if (NS_WARN_IF(NS_FAILED(rv))) {
-        resolver->Resolve(rv);
-        break;
-      }
-
-      // QuotaManager::OpenDirectory() will hold a reference to us as
-      // a listener.  We will then get DirectoryLockAcquired() on the main
-      // thread when it is safe to access our storage directory.
-      mState = STATE_WAIT_FOR_DIRECTORY_LOCK;
-      qm->OpenDirectory(PERSISTENCE_TYPE_DEFAULT,
-                        mQuotaInfo.mGroup,
-                        mQuotaInfo.mOrigin,
-                        mQuotaInfo.mIsApp,
-                        quota::Client::DOMCACHE,
-                        /* aExclusive */ false,
-                        this);
+      OpenDirectory();
       break;
     }
     // ----------------------------------
     case STATE_ENSURE_ORIGIN_INITIALIZED:
     {
       AssertIsOnIOThread();
 
       if (mCanceled) {
@@ -420,17 +473,17 @@ Context::QuotaInitRunnable::Run()
 
       break;
     }
     // -------------------
     case STATE_COMPLETING:
     {
       NS_ASSERT_OWNINGTHREAD(QuotaInitRunnable);
       mInitAction->CompleteOnInitiatingThread(mResult);
-      mContext->OnQuotaInit(mResult, mQuotaInfo, mDirectoryLock);
+      mContext->OnQuotaInit(mResult, mQuotaInfo, mDirectoryLock.forget());
       mState = STATE_COMPLETE;
 
       // Explicitly cleanup here as the destructor could fire on any of
       // the threads we have bounced through.
       Clear();
       break;
     }
     // -----
@@ -982,17 +1035,17 @@ Context::DispatchAction(Action* aAction,
     // for this invariant violation.
     MOZ_CRASH("Failed to dispatch ActionRunnable to target thread.");
   }
   AddActivity(runnable);
 }
 
 void
 Context::OnQuotaInit(nsresult aRv, const QuotaInfo& aQuotaInfo,
-                     nsMainThreadPtrHandle<DirectoryLock>& aDirectoryLock)
+                     already_AddRefed<DirectoryLock> aDirectoryLock)
 {
   NS_ASSERT_OWNINGTHREAD(Context);
 
   MOZ_ASSERT(mInitRunnable);
   mInitRunnable = nullptr;
 
   mQuotaInfo = aQuotaInfo;
 
--- a/dom/cache/Context.h
+++ b/dom/cache/Context.h
@@ -185,17 +185,18 @@ private:
   };
 
   Context(Manager* aManager, nsIThread* aTarget, Action* aInitAction);
   ~Context();
   void Init(Context* aOldContext);
   void Start();
   void DispatchAction(Action* aAction, bool aDoomData = false);
   void OnQuotaInit(nsresult aRv, const QuotaInfo& aQuotaInfo,
-                   nsMainThreadPtrHandle<DirectoryLock>& aDirectoryLock);
+                   already_AddRefed<DirectoryLock> aDirectoryLock);
+
 
   already_AddRefed<ThreadsafeHandle>
   CreateThreadsafeHandle();
 
   void
   SetNextContext(Context* aNextContext);
 
   void
@@ -216,17 +217,17 @@ private:
   typedef nsTObserverArray<Activity*> ActivityList;
   ActivityList mActivityList;
 
   // The ThreadsafeHandle may have a strong ref back to us.  This creates
   // a ref-cycle that keeps the Context alive.  The ref-cycle is broken
   // when ThreadsafeHandle::AllowToClose() is called.
   RefPtr<ThreadsafeHandle> mThreadsafeHandle;
 
-  nsMainThreadPtrHandle<DirectoryLock> mDirectoryLock;
+  RefPtr<DirectoryLock> mDirectoryLock;
   RefPtr<Context> mNextContext;
 
 public:
   NS_INLINE_DECL_REFCOUNTING(cache::Context)
 };
 
 } // namespace cache
 } // namespace dom
--- a/dom/cache/Manager.cpp
+++ b/dom/cache/Manager.cpp
@@ -237,65 +237,71 @@ public:
 
     MOZ_ALWAYS_TRUE(sFactory->mManagerList.RemoveElement(aManager));
 
     // clean up the factory singleton if there are no more managers
     MaybeDestroyInstance();
   }
 
   static void
-  StartAbortOnMainThread(const nsACString& aOrigin)
+  Abort(const nsACString& aOrigin)
   {
-    MOZ_ASSERT(NS_IsMainThread());
+    mozilla::ipc::AssertIsOnBackgroundThread();
 
-    // Lock for sBackgroundThread.
-    StaticMutexAutoLock lock(sMutex);
-
-    if (!sBackgroundThread) {
+    if (!sFactory) {
       return;
     }
 
-    // Guaranteed to succeed because we should get abort only before the
-    // background thread is destroyed.
-    nsCOMPtr<nsIRunnable> runnable = new AbortRunnable(aOrigin);
-    nsresult rv = sBackgroundThread->Dispatch(runnable,
-                                              nsIThread::DISPATCH_NORMAL);
-    MOZ_ALWAYS_TRUE(NS_SUCCEEDED(rv));
+    MOZ_ASSERT(!sFactory->mManagerList.IsEmpty());
+
+    {
+      ManagerList::ForwardIterator iter(sFactory->mManagerList);
+      while (iter.HasMore()) {
+        RefPtr<Manager> manager = iter.GetNext();
+        if (aOrigin.IsVoid() ||
+            manager->mManagerId->QuotaOrigin() == aOrigin) {
+          manager->Abort();
+        }
+      }
+    }
   }
 
   static void
-  StartShutdownAllOnMainThread()
+  ShutdownAll()
   {
-    MOZ_ASSERT(NS_IsMainThread());
+    mozilla::ipc::AssertIsOnBackgroundThread();
 
-    // Lock for sFactoryShutdown and sBackgroundThread.
-    StaticMutexAutoLock lock(sMutex);
-
-    sFactoryShutdown = true;
-
-    if (!sBackgroundThread) {
+    if (!sFactory) {
       return;
     }
 
-    // Guaranteed to succeed because we should be shutdown before the
-    // background thread is destroyed.
-    nsCOMPtr<nsIRunnable> runnable = new ShutdownAllRunnable();
-    nsresult rv = sBackgroundThread->Dispatch(runnable,
-                                              nsIThread::DISPATCH_NORMAL);
-    MOZ_ALWAYS_TRUE(NS_SUCCEEDED(rv));
+    MOZ_ASSERT(!sFactory->mManagerList.IsEmpty());
+
+    {
+      // Note that we are synchronously calling shutdown code here.  If any
+      // of the shutdown code synchronously decides to delete the Factory
+      // we need to delay that delete until the end of this method.
+      AutoRestore<bool> restore(sFactory->mInSyncShutdown);
+      sFactory->mInSyncShutdown = true;
+
+      ManagerList::ForwardIterator iter(sFactory->mManagerList);
+      while (iter.HasMore()) {
+        RefPtr<Manager> manager = iter.GetNext();
+        manager->Shutdown();
+      }
+    }
+
+    MaybeDestroyInstance();
   }
 
   static bool
-  IsShutdownAllCompleteOnMainThread()
+  IsShutdownAllComplete()
   {
-    MOZ_ASSERT(NS_IsMainThread());
-    StaticMutexAutoLock lock(sMutex);
-    // Infer whether we have shutdown using the sBackgroundThread value.  We
-    // guarantee this is nullptr when sFactory is destroyed.
-    return sFactoryShutdown && !sBackgroundThread;
+    mozilla::ipc::AssertIsOnBackgroundThread();
+    return !sFactory;
   }
 
 private:
   Factory()
     : mInSyncShutdown(false)
   {
     MOZ_COUNT_CTOR(cache::Manager::Factory);
   }
@@ -317,22 +323,16 @@ private:
       // we don't need to lock it here.  Just protect sFactoryShutdown and
       // sBackgroundThread.
       {
         StaticMutexAutoLock lock(sMutex);
 
         if (sFactoryShutdown) {
           return NS_ERROR_ILLEGAL_DURING_SHUTDOWN;
         }
-
-        // Cannot use ClearOnShutdown() because we're on the background thread.
-        // This is automatically cleared when Factory::Remove() calls
-        // MaybeDestroyInstance().
-        MOZ_ASSERT(!sBackgroundThread);
-        sBackgroundThread = NS_GetCurrentThread();
       }
 
       // We cannot use ClearOnShutdown() here because we're not on the main
       // thread.  Instead, we delete sFactory in Factory::Remove() after the
       // last manager is removed.  ShutdownObserver ensures this happens
       // before shutdown.
       sFactory = new Factory();
     }
@@ -354,144 +354,31 @@ private:
     // If the factory is is still in use then we cannot delete yet.  This
     // could be due to managers still existing or because we are in the
     // middle of shutting down.  We need to be careful not to delete ourself
     // synchronously during shutdown.
     if (!sFactory->mManagerList.IsEmpty() || sFactory->mInSyncShutdown) {
       return;
     }
 
-    // Be clear about what we are locking.  sFactory is bg thread only, so
-    // we don't need to lock it here.  Just protect sBackgroundThread.
-    {
-      StaticMutexAutoLock lock(sMutex);
-      MOZ_ASSERT(sBackgroundThread);
-      sBackgroundThread = nullptr;
-    }
-
     sFactory = nullptr;
   }
 
-  static void
-  AbortOnBackgroundThread(const nsACString& aOrigin)
-  {
-    mozilla::ipc::AssertIsOnBackgroundThread();
-
-    // The factory was destroyed between when abort started on main thread and
-    // when we could start abort on the worker thread.  Just declare abort
-    // complete.
-    if (!sFactory) {
-#ifdef DEBUG
-      StaticMutexAutoLock lock(sMutex);
-      MOZ_ASSERT(!sBackgroundThread);
-#endif
-      return;
-    }
-
-    MOZ_ASSERT(!sFactory->mManagerList.IsEmpty());
-
-    {
-      ManagerList::ForwardIterator iter(sFactory->mManagerList);
-      while (iter.HasMore()) {
-        RefPtr<Manager> manager = iter.GetNext();
-        if (aOrigin.IsVoid() ||
-            manager->mManagerId->QuotaOrigin() == aOrigin) {
-          manager->Abort();
-        }
-      }
-    }
-  }
-
-  static void
-  ShutdownAllOnBackgroundThread()
-  {
-    mozilla::ipc::AssertIsOnBackgroundThread();
-
-    // The factory shutdown between when shutdown started on main thread and
-    // when we could start shutdown on the worker thread.  Just declare
-    // shutdown complete.  The sFactoryShutdown flag prevents the factory
-    // from racing to restart here.
-    if (!sFactory) {
-#ifdef DEBUG
-      StaticMutexAutoLock lock(sMutex);
-      MOZ_ASSERT(!sBackgroundThread);
-#endif
-      return;
-    }
-
-    MOZ_ASSERT(!sFactory->mManagerList.IsEmpty());
-
-    {
-      // Note that we are synchronously calling shutdown code here.  If any
-      // of the shutdown code synchronously decides to delete the Factory
-      // we need to delay that delete until the end of this method.
-      AutoRestore<bool> restore(sFactory->mInSyncShutdown);
-      sFactory->mInSyncShutdown = true;
-
-      ManagerList::ForwardIterator iter(sFactory->mManagerList);
-      while (iter.HasMore()) {
-        RefPtr<Manager> manager = iter.GetNext();
-        manager->Shutdown();
-      }
-    }
-
-    MaybeDestroyInstance();
-  }
-
-  class AbortRunnable final : public nsRunnable
-  {
-  public:
-    explicit AbortRunnable(const nsACString& aOrigin)
-      : mOrigin(aOrigin)
-    { }
-
-    NS_IMETHOD
-    Run() override
-    {
-      mozilla::ipc::AssertIsOnBackgroundThread();
-      AbortOnBackgroundThread(mOrigin);
-      return NS_OK;
-    }
-  private:
-    ~AbortRunnable() { }
-
-    const nsCString mOrigin;
-  };
-
-  class ShutdownAllRunnable final : public nsRunnable
-  {
-  public:
-    NS_IMETHOD
-    Run() override
-    {
-      mozilla::ipc::AssertIsOnBackgroundThread();
-      ShutdownAllOnBackgroundThread();
-      return NS_OK;
-    }
-  private:
-    ~ShutdownAllRunnable() { }
-  };
-
   // Singleton created on demand and deleted when last Manager is cleared
   // in Remove().
   // PBackground thread only.
   static StaticAutoPtr<Factory> sFactory;
 
-  // protects following static attributes
+  // protects following static attribute
   static StaticMutex sMutex;
 
   // Indicate if shutdown has occurred to block re-creation of sFactory.
   // Must hold sMutex to access.
   static bool sFactoryShutdown;
 
-  // Background thread owning all Manager objects.  Only set while sFactory is
-  // set.
-  // Must hold sMutex to access.
-  static StaticRefPtr<nsIThread> sBackgroundThread;
-
   // Weak references as we don't want to keep Manager objects alive forever.
   // When a Manager is destroyed it calls Factory::Remove() to clear itself.
   // PBackground thread only.
   typedef nsTObserverArray<Manager*> ManagerList;
   ManagerList mManagerList;
 
   // This flag is set when we are looping through the list and calling
   // Shutdown() on each Manager.  We need to be careful not to synchronously
@@ -503,19 +390,16 @@ private:
 StaticAutoPtr<Manager::Factory> Manager::Factory::sFactory;
 
 // static
 StaticMutex Manager::Factory::sMutex;
 
 // static
 bool Manager::Factory::sFactoryShutdown = false;
 
-// static
-StaticRefPtr<nsIThread> Manager::Factory::sBackgroundThread;
-
 // ----------------------------------------------------------------------------
 
 // Abstract class to help implement the various Actions.  The vast majority
 // of Actions are synchronous and need to report back to a Listener on the
 // Manager.
 class Manager::BaseAction : public SyncDBAction
 {
 protected:
@@ -1526,37 +1410,37 @@ already_AddRefed<Manager>
 Manager::Get(ManagerId* aManagerId)
 {
   mozilla::ipc::AssertIsOnBackgroundThread();
   return Factory::Get(aManagerId);
 }
 
 // static
 void
-Manager::ShutdownAllOnMainThread()
+Manager::ShutdownAll()
 {
-  MOZ_ASSERT(NS_IsMainThread());
+  mozilla::ipc::AssertIsOnBackgroundThread();
 
-  Factory::StartShutdownAllOnMainThread();
+  Factory::ShutdownAll();
 
-  while (!Factory::IsShutdownAllCompleteOnMainThread()) {
+  while (!Factory::IsShutdownAllComplete()) {
     if (!NS_ProcessNextEvent()) {
       NS_WARNING("Something bad happened!");
       break;
     }
   }
 }
 
 // static
 void
-Manager::AbortOnMainThread(const nsACString& aOrigin)
+Manager::Abort(const nsACString& aOrigin)
 {
-  MOZ_ASSERT(NS_IsMainThread());
+  mozilla::ipc::AssertIsOnBackgroundThread();
 
-  Factory::StartAbortOnMainThread(aOrigin);
+  Factory::Abort(aOrigin);
 }
 
 void
 Manager::RemoveListener(Listener* aListener)
 {
   NS_ASSERT_OWNINGTHREAD(Manager);
   // There may not be a listener here in the case where an actor is killed
   // before it can perform any actual async requests on Manager.
--- a/dom/cache/Manager.h
+++ b/dom/cache/Manager.h
@@ -126,21 +126,21 @@ public:
   {
     Open,
     Closing
   };
 
   static nsresult GetOrCreate(ManagerId* aManagerId, Manager** aManagerOut);
   static already_AddRefed<Manager> Get(ManagerId* aManagerId);
 
-  // Synchronously shutdown from main thread.  This spins the event loop.
-  static void ShutdownAllOnMainThread();
+  // Synchronously shutdown.  This spins the event loop.
+  static void ShutdownAll();
 
   // Cancel actions for given origin or all actions if passed string is null.
-  static void AbortOnMainThread(const nsACString& aOrigin);
+  static void Abort(const nsACString& aOrigin);
 
   // Must be called by Listener objects before they are destroyed.
   void RemoveListener(Listener* aListener);
 
   // Must be called by Context objects before they are destroyed.
   void RemoveContext(Context* aContext);
 
   // Marks the Manager "invalid".  Once the Context completes no new operations
--- a/dom/cache/QuotaClient.cpp
+++ b/dom/cache/QuotaClient.cpp
@@ -5,29 +5,31 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/dom/cache/QuotaClient.h"
 
 #include "mozilla/DebugOnly.h"
 #include "mozilla/dom/cache/Manager.h"
 #include "mozilla/dom/quota/QuotaManager.h"
 #include "mozilla/dom/quota/UsageInfo.h"
+#include "mozilla/ipc/BackgroundParent.h"
 #include "nsIFile.h"
 #include "nsISimpleEnumerator.h"
 #include "nsThreadUtils.h"
 
 namespace {
 
 using mozilla::DebugOnly;
 using mozilla::dom::ContentParentId;
 using mozilla::dom::cache::Manager;
 using mozilla::dom::quota::Client;
 using mozilla::dom::quota::PersistenceType;
 using mozilla::dom::quota::QuotaManager;
 using mozilla::dom::quota::UsageInfo;
+using mozilla::ipc::AssertIsOnBackgroundThread;
 
 static nsresult
 GetBodyUsage(nsIFile* aDir, UsageInfo* aUsageInfo)
 {
   nsCOMPtr<nsISimpleEnumerator> entries;
   nsresult rv = aDir->GetDirectoryEntries(getter_AddRefs(entries));
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
@@ -170,19 +172,19 @@ public:
   {
     // Nothing to do here as the Context handles cleaning everything up
     // automatically.
   }
 
   virtual void
   AbortOperations(const nsACString& aOrigin) override
   {
-    MOZ_ASSERT(NS_IsMainThread());
+    AssertIsOnBackgroundThread();
 
-    Manager::AbortOnMainThread(aOrigin);
+    Manager::Abort(aOrigin);
   }
 
   virtual void
   AbortOperationsForProcess(ContentParentId aContentParentId) override
   {
     // The Cache and Context can be shared by multiple client processes.  They
     // are not exclusively owned by a single process.
     //
@@ -190,44 +192,50 @@ public:
     // when a particular process goes away.  We definitely don't want this
     // since we are shared.  Also, the Cache actor code already properly
     // handles asynchronous actor destruction when the child process dies.
     //
     // Therefore, do nothing here.
   }
 
   virtual void
-  PerformIdleMaintenance() override
+  StartIdleMaintenance() override
+  { }
+
+  virtual void
+  StopIdleMaintenance() override
   { }
 
   virtual void
   ShutdownWorkThreads() override
   {
-    MOZ_ASSERT(NS_IsMainThread());
+    AssertIsOnBackgroundThread();
 
     // spins the event loop and synchronously shuts down all Managers
-    Manager::ShutdownAllOnMainThread();
+    Manager::ShutdownAll();
   }
 
 private:
   ~CacheQuotaClient()
   {
-    MOZ_ASSERT(NS_IsMainThread());
+    AssertIsOnBackgroundThread();
   }
 
   NS_INLINE_DECL_REFCOUNTING(CacheQuotaClient, override)
 };
 
 } // namespace
 
 namespace mozilla {
 namespace dom {
 namespace cache {
 
 already_AddRefed<quota::Client> CreateQuotaClient()
 {
+  AssertIsOnBackgroundThread();
+
   RefPtr<CacheQuotaClient> ref = new CacheQuotaClient();
   return ref.forget();
 }
 
 } // namespace cache
 } // namespace dom
 } // namespace mozilla
--- a/dom/cache/test/mochitest/driver.js
+++ b/dom/cache/test/mochitest/driver.js
@@ -30,17 +30,21 @@ function runTests(testFile, order) {
         resolve();
       });
     });
   }
 
   // adapted from dom/indexedDB/test/helpers.js
   function clearStorage() {
     return new Promise(function(resolve, reject) {
-      SpecialPowers.clearStorageForDoc(SpecialPowers.wrap(document), resolve);
+      var qms = SpecialPowers.Services.qms;
+      var principal = SpecialPowers.wrap(document).nodePrincipal;
+      var request = qms.clearStoragesForPrincipal(principal);
+      var cb = SpecialPowers.wrapCallback(resolve);
+      request.callback = cb;
     });
   }
 
   function loadScript(script) {
     return new Promise(function(resolve, reject) {
       var s = document.createElement("script");
       s.src = script;
       s.onerror = reject;
--- a/dom/cache/test/mochitest/test_cache_orphaned_body.html
+++ b/dom/cache/test/mochitest/test_cache_orphaned_body.html
@@ -19,29 +19,41 @@ function setupTestIframe() {
       resolve();
     };
     document.body.appendChild(iframe);
   });
 }
 
 function clearStorage() {
   return new Promise(function(resolve, reject) {
-    SpecialPowers.clearStorageForDoc(SpecialPowers.wrap(document), resolve);
+    var qms = SpecialPowers.Services.qms;
+    var principal = SpecialPowers.wrap(document).nodePrincipal;
+    var request = qms.clearStoragesForPrincipal(principal);
+    var cb = SpecialPowers.wrapCallback(resolve);
+    request.callback = cb;
   });
 }
 
 function storageUsage() {
   return new Promise(function(resolve, reject) {
-    SpecialPowers.getStorageUsageForDoc(SpecialPowers.wrap(document), resolve);
+    var qms = SpecialPowers.Services.qms;
+    var principal = SpecialPowers.wrap(document).nodePrincipal;
+    var cb = SpecialPowers.wrapCallback(function(request) {
+      resolve(request.usage, request.fileUsage);
+    });
+    qms.getUsageForPrincipal(principal, cb);
   });
 }
 
 function resetStorage() {
   return new Promise(function(resolve, reject) {
-    SpecialPowers.resetStorageForDoc(SpecialPowers.wrap(document), resolve);
+    var qms = SpecialPowers.Services.qms;
+    var request = qms.reset();
+    var cb = SpecialPowers.wrapCallback(resolve);
+    request.callback = cb;
   });
 }
 
 function gc() {
   return new Promise(function(resolve, reject) {
     SpecialPowers.exactGC(window, resolve);
   });
 }
--- a/dom/cache/test/mochitest/test_cache_orphaned_cache.html
+++ b/dom/cache/test/mochitest/test_cache_orphaned_cache.html
@@ -19,29 +19,41 @@ function setupTestIframe() {
       resolve();
     };
     document.body.appendChild(iframe);
   });
 }
 
 function clearStorage() {
   return new Promise(function(resolve, reject) {
-    SpecialPowers.clearStorageForDoc(SpecialPowers.wrap(document), resolve);
+    var qms = SpecialPowers.Services.qms;
+    var principal = SpecialPowers.wrap(document).nodePrincipal;
+    var request = qms.clearStoragesForPrincipal(principal);
+    var cb = SpecialPowers.wrapCallback(resolve);
+    request.callback = cb;
   });
 }
 
 function storageUsage() {
   return new Promise(function(resolve, reject) {
-    SpecialPowers.getStorageUsageForDoc(SpecialPowers.wrap(document), resolve);
+    var qms = SpecialPowers.Services.qms;
+    var principal = SpecialPowers.wrap(document).nodePrincipal;
+    var cb = SpecialPowers.wrapCallback(function(request) {
+      resolve(request.usage, request.fileUsage);
+    });
+    qms.getUsageForPrincipal(principal, cb);
   });
 }
 
 function resetStorage() {
   return new Promise(function(resolve, reject) {
-    SpecialPowers.resetStorageForDoc(SpecialPowers.wrap(document), resolve);
+    var qms = SpecialPowers.Services.qms;
+    var request = qms.reset();
+    var cb = SpecialPowers.wrapCallback(resolve);
+    request.callback = cb;
   });
 }
 
 function gc() {
   return new Promise(function(resolve, reject) {
     SpecialPowers.exactGC(window, resolve);
   });
 }
--- a/dom/cache/test/mochitest/test_cache_restart.html
+++ b/dom/cache/test/mochitest/test_cache_restart.html
@@ -18,17 +18,20 @@ function setupTestIframe() {
       resolve();
     };
     document.body.appendChild(iframe);
   });
 }
 
 function resetStorage() {
   return new Promise(function(resolve, reject) {
-    SpecialPowers.resetStorageForDoc(SpecialPowers.wrap(document), resolve);
+    var qms = SpecialPowers.Services.qms;
+    var request = qms.reset();
+    var cb = SpecialPowers.wrapCallback(resolve);
+    request.callback = cb;
   });
 }
 
 SimpleTest.waitForExplicitFinish();
 SpecialPowers.pushPrefEnv({
   "set": [["dom.caches.enabled", true],
           ["dom.caches.testing.enabled", true],
           ["dom.quotaManager.testing", true]],
--- a/dom/cache/test/mochitest/test_cache_shrink.html
+++ b/dom/cache/test/mochitest/test_cache_shrink.html
@@ -19,29 +19,41 @@ function setupTestIframe() {
       resolve();
     };
     document.body.appendChild(iframe);
   });
 }
 
 function clearStorage() {
   return new Promise(function(resolve, reject) {
-    SpecialPowers.clearStorageForDoc(SpecialPowers.wrap(document), resolve);
+    var qms = SpecialPowers.Services.qms;
+    var principal = SpecialPowers.wrap(document).nodePrincipal;
+    var request = qms.clearStoragesForPrincipal(principal);
+    var cb = SpecialPowers.wrapCallback(resolve);
+    request.callback = cb;
   });
 }
 
 function storageUsage() {
   return new Promise(function(resolve, reject) {
-    SpecialPowers.getStorageUsageForDoc(SpecialPowers.wrap(document), resolve);
+    var qms = SpecialPowers.Services.qms;
+    var principal = SpecialPowers.wrap(document).nodePrincipal;
+    var cb = SpecialPowers.wrapCallback(function(request) {
+      resolve(request.usage, request.fileUsage);
+    });
+    qms.getUsageForPrincipal(principal, cb);
   });
 }
 
 function resetStorage() {
   return new Promise(function(resolve, reject) {
-    SpecialPowers.resetStorageForDoc(SpecialPowers.wrap(document), resolve);
+    var qms = SpecialPowers.Services.qms;
+    var request = qms.reset();
+    var cb = SpecialPowers.wrapCallback(resolve);
+    request.callback = cb;
   });
 }
 
 function gc() {
   return new Promise(function(resolve, reject) {
     SpecialPowers.exactGC(window, resolve);
   });
 }
--- a/dom/cache/test/xpcshell/make_profile.js
+++ b/dom/cache/test/xpcshell/make_profile.js
@@ -87,32 +87,21 @@ function resetQuotaManager() {
 
     var prefService = Cc['@mozilla.org/preferences-service;1']
                       .getService(Ci.nsIPrefService);
 
     // enable quota manager testing mode
     var pref = 'dom.quotaManager.testing';
     prefService.getBranch(null).setBoolPref(pref, true);
 
-    qm.reset();
+    var request = qm.reset();
+    request.callback = resolve;
 
     // disable quota manager testing mode
     //prefService.getBranch(null).setBoolPref(pref, false);
-
-    var uri = Cc['@mozilla.org/network/io-service;1']
-              .getService(Ci.nsIIOService)
-              .newURI('http://example.com', null, null);
-    var principal = Cc['@mozilla.org/scriptsecuritymanager;1']
-                    .getService(Ci.nsIScriptSecurityManager)
-                    .getSystemPrincipal();
-
-    // use getUsageForPrincipal() to get a callback when the reset() is done
-    qm.getUsageForPrincipal(principal, function(principal, usage, fileUsage) {
-      resolve(usage);
-    });
   });
 }
 
 function run_test() {
   do_test_pending();
   do_get_profile();
 
   var directoryService = Cc['@mozilla.org/file/directory_service;1']