Bug 878703 - Cleanup usage of IO thread only objects. r=bent, a=akeybl
authorJan Varga <jan.varga@gmail.com>
Wed, 05 Jun 2013 10:11:23 +0200
changeset 142875 d2f19cdab655ebfc77871207c4a204df105897bd
parent 142874 0fbf00829f385d37d7a39f7549f9496c46e9a88e
child 142876 3d3302ae35552e3bbaa60c88e858b468713a6b9e
push id2579
push userakeybl@mozilla.com
push dateMon, 24 Jun 2013 18:52:47 +0000
treeherdermozilla-beta@b69b7de8a05a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbent, akeybl
bugs878703
milestone23.0a2
Bug 878703 - Cleanup usage of IO thread only objects. r=bent, a=akeybl
dom/base/nsDOMWindowUtils.cpp
dom/indexedDB/Client.cpp
dom/indexedDB/Client.h
dom/indexedDB/FileInfo.cpp
dom/indexedDB/FileInfo.h
dom/indexedDB/FileManager.cpp
dom/indexedDB/IDBDatabase.cpp
dom/indexedDB/IDBFactory.cpp
dom/indexedDB/IndexedDatabaseManager.cpp
dom/indexedDB/IndexedDatabaseManager.h
dom/indexedDB/OpenDatabaseHelper.cpp
dom/quota/Client.h
dom/quota/QuotaCommon.h
dom/quota/QuotaManager.cpp
dom/quota/QuotaManager.h
--- a/dom/base/nsDOMWindowUtils.cpp
+++ b/dom/base/nsDOMWindowUtils.cpp
@@ -57,17 +57,16 @@
 #include <gdk/gdk.h>
 #include <gdk/gdkx.h>
 #endif
 
 #include "Layers.h"
 #include "nsIIOService.h"
 
 #include "mozilla/dom/Element.h"
-#include "mozilla/dom/indexedDB/FileInfo.h"
 #include "mozilla/dom/indexedDB/IndexedDatabaseManager.h"
 #include "mozilla/dom/quota/QuotaManager.h"
 #include "GeckoProfiler.h"
 #include "nsDOMBlobBuilder.h"
 #include "nsIDOMFileHandle.h"
 #include "nsPrintfCString.h"
 #include "nsViewportInfo.h"
 #include "nsIFormControl.h"
@@ -2793,38 +2792,25 @@ nsDOMWindowUtils::GetFileReferences(cons
   nsCString origin;
   nsresult rv = quota::QuotaManager::GetASCIIOriginFromWindow(window, origin);
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsRefPtr<indexedDB::IndexedDatabaseManager> mgr =
     indexedDB::IndexedDatabaseManager::Get();
 
   if (mgr) {
-    nsRefPtr<indexedDB::FileManager> fileManager =
-      mgr->GetFileManager(origin, aDatabaseName);
-
-    if (fileManager) {
-      nsRefPtr<indexedDB::FileInfo> fileInfo = fileManager->GetFileInfo(aId);
-
-      if (fileInfo) {
-        fileInfo->GetReferences(aRefCnt, aDBRefCnt, aSliceRefCnt);
-
-        if (*aRefCnt != -1) {
-          // We added an extra temp ref, so account for that accordingly.
-          (*aRefCnt)--;
-        }
-
-        *aResult = true;
-        return NS_OK;
-      }
-    }
+    rv = mgr->BlockAndGetFileReferences(origin, aDatabaseName, aId, aRefCnt,
+                                        aDBRefCnt, aSliceRefCnt, aResult);
+    NS_ENSURE_SUCCESS(rv, rv);
   }
-
-  *aRefCnt = *aDBRefCnt = *aSliceRefCnt = -1;
-  *aResult = false;
+  else {
+    *aRefCnt = *aDBRefCnt = *aSliceRefCnt = -1;
+    *aResult = false;
+  }
+
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDOMWindowUtils::IsIncrementalGCEnabled(JSContext* cx, bool* aResult)
 {
   if (!nsContentUtils::IsCallerChrome()) {
     return NS_ERROR_DOM_SECURITY_ERR;
--- a/dom/indexedDB/Client.cpp
+++ b/dom/indexedDB/Client.cpp
@@ -10,16 +10,17 @@
 #include "mozilla/dom/quota/UsageRunnable.h"
 #include "mozilla/dom/quota/Utilities.h"
 
 #include "IDBDatabase.h"
 #include "IndexedDatabaseManager.h"
 #include "TransactionThreadPool.h"
 
 USING_INDEXEDDB_NAMESPACE
+using mozilla::dom::quota::AssertIsOnIOThread;
 using mozilla::dom::quota::QuotaManager;
 
 namespace {
 
 bool
 GetDatabaseBaseFilename(const nsAString& aFilename,
                         nsAString& aDatabaseBaseFilename)
 {
@@ -41,16 +42,18 @@ GetDatabaseBaseFilename(const nsAString&
 
 // This needs to be fully qualified to not confuse trace refcnt assertions.
 NS_IMPL_ADDREF(mozilla::dom::indexedDB::Client)
 NS_IMPL_RELEASE(mozilla::dom::indexedDB::Client)
 
 nsresult
 Client::InitOrigin(const nsACString& aOrigin, UsageRunnable* aUsageRunnable)
 {
+  AssertIsOnIOThread();
+
   nsCOMPtr<nsIFile> directory;
   nsresult rv = GetDirectory(aOrigin, getter_AddRefs(directory));
   NS_ENSURE_SUCCESS(rv, rv);
 
   // We need to see if there are any files in the directory already. If they
   // are database files then we need to cleanup stored files (if it's needed)
   // and also get the usage.
 
@@ -157,38 +160,64 @@ Client::InitOrigin(const nsACString& aOr
 
   return NS_OK;
 }
 
 nsresult
 Client::GetUsageForOrigin(const nsACString& aOrigin,
                           UsageRunnable* aUsageRunnable)
 {
+  AssertIsOnIOThread();
   NS_ASSERTION(aUsageRunnable, "Null pointer!");
 
   nsCOMPtr<nsIFile> directory;
   nsresult rv = GetDirectory(aOrigin, getter_AddRefs(directory));
   NS_ENSURE_SUCCESS(rv, rv);
 
   rv = GetUsageForDirectoryInternal(directory, aUsageRunnable, true);
   NS_ENSURE_SUCCESS(rv, rv);
 
   return NS_OK;
 }
 
+void
+Client::OnOriginClearCompleted(const nsACString& aPattern)
+{
+  AssertIsOnIOThread();
+
+  IndexedDatabaseManager* mgr = IndexedDatabaseManager::Get();
+  if (mgr) {
+    mgr->InvalidateFileManagersForPattern(aPattern);
+  }
+}
+
+void
+Client::ReleaseIOThreadObjects()
+{
+  AssertIsOnIOThread();
+
+  IndexedDatabaseManager* mgr = IndexedDatabaseManager::Get();
+  if (mgr) {
+    mgr->InvalidateAllFileManagers();
+  }
+}
+
 bool
 Client::IsTransactionServiceActivated()
 {
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+
   return !!TransactionThreadPool::Get();
 }
 
 void
 Client::WaitForStoragesToComplete(nsTArray<nsIOfflineStorage*>& aStorages,
                                   nsIRunnable* aCallback)
 {
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
   NS_ASSERTION(!aStorages.IsEmpty(), "No storages to wait on!");
   NS_ASSERTION(aCallback, "Passed null callback!");
 
   TransactionThreadPool* pool = TransactionThreadPool::Get();
   NS_ASSERTION(pool, "Should have checked if transaction service is active!");
 
   nsTArray<nsRefPtr<IDBDatabase> > databases(aStorages.Length());
   for (uint32_t index = 0; index < aStorages.Length(); index++) {
@@ -201,61 +230,48 @@ Client::WaitForStoragesToComplete(nsTArr
   }
 
   pool->WaitForDatabasesToComplete(databases, aCallback);
 }
 
 void
 Client::AbortTransactionsForStorage(nsIOfflineStorage* aStorage)
 {
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
   NS_ASSERTION(aStorage, "Passed null storage!");
 
   TransactionThreadPool* pool = TransactionThreadPool::Get();
   NS_ASSERTION(pool, "Should have checked if transaction service is active!");
 
   IDBDatabase* database = IDBDatabase::FromStorage(aStorage);
   NS_ASSERTION(database, "This shouldn't be null!");
 
   pool->AbortTransactionsForDatabase(database);
 }
 
 bool
 Client::HasTransactionsForStorage(nsIOfflineStorage* aStorage)
 {
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+
   TransactionThreadPool* pool = TransactionThreadPool::Get();
   NS_ASSERTION(pool, "Should have checked if transaction service is active!");
 
   IDBDatabase* database = IDBDatabase::FromStorage(aStorage);
   NS_ASSERTION(database, "This shouldn't be null!");
 
   return pool->HasTransactionsForDatabase(database);
 }
 
 void
-Client::OnOriginClearCompleted(const nsACString& aPattern)
-{
-  IndexedDatabaseManager* mgr = IndexedDatabaseManager::Get();
-  if (mgr) {
-    mgr->InvalidateFileManagersForPattern(aPattern);
-  }
-}
-
-void
 Client::ShutdownTransactionService()
 {
-  TransactionThreadPool::Shutdown();
-}
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
-void
-Client::OnShutdownCompleted()
-{
-  IndexedDatabaseManager* mgr = IndexedDatabaseManager::Get();
-  if (mgr) {
-    mgr->InvalidateAllFileManagers();
-  }
+  TransactionThreadPool::Shutdown();
 }
 
 nsresult
 Client::GetDirectory(const nsACString& aOrigin, nsIFile** aDirectory)
 {
   QuotaManager* quotaManager = QuotaManager::Get();
   NS_ASSERTION(quotaManager, "This should never fail!");
 
--- a/dom/indexedDB/Client.h
+++ b/dom/indexedDB/Client.h
@@ -36,16 +36,22 @@ public:
   virtual nsresult
   InitOrigin(const nsACString& aOrigin,
              UsageRunnable* aUsageRunnable) MOZ_OVERRIDE;
 
   virtual nsresult
   GetUsageForOrigin(const nsACString& aOrigin,
                     UsageRunnable* aUsageRunnable) MOZ_OVERRIDE;
 
+  virtual void
+  OnOriginClearCompleted(const nsACString& aPattern) MOZ_OVERRIDE;
+
+  virtual void
+  ReleaseIOThreadObjects() MOZ_OVERRIDE;
+
   virtual bool
   IsFileServiceUtilized() MOZ_OVERRIDE
   {
     return true;
   }
 
   virtual bool
   IsTransactionServiceActivated() MOZ_OVERRIDE;
@@ -56,24 +62,18 @@ public:
 
   virtual void
   AbortTransactionsForStorage(nsIOfflineStorage* aStorage) MOZ_OVERRIDE;
 
   virtual bool
   HasTransactionsForStorage(nsIOfflineStorage* aStorage) MOZ_OVERRIDE;
 
   virtual void
-  OnOriginClearCompleted(const nsACString& aPattern) MOZ_OVERRIDE;
-
-  virtual void
   ShutdownTransactionService() MOZ_OVERRIDE;
 
-  virtual void
-  OnShutdownCompleted() MOZ_OVERRIDE;
-
 private:
   nsresult
   GetDirectory(const nsACString& aOrigin, nsIFile** aDirectory);
 
   nsresult
   GetUsageForDirectoryInternal(nsIFile* aDirectory,
                                UsageRunnable* aUsageRunnable,
                                bool aDatabaseFiles);
--- a/dom/indexedDB/FileInfo.cpp
+++ b/dom/indexedDB/FileInfo.cpp
@@ -68,38 +68,44 @@ void
 FileInfo::UpdateReferences(nsAutoRefCnt& aRefCount, int32_t aDelta,
                            bool aClear)
 {
   if (IndexedDatabaseManager::IsClosed()) {
     NS_ERROR("Shouldn't be called after shutdown!");
     return;
   }
 
+  bool needsCleanup;
   {
     MutexAutoLock lock(IndexedDatabaseManager::FileMutex());
 
     aRefCount = aClear ? 0 : aRefCount + aDelta;
 
     if (mRefCnt + mDBRefCnt + mSliceRefCnt > 0) {
       return;
     }
 
     mFileManager->mFileInfos.Remove(Id());
+
+    needsCleanup = !mFileManager->Invalidated();
   }
 
-  Cleanup();
+  if (needsCleanup) {
+    Cleanup();
+  }
 
   delete this;
 }
 
 void
 FileInfo::Cleanup()
 {
-  if (quota::QuotaManager::IsShuttingDown() ||
-      mFileManager->Invalidated()) {
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+
+  if (quota::QuotaManager::IsShuttingDown()) {
     return;
   }
 
   nsRefPtr<IndexedDatabaseManager> mgr = IndexedDatabaseManager::Get();
   NS_ASSERTION(mgr, "Shouldn't be null!");
 
   if (NS_FAILED(mgr->AsyncDeleteFile(mFileManager, Id()))) {
     NS_WARNING("Failed to delete file asynchronously!");
--- a/dom/indexedDB/FileInfo.h
+++ b/dom/indexedDB/FileInfo.h
@@ -22,21 +22,17 @@ class FileInfo
   friend class FileManager;
 
 public:
   FileInfo(FileManager* aFileManager)
   : mFileManager(aFileManager)
   { }
 
   virtual ~FileInfo()
-  {
-#ifdef DEBUG
-    NS_ASSERTION(NS_IsMainThread(), "File info destroyed on wrong thread!");
-#endif
-  }
+  { }
 
   static
   FileInfo* Create(FileManager* aFileManager, int64_t aId);
 
   void AddRef()
   {
     if (IndexedDatabaseManager::IsClosed()) {
       NS_AtomicIncrementRefcnt(mRefCnt);
--- a/dom/indexedDB/FileManager.cpp
+++ b/dom/indexedDB/FileManager.cpp
@@ -19,16 +19,17 @@
 #include "FileInfo.h"
 #include "IndexedDatabaseManager.h"
 #include "OpenDatabaseHelper.h"
 
 #include "IndexedDatabaseInlines.h"
 #include <algorithm>
 
 USING_INDEXEDDB_NAMESPACE
+using mozilla::dom::quota::AssertIsOnIOThread;
 
 namespace {
 
 PLDHashOperator
 EnumerateToTArray(const uint64_t& aKey,
                   FileInfo* aValue,
                   void* aUserArg)
 {
@@ -57,17 +58,17 @@ GetDirectoryFor(const nsAString& aDirect
 }
 
 } // anonymous namespace
 
 nsresult
 FileManager::Init(nsIFile* aDirectory,
                   mozIStorageConnection* aConnection)
 {
-  NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
+  AssertIsOnIOThread();
   NS_ASSERTION(aDirectory, "Null directory!");
   NS_ASSERTION(aConnection, "Null connection!");
 
   mFileInfos.Init();
 
   bool exists;
   nsresult rv = aDirectory->Exists(&exists);
   NS_ENSURE_SUCCESS(rv, rv);
@@ -264,17 +265,17 @@ FileManager::GetFileForId(nsIFile* aDire
 }
 
 // static
 nsresult
 FileManager::InitDirectory(nsIFile* aDirectory,
                            nsIFile* aDatabaseFile,
                            const nsACString& aOrigin)
 {
-  NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
+  AssertIsOnIOThread();
   NS_ASSERTION(aDirectory, "Null directory!");
   NS_ASSERTION(aDatabaseFile, "Null database file!");
 
   bool exists;
   nsresult rv = aDirectory->Exists(&exists);
   NS_ENSURE_SUCCESS(rv, rv);
 
   if (!exists) {
@@ -380,16 +381,18 @@ FileManager::InitDirectory(nsIFile* aDir
 
   return NS_OK;
 }
 
 // static
 nsresult
 FileManager::GetUsage(nsIFile* aDirectory, uint64_t* aUsage)
 {
+  AssertIsOnIOThread();
+
   uint64_t usage = 0;
 
   nsCOMPtr<nsISimpleEnumerator> entries;
   nsresult rv = aDirectory->GetDirectoryEntries(getter_AddRefs(entries));
   NS_ENSURE_SUCCESS(rv, rv);
 
   bool hasMore;
   while (NS_SUCCEEDED((rv = entries->HasMoreElements(&hasMore))) && hasMore) {
--- a/dom/indexedDB/IDBDatabase.cpp
+++ b/dom/indexedDB/IDBDatabase.cpp
@@ -33,16 +33,17 @@
 #include "ProfilerHelpers.h"
 #include "TransactionThreadPool.h"
 
 #include "ipc/IndexedDBChild.h"
 #include "ipc/IndexedDBParent.h"
 
 USING_INDEXEDDB_NAMESPACE
 using mozilla::dom::ContentParent;
+using mozilla::dom::quota::AssertIsOnIOThread;
 using mozilla::dom::quota::Client;
 using mozilla::dom::quota::QuotaManager;
 
 namespace {
 
 class NoRequestDatabaseHelper : public AsyncConnectionHelper
 {
 public:
@@ -981,17 +982,17 @@ DeleteObjectStoreHelper::DoDatabaseWork(
   NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
   return NS_OK;
 }
 
 nsresult
 CreateFileHelper::DoDatabaseWork(mozIStorageConnection* aConnection)
 {
-  NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
+  AssertIsOnIOThread();
   NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
 
   PROFILER_LABEL("IndexedDB", "CreateFileHelper::DoDatabaseWork");
 
   if (IndexedDatabaseManager::InLowDiskSpaceMode()) {
     NS_WARNING("Refusing to create file because disk space is low!");
     return NS_ERROR_DOM_INDEXEDDB_QUOTA_ERR;
   }
--- a/dom/indexedDB/IDBFactory.cpp
+++ b/dom/indexedDB/IDBFactory.cpp
@@ -330,17 +330,17 @@ IgnoreWhitespace(PRUnichar c)
 
 // static
 nsresult
 IDBFactory::LoadDatabaseInformation(mozIStorageConnection* aConnection,
                                     nsIAtom* aDatabaseId,
                                     uint64_t* aVersion,
                                     ObjectStoreInfoArray& aObjectStores)
 {
-  NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
+  AssertIsOnIOThread();
   NS_ASSERTION(aConnection, "Null pointer!");
 
   aObjectStores.Clear();
 
    // Load object store names and ids.
   nsCOMPtr<mozIStorageStatement> stmt;
   nsresult rv = aConnection->CreateStatement(NS_LITERAL_CSTRING(
     "SELECT name, id, key_path, auto_increment "
--- a/dom/indexedDB/IndexedDatabaseManager.cpp
+++ b/dom/indexedDB/IndexedDatabaseManager.cpp
@@ -10,16 +10,17 @@
 #include "nsIDiskSpaceWatcher.h"
 #include "nsIDOMScriptObjectFactory.h"
 #include "nsIFile.h"
 #include "nsIFileStorage.h"
 #include "nsIObserverService.h"
 #include "nsIScriptError.h"
 
 #include "mozilla/ClearOnShutdown.h"
+#include "mozilla/CondVar.h"
 #include "mozilla/dom/quota/QuotaManager.h"
 #include "mozilla/dom/quota/Utilities.h"
 #include "mozilla/dom/TabContext.h"
 #include "mozilla/Services.h"
 #include "mozilla/storage.h"
 #include "nsContentUtils.h"
 #include "nsEventDispatcher.h"
 #include "nsThreadUtils.h"
@@ -55,23 +56,62 @@ public:
 
   AsyncDeleteFileRunnable(FileManager* aFileManager, int64_t aFileId);
 
 private:
   nsRefPtr<FileManager> mFileManager;
   int64_t mFileId;
 };
 
+class GetFileReferencesHelper MOZ_FINAL : public nsIRunnable
+{
+public:
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIRUNNABLE
+
+  GetFileReferencesHelper(const nsACString& aOrigin,
+                          const nsAString& aDatabaseName,
+                          int64_t aFileId)
+  : mOrigin(aOrigin), mDatabaseName(aDatabaseName), mFileId(aFileId),
+    mMutex(IndexedDatabaseManager::FileMutex()),
+    mCondVar(mMutex, "GetFileReferencesHelper::mCondVar"),
+    mMemRefCnt(-1),
+    mDBRefCnt(-1),
+    mSliceRefCnt(-1),
+    mResult(false),
+    mWaiting(true)
+  { }
+
+  nsresult
+  DispatchAndReturnFileReferences(int32_t* aMemRefCnt,
+                                  int32_t* aDBRefCnt,
+                                  int32_t* aSliceRefCnt,
+                                  bool* aResult);
+
+private:
+  nsCString mOrigin;
+  nsString mDatabaseName;
+  int64_t mFileId;
+
+  mozilla::Mutex& mMutex;
+  mozilla::CondVar mCondVar;
+  int32_t mMemRefCnt;
+  int32_t mDBRefCnt;
+  int32_t mSliceRefCnt;
+  bool mResult;
+  bool mWaiting;
+};
+
 PLDHashOperator
 InvalidateAndRemoveFileManagers(
                            const nsACString& aKey,
                            nsAutoPtr<nsTArray<nsRefPtr<FileManager> > >& aValue,
                            void* aUserArg)
 {
-  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+  AssertIsOnIOThread();
   NS_ASSERTION(!aKey.IsEmpty(), "Empty key!");
   NS_ASSERTION(aValue, "Null pointer!");
 
   const nsACString* pattern =
     static_cast<const nsACString*>(aUserArg);
 
   if (!pattern || PatternMatchesOrigin(*pattern, aKey)) {
     for (uint32_t i = 0; i < aValue->Length(); i++) {
@@ -341,16 +381,18 @@ IndexedDatabaseManager::InLowDiskSpaceMo
   return !!sLowDiskSpaceMode;
 }
 #endif
 
 already_AddRefed<FileManager>
 IndexedDatabaseManager::GetFileManager(const nsACString& aOrigin,
                                        const nsAString& aDatabaseName)
 {
+  AssertIsOnIOThread();
+
   nsTArray<nsRefPtr<FileManager> >* array;
   if (!mFileManagers.Get(aOrigin, &array)) {
     return nullptr;
   }
 
   for (uint32_t i = 0; i < array->Length(); i++) {
     nsRefPtr<FileManager>& fileManager = array->ElementAt(i);
 
@@ -361,46 +403,53 @@ IndexedDatabaseManager::GetFileManager(c
   }
 
   return nullptr;
 }
 
 void
 IndexedDatabaseManager::AddFileManager(FileManager* aFileManager)
 {
+  AssertIsOnIOThread();
   NS_ASSERTION(aFileManager, "Null file manager!");
 
   nsTArray<nsRefPtr<FileManager> >* array;
   if (!mFileManagers.Get(aFileManager->Origin(), &array)) {
     array = new nsTArray<nsRefPtr<FileManager> >();
     mFileManagers.Put(aFileManager->Origin(), array);
   }
 
   array->AppendElement(aFileManager);
 }
 
 void
 IndexedDatabaseManager::InvalidateAllFileManagers()
 {
+  AssertIsOnIOThread();
+
   mFileManagers.Enumerate(InvalidateAndRemoveFileManagers, nullptr);
 }
 
 void
 IndexedDatabaseManager::InvalidateFileManagersForPattern(
                                                      const nsACString& aPattern)
 {
+  AssertIsOnIOThread();
   NS_ASSERTION(!aPattern.IsEmpty(), "Empty pattern!");
+
   mFileManagers.Enumerate(InvalidateAndRemoveFileManagers,
                           const_cast<nsACString*>(&aPattern));
 }
 
 void
 IndexedDatabaseManager::InvalidateFileManager(const nsACString& aOrigin,
                                               const nsAString& aDatabaseName)
 {
+  AssertIsOnIOThread();
+
   nsTArray<nsRefPtr<FileManager> >* array;
   if (!mFileManagers.Get(aOrigin, &array)) {
     return;
   }
 
   for (uint32_t i = 0; i < array->Length(); i++) {
     nsRefPtr<FileManager> fileManager = array->ElementAt(i);
     if (fileManager->DatabaseName().Equals(aDatabaseName)) {
@@ -438,16 +487,36 @@ IndexedDatabaseManager::AsyncDeleteFile(
 
   nsresult rv =
     quotaManager->IOThread()->Dispatch(runnable, NS_DISPATCH_NORMAL);
   NS_ENSURE_SUCCESS(rv, rv);
 
   return NS_OK;
 }
 
+nsresult
+IndexedDatabaseManager::BlockAndGetFileReferences(
+                                                 const nsACString& aOrigin,
+                                                 const nsAString& aDatabaseName,
+                                                 int64_t aFileId,
+                                                 int32_t* aRefCnt,
+                                                 int32_t* aDBRefCnt,
+                                                 int32_t* aSliceRefCnt,
+                                                 bool* aResult)
+{
+  nsRefPtr<GetFileReferencesHelper> helper =
+    new GetFileReferencesHelper(aOrigin, aDatabaseName, aFileId);
+
+  nsresult rv = helper->DispatchAndReturnFileReferences(aRefCnt, aDBRefCnt,
+                                                        aSliceRefCnt, aResult);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  return NS_OK;
+}
+
 NS_IMPL_ADDREF(IndexedDatabaseManager)
 NS_IMPL_RELEASE_WITH_DESTROY(IndexedDatabaseManager, Destroy())
 NS_IMPL_QUERY_INTERFACE2(IndexedDatabaseManager, nsIIndexedDatabaseManager,
                                                  nsIObserver)
 
 NS_IMETHODIMP
 IndexedDatabaseManager::InitWindowless(const jsval& aObj, JSContext* aCx)
 {
@@ -542,17 +611,17 @@ AsyncDeleteFileRunnable::AsyncDeleteFile
 }
 
 NS_IMPL_THREADSAFE_ISUPPORTS1(AsyncDeleteFileRunnable,
                               nsIRunnable)
 
 NS_IMETHODIMP
 AsyncDeleteFileRunnable::Run()
 {
-  NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
+  AssertIsOnIOThread();
 
   nsCOMPtr<nsIFile> directory = mFileManager->GetDirectory();
   NS_ENSURE_TRUE(directory, NS_ERROR_FAILURE);
 
   nsCOMPtr<nsIFile> file = mFileManager->GetFileForId(directory, mFileId);
   NS_ENSURE_TRUE(file, NS_ERROR_FAILURE);
 
   nsresult rv;
@@ -579,8 +648,74 @@ AsyncDeleteFileRunnable::Run()
   file = mFileManager->GetFileForId(directory, mFileId);
   NS_ENSURE_TRUE(file, NS_ERROR_FAILURE);
 
   rv = file->Remove(false);
   NS_ENSURE_SUCCESS(rv, rv);
 
   return NS_OK;
 }
+
+nsresult
+GetFileReferencesHelper::DispatchAndReturnFileReferences(int32_t* aMemRefCnt,
+                                                         int32_t* aDBRefCnt,
+                                                         int32_t* aSliceRefCnt,
+                                                         bool* aResult)
+{
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+
+  QuotaManager* quotaManager = QuotaManager::Get();
+  NS_ASSERTION(quotaManager, "Shouldn't be null!");
+
+  nsresult rv =
+    quotaManager->IOThread()->Dispatch(this, NS_DISPATCH_NORMAL);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  mozilla::MutexAutoLock autolock(mMutex);
+  while (mWaiting) {
+    mCondVar.Wait();
+  }
+
+  *aMemRefCnt = mMemRefCnt;
+  *aDBRefCnt = mDBRefCnt;
+  *aSliceRefCnt = mSliceRefCnt;
+  *aResult = mResult;
+
+  return NS_OK;
+}
+
+NS_IMPL_THREADSAFE_ISUPPORTS1(GetFileReferencesHelper,
+                              nsIRunnable)
+
+NS_IMETHODIMP
+GetFileReferencesHelper::Run()
+{
+  AssertIsOnIOThread();
+
+  IndexedDatabaseManager* mgr = IndexedDatabaseManager::Get();
+  NS_ASSERTION(mgr, "This should never fail!");
+
+  nsRefPtr<FileManager> fileManager =
+    mgr->GetFileManager(mOrigin, mDatabaseName);
+
+  if (fileManager) {
+    nsRefPtr<FileInfo> fileInfo = fileManager->GetFileInfo(mFileId);
+
+    if (fileInfo) {
+      fileInfo->GetReferences(&mMemRefCnt, &mDBRefCnt, &mSliceRefCnt);
+
+      if (mMemRefCnt != -1) {
+        // We added an extra temp ref, so account for that accordingly.
+        mMemRefCnt--;
+      }
+
+      mResult = true;
+    }
+  }
+
+  mozilla::MutexAutoLock lock(mMutex);
+  NS_ASSERTION(mWaiting, "Huh?!");
+
+  mWaiting = false;
+  mCondVar.Notify();
+
+  return NS_OK;
+}
--- a/dom/indexedDB/IndexedDatabaseManager.h
+++ b/dom/indexedDB/IndexedDatabaseManager.h
@@ -91,16 +91,28 @@ public:
   void
   InvalidateFileManager(const nsACString& aOrigin,
                         const nsAString& aDatabaseName);
 
   nsresult
   AsyncDeleteFile(FileManager* aFileManager,
                   int64_t aFileId);
 
+  // Don't call this method in real code, it blocks the main thread!
+  // It is intended to be used by mochitests to test correctness of the special
+  // reference counting of stored blobs/files.
+  nsresult
+  BlockAndGetFileReferences(const nsACString& aOrigin,
+                            const nsAString& aDatabaseName,
+                            int64_t aFileId,
+                            int32_t* aRefCnt,
+                            int32_t* aDBRefCnt,
+                            int32_t* aSliceRefCnt,
+                            bool* aResult);
+
   static mozilla::Mutex&
   FileMutex()
   {
     IndexedDatabaseManager* mgr = Get();
     NS_ASSERTION(mgr, "Must have a manager here!");
 
     return mgr->mFileMutex;
   }
--- a/dom/indexedDB/OpenDatabaseHelper.cpp
+++ b/dom/indexedDB/OpenDatabaseHelper.cpp
@@ -91,17 +91,17 @@ GetDatabaseFilename(const nsAString& aNa
   aDatabaseFilename.Append(NS_ConvertASCIItoUTF16(substring));
 
   return NS_OK;
 }
 
 nsresult
 CreateFileTables(mozIStorageConnection* aDBConn)
 {
-  NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
+  AssertIsOnIOThread();
   NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
 
   PROFILER_LABEL("IndexedDB", "CreateFileTables");
 
   // Table `file`
   nsresult rv = aDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
     "CREATE TABLE file ("
       "id INTEGER PRIMARY KEY, "
@@ -153,17 +153,17 @@ CreateFileTables(mozIStorageConnection* 
   NS_ENSURE_SUCCESS(rv, rv);
 
   return NS_OK;
 }
 
 nsresult
 CreateTables(mozIStorageConnection* aDBConn)
 {
-  NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
+  AssertIsOnIOThread();
   NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
   NS_ASSERTION(aDBConn, "Passing a null database connection!");
 
   PROFILER_LABEL("IndexedDB", "CreateTables");
 
   // Table `database`
   nsresult rv = aDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
     "CREATE TABLE database ("
@@ -270,17 +270,17 @@ CreateTables(mozIStorageConnection* aDBC
   NS_ENSURE_SUCCESS(rv, rv);
 
   return NS_OK;
 }
 
 nsresult
 UpgradeSchemaFrom4To5(mozIStorageConnection* aConnection)
 {
-  NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
+  AssertIsOnIOThread();
   NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
 
   PROFILER_LABEL("IndexedDB", "UpgradeSchemaFrom4To5");
 
   nsresult rv;
 
   // All we changed is the type of the version column, so lets try to
   // convert that to an integer, and if we fail, set it to 0.
@@ -359,17 +359,17 @@ UpgradeSchemaFrom4To5(mozIStorageConnect
   NS_ENSURE_SUCCESS(rv, rv);
 
   return NS_OK;
 }
 
 nsresult
 UpgradeSchemaFrom5To6(mozIStorageConnection* aConnection)
 {
-  NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
+  AssertIsOnIOThread();
   NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
 
   PROFILER_LABEL("IndexedDB", "UpgradeSchemaFrom5To6");
 
   // First, drop all the indexes we're no longer going to use.
   nsresult rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
     "DROP INDEX key_index;"
   ));
@@ -717,17 +717,17 @@ UpgradeSchemaFrom5To6(mozIStorageConnect
   NS_ENSURE_SUCCESS(rv, rv);
 
   return NS_OK;
 }
 
 nsresult
 UpgradeSchemaFrom6To7(mozIStorageConnection* aConnection)
 {
-  NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
+  AssertIsOnIOThread();
   NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
 
   PROFILER_LABEL("IndexedDB", "UpgradeSchemaFrom6To7");
 
   nsresult rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
     "CREATE TEMPORARY TABLE temp_upgrade ("
       "id, "
       "name, "
@@ -776,17 +776,17 @@ UpgradeSchemaFrom6To7(mozIStorageConnect
   NS_ENSURE_SUCCESS(rv, rv);
 
   return NS_OK;
 }
 
 nsresult
 UpgradeSchemaFrom7To8(mozIStorageConnection* aConnection)
 {
-  NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
+  AssertIsOnIOThread();
   NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
 
   PROFILER_LABEL("IndexedDB", "UpgradeSchemaFrom7To8");
 
   nsresult rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
     "CREATE TEMPORARY TABLE temp_upgrade ("
       "id, "
       "object_store_id, "
@@ -900,17 +900,17 @@ public:
   }
 };
 
 NS_IMPL_ISUPPORTS1(CompressDataBlobsFunction, mozIStorageFunction)
 
 nsresult
 UpgradeSchemaFrom8To9_0(mozIStorageConnection* aConnection)
 {
-  NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
+  AssertIsOnIOThread();
   NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
 
   PROFILER_LABEL("IndexedDB", "UpgradeSchemaFrom8To9_0");
 
   // We no longer use the dataVersion column.
   nsresult rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
     "UPDATE database SET dataVersion = 0;"
   ));
@@ -941,17 +941,17 @@ UpgradeSchemaFrom8To9_0(mozIStorageConne
   NS_ENSURE_SUCCESS(rv, rv);
 
   return NS_OK;
 }
 
 nsresult
 UpgradeSchemaFrom9_0To10_0(mozIStorageConnection* aConnection)
 {
-  NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
+  AssertIsOnIOThread();
   NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
 
   PROFILER_LABEL("IndexedDB", "UpgradeSchemaFrom9_0To10_0");
 
   nsresult rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
     "ALTER TABLE object_data ADD COLUMN file_ids TEXT;"
   ));
   NS_ENSURE_SUCCESS(rv, rv);
@@ -968,17 +968,17 @@ UpgradeSchemaFrom9_0To10_0(mozIStorageCo
   NS_ENSURE_SUCCESS(rv, rv);
 
   return NS_OK;
 }
 
 nsresult
 UpgradeSchemaFrom10_0To11_0(mozIStorageConnection* aConnection)
 {
-  NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
+  AssertIsOnIOThread();
   NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
 
   PROFILER_LABEL("IndexedDB", "UpgradeSchemaFrom10_0To11_0");
 
   nsresult rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
     "CREATE TEMPORARY TABLE temp_upgrade ("
       "id, "
       "object_store_id, "
@@ -1158,17 +1158,17 @@ public:
   }
 };
 
 NS_IMPL_ISUPPORTS1(EncodeKeysFunction, mozIStorageFunction)
 
 nsresult
 UpgradeSchemaFrom11_0To12_0(mozIStorageConnection* aConnection)
 {
-  NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
+  AssertIsOnIOThread();
   NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
 
   PROFILER_LABEL("IndexedDB", "UpgradeSchemaFrom11_0To12_0");
 
   NS_NAMED_LITERAL_CSTRING(encoderName, "encode");
 
   nsCOMPtr<mozIStorageFunction> encoder = new EncodeKeysFunction();
 
@@ -1375,17 +1375,17 @@ UpgradeSchemaFrom11_0To12_0(mozIStorageC
 
   return NS_OK;
 }
 
 nsresult
 UpgradeSchemaFrom12_0To13_0(mozIStorageConnection* aConnection,
                             bool* aVacuumNeeded)
 {
-  NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
+  AssertIsOnIOThread();
   NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
 
   PROFILER_LABEL("IndexedDB", "UpgradeSchemaFrom12_0To13_0");
 
   nsresult rv;
 
 #if defined(MOZ_WIDGET_ANDROID) || defined(MOZ_WIDGET_GONK)
   int32_t defaultPageSize;
@@ -1712,25 +1712,17 @@ OpenDatabaseHelper::RunImmediately()
 
   mState = eFiringEvents;
   return this->Run();
 }
 
 nsresult
 OpenDatabaseHelper::DoDatabaseWork()
 {
-#ifdef DEBUG
-  {
-    bool correctThread;
-    NS_ASSERTION(NS_SUCCEEDED(QuotaManager::Get()->IOThread()->
-                              IsOnCurrentThread(&correctThread)) &&
-                 correctThread,
-                 "Running on the wrong thread!");
-  }
-#endif
+  AssertIsOnIOThread();
   NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
 
   PROFILER_LABEL("IndexedDB", "OpenDatabaseHelper::DoDatabaseWork");
 
   mState = eFiringEvents; // In case we fail somewhere along the line.
 
   if (QuotaManager::IsShuttingDown()) {
     return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
@@ -1866,17 +1858,17 @@ OpenDatabaseHelper::DoDatabaseWork()
 nsresult
 OpenDatabaseHelper::CreateDatabaseConnection(
                                         nsIFile* aDBFile,
                                         nsIFile* aFMDirectory,
                                         const nsAString& aName,
                                         const nsACString& aOrigin,
                                         mozIStorageConnection** aConnection)
 {
-  NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
+  AssertIsOnIOThread();
   NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
 
   PROFILER_LABEL("IndexedDB", "OpenDatabaseHelper::CreateDatabaseConnection");
 
   nsresult rv;
   bool exists;
 
   if (IndexedDatabaseManager::InLowDiskSpaceMode()) {
@@ -2167,21 +2159,16 @@ OpenDatabaseHelper::Run()
       }
 
       case eDeleteCompleted: {
         // Destroy the database now (we should have the only ref).
         mDatabase = nullptr;
 
         DatabaseInfo::Remove(mDatabaseId);
 
-        IndexedDatabaseManager* mgr = IndexedDatabaseManager::Get();
-        NS_ASSERTION(mgr, "This should never fail!");
-
-        mgr->InvalidateFileManager(mASCIIOrigin, mName);
-
         mState = eFiringEvents;
         break;
       }
 
       case eFiringEvents: {
         // Notify the request that we're done, but only if we didn't just
         // finish a [SetVersion/DeleteDatabase]Helper.  In that case, the
         // helper tells the request that it is done, and we avoid calling
@@ -2574,17 +2561,17 @@ SetVersionHelper::NotifyTransactionPostC
   return rv;
 }
 
 NS_IMPL_ISUPPORTS_INHERITED0(DeleteDatabaseHelper, AsyncConnectionHelper);
 
 nsresult
 DeleteDatabaseHelper::DoDatabaseWork(mozIStorageConnection* aConnection)
 {
-  NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
+  AssertIsOnIOThread();
   NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
   NS_ASSERTION(!aConnection, "How did we get a connection here?");
 
   PROFILER_LABEL("IndexedDB", "DeleteDatabaseHelper::DoDatabaseWork");
 
   const StoragePrivilege& privilege = mOpenHelper->Privilege();
 
   QuotaManager* quotaManager = QuotaManager::Get();
@@ -2678,16 +2665,21 @@ DeleteDatabaseHelper::DoDatabaseWork(moz
     if (privilege != Chrome) {
       QuotaManager* quotaManager = QuotaManager::Get();
       NS_ASSERTION(quotaManager, "Shouldn't be null!");
 
       quotaManager->DecreaseUsageForOrigin(mASCIIOrigin, usage);
     }
   }
 
+  IndexedDatabaseManager* mgr = IndexedDatabaseManager::Get();
+  NS_ASSERTION(mgr, "This should never fail!");
+
+  mgr->InvalidateFileManager(mASCIIOrigin, mName);
+
   return NS_OK;
 }
 
 nsresult
 DeleteDatabaseHelper::GetSuccessResult(JSContext* aCx, jsval* aVal)
 {
   return NS_OK;
 }
--- a/dom/quota/Client.h
+++ b/dom/quota/Client.h
@@ -63,23 +63,31 @@ public:
     }
     else {
       return NS_ERROR_FAILURE;
     }
 
     return NS_OK;
   }
 
+  // Methods which are called on the IO thred.
   virtual nsresult
   InitOrigin(const nsACString& aOrigin, UsageRunnable* aUsageRunnable) = 0;
 
   virtual nsresult
   GetUsageForOrigin(const nsACString& aOrigin,
                     UsageRunnable* aUsageRunnable) = 0;
 
+  virtual void
+  OnOriginClearCompleted(const nsACString& aPattern) = 0;
+
+  virtual void
+  ReleaseIOThreadObjects() = 0;
+
+  // Methods which are called on the main thred.
   virtual bool
   IsFileServiceUtilized() = 0;
 
   virtual bool
   IsTransactionServiceActivated() = 0;
 
   virtual void
   WaitForStoragesToComplete(nsTArray<nsIOfflineStorage*>& aStorages,
@@ -87,24 +95,18 @@ public:
 
   virtual void
   AbortTransactionsForStorage(nsIOfflineStorage* aStorage) = 0;
 
   virtual bool
   HasTransactionsForStorage(nsIOfflineStorage* aStorage) = 0;
 
   virtual void
-  OnOriginClearCompleted(const nsACString& aPattern) = 0;
-
-  virtual void
   ShutdownTransactionService() = 0;
 
-  virtual void
-  OnShutdownCompleted() = 0;
-
 protected:
   virtual ~Client()
   { }
 };
 
 END_QUOTA_NAMESPACE
 
 #endif // mozilla_dom_quota_client_h__
--- a/dom/quota/QuotaCommon.h
+++ b/dom/quota/QuotaCommon.h
@@ -15,9 +15,22 @@
 
 #define BEGIN_QUOTA_NAMESPACE \
   namespace mozilla { namespace dom { namespace quota {
 #define END_QUOTA_NAMESPACE \
   } /* namespace quota */ } /* namespace dom */ } /* namespace mozilla */
 #define USING_QUOTA_NAMESPACE \
   using namespace mozilla::dom::quota;
 
+BEGIN_QUOTA_NAMESPACE
+
+#ifdef DEBUG
+void
+AssertIsOnIOThread();
+#else
+inline void
+AssertIsOnIOThread()
+{ }
+#endif
+
+END_QUOTA_NAMESPACE
+
 #endif // mozilla_dom_quota_quotacommon_h__
--- a/dom/quota/QuotaManager.cpp
+++ b/dom/quota/QuotaManager.cpp
@@ -235,16 +235,30 @@ public:
   nsCOMPtr<nsIURI> mURI;
   nsCOMPtr<nsIUsageCallback> mCallback;
   uint32_t mAppId;
   OriginOrPatternString mOrigin;
   CallbackState mCallbackState;
   bool mInMozBrowserOnly;
 };
 
+#ifdef DEBUG
+void
+AssertIsOnIOThread()
+{
+  QuotaManager* quotaManager = QuotaManager::Get();
+  NS_ASSERTION(quotaManager, "Must have a manager here!");
+
+  bool correctThread;
+  NS_ASSERTION(NS_SUCCEEDED(quotaManager->IOThread()->
+                            IsOnCurrentThread(&correctThread)) && correctThread,
+               "Running on the wrong thread!");
+}
+#endif
+
 END_QUOTA_NAMESPACE
 
 namespace {
 
 QuotaManager* gInstance = nullptr;
 int32_t gShutdown = 0;
 
 int32_t gStorageQuotaMB = DEFAULT_QUOTA_MB;
@@ -821,24 +835,17 @@ QuotaManager::GetDirectoryForOrigin(cons
   return NS_OK;
 }
 
 nsresult
 QuotaManager::EnsureOriginIsInitialized(const nsACString& aOrigin,
                                         bool aTrackQuota,
                                         nsIFile** aDirectory)
 {
-#ifdef DEBUG
-  {
-    bool correctThread;
-    NS_ASSERTION(NS_SUCCEEDED(mIOThread->IsOnCurrentThread(&correctThread)) &&
-                 correctThread,
-                 "Running on the wrong thread!");
-  }
-#endif
+  AssertIsOnIOThread();
 
   nsCOMPtr<nsIFile> directory;
   nsresult rv = GetDirectoryForOrigin(aOrigin, getter_AddRefs(directory));
   NS_ENSURE_SUCCESS(rv, rv);
 
   bool exists;
   rv = directory->Exists(&exists);
   NS_ENSURE_SUCCESS(rv, rv);
@@ -943,32 +950,30 @@ QuotaManager::EnsureOriginIsInitialized(
 
   mInitializedOrigins.AppendElement(aOrigin);
 
   NS_ADDREF(*aDirectory = directory);
   return NS_OK;
 }
 
 void
-QuotaManager::UninitializeOriginsByPattern(const nsACString& aPattern)
+QuotaManager::OriginClearCompleted(const nsACString& aPattern)
 {
-#ifdef DEBUG
-  {
-    bool correctThread;
-    NS_ASSERTION(NS_SUCCEEDED(mIOThread->IsOnCurrentThread(&correctThread)) &&
-                 correctThread,
-                 "Running on the wrong thread!");
-  }
-#endif
-
-  for (int32_t i = mInitializedOrigins.Length() - 1; i >= 0; i--) {
+  AssertIsOnIOThread();
+
+  int32_t i;
+  for (i = mInitializedOrigins.Length() - 1; i >= 0; i--) {
     if (PatternMatchesOrigin(aPattern, mInitializedOrigins[i])) {
       mInitializedOrigins.RemoveElementAt(i);
     }
   }
+
+  for (i = 0; i < Client::TYPE_MAX; i++) {
+    mClients[i]->OnOriginClearCompleted(aPattern);
+  }
 }
 
 already_AddRefed<mozilla::dom::quota::Client>
 QuotaManager::GetClient(Client::Type aClientType)
 {
   nsRefPtr<Client> client = mClients.SafeElementAt(aClientType);
   return client.forget();
 }
@@ -1223,16 +1228,27 @@ QuotaManager::Observe(nsISupports* aSubj
             if (!NS_ProcessNextEvent(thread)) {
               NS_ERROR("Failed to process next event!");
               break;
             }
           }
         }
       }
 
+      // 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!");
       }
 
       // Kick off the shutdown timer.
       if (NS_FAILED(mShutdownTimer->Init(this, DEFAULT_SHUTDOWN_TIMER_MS,
                                          nsITimer::TYPE_ONE_SHOT))) {
@@ -1246,20 +1262,16 @@ QuotaManager::Observe(nsISupports* aSubj
       }
 
       // Cancel the timer regardless of whether it actually fired.
       if (NS_FAILED(mShutdownTimer->Cancel())) {
         NS_WARNING("Failed to cancel shutdown timer!");
       }
     }
 
-    for (uint32_t index = 0; index < Client::TYPE_MAX; index++) {
-      mClients[index]->OnShutdownCompleted();
-    }
-
     return NS_OK;
   }
 
   if (!strcmp(aTopic, NS_TIMER_CALLBACK_TOPIC)) {
     NS_ASSERTION(IsMainProcess(), "Should only happen in the main process!");
 
     NS_WARNING("Some storage operations are taking longer than expected "
                "during shutdown and will be aborted!");
@@ -1815,17 +1827,17 @@ OriginClearRunnable::InvalidateOpenedSto
   for (uint32_t index = 0; index < storages.Length(); index++) {
     storages[index]->Invalidate();
   }
 }
 
 void
 OriginClearRunnable::DeleteFiles(QuotaManager* aQuotaManager)
 {
-  NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
+  AssertIsOnIOThread();
   NS_ASSERTION(aQuotaManager, "Don't pass me null!");
 
   nsresult rv;
 
   nsCOMPtr<nsIFile> directory =
     do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv);
   NS_ENSURE_SUCCESS_VOID(rv);
 
@@ -1873,17 +1885,17 @@ OriginClearRunnable::DeleteFiles(QuotaMa
       // This should never fail if we've closed all storage connections
       // correctly...
       NS_ERROR("Failed to remove directory!");
     }
   }
 
   aQuotaManager->RemoveQuotaForPattern(mOriginOrPattern);
 
-  aQuotaManager->UninitializeOriginsByPattern(mOriginOrPattern);
+  aQuotaManager->OriginClearCompleted(mOriginOrPattern);
 }
 
 NS_IMPL_THREADSAFE_ISUPPORTS1(OriginClearRunnable, nsIRunnable)
 
 NS_IMETHODIMP
 OriginClearRunnable::Run()
 {
   PROFILER_LABEL("Quota", "OriginClearRunnable::Run");
@@ -1908,17 +1920,17 @@ OriginClearRunnable::Run()
         quotaManager->AcquireExclusiveAccess(mOriginOrPattern, this,
                                              InvalidateOpenedStorages, nullptr);
       NS_ENSURE_SUCCESS(rv, rv);
 
       return NS_OK;
     }
 
     case IO: {
-      NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
+      AssertIsOnIOThread();
 
       AdvanceState();
 
       DeleteFiles(quotaManager);
 
       // Now dispatch back to the main thread.
       if (NS_FAILED(NS_DispatchToMainThread(this, NS_DISPATCH_NORMAL))) {
         NS_WARNING("Failed to dispatch to main thread!");
@@ -1926,20 +1938,16 @@ OriginClearRunnable::Run()
       }
 
       return NS_OK;
     }
 
     case Complete: {
       NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
-      for (uint32_t index = 0; index < Client::TYPE_MAX; index++) {
-        quotaManager->mClients[index]->OnOriginClearCompleted(mOriginOrPattern);
-      }
-
       // Tell the QuotaManager that we're done.
       quotaManager->AllowNextSynchronizedOp(mOriginOrPattern, nullptr);
 
       return NS_OK;
     }
 
     default:
       NS_ERROR("Unknown state value!");
@@ -2004,17 +2012,17 @@ AsyncUsageRunnable::RunInternal()
       if (NS_FAILED(rv)) {
         NS_WARNING("Failed to dispatch to the IO thread!");
       }
 
       return NS_OK;
     }
 
     case IO: {
-      NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
+      AssertIsOnIOThread();
 
       AdvanceState();
 
       // Get the directory that contains all the storage files we care about.
       nsCOMPtr<nsIFile> directory;
       rv = quotaManager->GetDirectoryForOrigin(mOrigin,
                                                getter_AddRefs(directory));
       NS_ENSURE_SUCCESS(rv, rv);
--- a/dom/quota/QuotaManager.h
+++ b/dom/quota/QuotaManager.h
@@ -191,17 +191,17 @@ public:
                         nsIFile** aDirectory) const;
 
   nsresult
   EnsureOriginIsInitialized(const nsACString& aOrigin,
                             bool aTrackQuota,
                             nsIFile** aDirectory);
 
   void
-  UninitializeOriginsByPattern(const nsACString& aPattern);
+  OriginClearCompleted(const nsACString& aPattern);
 
   nsIThread*
   IOThread()
   {
     NS_ASSERTION(mIOThread, "This should never be null!");
     return mIOThread;
   }
 
@@ -288,16 +288,26 @@ private:
                      nsISupports* aId);
 
   nsresult
   ClearStoragesForApp(uint32_t aAppId, bool aBrowserOnly);
 
   nsresult
   MaybeUpgradeOriginDirectory(nsIFile* aDirectory);
 
+  void
+  ReleaseIOThreadObjects()
+  {
+    AssertIsOnIOThread();
+
+    for (uint32_t index = 0; index < Client::TYPE_MAX; index++) {
+      mClients[index]->ReleaseIOThreadObjects();
+    }
+  }
+
   static void
   GetOriginPatternString(uint32_t aAppId,
                          MozBrowserPatternFlag aBrowserFlag,
                          const nsACString& aOrigin,
                          nsAutoCString& _retval);
 
   // TLS storage index for the current thread's window.
   unsigned int mCurrentWindowIndex;