Bug 767944 - Implement a manager for centralized quota and storage handling. r=bent
authorJan Varga <jan.varga@gmail.com>
Tue, 26 Mar 2013 12:13:17 +0100
changeset 136642 8d558e07caf4c6f9ef6660f2000f54754894e814
parent 136641 c039f412b7852f1198ce0bb1807c3f5a77c1daec
child 136643 23a3b9892ecb623cf15cf5fa1131e8f927c1d11d
push id2452
push userlsblakk@mozilla.com
push dateMon, 13 May 2013 16:59:38 +0000
treeherdermozilla-beta@d4b152d29d8d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbent
bugs767944
milestone22.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 767944 - Implement a manager for centralized quota and storage handling. r=bent
b2g/installer/package-manifest.in
browser/base/content/pageinfo/permissions.js
browser/installer/package-manifest.in
content/base/src/nsDocument.cpp
content/media/webrtc/MediaEngineWebRTC.h
dom/base/nsDOMWindowUtils.cpp
dom/base/nsGlobalWindow.cpp
dom/file/FileHandle.cpp
dom/file/FileService.cpp
dom/file/FileService.h
dom/file/LockedFile.cpp
dom/file/nsIFileStorage.h
dom/indexedDB/CheckPermissionsHelper.cpp
dom/indexedDB/Client.cpp
dom/indexedDB/Client.h
dom/indexedDB/FileInfo.cpp
dom/indexedDB/FileManager.cpp
dom/indexedDB/FileManager.h
dom/indexedDB/IDBDatabase.cpp
dom/indexedDB/IDBDatabase.h
dom/indexedDB/IDBFactory.cpp
dom/indexedDB/IDBFileHandle.cpp
dom/indexedDB/IDBTransaction.h
dom/indexedDB/IndexedDatabase.h
dom/indexedDB/IndexedDatabaseInlines.h
dom/indexedDB/IndexedDatabaseManager.cpp
dom/indexedDB/IndexedDatabaseManager.h
dom/indexedDB/Makefile.in
dom/indexedDB/OpenDatabaseHelper.cpp
dom/indexedDB/OpenDatabaseHelper.h
dom/indexedDB/TransactionThreadPool.cpp
dom/indexedDB/TransactionThreadPool.h
dom/indexedDB/ipc/IndexedDBChild.cpp
dom/indexedDB/nsIIndexedDatabaseManager.idl
dom/indexedDB/test/bug839193.js
dom/indexedDB/test/file.js
dom/indexedDB/test/helpers.js
dom/quota/AcquireListener.h
dom/quota/ArrayCluster.h
dom/quota/CheckQuotaHelper.cpp
dom/quota/CheckQuotaHelper.h
dom/quota/Client.h
dom/quota/FileStreams.cpp
dom/quota/FileStreams.h
dom/quota/Makefile.in
dom/quota/OriginOrPatternString.h
dom/quota/QuotaManager.cpp
dom/quota/QuotaManager.h
dom/quota/QuotaObject.cpp
dom/quota/QuotaObject.h
dom/quota/StorageMatcher.h
dom/quota/StoragePrivilege.h
dom/quota/UsageRunnable.h
dom/quota/Utilities.h
dom/quota/moz.build
dom/quota/nsIOfflineStorage.h
dom/quota/nsIQuotaManager.idl
dom/quota/nsIQuotaRequest.idl
dom/quota/nsIUsageCallback.idl
js/xpconnect/src/dom_quickstubs.qsconf
layout/build/nsLayoutCID.h
layout/build/nsLayoutModule.cpp
mobile/android/installer/package-manifest.in
storage/src/TelemetryVFS.cpp
toolkit/forgetaboutsite/ForgetAboutSite.jsm
--- a/b2g/installer/package-manifest.in
+++ b/b2g/installer/package-manifest.in
@@ -197,16 +197,17 @@
 @BINPATH@/components/dom_payment.xpt
 @BINPATH@/components/dom_json.xpt
 #ifdef MOZ_B2G_RIL
 @BINPATH@/components/dom_mms.xpt
 #endif
 @BINPATH@/components/dom_browserelement.xpt
 @BINPATH@/components/dom_messages.xpt
 @BINPATH@/components/dom_power.xpt
+@BINPATH@/components/dom_quota.xpt
 @BINPATH@/components/dom_range.xpt
 @BINPATH@/components/dom_settings.xpt
 @BINPATH@/components/dom_permissionsettings.xpt
 @BINPATH@/components/dom_sidebar.xpt
 @BINPATH@/components/dom_mobilemessage.xpt
 @BINPATH@/components/dom_storage.xpt
 @BINPATH@/components/dom_stylesheets.xpt
 @BINPATH@/components/dom_threads.xpt
--- a/browser/base/content/pageinfo/permissions.js
+++ b/browser/base/content/pageinfo/permissions.js
@@ -2,21 +2,21 @@
  * 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/. */
 
 const UNKNOWN = nsIPermissionManager.UNKNOWN_ACTION;   // 0
 const ALLOW = nsIPermissionManager.ALLOW_ACTION;       // 1
 const BLOCK = nsIPermissionManager.DENY_ACTION;        // 2
 const SESSION = nsICookiePermission.ACCESS_SESSION;    // 8
 
-const nsIIndexedDatabaseManager =
-  Components.interfaces.nsIIndexedDatabaseManager;
+const nsIQuotaManager = Components.interfaces.nsIQuotaManager;
 
 var gPermURI;
 var gPrefs;
+var gUsageRequest;
 
 var gPermObj = {
   image: function getImageDefaultPermission()
   {
     if (gPrefs.getIntPref("permissions.default.image") == 2)
       return BLOCK;
     return ALLOW;
   },
@@ -111,19 +111,20 @@ function onLoadPermission()
 }
 
 function onUnloadPermission()
 {
   var os = Components.classes["@mozilla.org/observer-service;1"]
                      .getService(Components.interfaces.nsIObserverService);
   os.removeObserver(permissionObserver, "perm-changed");
 
-  var dbManager = Components.classes["@mozilla.org/dom/indexeddb/manager;1"]
-                            .getService(nsIIndexedDatabaseManager);
-  dbManager.cancelGetUsageForURI(gPermURI, onIndexedDBUsageCallback);
+  if (gUsageRequest) {
+    gUsageRequest.cancel();
+    gUsageRequest = null;
+  }
 }
 
 function initRow(aPartId)
 {
   if (aPartId == "plugins") {
     initPluginsRow();
     return;
   }
@@ -200,33 +201,34 @@ function onRadioClick(aPartId)
 function setRadioState(aPartId, aValue)
 {
   var radio = document.getElementById(aPartId + "#" + aValue);
   radio.radioGroup.selectedItem = radio;
 }
 
 function initIndexedDBRow()
 {
-  var dbManager = Components.classes["@mozilla.org/dom/indexeddb/manager;1"]
-                            .getService(nsIIndexedDatabaseManager);
-  dbManager.getUsageForURI(gPermURI, onIndexedDBUsageCallback);
+  var quotaManager = Components.classes["@mozilla.org/dom/quota/manager;1"]
+                               .getService(nsIQuotaManager);
+  gUsageRequest =
+    quotaManager.getUsageForURI(gPermURI, onIndexedDBUsageCallback);
 
   var status = document.getElementById("indexedDBStatus");
   var button = document.getElementById("indexedDBClear");
 
   status.value = "";
   status.setAttribute("hidden", "true");
   button.setAttribute("hidden", "true");
 }
 
 function onIndexedDBClear()
 {
-  Components.classes["@mozilla.org/dom/indexeddb/manager;1"]
-            .getService(nsIIndexedDatabaseManager)
-            .clearDatabasesForURI(gPermURI);
+  Components.classes["@mozilla.org/dom/quota/manager;1"]
+            .getService(nsIQuotaManager)
+            .clearStoragesForURI(gPermURI);
 
   var permissionManager = Components.classes[PERMISSION_CONTRACTID]
                                     .getService(nsIPermissionManager);
   permissionManager.remove(gPermURI.host, "indexedDB-unlimited");
   initIndexedDBRow();
 }
 
 function onIndexedDBUsageCallback(uri, usage, fileUsage)
--- a/browser/installer/package-manifest.in
+++ b/browser/installer/package-manifest.in
@@ -198,16 +198,17 @@
 @BINPATH@/components/dom_indexeddb.xpt
 @BINPATH@/components/dom_offline.xpt
 @BINPATH@/components/dom_json.xpt
 #ifdef MOZ_B2G_RIL
 @BINPATH@/components/dom_mms.xpt
 #endif
 @BINPATH@/components/dom_browserelement.xpt
 @BINPATH@/components/dom_power.xpt
+@BINPATH@/components/dom_quota.xpt
 @BINPATH@/components/dom_range.xpt
 @BINPATH@/components/dom_settings.xpt
 @BINPATH@/components/dom_permissionsettings.xpt
 @BINPATH@/components/dom_sidebar.xpt
 @BINPATH@/components/dom_mobilemessage.xpt
 @BINPATH@/components/dom_storage.xpt
 @BINPATH@/components/dom_stylesheets.xpt
 @BINPATH@/components/dom_traversal.xpt
--- a/content/base/src/nsDocument.cpp
+++ b/content/base/src/nsDocument.cpp
@@ -151,17 +151,17 @@
 #endif // MOZ_MEDIA
 #ifdef MOZ_WEBRTC
 #include "IPeerConnection.h"
 #endif // MOZ_WEBRTC
 
 #include "mozAutoDocUpdate.h"
 #include "nsGlobalWindow.h"
 #include "mozilla/dom/EncodingUtils.h"
-#include "mozilla/dom/indexedDB/IndexedDatabaseManager.h"
+#include "mozilla/dom/quota/QuotaManager.h"
 #include "nsDOMNavigationTiming.h"
 #include "nsEventStateManager.h"
 
 #include "nsSMILAnimationController.h"
 #include "imgIContainer.h"
 #include "nsSVGUtils.h"
 
 #include "nsRefreshDriver.h"
@@ -7633,21 +7633,21 @@ nsDocument::CanSavePresentation(nsIReque
         printf("document %s has request %s\n",
                docSpec.get(), requestName.get());
 #endif
         return false;
       }
     }
   }
 
-  // Check if we have running IndexedDB transactions
-  indexedDB::IndexedDatabaseManager* idbManager =
-    win ? indexedDB::IndexedDatabaseManager::Get() : nullptr;
-  if (idbManager && idbManager->HasOpenTransactions(win)) {
-    return false;
+  // Check if we have running offline storage transactions
+  quota::QuotaManager* quotaManager =
+    win ? quota::QuotaManager::Get() : nullptr;
+  if (quotaManager && quotaManager->HasOpenTransactions(win)) {
+   return false;
   }
 
 #ifdef MOZ_WEBRTC
   // Check if we have active PeerConnections
   nsCOMPtr<IPeerConnectionManager> pcManager =
     do_GetService(IPEERCONNECTION_MANAGER_CONTRACTID);
 
   if (pcManager && win) {
--- a/content/media/webrtc/MediaEngineWebRTC.h
+++ b/content/media/webrtc/MediaEngineWebRTC.h
@@ -13,16 +13,17 @@
 #include "mozilla/Mutex.h"
 #include "mozilla/Monitor.h"
 #include "nsCOMPtr.h"
 #include "nsDOMFile.h"
 #include "nsThreadUtils.h"
 #include "DOMMediaStream.h"
 #include "nsDirectoryServiceDefs.h"
 #include "nsComponentManagerUtils.h"
+#include "nsRefPtrHashtable.h"
 
 #include "VideoUtils.h"
 #include "MediaEngine.h"
 #include "VideoSegment.h"
 #include "AudioSegment.h"
 #include "StreamBuffer.h"
 #include "MediaStreamGraph.h"
 
--- a/dom/base/nsDOMWindowUtils.cpp
+++ b/dom/base/nsDOMWindowUtils.cpp
@@ -58,16 +58,17 @@
 #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"
 
 using namespace mozilla;
@@ -2751,18 +2752,17 @@ nsDOMWindowUtils::GetFileReferences(cons
   if (!nsContentUtils::IsCallerChrome()) {
     return NS_ERROR_DOM_SECURITY_ERR;
   }
 
   nsCOMPtr<nsPIDOMWindow> window = do_QueryReferent(mWindow);
   NS_ENSURE_TRUE(window, NS_ERROR_FAILURE);
 
   nsCString origin;
-  nsresult rv = indexedDB::IndexedDatabaseManager::GetASCIIOriginFromWindow(
-    window, 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);
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -202,17 +202,17 @@
 #ifdef MOZ_LOGGING
 // so we can get logging even in release builds
 #define FORCE_PR_LOG 1
 #endif
 #include "prlog.h"
 #include "prenv.h"
 
 #include "mozilla/dom/indexedDB/IDBFactory.h"
-#include "mozilla/dom/indexedDB/IndexedDatabaseManager.h"
+#include "mozilla/dom/quota/QuotaManager.h"
 
 #include "mozilla/dom/StructuredCloneTags.h"
 
 #ifdef MOZ_GAMEPAD
 #include "mozilla/dom/GamepadService.h"
 #endif
 
 #include "nsRefreshDriver.h"
@@ -1385,21 +1385,20 @@ nsGlobalWindow::FreeInnerObjects()
   // re-create.
   NotifyDOMWindowDestroyed(this);
 
   // Kill all of the workers for this window.
   nsIScriptContext *scx = GetContextInternal();
   AutoPushJSContext cx(scx ? scx->GetNativeContext() : nullptr);
   mozilla::dom::workers::CancelWorkersForWindow(cx, this);
 
-  // Close all IndexedDB databases for this window.
-  indexedDB::IndexedDatabaseManager* idbManager =
-    indexedDB::IndexedDatabaseManager::Get();
-  if (idbManager) {
-    idbManager->AbortCloseDatabasesForWindow(this);
+  // Close all offline storages for this window.
+  quota::QuotaManager* quotaManager = quota::QuotaManager::Get();
+  if (quotaManager) {
+    quotaManager->AbortCloseStoragesForWindow(this);
   }
 
   ClearAllTimeouts();
 
   if (mIdleTimer) {
     mIdleTimer->Cancel();
     mIdleTimer = nullptr;
   }
--- a/dom/file/FileHandle.cpp
+++ b/dom/file/FileHandle.cpp
@@ -102,17 +102,17 @@ FileHandle::Open(const nsAString& aMode,
   return rv.ErrorCode();
 }
 
 already_AddRefed<nsIDOMLockedFile>
 FileHandle::Open(FileMode aMode, ErrorResult& aError)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
-  if (FileService::IsShuttingDown() || mFileStorage->IsStorageShuttingDown()) {
+  if (FileService::IsShuttingDown() || mFileStorage->IsShuttingDown()) {
     aError.Throw(NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR);
     return nullptr;
   }
 
   MOZ_STATIC_ASSERT(static_cast<uint32_t>(FileModeValues::Readonly) ==
                     static_cast<uint32_t>(LockedFile::READ_ONLY),
                     "Enum values should match.");
   MOZ_STATIC_ASSERT(static_cast<uint32_t>(FileModeValues::Readwrite) ==
--- a/dom/file/FileService.cpp
+++ b/dom/file/FileService.cpp
@@ -147,21 +147,21 @@ FileService::IsShuttingDown()
 nsresult
 FileService::Enqueue(LockedFile* aLockedFile, FileHelper* aFileHelper)
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
   NS_ASSERTION(aLockedFile, "Null pointer!");
 
   FileHandle* fileHandle = aLockedFile->mFileHandle;
 
-  if (fileHandle->mFileStorage->IsStorageInvalidated()) {
+  if (fileHandle->mFileStorage->IsInvalidated()) {
     return NS_ERROR_NOT_AVAILABLE;
   }
 
-  nsISupports* storageId = fileHandle->mFileStorage->StorageId();
+  nsIAtom* storageId = fileHandle->mFileStorage->Id();
   const nsAString& fileName = fileHandle->mFileName;
   bool modeIsWrite = aLockedFile->mMode == LockedFile::READ_WRITE;
 
   FileStorageInfo* fileStorageInfo;
   if (!mFileStorageInfos.Get(storageId, &fileStorageInfo)) {
     nsAutoPtr<FileStorageInfo> newFileStorageInfo(new FileStorageInfo());
 
     mFileStorageInfos.Put(storageId, newFileStorageInfo);
@@ -221,17 +221,17 @@ FileService::Enqueue(LockedFile* aLocked
 
 void
 FileService::NotifyLockedFileCompleted(LockedFile* aLockedFile)
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
   NS_ASSERTION(aLockedFile, "Null pointer!");
 
   FileHandle* fileHandle = aLockedFile->mFileHandle;
-  nsISupports* storageId = fileHandle->mFileStorage->StorageId();
+  nsIAtom* storageId = fileHandle->mFileStorage->Id();
 
   FileStorageInfo* fileStorageInfo;
   if (!mFileStorageInfos.Get(storageId, &fileStorageInfo)) {
     NS_ERROR("We don't know anyting about this locked file?!");
     return;
   }
 
   fileStorageInfo->RemoveLockedFileQueue(aLockedFile);
@@ -251,44 +251,42 @@ FileService::NotifyLockedFileCompleted(L
       }
       else {
         index++;
       }
     }
   }
 }
 
-bool
-FileService::WaitForAllStoragesToComplete(
-                                nsTArray<nsCOMPtr<nsIFileStorage> >& aStorages,
-                                nsIRunnable* aCallback)
+void
+FileService::WaitForStoragesToComplete(
+                                 nsTArray<nsCOMPtr<nsIFileStorage> >& aStorages,
+                                 nsIRunnable* aCallback)
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
   NS_ASSERTION(!aStorages.IsEmpty(), "No databases to wait on!");
   NS_ASSERTION(aCallback, "Null pointer!");
 
   StoragesCompleteCallback* callback = mCompleteCallbacks.AppendElement();
   callback->mCallback = aCallback;
   callback->mStorages.SwapElements(aStorages);
 
   if (MaybeFireCallback(*callback)) {
     mCompleteCallbacks.RemoveElementAt(mCompleteCallbacks.Length() - 1);
   }
-
-  return true;
 }
 
 void
 FileService::AbortLockedFilesForStorage(nsIFileStorage* aFileStorage)
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
   NS_ASSERTION(aFileStorage, "Null pointer!");
 
   FileStorageInfo* fileStorageInfo;
-  if (!mFileStorageInfos.Get(aFileStorage->StorageId(), &fileStorageInfo)) {
+  if (!mFileStorageInfos.Get(aFileStorage->Id(), &fileStorageInfo)) {
     return;
   }
 
   nsAutoTArray<nsRefPtr<LockedFile>, 10> lockedFiles;
   fileStorageInfo->CollectRunningAndDelayedLockedFiles(aFileStorage,
                                                        lockedFiles);
 
   for (uint32_t index = 0; index < lockedFiles.Length(); index++) {
@@ -298,17 +296,17 @@ FileService::AbortLockedFilesForStorage(
 
 bool
 FileService::HasLockedFilesForStorage(nsIFileStorage* aFileStorage)
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
   NS_ASSERTION(aFileStorage, "Null pointer!");
 
   FileStorageInfo* fileStorageInfo;
-  if (!mFileStorageInfos.Get(aFileStorage->StorageId(), &fileStorageInfo)) {
+  if (!mFileStorageInfos.Get(aFileStorage->Id(), &fileStorageInfo)) {
     return false;
   }
 
   return fileStorageInfo->HasRunningLockedFiles(aFileStorage);
 }
 
 NS_IMPL_ISUPPORTS1(FileService, nsIObserver)
 
@@ -325,18 +323,17 @@ FileService::Observe(nsISupports* aSubje
 }
 
 bool
 FileService::MaybeFireCallback(StoragesCompleteCallback& aCallback)
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
   for (uint32_t index = 0; index < aCallback.mStorages.Length(); index++) {
-    if (mFileStorageInfos.Get(aCallback.mStorages[index]->StorageId(),
-                              nullptr)) {
+    if (mFileStorageInfos.Get(aCallback.mStorages[index]->Id(), nullptr)) {
       return false;
     }
   }
 
   aCallback.mCallback->Run();
   return true;
 }
 
@@ -483,17 +480,17 @@ FileService::FileStorageInfo::RemoveLock
                                      delayedEnqueueInfo.mFileHelper))) {
       NS_WARNING("Enqueue failed!");
     }
   }
 }
 
 bool
 FileService::FileStorageInfo::HasRunningLockedFiles(
-                                                  nsIFileStorage* aFileStorage)
+                                                   nsIFileStorage* aFileStorage)
 {
   for (uint32_t index = 0; index < mLockedFileQueues.Length(); index++) {
     LockedFile* lockedFile = mLockedFileQueues[index]->mLockedFile;
     if (lockedFile->mFileHandle->mFileStorage == aFileStorage) {
       return true;
     }
   }
   return false;
--- a/dom/file/FileService.h
+++ b/dom/file/FileService.h
@@ -41,19 +41,19 @@ public:
   IsShuttingDown();
 
   nsresult
   Enqueue(LockedFile* aLockedFile, FileHelper* aFileHelper);
 
   void
   NotifyLockedFileCompleted(LockedFile* aLockedFile);
 
-  bool
-  WaitForAllStoragesToComplete(nsTArray<nsCOMPtr<nsIFileStorage> >& aStorages,
-                               nsIRunnable* aCallback);
+  void
+  WaitForStoragesToComplete(nsTArray<nsCOMPtr<nsIFileStorage> >& aStorages,
+                            nsIRunnable* aCallback);
 
   void
   AbortLockedFilesForStorage(nsIFileStorage* aFileStorage);
 
   bool
   HasLockedFilesForStorage(nsIFileStorage* aFileStorage);
 
   nsIEventTarget*
@@ -124,18 +124,18 @@ private:
     inline bool
     HasRunningLockedFiles(nsIFileStorage* aFileStorage);
 
     inline DelayedEnqueueInfo*
     CreateDelayedEnqueueInfo(LockedFile* aLockedFile, FileHelper* aFileHelper);
 
     inline void
     CollectRunningAndDelayedLockedFiles(
-                                nsIFileStorage* aFileStorage,
-                                nsTArray<nsRefPtr<LockedFile> >& aLockedFiles);
+                                 nsIFileStorage* aFileStorage,
+                                 nsTArray<nsRefPtr<LockedFile> >& aLockedFiles);
 
     void
     LockFileForReading(const nsAString& aFileName)
     {
       mFilesReading.PutEntry(aFileName);
     }
 
     void
--- a/dom/file/LockedFile.cpp
+++ b/dom/file/LockedFile.cpp
@@ -379,17 +379,17 @@ LockedFile::OnRequestFinished()
 }
 
 nsresult
 LockedFile::CreateParallelStream(nsISupports** aStream)
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
   nsIFileStorage* fileStorage = mFileHandle->mFileStorage;
-  if (fileStorage->IsStorageInvalidated()) {
+  if (fileStorage->IsInvalidated()) {
     return NS_ERROR_NOT_AVAILABLE;
   }
 
   nsCOMPtr<nsISupports> stream =
     mFileHandle->CreateStream(mFileHandle->mFile, mMode == READ_ONLY);
   NS_ENSURE_TRUE(stream, NS_ERROR_FAILURE);
 
   mParallelStreams.AppendElement(stream);
@@ -399,17 +399,17 @@ LockedFile::CreateParallelStream(nsISupp
 }
 
 nsresult
 LockedFile::GetOrCreateStream(nsISupports** aStream)
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
   nsIFileStorage* fileStorage = mFileHandle->mFileStorage;
-  if (fileStorage->IsStorageInvalidated()) {
+  if (fileStorage->IsInvalidated()) {
     return NS_ERROR_NOT_AVAILABLE;
   }
 
   if (!mStream) {
     nsCOMPtr<nsISupports> stream =
       mFileHandle->CreateStream(mFileHandle->mFile, mMode == READ_ONLY);
     NS_ENSURE_TRUE(stream, NS_ERROR_FAILURE);
 
@@ -952,17 +952,17 @@ FinishHelper::Run()
     }
 
     mLockedFile = nullptr;
 
     return NS_OK;
   }
 
   nsIFileStorage* fileStorage = mLockedFile->mFileHandle->mFileStorage;
-  if (fileStorage->IsStorageInvalidated()) {
+  if (fileStorage->IsInvalidated()) {
     mAborted = true;
   }
 
   for (uint32_t index = 0; index < mParallelStreams.Length(); index++) {
     nsCOMPtr<nsIInputStream> stream =
       do_QueryInterface(mParallelStreams[index]);
 
     if (NS_FAILED(stream->Close())) {
--- a/dom/file/nsIFileStorage.h
+++ b/dom/file/nsIFileStorage.h
@@ -5,57 +5,55 @@
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef nsIFileStorage_h__
 #define nsIFileStorage_h__
 
 #include "nsISupports.h"
 
 #define NS_FILESTORAGE_IID \
-  {0xa0801944, 0x2f1c, 0x4203, \
-  { 0x9c, 0xaa, 0xaa, 0x47, 0xe0, 0x0c, 0x67, 0x92 } }
+  {0x6278f453, 0xd557, 0x4a55, \
+  { 0x99, 0x3e, 0xf4, 0x69, 0xe2, 0xa5, 0xe1, 0xd0 } }
+
+class nsIAtom;
 
 class nsIFileStorage : public nsISupports
 {
 public:
   NS_DECLARE_STATIC_IID_ACCESSOR(NS_FILESTORAGE_IID)
 
-  virtual const nsACString&
-  StorageOrigin() = 0;
-
-  virtual nsISupports*
-  StorageId() = 0;
+  NS_IMETHOD_(nsIAtom*)
+  Id() = 0;
 
-  virtual bool
-  IsStorageInvalidated() = 0;
+  // Whether or not the storage has been invalidated. If it has then no further
+  // operations for this storage will be allowed to run.
+  NS_IMETHOD_(bool)
+  IsInvalidated() = 0;
 
-  virtual bool
-  IsStorageShuttingDown() = 0;
+  NS_IMETHOD_(bool)
+  IsShuttingDown() = 0;
 
-  virtual void
+  NS_IMETHOD_(void)
   SetThreadLocals() = 0;
 
-  virtual void
+  NS_IMETHOD_(void)
   UnsetThreadLocals() = 0;
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(nsIFileStorage, NS_FILESTORAGE_IID)
 
 #define NS_DECL_NSIFILESTORAGE                                                 \
-  virtual const nsACString&                                                    \
-  StorageOrigin() MOZ_OVERRIDE;                                                \
-                                                                               \
-  virtual nsISupports*                                                         \
-  StorageId() MOZ_OVERRIDE;                                                    \
+  NS_IMETHOD_(nsIAtom*)                                                        \
+  Id() MOZ_OVERRIDE;                                                           \
                                                                                \
-  virtual bool                                                                 \
-  IsStorageInvalidated() MOZ_OVERRIDE;                                         \
+  NS_IMETHOD_(bool)                                                            \
+  IsInvalidated() MOZ_OVERRIDE;                                                \
                                                                                \
-  virtual bool                                                                 \
-  IsStorageShuttingDown() MOZ_OVERRIDE;                                        \
+  NS_IMETHOD_(bool)                                                            \
+  IsShuttingDown() MOZ_OVERRIDE;                                               \
                                                                                \
-  virtual void                                                                 \
+  NS_IMETHOD_(void)                                                            \
   SetThreadLocals() MOZ_OVERRIDE;                                              \
                                                                                \
-  virtual void                                                                 \
+  NS_IMETHOD_(void)                                                            \
   UnsetThreadLocals() MOZ_OVERRIDE;
 
 #endif // nsIFileStorage_h__
--- a/dom/indexedDB/CheckPermissionsHelper.cpp
+++ b/dom/indexedDB/CheckPermissionsHelper.cpp
@@ -14,18 +14,19 @@
 #include "nsIPrincipal.h"
 #include "nsIScriptObjectPrincipal.h"
 #include "nsIURI.h"
 
 #include "nsContentUtils.h"
 #include "nsDOMStorage.h"
 #include "nsNetUtil.h"
 #include "nsThreadUtils.h"
+#include "mozilla/dom/quota/QuotaManager.h"
+#include "mozilla/Preferences.h"
 #include "mozilla/Services.h"
-#include "mozilla/Preferences.h"
 
 #include "IndexedDatabaseManager.h"
 
 #define PERMISSION_INDEXEDDB "indexedDB"
 #define PREF_INDEXEDDB_ENABLED "dom.indexedDB.enabled"
 #define TOPIC_PERMISSIONS_PROMPT "indexedDB-permissions-prompt"
 #define TOPIC_PERMISSIONS_RESPONSE "indexedDB-permissions-response"
 
@@ -135,20 +136,20 @@ CheckPermissionsHelper::Run()
 
   nsRefPtr<OpenDatabaseHelper> helper;
   helper.swap(mHelper);
 
   nsCOMPtr<nsIDOMWindow> window;
   window.swap(mWindow);
 
   if (permission == PERMISSION_ALLOWED) {
-    IndexedDatabaseManager* mgr = IndexedDatabaseManager::Get();
-    NS_ASSERTION(mgr, "This should never be null!");
+    quota::QuotaManager* quotaManager = quota::QuotaManager::Get();
+    NS_ASSERTION(quotaManager, "This should never be null!");
 
-    return helper->Dispatch(mgr->IOThread());
+    return helper->Dispatch(quotaManager->IOThread());
   }
 
   NS_ASSERTION(permission == PERMISSION_PROMPT ||
                permission == PERMISSION_DENIED,
                "Unknown permission!");
 
   helper->SetError(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR);
 
new file mode 100644
--- /dev/null
+++ b/dom/indexedDB/Client.cpp
@@ -0,0 +1,338 @@
+/* -*- 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 "Client.h"
+
+#include "mozilla/dom/quota/QuotaManager.h"
+#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::QuotaManager;
+
+namespace {
+
+bool
+GetDatabaseBaseFilename(const nsAString& aFilename,
+                        nsAString& aDatabaseBaseFilename)
+{
+  NS_ASSERTION(!aFilename.IsEmpty(), "Bad argument!");
+
+  NS_NAMED_LITERAL_STRING(sqlite, ".sqlite");
+
+  if (!StringEndsWith(aFilename, sqlite)) {
+    return false;
+  }
+
+  aDatabaseBaseFilename =
+    Substring(aFilename, 0, aFilename.Length() - sqlite.Length());
+
+  return true;
+}
+
+} // anonymous namespace
+
+// 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)
+{
+  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.
+
+  nsAutoTArray<nsString, 20> subdirsToProcess;
+  nsAutoTArray<nsCOMPtr<nsIFile> , 20> unknownFiles;
+  nsTHashtable<nsStringHashKey> validSubdirs;
+  validSubdirs.Init(20);
+
+  nsCOMPtr<nsISimpleEnumerator> entries;
+  rv = directory->GetDirectoryEntries(getter_AddRefs(entries));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  bool hasMore;
+  while (NS_SUCCEEDED((rv = entries->HasMoreElements(&hasMore))) &&
+         hasMore && (!aUsageRunnable || !aUsageRunnable->Canceled())) {
+    nsCOMPtr<nsISupports> entry;
+    rv = entries->GetNext(getter_AddRefs(entry));
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    nsCOMPtr<nsIFile> file = do_QueryInterface(entry);
+    NS_ENSURE_TRUE(file, NS_NOINTERFACE);
+
+    nsString leafName;
+    rv = file->GetLeafName(leafName);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    if (StringEndsWith(leafName, NS_LITERAL_STRING(".sqlite-journal"))) {
+      continue;
+    }
+
+    bool isDirectory;
+    rv = file->IsDirectory(&isDirectory);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    if (isDirectory) {
+      if (!validSubdirs.GetEntry(leafName)) {
+        subdirsToProcess.AppendElement(leafName);
+      }
+      continue;
+    }
+
+    nsString dbBaseFilename;
+    if (!GetDatabaseBaseFilename(leafName, dbBaseFilename)) {
+      unknownFiles.AppendElement(file);
+      continue;
+    }
+
+    nsCOMPtr<nsIFile> fmDirectory;
+    rv = directory->Clone(getter_AddRefs(fmDirectory));
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    rv = fmDirectory->Append(dbBaseFilename);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    rv = FileManager::InitDirectory(fmDirectory, file, aOrigin);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    if (aUsageRunnable) {
+      int64_t fileSize;
+      rv = file->GetFileSize(&fileSize);
+      NS_ENSURE_SUCCESS(rv, rv);
+
+      aUsageRunnable->AppendToDatabaseUsage(uint64_t(fileSize));
+
+      uint64_t usage;
+      rv = FileManager::GetUsage(fmDirectory, &usage);
+      NS_ENSURE_SUCCESS(rv, rv);
+
+      aUsageRunnable->AppendToFileUsage(usage);
+    }
+
+    validSubdirs.PutEntry(dbBaseFilename);
+  }
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  for (uint32_t i = 0; i < subdirsToProcess.Length(); i++) {
+    const nsString& subdir = subdirsToProcess[i];
+    if (!validSubdirs.GetEntry(subdir)) {
+      NS_WARNING("Unknown subdirectory found!");
+      return NS_ERROR_UNEXPECTED;
+    }
+  }
+
+  for (uint32_t i = 0; i < unknownFiles.Length(); i++) {
+    nsCOMPtr<nsIFile>& unknownFile = unknownFiles[i];
+
+    // Some temporary SQLite files could disappear, so we have to check if the
+    // unknown file still exists.
+    bool exists;
+    rv = unknownFile->Exists(&exists);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    if (exists) {
+      nsString leafName;
+      unknownFile->GetLeafName(leafName);
+
+      // The journal file may exists even after db has been correctly opened.
+      if (!StringEndsWith(leafName, NS_LITERAL_STRING(".sqlite-journal"))) {
+        NS_WARNING("Unknown file found!");
+        return NS_ERROR_UNEXPECTED;
+      }
+    }
+  }
+
+  return NS_OK;
+}
+
+nsresult
+Client::GetUsageForOrigin(const nsACString& aOrigin,
+                          UsageRunnable* aUsageRunnable)
+{
+  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;
+}
+
+bool
+Client::IsTransactionServiceActivated()
+{
+  return !!TransactionThreadPool::Get();
+}
+
+void
+Client::WaitForStoragesToComplete(nsTArray<nsIOfflineStorage*>& aStorages,
+                                  nsIRunnable* aCallback)
+{
+  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<IDBDatabase*> databases(aStorages.Length());
+  for (uint32_t index = 0; index < aStorages.Length(); index++) {
+    IDBDatabase* database = IDBDatabase::FromStorage(aStorages[index]);
+    NS_ASSERTION(database, "This shouldn't be null!");
+
+    databases.AppendElement(database);
+  }
+
+  pool->WaitForDatabasesToComplete(databases, aCallback);
+}
+
+void
+Client::AbortTransactionsForStorage(nsIOfflineStorage* aStorage)
+{
+  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)
+{
+  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();
+}
+
+void
+Client::OnShutdownCompleted()
+{
+  IndexedDatabaseManager* mgr = IndexedDatabaseManager::Get();
+  if (mgr) {
+    mgr->InvalidateAllFileManagers();
+  }
+}
+
+nsresult
+Client::GetDirectory(const nsACString& aOrigin, nsIFile** aDirectory)
+{
+  QuotaManager* quotaManager = QuotaManager::Get();
+  NS_ASSERTION(quotaManager, "This should never fail!");
+
+  nsCOMPtr<nsIFile> directory;
+  nsresult rv =
+    quotaManager->GetDirectoryForOrigin(aOrigin, getter_AddRefs(directory));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  NS_ASSERTION(directory, "What?");
+
+  rv = directory->Append(NS_LITERAL_STRING(IDB_DIRECTORY_NAME));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  directory.forget(aDirectory);
+  return NS_OK;
+}
+
+nsresult
+Client::GetUsageForDirectoryInternal(nsIFile* aDirectory,
+                                     UsageRunnable* aUsageRunnable,
+                                     bool aDatabaseFiles)
+{
+  NS_ASSERTION(aDirectory, "Null pointer!");
+  NS_ASSERTION(aUsageRunnable, "Null pointer!");
+
+  nsCOMPtr<nsISimpleEnumerator> entries;
+  nsresult rv = aDirectory->GetDirectoryEntries(getter_AddRefs(entries));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  if (!entries) {
+    return NS_OK;
+  }
+
+  bool hasMore;
+  while (NS_SUCCEEDED((rv = entries->HasMoreElements(&hasMore))) &&
+         hasMore && !aUsageRunnable->Canceled()) {
+    nsCOMPtr<nsISupports> entry;
+    rv = entries->GetNext(getter_AddRefs(entry));
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    nsCOMPtr<nsIFile> file(do_QueryInterface(entry));
+    NS_ASSERTION(file, "Don't know what this is!");
+
+    bool isDirectory;
+    rv = file->IsDirectory(&isDirectory);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    if (isDirectory) {
+      if (aDatabaseFiles) {
+        rv = GetUsageForDirectoryInternal(file, aUsageRunnable, false);
+        NS_ENSURE_SUCCESS(rv, rv);
+      }
+      else {
+        nsString leafName;
+        rv = file->GetLeafName(leafName);
+        NS_ENSURE_SUCCESS(rv, rv);
+
+        if (!leafName.EqualsLiteral(JOURNAL_DIRECTORY_NAME)) {
+          NS_WARNING("Unknown directory found!");
+        }
+      }
+
+      continue;
+    }
+
+    int64_t fileSize;
+    rv = file->GetFileSize(&fileSize);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    NS_ASSERTION(fileSize >= 0, "Negative size?!");
+
+    if (aDatabaseFiles) {
+      aUsageRunnable->AppendToDatabaseUsage(uint64_t(fileSize));
+    }
+    else {
+      aUsageRunnable->AppendToFileUsage(uint64_t(fileSize));
+    }
+  }
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  return NS_OK;
+}
new file mode 100644
--- /dev/null
+++ b/dom/indexedDB/Client.h
@@ -0,0 +1,87 @@
+/* -*- 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/. */
+
+#ifndef mozilla_dom_indexeddb_client_h__
+#define mozilla_dom_indexeddb_client_h__
+
+#include "IndexedDatabase.h"
+
+#include "mozilla/dom/quota/Client.h"
+
+#define IDB_DIRECTORY_NAME "idb"
+#define JOURNAL_DIRECTORY_NAME "journals"
+
+BEGIN_INDEXEDDB_NAMESPACE
+
+class Client : public mozilla::dom::quota::Client
+{
+  typedef mozilla::dom::quota::UsageRunnable UsageRunnable;
+
+public:
+  NS_IMETHOD_(nsrefcnt)
+  AddRef() MOZ_OVERRIDE;
+
+  NS_IMETHOD_(nsrefcnt)
+  Release() MOZ_OVERRIDE;
+
+  virtual Type
+  GetType() MOZ_OVERRIDE
+  {
+    return IDB;
+  }
+
+  virtual nsresult
+  InitOrigin(const nsACString& aOrigin,
+             UsageRunnable* aUsageRunnable) MOZ_OVERRIDE;
+
+  virtual nsresult
+  GetUsageForOrigin(const nsACString& aOrigin,
+                    UsageRunnable* aUsageRunnable) MOZ_OVERRIDE;
+
+  virtual bool
+  IsFileServiceUtilized() MOZ_OVERRIDE
+  {
+    return true;
+  }
+
+  virtual bool
+  IsTransactionServiceActivated() MOZ_OVERRIDE;
+
+  virtual void
+  WaitForStoragesToComplete(nsTArray<nsIOfflineStorage*>& aStorages,
+                            nsIRunnable* aCallback) MOZ_OVERRIDE;
+
+  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);
+
+  nsAutoRefCnt mRefCnt;
+  NS_DECL_OWNINGTHREAD
+};
+
+END_INDEXEDDB_NAMESPACE
+
+#endif // mozilla_dom_indexeddb_client_h__
--- a/dom/indexedDB/FileInfo.cpp
+++ b/dom/indexedDB/FileInfo.cpp
@@ -1,16 +1,18 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "FileInfo.h"
 
+#include "mozilla/dom/quota/QuotaManager.h"
+
 USING_INDEXEDDB_NAMESPACE
 
 // static
 FileInfo*
 FileInfo::Create(FileManager* aFileManager, int64_t aId)
 {
   NS_ASSERTION(aId > 0, "Wrong id!");
 
@@ -86,17 +88,17 @@ FileInfo::UpdateReferences(nsAutoRefCnt&
   Cleanup();
 
   delete this;
 }
 
 void
 FileInfo::Cleanup()
 {
-  if (IndexedDatabaseManager::IsShuttingDown() ||
+  if (quota::QuotaManager::IsShuttingDown() ||
       mFileManager->Invalidated()) {
     return;
   }
 
   nsRefPtr<IndexedDatabaseManager> mgr = IndexedDatabaseManager::Get();
   NS_ASSERTION(mgr, "Shouldn't be null!");
 
   if (NS_FAILED(mgr->AsyncDeleteFile(mFileManager, Id()))) {
--- a/dom/indexedDB/FileManager.cpp
+++ b/dom/indexedDB/FileManager.cpp
@@ -6,28 +6,28 @@
 
 #include "FileManager.h"
 
 #include "mozIStorageConnection.h"
 #include "mozIStorageStatement.h"
 #include "nsIInputStream.h"
 #include "nsISimpleEnumerator.h"
 
+#include "mozilla/dom/quota/Utilities.h"
 #include "mozStorageCID.h"
 #include "mozStorageHelper.h"
 
+#include "Client.h"
 #include "FileInfo.h"
 #include "IndexedDatabaseManager.h"
 #include "OpenDatabaseHelper.h"
 
 #include "IndexedDatabaseInlines.h"
 #include <algorithm>
 
-#define JOURNAL_DIRECTORY_NAME "journals"
-
 USING_INDEXEDDB_NAMESPACE
 
 namespace {
 
 PLDHashOperator
 EnumerateToTArray(const uint64_t& aKey,
                   FileInfo* aValue,
                   void* aUserArg)
@@ -407,14 +407,14 @@ FileManager::GetUsage(nsIFile* aDirector
     if (leafName.EqualsLiteral(JOURNAL_DIRECTORY_NAME)) {
       continue;
     }
 
     int64_t fileSize;
     rv = file->GetFileSize(&fileSize);
     NS_ENSURE_SUCCESS(rv, rv);
 
-    IncrementUsage(&usage, uint64_t(fileSize));
+    quota::IncrementUsage(&usage, uint64_t(fileSize));
   }
 
   *aUsage = usage;
   return NS_OK;
 }
--- a/dom/indexedDB/FileManager.h
+++ b/dom/indexedDB/FileManager.h
@@ -3,49 +3,53 @@
 /* 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_indexeddb_filemanager_h__
 #define mozilla_dom_indexeddb_filemanager_h__
 
 #include "IndexedDatabase.h"
+
+#include "nsIDOMFile.h"
 #include "nsIFile.h"
-#include "nsIDOMFile.h"
+
+#include "mozilla/dom/quota/StoragePrivilege.h"
 #include "nsDataHashtable.h"
 
 class mozIStorageConnection;
-class mozIStorageServiceQuotaManagement;
 
 BEGIN_INDEXEDDB_NAMESPACE
 
 class FileInfo;
 
 class FileManager
 {
   friend class FileInfo;
 
+  typedef mozilla::dom::quota::StoragePrivilege StoragePrivilege;
+
 public:
-  FileManager(const nsACString& aOrigin, FactoryPrivilege aPrivilege,
+  FileManager(const nsACString& aOrigin, StoragePrivilege aPrivilege,
               const nsAString& aDatabaseName)
   : mOrigin(aOrigin), mPrivilege(aPrivilege), mDatabaseName(aDatabaseName),
     mLastFileId(0), mInvalidated(false)
   { }
 
   ~FileManager()
   { }
 
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(FileManager)
 
   const nsACString& Origin() const
   {
     return mOrigin;
   }
 
-  const FactoryPrivilege& Privilege() const
+  const StoragePrivilege& Privilege() const
   {
     return mPrivilege;
   }
 
   const nsAString& DatabaseName() const
   {
     return mDatabaseName;
   }
@@ -76,17 +80,17 @@ public:
   static nsresult InitDirectory(nsIFile* aDirectory,
                                 nsIFile* aDatabaseFile,
                                 const nsACString& aOrigin);
 
   static nsresult GetUsage(nsIFile* aDirectory, uint64_t* aUsage);
 
 private:
   nsCString mOrigin;
-  FactoryPrivilege mPrivilege;
+  StoragePrivilege mPrivilege;
   nsString mDatabaseName;
 
   nsString mDirectoryPath;
   nsString mJournalDirectoryPath;
 
   int64_t mLastFileId;
 
   // Protected by IndexedDatabaseManager::FileMutex()
--- a/dom/indexedDB/IDBDatabase.cpp
+++ b/dom/indexedDB/IDBDatabase.cpp
@@ -6,42 +6,43 @@
 
 #include "base/basictypes.h"
 
 #include "IDBDatabase.h"
 
 #include "mozilla/Mutex.h"
 #include "mozilla/storage.h"
 #include "mozilla/dom/ContentParent.h"
+#include "mozilla/dom/quota/Client.h"
 #include "mozilla/dom/quota/QuotaManager.h"
 #include "nsDOMClassInfo.h"
 #include "nsDOMLists.h"
 #include "nsJSUtils.h"
 #include "nsProxyRelease.h"
 #include "nsThreadUtils.h"
 
 #include "AsyncConnectionHelper.h"
 #include "DatabaseInfo.h"
 #include "IDBEvents.h"
 #include "IDBFactory.h"
 #include "IDBFileHandle.h"
 #include "IDBIndex.h"
 #include "IDBObjectStore.h"
 #include "IDBTransaction.h"
 #include "IDBFactory.h"
-#include "IndexedDatabaseManager.h"
 #include "TransactionThreadPool.h"
 #include "DictionaryHelpers.h"
 #include "nsContentUtils.h"
 
 #include "ipc/IndexedDBChild.h"
 #include "ipc/IndexedDBParent.h"
 
 USING_INDEXEDDB_NAMESPACE
 using mozilla::dom::ContentParent;
+using mozilla::dom::quota::Client;
 using mozilla::dom::quota::QuotaManager;
 
 namespace {
 
 class NoRequestDatabaseHelper : public AsyncConnectionHelper
 {
 public:
   NoRequestDatabaseHelper(IDBTransaction* aTransaction)
@@ -194,27 +195,40 @@ IDBDatabase::Create(IDBWrapperCache* aOw
   db->mDatabaseId = databaseInfo->id;
   db->mName = databaseInfo->name;
   db->mFilePath = databaseInfo->filePath;
   databaseInfo.swap(db->mDatabaseInfo);
   db->mASCIIOrigin = aASCIIOrigin;
   db->mFileManager = aFileManager;
   db->mContentParent = aContentParent;
 
-  IndexedDatabaseManager* mgr = IndexedDatabaseManager::Get();
-  NS_ASSERTION(mgr, "This should never be null!");
+  QuotaManager* quotaManager = QuotaManager::Get();
+  NS_ASSERTION(quotaManager, "This should never be null!");
 
-  if (!mgr->RegisterDatabase(db)) {
+  db->mQuotaClient = quotaManager->GetClient(Client::IDB);
+  NS_ASSERTION(db->mQuotaClient, "This shouldn't fail!");
+
+  if (!quotaManager->RegisterStorage(db)) {
     // Either out of memory or shutting down.
     return nullptr;
   }
 
+  db->mRegistered = true;
+
   return db.forget();
 }
 
+// static
+IDBDatabase*
+IDBDatabase::FromStorage(nsIOfflineStorage* aStorage)
+{
+  return aStorage->GetClient()->GetType() == Client::IDB ?
+         static_cast<IDBDatabase*>(aStorage) : nullptr;
+}
+
 IDBDatabase::IDBDatabase()
 : mDatabaseId(0),
   mActorChild(nullptr),
   mActorParent(nullptr),
   mContentParent(nullptr),
   mInvalidated(false),
   mRegistered(false),
   mClosed(false),
@@ -232,24 +246,24 @@ IDBDatabase::~IDBDatabase()
     NS_ASSERTION(!IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
     mActorChild->Send__delete__(mActorChild);
     NS_ASSERTION(!mActorChild, "Should have cleared in Send__delete__!");
   }
 
   if (mRegistered) {
     CloseInternal(true);
 
-    IndexedDatabaseManager* mgr = IndexedDatabaseManager::Get();
-    if (mgr) {
-      mgr->UnregisterDatabase(this);
+    QuotaManager* quotaManager = QuotaManager::Get();
+    if (quotaManager) {
+      quotaManager->UnregisterStorage(this);
     }
   }
 }
 
-void
+NS_IMETHODIMP_(void)
 IDBDatabase::Invalidate()
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
   if (IsInvalidated()) {
     return;
   }
 
@@ -306,31 +320,31 @@ IDBDatabase::CloseInternal(bool aIsDead)
 
       if (!aIsDead) {
         nsRefPtr<DatabaseInfo> clonedInfo = previousInfo->Clone();
 
         clonedInfo.swap(mDatabaseInfo);
       }
     }
 
-    IndexedDatabaseManager* mgr = IndexedDatabaseManager::Get();
-    if (mgr) {
-      mgr->OnDatabaseClosed(this);
+    QuotaManager* quotaManager = QuotaManager::Get();
+    if (quotaManager) {
+      quotaManager->OnStorageClosed(this);
     }
 
     // And let the parent process know as well.
     if (mActorChild && !IsInvalidated()) {
       NS_ASSERTION(!IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
       mActorChild->SendClose(aIsDead);
     }
   }
 }
 
-bool
-IDBDatabase::IsClosed() const
+NS_IMETHODIMP_(bool)
+IDBDatabase::IsClosed()
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
   return mClosed;
 }
 
 void
 IDBDatabase::EnterSetVersionTransaction()
 {
@@ -362,20 +376,20 @@ void
 IDBDatabase::OnUnlink()
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
   // We've been unlinked, at the very least we should be able to prevent further
   // transactions from starting and unblock any other SetVersion callers.
   CloseInternal(true);
 
-  // No reason for the IndexedDatabaseManager to track us any longer.
-  IndexedDatabaseManager* mgr = IndexedDatabaseManager::Get();
-  if (mgr) {
-    mgr->UnregisterDatabase(this);
+  // No reason for the QuotaManager to track us any longer.
+  QuotaManager* quotaManager = QuotaManager::Get();
+  if (quotaManager) {
+    quotaManager->UnregisterStorage(this);
 
     // Don't try to unregister again in the destructor.
     mRegistered = false;
   }
 }
 
 nsresult
 IDBDatabase::CreateObjectStoreInternal(IDBTransaction* aTransaction,
@@ -428,16 +442,17 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_IN
 
   // Do some cleanup.
   tmp->OnUnlink();
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(IDBDatabase)
   NS_INTERFACE_MAP_ENTRY(nsIIDBDatabase)
   NS_INTERFACE_MAP_ENTRY(nsIFileStorage)
+  NS_INTERFACE_MAP_ENTRY(nsIOfflineStorage)
   NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(IDBDatabase)
 NS_INTERFACE_MAP_END_INHERITING(IDBWrapperCache)
 
 NS_IMPL_ADDREF_INHERITED(IDBDatabase, IDBWrapperCache)
 NS_IMPL_RELEASE_INHERITED(IDBDatabase, IDBWrapperCache)
 
 DOMCI_DATA(IDBDatabase, IDBDatabase)
 
@@ -597,17 +612,17 @@ NS_IMETHODIMP
 IDBDatabase::Transaction(const jsval& aStoreNames,
                          const nsAString& aMode,
                          JSContext* aCx,
                          uint8_t aOptionalArgCount,
                          nsIIDBTransaction** _retval)
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
-  if (IndexedDatabaseManager::IsShuttingDown()) {
+  if (QuotaManager::IsShuttingDown()) {
     return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
   }
 
   if (mClosed) {
     return NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR;
   }
 
   if (mRunningVersionChange) {
@@ -739,33 +754,33 @@ IDBDatabase::MozCreateFileHandle(const n
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
   if (!IndexedDatabaseManager::IsMainProcess()) {
     NS_WARNING("Not supported yet!");
     return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
   }
 
-  if (IndexedDatabaseManager::IsShuttingDown()) {
+  if (QuotaManager::IsShuttingDown()) {
     return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
   }
 
   if (mClosed) {
     return NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR;
   }
 
   nsRefPtr<IDBRequest> request = IDBRequest::Create(nullptr, this, nullptr, aCx);
 
   nsRefPtr<CreateFileHelper> helper =
     new CreateFileHelper(this, request, aName, aType);
 
-  IndexedDatabaseManager* manager = IndexedDatabaseManager::Get();
-  NS_ASSERTION(manager, "We should definitely have a manager here");
+  QuotaManager* quotaManager = QuotaManager::Get();
+  NS_ASSERTION(quotaManager, "We should definitely have a manager here");
 
-  nsresult rv = helper->Dispatch(manager->IOThread());
+  nsresult rv = helper->Dispatch(quotaManager->IOThread());
   NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
   request.forget(_retval);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 IDBDatabase::Close()
@@ -773,53 +788,65 @@ IDBDatabase::Close()
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
   CloseInternal(false);
 
   NS_ASSERTION(mClosed, "Should have set the closed flag!");
   return NS_OK;
 }
 
-const nsACString&
-IDBDatabase::StorageOrigin()
+NS_IMETHODIMP_(nsIAtom*)
+IDBDatabase::Id()
 {
-  return Origin();
-}
-
-nsISupports*
-IDBDatabase::StorageId()
-{
-  return Id();
+  return mDatabaseId;
 }
 
-bool
-IDBDatabase::IsStorageInvalidated()
+NS_IMETHODIMP_(bool)
+IDBDatabase::IsInvalidated()
 {
-  return IsInvalidated();
+  return mInvalidated;
 }
 
-bool
-IDBDatabase::IsStorageShuttingDown()
+NS_IMETHODIMP_(bool)
+IDBDatabase::IsShuttingDown()
 {
-  return IndexedDatabaseManager::IsShuttingDown();
+  return QuotaManager::IsShuttingDown();
 }
 
-void
+NS_IMETHODIMP_(void)
 IDBDatabase::SetThreadLocals()
 {
   NS_ASSERTION(GetOwner(), "Should have owner!");
   QuotaManager::SetCurrentWindow(GetOwner());
 }
 
-void
+NS_IMETHODIMP_(void)
 IDBDatabase::UnsetThreadLocals()
 {
   QuotaManager::SetCurrentWindow(nullptr);
 }
 
+NS_IMETHODIMP_(mozilla::dom::quota::Client*)
+IDBDatabase::GetClient()
+{
+  return mQuotaClient;
+}
+
+NS_IMETHODIMP_(bool)
+IDBDatabase::IsOwned(nsPIDOMWindow* aOwner)
+{
+  return GetOwner() == aOwner;
+}
+
+NS_IMETHODIMP_(const nsACString&)
+IDBDatabase::Origin()
+{
+  return mASCIIOrigin;
+}
+
 nsresult
 IDBDatabase::PostHandleEvent(nsEventChainPostVisitor& aVisitor)
 {
   return IndexedDatabaseManager::FireWindowOnError(GetOwner(), aVisitor);
 }
 
 AsyncConnectionHelper::ChildProcessSendResult
 NoRequestDatabaseHelper::SendResponseToChildProcess(nsresult aResultCode)
--- a/dom/indexedDB/IDBDatabase.h
+++ b/dom/indexedDB/IDBDatabase.h
@@ -7,26 +7,32 @@
 #ifndef mozilla_dom_indexeddb_idbdatabase_h__
 #define mozilla_dom_indexeddb_idbdatabase_h__
 
 #include "mozilla/dom/indexedDB/IndexedDatabase.h"
 
 #include "nsIDocument.h"
 #include "nsIFileStorage.h"
 #include "nsIIDBDatabase.h"
+#include "nsIOfflineStorage.h"
+
 #include "nsDOMEventTargetHelper.h"
+
+#include "mozilla/dom/indexedDB/FileManager.h"
 #include "mozilla/dom/indexedDB/IDBWrapperCache.h"
-#include "mozilla/dom/indexedDB/FileManager.h"
 
 class nsIScriptContext;
 class nsPIDOMWindow;
 
 namespace mozilla {
 namespace dom {
 class ContentParent;
+namespace quota {
+class Client;
+}
 }
 }
 
 BEGIN_INDEXEDDB_NAMESPACE
 
 class AsyncConnectionHelper;
 struct DatabaseInfo;
 class IDBFactory;
@@ -35,45 +41,51 @@ class IDBObjectStore;
 class IDBTransaction;
 class IndexedDatabaseManager;
 class IndexedDBDatabaseChild;
 class IndexedDBDatabaseParent;
 struct ObjectStoreInfoGuts;
 
 class IDBDatabase : public IDBWrapperCache,
                     public nsIIDBDatabase,
-                    public nsIFileStorage
+                    public nsIOfflineStorage
 {
   friend class AsyncConnectionHelper;
   friend class IndexedDatabaseManager;
   friend class IndexedDBDatabaseChild;
 
 public:
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_NSIIDBDATABASE
   NS_DECL_NSIFILESTORAGE
+  NS_DECL_NSIOFFLINESTORAGE_NOCLOSE
 
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(IDBDatabase, IDBWrapperCache)
 
   static already_AddRefed<IDBDatabase>
   Create(IDBWrapperCache* aOwnerCache,
          IDBFactory* aFactory,
          already_AddRefed<DatabaseInfo> aDatabaseInfo,
          const nsACString& aASCIIOrigin,
          FileManager* aFileManager,
          mozilla::dom::ContentParent* aContentParent);
 
+  static IDBDatabase*
+  FromStorage(nsIOfflineStorage* aStorage);
+
+  static IDBDatabase*
+  FromStorage(nsIFileStorage* aStorage)
+  {
+    nsCOMPtr<nsIOfflineStorage> storage = do_QueryInterface(aStorage);
+    return storage ? FromStorage(storage) : nullptr;
+  }
+
   // nsIDOMEventTarget
   virtual nsresult PostHandleEvent(nsEventChainPostVisitor& aVisitor);
 
-  nsIAtom* Id() const
-  {
-    return mDatabaseId;
-  }
-
   DatabaseInfo* Info() const
   {
     return mDatabaseInfo;
   }
 
   const nsString& Name() const
   {
     return mName;
@@ -90,38 +102,20 @@ public:
       return nullptr;
     }
 
     nsCOMPtr<nsIDocument> doc =
       do_QueryInterface(GetOwner()->GetExtantDocument());
     return doc.forget();
   }
 
-  const nsCString& Origin() const
-  {
-    return mASCIIOrigin;
-  }
-
-  void Invalidate();
-
-  // Whether or not the database has been invalidated. If it has then no further
-  // transactions for this database will be allowed to run. This function may be
-  // called on any thread.
-  bool IsInvalidated() const
-  {
-    return mInvalidated;
-  }
-
   void DisconnectFromActorParent();
 
   void CloseInternal(bool aIsDead);
 
-  // Whether or not the database has had Close called on it.
-  bool IsClosed() const;
-
   void EnterSetVersionTransaction();
   void ExitSetVersionTransaction();
 
   // Called when a versionchange transaction is aborted to reset the
   // DatabaseInfo.
   void RevertToPreviousState();
 
   FileManager* Manager() const
@@ -190,16 +184,18 @@ private:
 
   nsRefPtr<FileManager> mFileManager;
 
   IndexedDBDatabaseChild* mActorChild;
   IndexedDBDatabaseParent* mActorParent;
 
   mozilla::dom::ContentParent* mContentParent;
 
+  nsRefPtr<mozilla::dom::quota::Client> mQuotaClient;
+
   bool mInvalidated;
   bool mRegistered;
   bool mClosed;
   bool mRunningVersionChange;
 };
 
 END_INDEXEDDB_NAMESPACE
 
--- a/dom/indexedDB/IDBFactory.cpp
+++ b/dom/indexedDB/IDBFactory.cpp
@@ -13,16 +13,18 @@
 #include "nsIScriptContext.h"
 #include "nsIXPConnect.h"
 #include "nsIXPCScriptable.h"
 
 #include "jsdbgapi.h"
 #include "mozilla/dom/ContentParent.h"
 #include "mozilla/dom/ContentChild.h"
 #include "mozilla/dom/PBrowserChild.h"
+#include "mozilla/dom/quota/OriginOrPatternString.h"
+#include "mozilla/dom/quota/QuotaManager.h"
 #include "mozilla/dom/TabChild.h"
 #include "mozilla/storage.h"
 #include "nsComponentManagerUtils.h"
 #include "nsIScriptSecurityManager.h"
 #include "nsCharSeparatedTokenizer.h"
 #include "nsContentUtils.h"
 #include "nsDOMClassInfoID.h"
 #include "nsGlobalWindow.h"
@@ -40,16 +42,17 @@
 #include "IDBKeyRange.h"
 #include "IndexedDatabaseManager.h"
 #include "Key.h"
 
 #include "ipc/IndexedDBChild.h"
 #include <algorithm>
 
 USING_INDEXEDDB_NAMESPACE
+USING_QUOTA_NAMESPACE
 
 using mozilla::dom::ContentChild;
 using mozilla::dom::ContentParent;
 using mozilla::dom::TabChild;
 
 namespace {
 
 struct ObjectStoreInfoMap
@@ -98,25 +101,25 @@ IDBFactory::Create(nsPIDOMWindow* aWindo
 
   if (aWindow->IsOuterWindow()) {
     aWindow = aWindow->GetCurrentInnerWindow();
     NS_ENSURE_TRUE(aWindow, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
   }
 
   // Make sure that the manager is up before we do anything here since lots of
   // decisions depend on which process we're running in.
-  nsRefPtr<indexedDB::IndexedDatabaseManager> mgr =
+  indexedDB::IndexedDatabaseManager* mgr =
     indexedDB::IndexedDatabaseManager::GetOrCreate();
   NS_ENSURE_TRUE(mgr, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
   nsresult rv;
 
   nsCString origin(aASCIIOrigin);
   if (origin.IsEmpty()) {
-    rv = IndexedDatabaseManager::GetASCIIOriginFromWindow(aWindow, origin);
+    rv = QuotaManager::GetASCIIOriginFromWindow(aWindow, origin);
     if (NS_FAILED(rv)) {
       // Not allowed.
       *aFactory = nullptr;
       return NS_OK;
     }
   }
 
   nsRefPtr<IDBFactory> factory = new IDBFactory();
@@ -156,18 +159,17 @@ IDBFactory::Create(JSContext* aCx,
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
   NS_ASSERTION(aCx, "Null context!");
   NS_ASSERTION(aOwningObject, "Null object!");
   NS_ASSERTION(JS_GetGlobalForObject(aCx, aOwningObject) == aOwningObject,
                "Not a global object!");
   NS_ASSERTION(nsContentUtils::IsCallerChrome(), "Only for chrome!");
 
   nsCString origin;
-  nsresult rv =
-    IndexedDatabaseManager::GetASCIIOriginFromWindow(nullptr, origin);
+  nsresult rv = QuotaManager::GetASCIIOriginFromWindow(nullptr, origin);
   NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
   nsRefPtr<IDBFactory> factory = new IDBFactory();
   factory->mASCIIOrigin = origin;
   factory->mOwningObject = aOwningObject;
   factory->mContentParent = aContentParent;
 
   if (!IndexedDatabaseManager::IsMainProcess()) {
@@ -516,17 +518,17 @@ IDBFactory::OpenCommon(const nsAString& 
                        JSContext* aCallingCx,
                        IDBOpenDBRequest** _retval)
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
   NS_ASSERTION(mWindow || mOwningObject, "Must have one of these!");
 
   nsCOMPtr<nsPIDOMWindow> window;
   JSObject* scriptOwner = nullptr;
-  FactoryPrivilege privilege;
+  StoragePrivilege privilege;
 
   if (mWindow) {
     window = mWindow;
     scriptOwner =
       static_cast<nsGlobalWindow*>(window.get())->FastGetGlobalJSObject();
     privilege = Content;
   }
   else {
@@ -546,27 +548,27 @@ IDBFactory::OpenCommon(const nsAString& 
                              mContentParent, privilege);
 
     rv = openHelper->Init();
     NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
     nsRefPtr<CheckPermissionsHelper> permissionHelper =
       new CheckPermissionsHelper(openHelper, window, aDeleting);
 
-    IndexedDatabaseManager* mgr = IndexedDatabaseManager::Get();
-    NS_ASSERTION(mgr, "This should never be null!");
+    QuotaManager* quotaManager = QuotaManager::Get();
+    NS_ASSERTION(quotaManager, "This should never be null!");
 
-    rv =
-      mgr->WaitForOpenAllowed(OriginOrPatternString::FromOrigin(aASCIIOrigin),
-                              openHelper->Id(), permissionHelper);
+    rv = quotaManager->WaitForOpenAllowed(OriginOrPatternString::FromOrigin(
+                                          aASCIIOrigin), openHelper->Id(),
+                                          permissionHelper);
     NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
   }
   else if (aDeleting) {
     nsCOMPtr<nsIAtom> databaseId =
-      IndexedDatabaseManager::GetDatabaseId(aASCIIOrigin, aName);
+      QuotaManager::GetStorageId(aASCIIOrigin, aName);
     NS_ENSURE_TRUE(databaseId, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
     IndexedDBDeleteDatabaseRequestChild* actor =
       new IndexedDBDeleteDatabaseRequestChild(this, request, databaseId);
 
     mActorChild->SendPIndexedDBDeleteDatabaseRequestConstructor(
                                                                actor,
                                                                nsString(aName));
--- a/dom/indexedDB/IDBFileHandle.cpp
+++ b/dom/indexedDB/IDBFileHandle.cpp
@@ -62,17 +62,20 @@ IDBFileHandle::Create(IDBDatabase* aData
   fileInfo.swap(newFile->mFileInfo);
 
   return newFile.forget();
 }
 
 already_AddRefed<nsISupports>
 IDBFileHandle::CreateStream(nsIFile* aFile, bool aReadOnly)
 {
-  const nsACString& origin = mFileStorage->StorageOrigin();
+  nsCOMPtr<nsIOfflineStorage> storage = do_QueryInterface(mFileStorage);
+  NS_ASSERTION(storage, "This should always succeed!");
+
+  const nsACString& origin = storage->Origin();
 
   nsCOMPtr<nsISupports> result;
 
   if (aReadOnly) {
     nsRefPtr<FileInputStream> stream = FileInputStream::Create(
       origin, aFile, -1, -1, nsIFileInputStream::DEFER_OPEN);
     result = NS_ISUPPORTS_CAST(nsIFileInputStream*, stream);
   }
--- a/dom/indexedDB/IDBTransaction.h
+++ b/dom/indexedDB/IDBTransaction.h
@@ -15,16 +15,17 @@
 #include "nsIIDBTransaction.h"
 #include "nsIDOMDOMError.h"
 #include "nsIRunnable.h"
 
 #include "nsAutoPtr.h"
 #include "nsClassHashtable.h"
 #include "nsHashKeys.h"
 #include "nsInterfaceHashtable.h"
+#include "nsRefPtrHashtable.h"
 
 #include "mozilla/dom/indexedDB/IDBDatabase.h"
 #include "mozilla/dom/indexedDB/IDBWrapperCache.h"
 #include "mozilla/dom/indexedDB/FileInfo.h"
 
 class nsIThread;
 
 BEGIN_INDEXEDDB_NAMESPACE
--- a/dom/indexedDB/IndexedDatabase.h
+++ b/dom/indexedDB/IndexedDatabase.h
@@ -31,21 +31,16 @@ class nsIDOMBlob;
 class nsIInputStream;
 
 BEGIN_INDEXEDDB_NAMESPACE
 
 class FileInfo;
 class IDBDatabase;
 class IDBTransaction;
 
-enum FactoryPrivilege {
-  Content,
-  Chrome
-};
-
 template <class T>
 void SwapData(T& aData1, T& aData2)
 {
   T temp = aData2;
   aData2 = aData1;
   aData1 = temp;
 }
 
@@ -170,49 +165,11 @@ struct SerializedStructuredCloneWriteInf
   }
 
   // Make sure to update ipc/SerializationHelpers.h when changing members here!
   uint64_t* data;
   size_t dataLength;
   uint64_t offsetToKeyProp;
 };
 
-class OriginOrPatternString : public nsCString
-{
-public:
-  static OriginOrPatternString
-  FromOrigin(const nsACString& aOrigin)
-  {
-    return OriginOrPatternString(aOrigin, true);
-  }
-
-  static OriginOrPatternString
-  FromPattern(const nsACString& aPattern)
-  {
-    return OriginOrPatternString(aPattern, false);
-  }
-
-  bool
-  IsOrigin() const
-  {
-    return mIsOrigin;
-  }
-
-  bool
-  IsPattern() const
-  {
-    return !mIsOrigin;
-  }
-
-private:
-  OriginOrPatternString(const nsACString& aOriginOrPattern, bool aIsOrigin)
-  : nsCString(aOriginOrPattern), mIsOrigin(aIsOrigin)
-  { }
-
-  bool
-  operator==(const OriginOrPatternString& aOther) MOZ_DELETE;
-
-  bool mIsOrigin;
-};
-
 END_INDEXEDDB_NAMESPACE
 
 #endif // mozilla_dom_indexeddb_indexeddatabase_h__
--- a/dom/indexedDB/IndexedDatabaseInlines.h
+++ b/dom/indexedDB/IndexedDatabaseInlines.h
@@ -74,22 +74,9 @@ AppendConditionClause(const nsACString& 
 
   if (aEquals) {
     aResult.AppendLiteral("=");
   }
 
   aResult += NS_LITERAL_CSTRING(" :") + aArgName;
 }
 
-inline void
-IncrementUsage(uint64_t* aUsage, uint64_t aDelta)
-{
-  // Watch for overflow!
-  if ((UINT64_MAX - *aUsage) < aDelta) {
-    NS_WARNING("Usage exceeds the maximum!");
-    *aUsage = UINT64_MAX;
-  }
-  else {
-    *aUsage += aDelta;
-  }
-}
-
 END_INDEXEDDB_NAMESPACE
--- a/dom/indexedDB/IndexedDatabaseManager.cpp
+++ b/dom/indexedDB/IndexedDatabaseManager.cpp
@@ -1,255 +1,62 @@
 /* -*- 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 "IndexedDatabaseManager.h"
 
-#include "mozIApplicationClearPrivateDataParams.h"
-#include "nsIAtom.h"
 #include "nsIConsoleService.h"
 #include "nsIDOMScriptObjectFactory.h"
 #include "nsIFile.h"
 #include "nsIFileStorage.h"
-#include "nsIObserverService.h"
-#include "nsIPrincipal.h"
 #include "nsIScriptError.h"
-#include "nsIScriptObjectPrincipal.h"
-#include "nsIScriptSecurityManager.h"
-#include "nsISHEntry.h"
-#include "nsISimpleEnumerator.h"
-#include "nsITimer.h"
 
-#include "mozilla/dom/file/FileService.h"
+#include "mozilla/ClearOnShutdown.h"
 #include "mozilla/dom/quota/QuotaManager.h"
+#include "mozilla/dom/quota/Utilities.h"
 #include "mozilla/dom/TabContext.h"
-#include "mozilla/LazyIdleThread.h"
-#include "mozilla/Preferences.h"
-#include "mozilla/Services.h"
 #include "mozilla/storage.h"
-#include "nsAppDirectoryServiceDefs.h"
 #include "nsContentUtils.h"
-#include "nsCRTGlue.h"
-#include "nsDirectoryServiceUtils.h"
 #include "nsEventDispatcher.h"
-#include "nsScriptSecurityManager.h"
 #include "nsThreadUtils.h"
-#include "nsXPCOM.h"
-#include "nsXPCOMPrivate.h"
 
-#include "AsyncConnectionHelper.h"
-#include "IDBDatabase.h"
 #include "IDBEvents.h"
 #include "IDBFactory.h"
 #include "IDBKeyRange.h"
-#include "OpenDatabaseHelper.h"
-#include "TransactionThreadPool.h"
-
-#include "IndexedDatabaseInlines.h"
-#include <algorithm>
-
-// 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 database
-// transactions on shutdown before aborting them.
-#define DEFAULT_SHUTDOWN_TIMER_MS 30000
-
-// Amount of space that IndexedDB databases may use by default in megabytes.
-#define DEFAULT_QUOTA_MB 50
-
-// Preference that users can set to override DEFAULT_QUOTA_MB
-#define PREF_INDEXEDDB_QUOTA "dom.indexedDB.warningQuota"
-
-// profile-before-change, when we need to shut down IDB
-#define PROFILE_BEFORE_CHANGE_OBSERVER_ID "profile-before-change"
+#include "IDBRequest.h"
 
 USING_INDEXEDDB_NAMESPACE
-using namespace mozilla::services;
 using namespace mozilla::dom;
-using mozilla::Preferences;
-using mozilla::dom::file::FileService;
-using mozilla::dom::quota::QuotaManager;
+USING_QUOTA_NAMESPACE
 
 static NS_DEFINE_CID(kDOMSOF_CID, NS_DOM_SCRIPT_OBJECT_FACTORY_CID);
 
 namespace {
 
-int32_t gShutdown = 0;
+mozilla::StaticRefPtr<IndexedDatabaseManager> gInstance;
+
+int32_t gInitialized = 0;
 int32_t gClosed = 0;
 
-// Does not hold a reference.
-IndexedDatabaseManager* gInstance = nullptr;
-
-int32_t gIndexedDBQuotaMB = DEFAULT_QUOTA_MB;
-
-bool
-GetDatabaseBaseFilename(const nsAString& aFilename,
-                        nsAString& aDatabaseBaseFilename)
-{
-  NS_ASSERTION(!aFilename.IsEmpty(), "Bad argument!");
-
-  NS_NAMED_LITERAL_STRING(sqlite, ".sqlite");
-  nsAString::size_type filenameLen = aFilename.Length();
-  nsAString::size_type sqliteLen = sqlite.Length();
-
-  if (sqliteLen > filenameLen ||
-      Substring(aFilename, filenameLen - sqliteLen, sqliteLen) != sqlite) {
-    return false;
-  }
-
-  aDatabaseBaseFilename = Substring(aFilename, 0, filenameLen - sqliteLen);
-
-  return true;
-}
-
-// Adds all databases in the hash to the given array.
-template <class T>
-PLDHashOperator
-EnumerateToTArray(const nsACString& aKey,
-                  nsTArray<IDBDatabase*>* aValue,
-                  void* aUserArg)
+class AsyncDeleteFileRunnable MOZ_FINAL : public nsIRunnable
 {
-  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
-  NS_ASSERTION(!aKey.IsEmpty(), "Empty key!");
-  NS_ASSERTION(aValue, "Null pointer!");
-  NS_ASSERTION(aUserArg, "Null pointer!");
-
-  static_cast<nsTArray<T>*>(aUserArg)->AppendElements(*aValue);
-  return PL_DHASH_NEXT;
-}
-
-bool
-PatternMatchesOrigin(const nsACString& aPatternString, const nsACString& aOrigin)
-{
-  // Aren't we smart!
-  return StringBeginsWith(aOrigin, aPatternString);
-}
-
-enum MozBrowserPatternFlag
-{
-  MozBrowser = 0,
-  NotMozBrowser,
-  IgnoreMozBrowser
-};
-
-// Use one of the friendly overloads below.
-void
-GetOriginPatternString(uint32_t aAppId, MozBrowserPatternFlag aBrowserFlag,
-                       const nsACString& aOrigin, nsAutoCString& _retval)
-{
-  NS_ASSERTION(aAppId != nsIScriptSecurityManager::UNKNOWN_APP_ID,
-               "Bad appId!");
-  NS_ASSERTION(aOrigin.IsEmpty() || aBrowserFlag != IgnoreMozBrowser,
-               "Bad args!");
-
-  if (aOrigin.IsEmpty()) {
-    _retval.Truncate();
-
-    _retval.AppendInt(aAppId);
-    _retval.Append('+');
+public:
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIRUNNABLE
 
-    if (aBrowserFlag != IgnoreMozBrowser) {
-      if (aBrowserFlag == MozBrowser) {
-        _retval.Append('t');
-      }
-      else {
-        _retval.Append('f');
-      }
-      _retval.Append('+');
-    }
-
-    return;
-  }
-
-#ifdef DEBUG
-  if (aAppId != nsIScriptSecurityManager::NO_APP_ID ||
-      aBrowserFlag == MozBrowser) {
-    nsAutoCString pattern;
-    GetOriginPatternString(aAppId, aBrowserFlag, EmptyCString(), pattern);
-    NS_ASSERTION(PatternMatchesOrigin(pattern, aOrigin),
-                 "Origin doesn't match parameters!");
-  }
-#endif
-
-  _retval = aOrigin;
-}
-
-void
-GetOriginPatternString(uint32_t aAppId, bool aBrowserOnly,
-                       const nsACString& aOrigin, nsAutoCString& _retval)
-{
-  return GetOriginPatternString(aAppId,
-                                aBrowserOnly ? MozBrowser : NotMozBrowser,
-                                aOrigin, _retval);
-}
-
-void
-GetOriginPatternStringMaybeIgnoreBrowser(uint32_t aAppId, bool aBrowserOnly,
-                                         nsAutoCString& _retval)
-{
-  return GetOriginPatternString(aAppId,
-                                aBrowserOnly ? MozBrowser : IgnoreMozBrowser,
-                                EmptyCString(), _retval);
-}
-
-template <class ValueType>
-class PatternMatchArray : public nsAutoTArray<ValueType, 20>
-{
-  typedef PatternMatchArray<ValueType> SelfType;
-
-  struct Closure
-  {
-    Closure(SelfType& aSelf, const nsACString& aPattern)
-    : mSelf(aSelf), mPattern(aPattern)
-    { }
-
-    SelfType& mSelf;
-    const nsACString& mPattern;
-  };
-
-public:
-  template <class T>
-  void
-  Find(const T& aHashtable,
-       const nsACString& aPattern)
-  {
-    SelfType::Clear();
-
-    Closure closure(*this, aPattern);
-    aHashtable.EnumerateRead(SelfType::Enumerate, &closure);
-  }
+  AsyncDeleteFileRunnable(FileManager* aFileManager, int64_t aFileId);
 
 private:
-  static PLDHashOperator
-  Enumerate(const nsACString& aKey,
-            nsTArray<ValueType>* aValue,
-            void* aUserArg)
-  {
-    NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
-    NS_ASSERTION(!aKey.IsEmpty(), "Empty key!");
-    NS_ASSERTION(aValue, "Null pointer!");
-    NS_ASSERTION(aUserArg, "Null pointer!");
-
-    Closure* closure = static_cast<Closure*>(aUserArg);
-
-    if (PatternMatchesOrigin(closure->mPattern, aKey)) {
-      closure->mSelf.AppendElements(*aValue);
-    }
-
-    return PL_DHASH_NEXT;
-  }
+  nsRefPtr<FileManager> mFileManager;
+  int64_t mFileId;
 };
 
-typedef PatternMatchArray<IDBDatabase*> DatabasePatternMatchArray;
-
 PLDHashOperator
 InvalidateAndRemoveFileManagers(
                            const nsACString& aKey,
                            nsAutoPtr<nsTArray<nsRefPtr<FileManager> > >& aValue,
                            void* aUserArg)
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
   NS_ASSERTION(!aKey.IsEmpty(), "Empty key!");
@@ -264,240 +71,107 @@ InvalidateAndRemoveFileManagers(
       fileManager->Invalidate();
     }
     return PL_DHASH_REMOVE;
   }
 
   return PL_DHASH_NEXT;
 }
 
-void
-SanitizeOriginString(nsCString& aOrigin)
-{
-  // 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.
-  static const char kReplaceChars[] = CONTROL_CHARACTERS "/:*?\"<>|\\";
-
-#ifdef XP_WIN
-  NS_ASSERTION(!strcmp(kReplaceChars,
-                       FILE_ILLEGAL_CHARACTERS FILE_PATH_SEPARATOR),
-               "Illegal file characters have changed!");
-#endif
-
-  aOrigin.ReplaceChar(kReplaceChars, '+');
-}
-
-nsresult
-GetASCIIOriginFromURI(nsIURI* aURI,
-                      uint32_t aAppId,
-                      bool aInMozBrowser,
-                      nsACString& aOrigin)
-{
-  NS_ASSERTION(aURI, "Null uri!");
-
-  nsCString origin;
-  mozilla::GetExtendedOrigin(aURI, aAppId, aInMozBrowser, origin);
-
-  if (origin.IsEmpty()) {
-    NS_WARNING("GetExtendedOrigin returned empty string!");
-    return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
-  }
-
-  aOrigin.Assign(origin);
-  return NS_OK;
-}
-
-nsresult
-GetASCIIOriginFromPrincipal(nsIPrincipal* aPrincipal,
-                            nsACString& aOrigin)
-{
-  NS_ASSERTION(aPrincipal, "Don't hand me a null principal!");
-
-  static const char kChromeOrigin[] = "chrome";
-
-  nsCString origin;
-  if (nsContentUtils::IsSystemPrincipal(aPrincipal)) {
-    origin.AssignLiteral(kChromeOrigin);
-  }
-  else {
-    bool isNullPrincipal;
-    nsresult rv = aPrincipal->GetIsNullPrincipal(&isNullPrincipal);
-    NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
-
-    if (isNullPrincipal) {
-      NS_WARNING("IndexedDB not supported from this principal!");
-      return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
-    }
-
-    rv = aPrincipal->GetExtendedOrigin(origin);
-    NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
-
-    if (origin.EqualsLiteral(kChromeOrigin)) {
-      NS_WARNING("Non-chrome principal can't use chrome origin!");
-      return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
-    }
-  }
-
-  aOrigin.Assign(origin);
-  return NS_OK;
-}
-
 } // anonymous namespace
 
 IndexedDatabaseManager::IndexedDatabaseManager()
 : mFileMutex("IndexedDatabaseManager.mFileMutex")
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
-  NS_ASSERTION(!gInstance, "More than one instance!");
+
+  mFileManagers.Init();
 }
 
 IndexedDatabaseManager::~IndexedDatabaseManager()
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
-  NS_ASSERTION(!gInstance || gInstance == this, "Different instances!");
-  gInstance = nullptr;
 }
 
 bool IndexedDatabaseManager::sIsMainProcess = false;
 
 // static
-already_AddRefed<IndexedDatabaseManager>
+IndexedDatabaseManager*
 IndexedDatabaseManager::GetOrCreate()
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
-  if (IsShuttingDown()) {
-    NS_ERROR("Calling GetOrCreateInstance() after shutdown!");
+  if (IsClosed()) {
+    NS_ERROR("Calling GetOrCreate() after shutdown!");
     return nullptr;
   }
 
-  nsRefPtr<IndexedDatabaseManager> instance(gInstance);
-
-  if (!instance) {
+  if (!gInstance) {
     sIsMainProcess = XRE_GetProcessType() == GeckoProcessType_Default;
 
-    instance = new IndexedDatabaseManager();
-
-    instance->mLiveDatabases.Init();
-    instance->mFileManagers.Init();
-
-    nsresult rv;
-
-    if (sIsMainProcess) {
-      nsCOMPtr<nsIFile> dbBaseDirectory;
-      rv = NS_GetSpecialDirectory(NS_APP_INDEXEDDB_PARENT_DIR,
-                                  getter_AddRefs(dbBaseDirectory));
-      if (NS_FAILED(rv)) {
-          rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR,
-                                      getter_AddRefs(dbBaseDirectory));
-      }
-      NS_ENSURE_SUCCESS(rv, nullptr);
+    nsRefPtr<IndexedDatabaseManager> instance(new IndexedDatabaseManager());
 
-      rv = dbBaseDirectory->Append(NS_LITERAL_STRING("indexedDB"));
-      NS_ENSURE_SUCCESS(rv, nullptr);
-
-      rv = dbBaseDirectory->GetPath(instance->mDatabaseBasePath);
-      NS_ENSURE_SUCCESS(rv, nullptr);
+    nsresult rv = instance->Init();
+    NS_ENSURE_SUCCESS(rv, nullptr);
 
-      // Make a lazy thread for any IO we need (like clearing or enumerating the
-      // contents of indexedDB database directories).
-      instance->mIOThread =
-        new LazyIdleThread(DEFAULT_THREAD_TIMEOUT_MS,
-                           NS_LITERAL_CSTRING("IndexedDB I/O"),
-                           LazyIdleThread::ManualShutdown);
-
-      // Make a timer here to avoid potential failures later. We don't actually
-      // initialize the timer until shutdown.
-      instance->mShutdownTimer = do_CreateInstance(NS_TIMER_CONTRACTID);
-      NS_ENSURE_TRUE(instance->mShutdownTimer, nullptr);
+    if (PR_ATOMIC_SET(&gInitialized, 1)) {
+      NS_ERROR("Initialized more than once?!");
     }
 
-    // Make sure that the quota manager is up.
-    NS_ENSURE_TRUE(QuotaManager::GetOrCreate(), nullptr);
-
-    nsCOMPtr<nsIObserverService> obs = GetObserverService();
-    NS_ENSURE_TRUE(obs, nullptr);
-
-    // Must initialize the storage service on the main thread.
-    nsCOMPtr<mozIStorageService> ss =
-      do_GetService(MOZ_STORAGE_SERVICE_CONTRACTID);
-    NS_ENSURE_TRUE(ss, nullptr);
+    gInstance = instance;
 
-    // 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);
-
-    if (NS_FAILED(Preferences::AddIntVarCache(&gIndexedDBQuotaMB,
-                                              PREF_INDEXEDDB_QUOTA,
-                                              DEFAULT_QUOTA_MB))) {
-      NS_WARNING("Unable to respond to quota pref changes!");
-      gIndexedDBQuotaMB = DEFAULT_QUOTA_MB;
-    }
-
-    // The observer service will hold our last reference, don't AddRef here.
-    gInstance = instance;
+    ClearOnShutdown(&gInstance);
   }
 
-  return instance.forget();
+  return gInstance;
 }
 
 // static
 IndexedDatabaseManager*
 IndexedDatabaseManager::Get()
 {
   // Does not return an owning reference.
   return gInstance;
 }
 
 // static
 IndexedDatabaseManager*
 IndexedDatabaseManager::FactoryCreate()
 {
   // Returns a raw pointer that carries an owning reference! Lame, but the
   // singleton factory macros force this.
-  return GetOrCreate().get();
+  IndexedDatabaseManager* mgr = GetOrCreate();
+  NS_IF_ADDREF(mgr);
+  return mgr;
 }
 
 nsresult
-IndexedDatabaseManager::GetDirectoryForOrigin(const nsACString& aASCIIOrigin,
-                                              nsIFile** aDirectory) const
+IndexedDatabaseManager::Init()
 {
-  nsresult rv;
-  nsCOMPtr<nsIFile> directory =
-    do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv);
-  NS_ENSURE_SUCCESS(rv, rv);
+  // Make sure that the quota manager is up.
+  NS_ENSURE_TRUE(QuotaManager::GetOrCreate(), NS_ERROR_FAILURE);
 
-  rv = directory->InitWithPath(GetBaseDirectory());
-  NS_ENSURE_SUCCESS(rv, rv);
+  // Must initialize the storage service on the main thread.
+  nsCOMPtr<mozIStorageService> ss =
+    do_GetService(MOZ_STORAGE_SERVICE_CONTRACTID);
+  NS_ENSURE_TRUE(ss, NS_ERROR_FAILURE);
 
-  nsAutoCString originSanitized(aASCIIOrigin);
-  SanitizeOriginString(originSanitized);
-
-  rv = directory->Append(NS_ConvertASCIItoUTF16(originSanitized));
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  directory.forget(aDirectory);
   return NS_OK;
 }
 
-// static
-already_AddRefed<nsIAtom>
-IndexedDatabaseManager::GetDatabaseId(const nsACString& aOrigin,
-                                      const nsAString& aName)
+void
+IndexedDatabaseManager::Destroy()
 {
-  nsCString str(aOrigin);
-  str.Append("*");
-  str.Append(NS_ConvertUTF16toUTF8(aName));
+  // Setting the closed flag prevents the service from being recreated.
+  // Don't set it though if there's no real instance created.
+  if (!!gInitialized && PR_ATOMIC_SET(&gClosed, 1)) {
+    NS_ERROR("Shutdown more than once?!");
+  }
 
-  nsCOMPtr<nsIAtom> atom = do_GetAtom(str);
-  NS_ENSURE_TRUE(atom, nullptr);
-
-  return atom.forget();
+  delete this;
 }
 
 // static
 nsresult
 IndexedDatabaseManager::FireWindowOnError(nsPIDOMWindow* aOwner,
                                           nsEventChainPostVisitor& aVisitor)
 {
   NS_ENSURE_TRUE(aVisitor.mDOMEvent, NS_ERROR_UNEXPECTED);
@@ -585,541 +259,31 @@ IndexedDatabaseManager::TabContextMayAcc
                                                   const nsACString& aOrigin)
 {
   NS_ASSERTION(!aOrigin.IsEmpty(), "Empty origin!");
 
   // If aContext is for a browser element, it's allowed only to access other
   // browser elements.  But if aContext is not for a browser element, it may
   // access both browser and non-browser elements.
   nsAutoCString pattern;
-  GetOriginPatternStringMaybeIgnoreBrowser(aContext.OwnOrContainingAppId(),
-                                           aContext.IsBrowserElement(),
-                                           pattern);
+  QuotaManager::GetOriginPatternStringMaybeIgnoreBrowser(
+                                                aContext.OwnOrContainingAppId(),
+                                                aContext.IsBrowserElement(),
+                                                pattern);
 
   return PatternMatchesOrigin(pattern, aOrigin);
 }
 
-bool
-IndexedDatabaseManager::RegisterDatabase(IDBDatabase* aDatabase)
-{
-  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
-  NS_ASSERTION(aDatabase, "Null pointer!");
-
-  // Don't allow any new databases to be created after shutdown.
-  if (IsShuttingDown()) {
-    return false;
-  }
-
-  // Add this database to its origin array if it exists, create it otherwise.
-  nsTArray<IDBDatabase*>* array;
-  if (!mLiveDatabases.Get(aDatabase->Origin(), &array)) {
-    nsAutoPtr<nsTArray<IDBDatabase*> > newArray(new nsTArray<IDBDatabase*>());
-    mLiveDatabases.Put(aDatabase->Origin(), newArray);
-    array = newArray.forget();
-  }
-  if (!array->AppendElement(aDatabase)) {
-    NS_WARNING("Out of memory?");
-    return false;
-  }
-
-  aDatabase->mRegistered = true;
-  return true;
-}
-
-void
-IndexedDatabaseManager::UnregisterDatabase(IDBDatabase* aDatabase)
-{
-  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
-  NS_ASSERTION(aDatabase, "Null pointer!");
-
-  // Remove this database from its origin array, maybe remove the array if it
-  // is then empty.
-  nsTArray<IDBDatabase*>* array;
-  if (mLiveDatabases.Get(aDatabase->Origin(), &array) &&
-      array->RemoveElement(aDatabase)) {
-    if (array->IsEmpty()) {
-      mLiveDatabases.Remove(aDatabase->Origin());
-    }
-    return;
-  }
-  NS_ERROR("Didn't know anything about this database!");
-}
-
-void
-IndexedDatabaseManager::OnUsageCheckComplete(AsyncUsageRunnable* aRunnable)
-{
-  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
-  NS_ASSERTION(aRunnable, "Null pointer!");
-  NS_ASSERTION(!aRunnable->mURI, "Should have been cleared!");
-  NS_ASSERTION(!aRunnable->mCallback, "Should have been cleared!");
-
-  if (!mUsageRunnables.RemoveElement(aRunnable)) {
-    NS_ERROR("Don't know anything about this runnable!");
-  }
-}
-
-nsresult
-IndexedDatabaseManager::WaitForOpenAllowed(
-                                  const OriginOrPatternString& aOriginOrPattern,
-                                  nsIAtom* aId,
-                                  nsIRunnable* aRunnable)
-{
-  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
-  NS_ASSERTION(!aOriginOrPattern.IsEmpty(), "Empty pattern!");
-  NS_ASSERTION(aRunnable, "Null pointer!");
-
-  nsAutoPtr<SynchronizedOp> op(new SynchronizedOp(aOriginOrPattern, aId));
-
-  // See if this runnable needs to wait.
-  bool delayed = false;
-  for (uint32_t index = mSynchronizedOps.Length(); index > 0; index--) {
-    nsAutoPtr<SynchronizedOp>& existingOp = mSynchronizedOps[index - 1];
-    if (op->MustWaitFor(*existingOp)) {
-      existingOp->DelayRunnable(aRunnable);
-      delayed = true;
-      break;
-    }
-  }
-
-  // Otherwise, dispatch it immediately.
-  if (!delayed) {
-    nsresult rv = NS_DispatchToCurrentThread(aRunnable);
-    NS_ENSURE_SUCCESS(rv, rv);
-  }
-
-  // Adding this to the synchronized ops list will block any additional
-  // ops from proceeding until this one is done.
-  mSynchronizedOps.AppendElement(op.forget());
-
-  return NS_OK;
-}
-
-void
-IndexedDatabaseManager::AllowNextSynchronizedOp(
-                                  const OriginOrPatternString& aOriginOrPattern,
-                                  nsIAtom* aId)
-{
-  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
-  NS_ASSERTION(!aOriginOrPattern.IsEmpty(), "Empty origin/pattern!");
-
-  uint32_t count = mSynchronizedOps.Length();
-  for (uint32_t index = 0; index < count; index++) {
-    nsAutoPtr<SynchronizedOp>& op = mSynchronizedOps[index];
-    if (op->mOriginOrPattern.IsOrigin() == aOriginOrPattern.IsOrigin() &&
-        op->mOriginOrPattern == aOriginOrPattern) {
-      if (op->mId == aId) {
-        NS_ASSERTION(op->mDatabases.IsEmpty(), "How did this happen?");
-
-        op->DispatchDelayedRunnables();
-
-        mSynchronizedOps.RemoveElementAt(index);
-        return;
-      }
-
-      // If one or the other is for an origin clear, we should have matched
-      // solely on origin.
-      NS_ASSERTION(op->mId && aId, "Why didn't we match earlier?");
-    }
-  }
-
-  NS_NOTREACHED("Why didn't we find a SynchronizedOp?");
-}
-
-nsresult
-IndexedDatabaseManager::AcquireExclusiveAccess(
-                                           const nsACString& aPattern,
-                                           IDBDatabase* aDatabase,
-                                           AsyncConnectionHelper* aHelper,
-                                           nsIRunnable* aRunnable,
-                                           WaitingOnDatabasesCallback aCallback,
-                                           void* aClosure)
-{
-  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
-  NS_ASSERTION(!aDatabase || aHelper, "Need a helper with a database!");
-  NS_ASSERTION(aDatabase || aRunnable, "Need a runnable without a database!");
-
-  // Find the right SynchronizedOp.
-  SynchronizedOp* op =
-    FindSynchronizedOp(aPattern, aDatabase ? aDatabase->Id() : nullptr);
-
-  NS_ASSERTION(op, "We didn't find a SynchronizedOp?");
-  NS_ASSERTION(!op->mHelper, "SynchronizedOp already has a helper?!?");
-  NS_ASSERTION(!op->mRunnable, "SynchronizedOp already has a runnable?!?");
-
-  DatabasePatternMatchArray matches;
-  matches.Find(mLiveDatabases, aPattern);
-
-  // We need to wait for the databases to go away.
-  // Hold on to all database objects that represent the same database file
-  // (except the one that is requesting this version change).
-  nsTArray<nsRefPtr<IDBDatabase> > liveDatabases;
-
-  if (!matches.IsEmpty()) {
-    if (aDatabase) {
-      // Grab all databases that are not yet closed but whose database id match
-      // the one we're looking for.
-      for (uint32_t index = 0; index < matches.Length(); index++) {
-        IDBDatabase*& database = matches[index];
-        if (!database->IsClosed() &&
-            database != aDatabase &&
-            database->Id() == aDatabase->Id()) {
-          liveDatabases.AppendElement(database);
-        }
-      }
-    }
-    else {
-      // We want *all* databases, even those that are closed, if we're going to
-      // clear the origin.
-      liveDatabases.AppendElements(matches);
-    }
-  }
-
-  op->mHelper = aHelper;
-  op->mRunnable = aRunnable;
-
-  if (!liveDatabases.IsEmpty()) {
-    NS_ASSERTION(op->mDatabases.IsEmpty(),
-                 "How do we already have databases here?");
-    op->mDatabases.AppendElements(liveDatabases);
-
-    // Give our callback the databases so it can decide what to do with them.
-    aCallback(liveDatabases, aClosure);
-
-    NS_ASSERTION(liveDatabases.IsEmpty(),
-                 "Should have done something with the array!");
-
-    if (aDatabase) {
-      // Wait for those databases to close.
-      return NS_OK;
-    }
-  }
-
-  // If we're trying to open a database and nothing blocks it, or if we're
-  // clearing an origin, then go ahead and schedule the op.
-  nsresult rv = RunSynchronizedOp(aDatabase, op);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  return NS_OK;
-}
-
-// static
-bool
-IndexedDatabaseManager::IsShuttingDown()
-{
-  return !!gShutdown;
-}
-
 // static
 bool
 IndexedDatabaseManager::IsClosed()
 {
   return !!gClosed;
 }
 
-void
-IndexedDatabaseManager::AbortCloseDatabasesForWindow(nsPIDOMWindow* aWindow)
-{
-  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
-  NS_ASSERTION(aWindow, "Null pointer!");
-
-  nsAutoTArray<IDBDatabase*, 50> liveDatabases;
-  mLiveDatabases.EnumerateRead(EnumerateToTArray<IDBDatabase*>,
-                               &liveDatabases);
-
-  FileService* service = FileService::Get();
-  TransactionThreadPool* pool = TransactionThreadPool::Get();
-
-  for (uint32_t index = 0; index < liveDatabases.Length(); index++) {
-    IDBDatabase*& database = liveDatabases[index];
-    if (database->GetOwner() == aWindow) {
-      if (NS_FAILED(database->Close())) {
-        NS_WARNING("Failed to close database for dying window!");
-      }
-
-      if (service) {
-        service->AbortLockedFilesForStorage(database);
-      }
-
-      if (pool) {
-        pool->AbortTransactionsForDatabase(database);
-      }
-    }
-  }
-}
-
-bool
-IndexedDatabaseManager::HasOpenTransactions(nsPIDOMWindow* aWindow)
-{
-  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
-  NS_ASSERTION(aWindow, "Null pointer!");
-
-  nsAutoTArray<IDBDatabase*, 50> liveDatabases;
-  mLiveDatabases.EnumerateRead(EnumerateToTArray<IDBDatabase*>,
-                               &liveDatabases);
-  
-  FileService* service = FileService::Get();
-  TransactionThreadPool* pool = TransactionThreadPool::Get();
-  if (!service && !pool) {
-    return false;
-  }
-
-  for (uint32_t index = 0; index < liveDatabases.Length(); index++) {
-    IDBDatabase*& database = liveDatabases[index];
-    if (database->GetOwner() == aWindow &&
-        ((service && service->HasLockedFilesForStorage(database)) ||
-         (pool && pool->HasTransactionsForDatabase(database)))) {
-      return true;
-    }
-  }
-  
-  return false;
-}
-
-void
-IndexedDatabaseManager::OnDatabaseClosed(IDBDatabase* aDatabase)
-{
-  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
-  NS_ASSERTION(aDatabase, "Null pointer!");
-
-  // Check through the list of SynchronizedOps to see if any are waiting for
-  // this database to close before proceeding.
-  SynchronizedOp* op = FindSynchronizedOp(aDatabase->Origin(), aDatabase->Id());
-  if (op) {
-    // This database is in the scope of this SynchronizedOp.  Remove it
-    // from the list if necessary.
-    if (op->mDatabases.RemoveElement(aDatabase)) {
-      // Now set up the helper if there are no more live databases.
-      NS_ASSERTION(op->mHelper || op->mRunnable,
-                   "How did we get rid of the helper/runnable before "
-                    "removing the last database?");
-      if (op->mDatabases.IsEmpty()) {
-        // At this point, all databases are closed, so no new transactions
-        // can be started.  There may, however, still be outstanding
-        // transactions that have not completed.  We need to wait for those
-        // before we dispatch the helper.
-        if (NS_FAILED(RunSynchronizedOp(aDatabase, op))) {
-          NS_WARNING("Failed to run synchronized op!");
-        }
-      }
-    }
-  }
-}
-
-// static
-uint32_t
-IndexedDatabaseManager::GetIndexedDBQuotaMB()
-{
-  return uint32_t(std::max(gIndexedDBQuotaMB, 0));
-}
-
-nsresult
-IndexedDatabaseManager::EnsureOriginIsInitialized(const nsACString& aOrigin,
-                                                  FactoryPrivilege aPrivilege,
-                                                  nsIFile** aDirectory)
-{
-#ifdef DEBUG
-  {
-    bool correctThread;
-    NS_ASSERTION(NS_SUCCEEDED(mIOThread->IsOnCurrentThread(&correctThread)) &&
-                 correctThread,
-                 "Running on the wrong thread!");
-  }
-#endif
-
-  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);
-
-  if (exists) {
-    bool isDirectory;
-    rv = directory->IsDirectory(&isDirectory);
-    NS_ENSURE_SUCCESS(rv, rv);
-    NS_ENSURE_TRUE(isDirectory, NS_ERROR_UNEXPECTED);
-  }
-  else {
-    rv = directory->Create(nsIFile::DIRECTORY_TYPE, 0755);
-    NS_ENSURE_SUCCESS(rv, rv);
-  }
-
-  if (mInitializedOrigins.Contains(aOrigin)) {
-    NS_ADDREF(*aDirectory = directory);
-    return NS_OK;
-  }
-
-  // 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 initialize the quota.
-
-  nsAutoTArray<nsString, 20> subdirsToProcess;
-  nsAutoTArray<nsCOMPtr<nsIFile> , 20> unknownFiles;
-
-  uint64_t usage = 0;
-
-  nsTHashtable<nsStringHashKey> validSubdirs;
-  validSubdirs.Init(20);
-
-  nsCOMPtr<nsISimpleEnumerator> entries;
-  rv = directory->GetDirectoryEntries(getter_AddRefs(entries));
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  bool hasMore;
-  while (NS_SUCCEEDED((rv = entries->HasMoreElements(&hasMore))) && hasMore) {
-    nsCOMPtr<nsISupports> entry;
-    rv = entries->GetNext(getter_AddRefs(entry));
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    nsCOMPtr<nsIFile> file = do_QueryInterface(entry);
-    NS_ENSURE_TRUE(file, NS_NOINTERFACE);
-
-    nsString leafName;
-    rv = file->GetLeafName(leafName);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    if (StringEndsWith(leafName, NS_LITERAL_STRING(".sqlite-journal"))) {
-      continue;
-    }
-
-    bool isDirectory;
-    rv = file->IsDirectory(&isDirectory);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    if (isDirectory) {
-      if (!validSubdirs.GetEntry(leafName)) {
-        subdirsToProcess.AppendElement(leafName);
-      }
-      continue;
-    }
-
-    nsString dbBaseFilename;
-    if (!GetDatabaseBaseFilename(leafName, dbBaseFilename)) {
-      unknownFiles.AppendElement(file);
-      continue;
-    }
-
-    nsCOMPtr<nsIFile> fmDirectory;
-    rv = directory->Clone(getter_AddRefs(fmDirectory));
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    rv = fmDirectory->Append(dbBaseFilename);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    rv = FileManager::InitDirectory(fmDirectory, file, aOrigin);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    if (aPrivilege != Chrome) {
-      uint64_t fileUsage;
-      rv = FileManager::GetUsage(fmDirectory, &fileUsage);
-      NS_ENSURE_SUCCESS(rv, rv);
-
-      IncrementUsage(&usage, fileUsage);
-
-      int64_t fileSize;
-      rv = file->GetFileSize(&fileSize);
-      NS_ENSURE_SUCCESS(rv, rv);
-
-      IncrementUsage(&usage, uint64_t(fileSize));
-    }
-
-    validSubdirs.PutEntry(dbBaseFilename);
-  }
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  for (uint32_t i = 0; i < subdirsToProcess.Length(); i++) {
-    const nsString& subdir = subdirsToProcess[i];
-    if (!validSubdirs.GetEntry(subdir)) {
-      NS_WARNING("Unknown subdirectory found!");
-      return NS_ERROR_UNEXPECTED;
-    }
-  }
-
-  for (uint32_t i = 0; i < unknownFiles.Length(); i++) {
-    nsCOMPtr<nsIFile>& unknownFile = unknownFiles[i];
-
-    // Some temporary SQLite files could disappear, so we have to check if the
-    // unknown file still exists.
-    bool exists;
-    rv = unknownFile->Exists(&exists);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    if (exists) {
-      nsString leafName;
-      unknownFile->GetLeafName(leafName);
-
-      // The journal file may exists even after db has been correctly opened.
-      if (!StringEndsWith(leafName, NS_LITERAL_STRING(".sqlite-journal"))) {
-        NS_WARNING("Unknown file found!");
-        return NS_ERROR_UNEXPECTED;
-      }
-    }
-  }
-
-  if (aPrivilege != Chrome) {
-    QuotaManager* quotaManager = QuotaManager::Get();
-    NS_ASSERTION(quotaManager, "Shouldn't be null!");
-
-    quotaManager->InitQuotaForOrigin(aOrigin, GetIndexedDBQuotaMB(), usage);
-  }
-
-  mInitializedOrigins.AppendElement(aOrigin);
-
-  NS_ADDREF(*aDirectory = directory);
-  return NS_OK;
-}
-
-void
-IndexedDatabaseManager::UninitializeOriginsByPattern(
-                                                    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--) {
-    if (PatternMatchesOrigin(aPattern, mInitializedOrigins[i])) {
-      mInitializedOrigins.RemoveElementAt(i);
-    }
-  }
-}
-
-// static
-nsresult
-IndexedDatabaseManager::GetASCIIOriginFromWindow(nsPIDOMWindow* aWindow,
-                                                 nsCString& aASCIIOrigin)
-{
-  NS_ASSERTION(NS_IsMainThread(),
-               "We're about to touch a window off the main thread!");
-
-  if (!aWindow) {
-    aASCIIOrigin.AssignLiteral("chrome");
-    NS_ASSERTION(nsContentUtils::IsCallerChrome(),
-                 "Null window but not chrome!");
-    return NS_OK;
-  }
-
-  nsCOMPtr<nsIScriptObjectPrincipal> sop = do_QueryInterface(aWindow);
-  NS_ENSURE_TRUE(sop, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
-
-  nsCOMPtr<nsIPrincipal> principal = sop->GetPrincipal();
-  NS_ENSURE_TRUE(principal, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
-
-  nsresult rv = GetASCIIOriginFromPrincipal(principal, aASCIIOrigin);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  return NS_OK;
-}
-
 #ifdef DEBUG
 //static
 bool
 IndexedDatabaseManager::IsMainProcess()
 {
   NS_ASSERTION(gInstance,
                "IsMainProcess() called before indexedDB has been initialized!");
   NS_ASSERTION((XRE_GetProcessType() == GeckoProcessType_Default) ==
@@ -1140,17 +304,17 @@ IndexedDatabaseManager::GetFileManager(c
   for (uint32_t i = 0; i < array->Length(); i++) {
     nsRefPtr<FileManager>& fileManager = array->ElementAt(i);
 
     if (fileManager->DatabaseName().Equals(aDatabaseName)) {
       nsRefPtr<FileManager> result = fileManager;
       return result.forget();
     }
   }
-  
+
   return nullptr;
 }
 
 void
 IndexedDatabaseManager::AddFileManager(FileManager* aFileManager)
 {
   NS_ASSERTION(aFileManager, "Null file manager!");
 
@@ -1159,16 +323,22 @@ IndexedDatabaseManager::AddFileManager(F
     array = new nsTArray<nsRefPtr<FileManager> >();
     mFileManagers.Put(aFileManager->Origin(), array);
   }
 
   array->AppendElement(aFileManager);
 }
 
 void
+IndexedDatabaseManager::InvalidateAllFileManagers()
+{
+  mFileManagers.Enumerate(InvalidateAndRemoveFileManagers, nullptr);
+}
+
+void
 IndexedDatabaseManager::InvalidateFileManagersForPattern(
                                                      const nsACString& aPattern)
 {
   NS_ASSERTION(!aPattern.IsEmpty(), "Empty pattern!");
   mFileManagers.Enumerate(InvalidateAndRemoveFileManagers,
                           const_cast<nsACString*>(&aPattern));
 }
 
@@ -1199,916 +369,38 @@ IndexedDatabaseManager::InvalidateFileMa
 nsresult
 IndexedDatabaseManager::AsyncDeleteFile(FileManager* aFileManager,
                                         int64_t aFileId)
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
   NS_ENSURE_ARG_POINTER(aFileManager);
 
-  // See if we're currently clearing the databases for this origin. If so then
+  QuotaManager* quotaManager = QuotaManager::Get();
+  NS_ASSERTION(quotaManager, "Shouldn't be null!");
+
+  // See if we're currently clearing the storages for this origin. If so then
   // we pretend that we've already deleted everything.
-  if (IsClearOriginPending(aFileManager->Origin())) {
+  if (quotaManager->IsClearOriginPending(aFileManager->Origin())) {
     return NS_OK;
   }
 
   nsRefPtr<AsyncDeleteFileRunnable> runnable =
     new AsyncDeleteFileRunnable(aFileManager, aFileId);
 
-  nsresult rv = mIOThread->Dispatch(runnable, NS_DISPATCH_NORMAL);
+  nsresult rv =
+    quotaManager->IOThread()->Dispatch(runnable, NS_DISPATCH_NORMAL);
   NS_ENSURE_SUCCESS(rv, rv);
 
   return NS_OK;
 }
 
-// static
-nsresult
-IndexedDatabaseManager::RunSynchronizedOp(IDBDatabase* aDatabase,
-                                          SynchronizedOp* aOp)
-{
-  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
-  NS_ASSERTION(aOp, "Null pointer!");
-  NS_ASSERTION(!aDatabase || aOp->mHelper, "No helper on this op!");
-  NS_ASSERTION(aDatabase || aOp->mRunnable, "No runnable on this op!");
-  NS_ASSERTION(!aDatabase || aOp->mDatabases.IsEmpty(),
-               "This op isn't ready to run!");
-
-  FileService* service = FileService::Get();
-  TransactionThreadPool* pool = TransactionThreadPool::Get();
-
-  nsTArray<IDBDatabase*> databases;
-  if (aDatabase) {
-    if (service || pool) {
-      databases.AppendElement(aDatabase);
-    }
-  }
-  else {
-    aOp->mDatabases.SwapElements(databases);
-  }
-
-  uint32_t waitCount = service && pool && !databases.IsEmpty() ? 2 : 1;
-
-  nsRefPtr<WaitForTransactionsToFinishRunnable> runnable =
-    new WaitForTransactionsToFinishRunnable(aOp, waitCount);
-
-  // There's no point in delaying if we don't yet have a transaction thread pool
-  // or a file service. Also, if we're not waiting on any databases then we can
-  // also run immediately.
-  if (!(service || pool) || databases.IsEmpty()) {
-    nsresult rv = runnable->Run();
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    return NS_OK;
-  }
-
-  // Ask each service to call us back when they're done with this database.
-  if (service) {
-    // Have to copy here in case the pool needs a list too.
-    nsTArray<nsCOMPtr<nsIFileStorage> > array;
-    array.AppendElements(databases);
-
-    if (!service->WaitForAllStoragesToComplete(array, runnable)) {
-      NS_WARNING("Failed to wait for storages to complete!");
-      return NS_ERROR_FAILURE;
-    }
-  }
-
-  if (pool && !pool->WaitForAllDatabasesToComplete(databases, runnable)) {
-    NS_WARNING("Failed to wait for databases to complete!");
-    return NS_ERROR_FAILURE;
-  }
-
-  return NS_OK;
-}
-
-IndexedDatabaseManager::SynchronizedOp*
-IndexedDatabaseManager::FindSynchronizedOp(const nsACString& aPattern,
-                                           nsIAtom* aId)
-{
-  for (uint32_t index = 0; index < mSynchronizedOps.Length(); index++) {
-    const nsAutoPtr<SynchronizedOp>& currentOp = mSynchronizedOps[index];
-    if (PatternMatchesOrigin(aPattern, currentOp->mOriginOrPattern) &&
-        (!currentOp->mId || currentOp->mId == aId)) {
-      return currentOp;
-    }
-  }
-
-  return nullptr;
-}
-
-nsresult
-IndexedDatabaseManager::ClearDatabasesForApp(uint32_t aAppId, bool aBrowserOnly)
-{
-  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
-  NS_ASSERTION(aAppId != nsIScriptSecurityManager::UNKNOWN_APP_ID,
-               "Bad appId!");
-
-  // This only works from the main process.
-  NS_ENSURE_TRUE(IsMainProcess(), NS_ERROR_NOT_AVAILABLE);
-
-  nsAutoCString pattern;
-  GetOriginPatternStringMaybeIgnoreBrowser(aAppId, aBrowserOnly, pattern);
-
-  // If there is a pending or running clear operation for this app, return
-  // immediately.
-  if (IsClearOriginPending(pattern)) {
-    return NS_OK;
-  }
-
-  OriginOrPatternString oops = OriginOrPatternString::FromPattern(pattern);
-
-  // Queue up the origin clear runnable.
-  nsRefPtr<OriginClearRunnable> runnable = new OriginClearRunnable(oops);
-
-  nsresult rv = WaitForOpenAllowed(oops, nullptr, runnable);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  runnable->AdvanceState();
-
-  // Give the runnable some help by invalidating any databases in the way.
-  DatabasePatternMatchArray matches;
-  matches.Find(mLiveDatabases, pattern);
-
-  for (uint32_t index = 0; index < matches.Length(); index++) {
-    // We need to grab references here to prevent the database from dying while
-    // we invalidate it.
-    nsRefPtr<IDBDatabase> database = matches[index];
-    database->Invalidate();
-  }
-
-  return NS_OK;
-}
-
-NS_IMPL_ISUPPORTS2(IndexedDatabaseManager, nsIIndexedDatabaseManager,
-                                           nsIObserver)
-
-NS_IMETHODIMP
-IndexedDatabaseManager::GetUsageForURI(
-                                     nsIURI* aURI,
-                                     nsIIndexedDatabaseUsageCallback* aCallback,
-                                     uint32_t aAppId,
-                                     bool aInMozBrowserOnly,
-                                     uint8_t aOptionalArgCount)
-{
-  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
-
-  NS_ENSURE_ARG_POINTER(aURI);
-  NS_ENSURE_ARG_POINTER(aCallback);
-
-  // This only works from the main process.
-  NS_ENSURE_TRUE(IsMainProcess(), NS_ERROR_NOT_AVAILABLE);
-
-  if (!aOptionalArgCount) {
-    aAppId = nsIScriptSecurityManager::NO_APP_ID;
-  }
-
-  // Figure out which origin we're dealing with.
-  nsCString origin;
-  nsresult rv = GetASCIIOriginFromURI(aURI, aAppId, aInMozBrowserOnly, origin);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  OriginOrPatternString oops = OriginOrPatternString::FromOrigin(origin);
-
-  nsRefPtr<AsyncUsageRunnable> runnable =
-    new AsyncUsageRunnable(aAppId, aInMozBrowserOnly, oops, aURI, aCallback);
-
-  nsRefPtr<AsyncUsageRunnable>* newRunnable =
-    mUsageRunnables.AppendElement(runnable);
-  NS_ENSURE_TRUE(newRunnable, NS_ERROR_OUT_OF_MEMORY);
-
-  // Otherwise put the computation runnable in the queue.
-  rv = WaitForOpenAllowed(oops, nullptr, runnable);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  runnable->AdvanceState();
-
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-IndexedDatabaseManager::CancelGetUsageForURI(
-                                     nsIURI* aURI,
-                                     nsIIndexedDatabaseUsageCallback* aCallback,
-                                     uint32_t aAppId,
-                                     bool aInMozBrowserOnly,
-                                     uint8_t aOptionalArgCount)
-{
-  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
-
-  NS_ENSURE_ARG_POINTER(aURI);
-  NS_ENSURE_ARG_POINTER(aCallback);
-
-  // This only works from the main process.
-  NS_ENSURE_TRUE(IsMainProcess(), NS_ERROR_NOT_AVAILABLE);
-
-  if (!aOptionalArgCount) {
-    aAppId = nsIScriptSecurityManager::NO_APP_ID;
-  }
-
-  // See if one of our pending callbacks matches both the URI and the callback
-  // given. Cancel an remove it if so.
-  for (uint32_t index = 0; index < mUsageRunnables.Length(); index++) {
-    nsRefPtr<AsyncUsageRunnable>& runnable = mUsageRunnables[index];
-
-    if (runnable->mAppId == aAppId &&
-        runnable->mInMozBrowserOnly == aInMozBrowserOnly) {
-      bool equals;
-      nsresult rv = runnable->mURI->Equals(aURI, &equals);
-      NS_ENSURE_SUCCESS(rv, rv);
-
-      if (equals && SameCOMIdentity(aCallback, runnable->mCallback)) {
-        runnable->Cancel();
-        break;
-      }
-    }
-  }
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-IndexedDatabaseManager::ClearDatabasesForURI(nsIURI* aURI,
-                                             uint32_t aAppId,
-                                             bool aInMozBrowserOnly,
-                                             uint8_t aOptionalArgCount)
-{
-  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
-
-  NS_ENSURE_ARG_POINTER(aURI);
-
-  // This only works from the main process.
-  NS_ENSURE_TRUE(IsMainProcess(), NS_ERROR_NOT_AVAILABLE);
-
-  if (!aOptionalArgCount) {
-    aAppId = nsIScriptSecurityManager::NO_APP_ID;
-  }
-
-  // Figure out which origin we're dealing with.
-  nsCString origin;
-  nsresult rv = GetASCIIOriginFromURI(aURI, aAppId, aInMozBrowserOnly, origin);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  nsAutoCString pattern;
-  GetOriginPatternString(aAppId, aInMozBrowserOnly, origin, pattern);
-
-  // If there is a pending or running clear operation for this origin, return
-  // immediately.
-  if (IsClearOriginPending(pattern)) {
-    return NS_OK;
-  }
-
-  OriginOrPatternString oops = OriginOrPatternString::FromPattern(pattern);
-
-  // Queue up the origin clear runnable.
-  nsRefPtr<OriginClearRunnable> runnable = new OriginClearRunnable(oops);
-
-  rv = WaitForOpenAllowed(oops, nullptr, runnable);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  runnable->AdvanceState();
-
-  // Give the runnable some help by invalidating any databases in the way.
-  DatabasePatternMatchArray matches;
-  matches.Find(mLiveDatabases, pattern);
-
-  for (uint32_t index = 0; index < matches.Length(); index++) {
-    // We need to grab references to any live databases here to prevent them
-    // from dying while we invalidate them.
-    nsRefPtr<IDBDatabase> database = matches[index];
-    database->Invalidate();
-  }
-
-  // After everything has been invalidated the helper should be dispatched to
-  // the end of the event queue.
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-IndexedDatabaseManager::Observe(nsISupports* aSubject,
-                                const char* aTopic,
-                                const PRUnichar* 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 databases from being created.
-    if (PR_ATOMIC_SET(&gShutdown, 1)) {
-      NS_ERROR("Shutdown more than once?!");
-    }
-
-    if (sIsMainProcess) {
-      FileService* service = FileService::Get();
-      if (service) {
-        // This should only wait for IDB databases (file storages) to complete.
-        // Other file storages may still have running locked files.
-        // If the necko service (thread pool) gets the shutdown notification
-        // first then the sync loop won't be processed at all, otherwise it will
-        // lock the main thread until all IDB file storages are finished.
-
-        nsTArray<nsCOMPtr<nsIFileStorage> >
-          liveDatabases(mLiveDatabases.Count());
-        mLiveDatabases.EnumerateRead(
-                                  EnumerateToTArray<nsCOMPtr<nsIFileStorage> >,
-                                  &liveDatabases);
-
-        if (!liveDatabases.IsEmpty()) {
-          nsRefPtr<WaitForLockedFilesToFinishRunnable> runnable =
-            new WaitForLockedFilesToFinishRunnable();
-
-          if (!service->WaitForAllStoragesToComplete(liveDatabases,
-                                                     runnable)) {
-            NS_WARNING("Failed to wait for databases to complete!");
-          }
-
-          nsIThread* thread = NS_GetCurrentThread();
-          while (runnable->IsBusy()) {
-            if (!NS_ProcessNextEvent(thread)) {
-              NS_ERROR("Failed to process next event!");
-              break;
-            }
-          }
-        }
-      }
-
-      // 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))) {
-        NS_WARNING("Failed to initialize shutdown timer!");
-      }
-
-      // This will spin the event loop while we wait on all the database threads
-      // to close. Our timer may fire during that loop.
-      TransactionThreadPool::Shutdown();
-
-      // Cancel the timer regardless of whether it actually fired.
-      if (NS_FAILED(mShutdownTimer->Cancel())) {
-        NS_WARNING("Failed to cancel shutdown timer!");
-      }
-    }
-
-    mFileManagers.Enumerate(InvalidateAndRemoveFileManagers, nullptr);
-
-    if (PR_ATOMIC_SET(&gClosed, 1)) {
-      NS_ERROR("Close more than once?!");
-    }
-
-    return NS_OK;
-  }
-
-  if (!strcmp(aTopic, NS_TIMER_CALLBACK_TOPIC)) {
-    NS_ASSERTION(sIsMainProcess, "Should only happen in the main process!");
-
-    NS_WARNING("Some database operations are taking longer than expected "
-               "during shutdown and will be aborted!");
-
-    // Grab all live databases, for all origins.
-    nsAutoTArray<IDBDatabase*, 50> liveDatabases;
-    mLiveDatabases.EnumerateRead(EnumerateToTArray<IDBDatabase*>,
-                                 &liveDatabases);
-
-    // Invalidate them all.
-    if (!liveDatabases.IsEmpty()) {
-      uint32_t count = liveDatabases.Length();
-      for (uint32_t index = 0; index < count; index++) {
-        liveDatabases[index]->Invalidate();
-      }
-    }
-
-    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 = ClearDatabasesForApp(appId, browserOnly);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    return NS_OK;
-  }
-
-  NS_NOTREACHED("Unknown topic!");
-  return NS_ERROR_UNEXPECTED;
-}
-
-NS_IMPL_THREADSAFE_ISUPPORTS1(IndexedDatabaseManager::OriginClearRunnable,
-                              nsIRunnable)
-
-// static
-void
-IndexedDatabaseManager::
-OriginClearRunnable::InvalidateOpenedDatabases(
-                                   nsTArray<nsRefPtr<IDBDatabase> >& aDatabases,
-                                   void* aClosure)
-{
-  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
-
-  nsTArray<nsRefPtr<IDBDatabase> > databases;
-  databases.SwapElements(aDatabases);
-
-  for (uint32_t index = 0; index < databases.Length(); index++) {
-    databases[index]->Invalidate();
-  }
-}
-
-void
-IndexedDatabaseManager::
-OriginClearRunnable::DeleteFiles(IndexedDatabaseManager* aManager)
-{
-  NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
-  NS_ASSERTION(aManager, "Don't pass me null!");
-
-  nsresult rv;
-
-  nsCOMPtr<nsIFile> directory =
-    do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv);
-  NS_ENSURE_SUCCESS_VOID(rv);
-
-  rv = directory->InitWithPath(aManager->GetBaseDirectory());
-  NS_ENSURE_SUCCESS_VOID(rv);
-
-  nsCOMPtr<nsISimpleEnumerator> entries;
-  if (NS_FAILED(directory->GetDirectoryEntries(getter_AddRefs(entries))) ||
-      !entries) {
-    return;
-  }
-
-  nsCString originSanitized(mOriginOrPattern);
-  SanitizeOriginString(originSanitized);
-
-  bool hasMore;
-  while (NS_SUCCEEDED((rv = entries->HasMoreElements(&hasMore))) && hasMore) {
-    nsCOMPtr<nsISupports> entry;
-    rv = entries->GetNext(getter_AddRefs(entry));
-    NS_ENSURE_SUCCESS_VOID(rv);
-
-    nsCOMPtr<nsIFile> file = do_QueryInterface(entry);
-    NS_ASSERTION(file, "Don't know what this is!");
-
-    bool isDirectory;
-    rv = file->IsDirectory(&isDirectory);
-    NS_ENSURE_SUCCESS_VOID(rv);
-
-    if (!isDirectory) {
-      NS_WARNING("Something in the IndexedDB directory that doesn't belong!");
-      continue;
-    }
-
-    nsString leafName;
-    rv = file->GetLeafName(leafName);
-    NS_ENSURE_SUCCESS_VOID(rv);
-
-    // Skip databases for other apps.
-    if (!PatternMatchesOrigin(originSanitized,
-                              NS_ConvertUTF16toUTF8(leafName))) {
-      continue;
-    }
-
-    if (NS_FAILED(file->Remove(true))) {
-      // This should never fail if we've closed all database connections
-      // correctly...
-      NS_ERROR("Failed to remove directory!");
-    }
-
-    QuotaManager* quotaManager = QuotaManager::Get();
-    NS_ASSERTION(quotaManager, "Shouldn't be null!");
-
-    quotaManager->RemoveQuotaForPattern(mOriginOrPattern);
-
-    aManager->UninitializeOriginsByPattern(mOriginOrPattern);
-  }
-}
-
-NS_IMETHODIMP
-IndexedDatabaseManager::OriginClearRunnable::Run()
-{
-  IndexedDatabaseManager* mgr = IndexedDatabaseManager::Get();
-  NS_ASSERTION(mgr, "This should never fail!");
-
-  switch (mCallbackState) {
-    case Pending: {
-      NS_NOTREACHED("Should never get here without being dispatched!");
-      return NS_ERROR_UNEXPECTED;
-    }
-
-    case OpenAllowed: {
-      NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
-
-      AdvanceState();
-
-      // Now we have to wait until the thread pool is done with all of the
-      // databases we care about.
-      nsresult rv = mgr->AcquireExclusiveAccess(mOriginOrPattern, this,
-                                                InvalidateOpenedDatabases,
-                                                nullptr);
-      NS_ENSURE_SUCCESS(rv, rv);
-
-      return NS_OK;
-    }
-
-    case IO: {
-      NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
-
-      AdvanceState();
-
-      DeleteFiles(mgr);
-
-      // Now dispatch back to the main thread.
-      if (NS_FAILED(NS_DispatchToMainThread(this, NS_DISPATCH_NORMAL))) {
-        NS_WARNING("Failed to dispatch to main thread!");
-        return NS_ERROR_FAILURE;
-      }
-
-      return NS_OK;
-    }
-
-    case Complete: {
-      NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
-
-      mgr->InvalidateFileManagersForPattern(mOriginOrPattern);
-
-      // Tell the IndexedDatabaseManager that we're done.
-      mgr->AllowNextSynchronizedOp(mOriginOrPattern, nullptr);
-
-      return NS_OK;
-    }
-
-    default:
-      NS_ERROR("Unknown state value!");
-      return NS_ERROR_UNEXPECTED;
-  }
-
-  NS_NOTREACHED("Should never get here!");
-  return NS_ERROR_UNEXPECTED;
-}
-
-IndexedDatabaseManager::AsyncUsageRunnable::AsyncUsageRunnable(
-                                     uint32_t aAppId,
-                                     bool aInMozBrowserOnly,
-                                     const OriginOrPatternString& aOrigin,
-                                     nsIURI* aURI,
-                                     nsIIndexedDatabaseUsageCallback* aCallback)
-: mURI(aURI),
-  mCallback(aCallback),
-  mUsage(0),
-  mFileUsage(0),
-  mAppId(aAppId),
-  mCanceled(0),
-  mOrigin(aOrigin),
-  mCallbackState(Pending),
-  mInMozBrowserOnly(aInMozBrowserOnly)
-{
-  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
-  NS_ASSERTION(aURI, "Null pointer!");
-  NS_ASSERTION(aOrigin.IsOrigin(), "Expect origin only here!");
-  NS_ASSERTION(!aOrigin.IsEmpty(), "Empty origin!");
-  NS_ASSERTION(aCallback, "Null pointer!");
-}
-
-void
-IndexedDatabaseManager::AsyncUsageRunnable::Cancel()
-{
-  if (PR_ATOMIC_SET(&mCanceled, 1)) {
-    NS_ERROR("Canceled more than once?!");
-  }
-}
-
-nsresult
-IndexedDatabaseManager::AsyncUsageRunnable::TakeShortcut()
-{
-  NS_ASSERTION(mCallbackState == Pending, "Huh?");
-
-  nsresult rv = NS_DispatchToCurrentThread(this);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  mCallbackState = Shortcut;
-  return NS_OK;
-}
-
-nsresult
-IndexedDatabaseManager::AsyncUsageRunnable::RunInternal()
-{
-  IndexedDatabaseManager* mgr = IndexedDatabaseManager::Get();
-  NS_ASSERTION(mgr, "This should never fail!");
-
-  switch (mCallbackState) {
-    case Pending: {
-      NS_NOTREACHED("Should never get here without being dispatched!");
-      return NS_ERROR_UNEXPECTED;
-    }
-
-    case OpenAllowed: {
-      NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
-
-      AdvanceState();
-
-      if (NS_FAILED(mgr->IOThread()->Dispatch(this, NS_DISPATCH_NORMAL))) {
-        NS_WARNING("Failed to dispatch to the IO thread!");
-      }
-
-      return NS_OK;
-    }
-
-    case IO: {
-      NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
-
-      AdvanceState();
-
-      // Get the directory that contains all the database files we care about.
-      nsCOMPtr<nsIFile> directory;
-      nsresult rv = mgr->GetDirectoryForOrigin(mOrigin, getter_AddRefs(directory));
-      NS_ENSURE_SUCCESS(rv, rv);
-
-      bool exists;
-      rv = directory->Exists(&exists);
-      NS_ENSURE_SUCCESS(rv, rv);
-
-      // If the directory exists then enumerate all the files inside, adding up the
-      // sizes to get the final usage statistic.
-      if (exists && !mCanceled) {
-        rv = GetUsageForDirectory(directory, &mUsage);
-        NS_ENSURE_SUCCESS(rv, rv);
-      }
-
-      // Run dispatches us back to the main thread.
-      return NS_OK;
-    }
-
-    case Complete: // Fall through
-    case Shortcut: {
-      NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
-
-      // Call the callback unless we were canceled.
-      if (!mCanceled) {
-        uint64_t usage = mUsage;
-        IncrementUsage(&usage, mFileUsage);
-        mCallback->OnUsageResult(mURI, usage, mFileUsage, mAppId,
-                                 mInMozBrowserOnly);
-      }
-
-      // Clean up.
-      mURI = nullptr;
-      mCallback = nullptr;
-
-      // And tell the IndexedDatabaseManager that we're done.
-      mgr->OnUsageCheckComplete(this);
-      if (mCallbackState == Complete) {
-        mgr->AllowNextSynchronizedOp(mOrigin, nullptr);
-      }
-
-      return NS_OK;
-    }
-
-    default:
-      NS_ERROR("Unknown state value!");
-      return NS_ERROR_UNEXPECTED;
-  }
-
-  NS_NOTREACHED("Should never get here!");
-  return NS_ERROR_UNEXPECTED;
-}
-
-nsresult
-IndexedDatabaseManager::AsyncUsageRunnable::GetUsageForDirectory(
-                                     nsIFile* aDirectory,
-                                     uint64_t* aUsage)
-{
-  NS_ASSERTION(aDirectory, "Null pointer!");
-  NS_ASSERTION(aUsage, "Null pointer!");
-
-  nsCOMPtr<nsISimpleEnumerator> entries;
-  nsresult rv = aDirectory->GetDirectoryEntries(getter_AddRefs(entries));
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  if (!entries) {
-    return NS_OK;
-  }
-
-  bool hasMore;
-  while (NS_SUCCEEDED((rv = entries->HasMoreElements(&hasMore))) &&
-         hasMore && !mCanceled) {
-    nsCOMPtr<nsISupports> entry;
-    rv = entries->GetNext(getter_AddRefs(entry));
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    nsCOMPtr<nsIFile> file(do_QueryInterface(entry));
-    NS_ASSERTION(file, "Don't know what this is!");
-
-    bool isDirectory;
-    rv = file->IsDirectory(&isDirectory);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    if (isDirectory) {
-      if (aUsage == &mFileUsage) {
-        NS_WARNING("Unknown directory found!");
-      }
-      else {
-        rv = GetUsageForDirectory(file, &mFileUsage);
-        NS_ENSURE_SUCCESS(rv, rv);
-      }
-
-      continue;
-    }
-
-    int64_t fileSize;
-    rv = file->GetFileSize(&fileSize);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    NS_ASSERTION(fileSize >= 0, "Negative size?!");
-
-    IncrementUsage(aUsage, uint64_t(fileSize));
-  }
-  NS_ENSURE_SUCCESS(rv, rv);
- 
-  return NS_OK;
-}
-
-NS_IMPL_THREADSAFE_ISUPPORTS1(IndexedDatabaseManager::AsyncUsageRunnable,
-                              nsIRunnable)
-
-NS_IMETHODIMP
-IndexedDatabaseManager::AsyncUsageRunnable::Run()
-{
-  nsresult rv = RunInternal();
-
-  if (!NS_IsMainThread()) {
-    if (NS_FAILED(rv)) {
-      mUsage = 0;
-    }
-
-    if (NS_FAILED(NS_DispatchToMainThread(this, NS_DISPATCH_NORMAL))) {
-      NS_WARNING("Failed to dispatch to main thread!");
-    }
-  }
-
-  return NS_OK;
-}
-
-NS_IMPL_THREADSAFE_ISUPPORTS1(
-                    IndexedDatabaseManager::WaitForTransactionsToFinishRunnable,
-                    nsIRunnable)
-
-NS_IMETHODIMP
-IndexedDatabaseManager::WaitForTransactionsToFinishRunnable::Run()
-{
-  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
-  NS_ASSERTION(mOp, "Null op!");
-  NS_ASSERTION(mOp->mHelper || mOp->mRunnable, "Nothing to run!");
-  NS_ASSERTION(mCountdown, "Wrong countdown!");
-
-  if (--mCountdown) {
-    return NS_OK;
-  }
-
-  // Don't hold the callback alive longer than necessary.
-  nsRefPtr<AsyncConnectionHelper> helper;
-  helper.swap(mOp->mHelper);
-
-  nsCOMPtr<nsIRunnable> runnable;
-  runnable.swap(mOp->mRunnable);
-
-  mOp = nullptr;
-
-  nsresult rv;
-
-  if (helper && helper->HasTransaction()) {
-    // If the helper has a transaction, dispatch it to the transaction
-    // threadpool.
-    rv = helper->DispatchToTransactionPool();
-    NS_ENSURE_SUCCESS(rv, rv);
-  }
-  else {
-    // Otherwise, dispatch it to the IO thread.
-    IndexedDatabaseManager* manager = IndexedDatabaseManager::Get();
-    NS_ASSERTION(manager, "We should definitely have a manager here");
-
-    nsIEventTarget* target = manager->IOThread();
-
-    rv = helper ?
-         helper->Dispatch(target) :
-         target->Dispatch(runnable, NS_DISPATCH_NORMAL);
-    NS_ENSURE_SUCCESS(rv, rv);
-  }
-
-  // The helper or runnable is responsible for calling
-  // IndexedDatabaseManager::AllowNextSynchronizedOp.
-  return NS_OK;
-}
-
-NS_IMPL_THREADSAFE_ISUPPORTS1(
-                     IndexedDatabaseManager::WaitForLockedFilesToFinishRunnable,
-                     nsIRunnable)
-
-NS_IMETHODIMP
-IndexedDatabaseManager::WaitForLockedFilesToFinishRunnable::Run()
-{
-  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
-
-  mBusy = false;
-
-  return NS_OK;
-}
-
-IndexedDatabaseManager::
-SynchronizedOp::SynchronizedOp(const OriginOrPatternString& aOriginOrPattern,
-                               nsIAtom* aId)
-: mOriginOrPattern(aOriginOrPattern), mId(aId)
-{
-  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
-  MOZ_COUNT_CTOR(IndexedDatabaseManager::SynchronizedOp);
-}
-
-IndexedDatabaseManager::SynchronizedOp::~SynchronizedOp()
-{
-  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
-  MOZ_COUNT_DTOR(IndexedDatabaseManager::SynchronizedOp);
-}
-
-bool
-IndexedDatabaseManager::
-SynchronizedOp::MustWaitFor(const SynchronizedOp& aExistingOp)
-{
-  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
-
-  bool match;
-
-  if (aExistingOp.mOriginOrPattern.IsOrigin()) {
-    if (mOriginOrPattern.IsOrigin()) {
-      match = aExistingOp.mOriginOrPattern.Equals(mOriginOrPattern);
-    }
-    else {
-      match = PatternMatchesOrigin(mOriginOrPattern, aExistingOp.mOriginOrPattern);
-    }
-  }
-  else if (mOriginOrPattern.IsOrigin()) {
-    match = PatternMatchesOrigin(aExistingOp.mOriginOrPattern, mOriginOrPattern);
-  }
-  else {
-    match = PatternMatchesOrigin(mOriginOrPattern, aExistingOp.mOriginOrPattern) ||
-            PatternMatchesOrigin(aExistingOp.mOriginOrPattern, mOriginOrPattern);
-  }
-
-  // If the origins don't match, the second can proceed.
-  if (!match) {
-    return false;
-  }
-
-  // If the origins and the ids match, the second must wait.
-  if (aExistingOp.mId == mId) {
-    return true;
-  }
-
-  // Waiting is required if either one corresponds to an origin clearing
-  // (a null Id).
-  if (!aExistingOp.mId || !mId) {
-    return true;
-  }
-
-  // Otherwise, things for the same origin but different databases can proceed
-  // independently.
-  return false;
-}
-
-void
-IndexedDatabaseManager::SynchronizedOp::DelayRunnable(nsIRunnable* aRunnable)
-{
-  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
-  NS_ASSERTION(mDelayedRunnables.IsEmpty() || !mId,
-               "Only ClearOrigin operations can delay multiple runnables!");
-
-  mDelayedRunnables.AppendElement(aRunnable);
-}
-
-void
-IndexedDatabaseManager::SynchronizedOp::DispatchDelayedRunnables()
-{
-  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
-  NS_ASSERTION(!mHelper, "Any helper should be gone by now!");
-
-  uint32_t count = mDelayedRunnables.Length();
-  for (uint32_t index = 0; index < count; index++) {
-    NS_DispatchToCurrentThread(mDelayedRunnables[index]);
-  }
-
-  mDelayedRunnables.Clear();
-}
+NS_IMPL_ADDREF(IndexedDatabaseManager)
+NS_IMPL_RELEASE_WITH_DESTROY(IndexedDatabaseManager, Destroy())
+NS_IMPL_QUERY_INTERFACE1(IndexedDatabaseManager, nsIIndexedDatabaseManager)
 
 NS_IMETHODIMP
 IndexedDatabaseManager::InitWindowless(const jsval& aObj, JSContext* aCx)
 {
   NS_ENSURE_TRUE(nsContentUtils::IsCallerChrome(), NS_ERROR_NOT_AVAILABLE);
   NS_ENSURE_ARG(!JSVAL_IS_PRIMITIVE(aObj));
 
   JSObject* obj = JSVAL_TO_OBJECT(aObj);
@@ -2157,28 +449,27 @@ IndexedDatabaseManager::InitWindowless(c
   if (!JS_DefineProperty(aCx, obj, "IDBKeyRange", OBJECT_TO_JSVAL(keyrangeObj),
                          nullptr, nullptr, JSPROP_ENUMERATE)) {
     return NS_ERROR_FAILURE;
   }
 
   return NS_OK;
 }
 
-IndexedDatabaseManager::
 AsyncDeleteFileRunnable::AsyncDeleteFileRunnable(FileManager* aFileManager,
                                                  int64_t aFileId)
 : mFileManager(aFileManager), mFileId(aFileId)
 {
 }
 
-NS_IMPL_THREADSAFE_ISUPPORTS1(IndexedDatabaseManager::AsyncDeleteFileRunnable,
+NS_IMPL_THREADSAFE_ISUPPORTS1(AsyncDeleteFileRunnable,
                               nsIRunnable)
 
 NS_IMETHODIMP
-IndexedDatabaseManager::AsyncDeleteFileRunnable::Run()
+AsyncDeleteFileRunnable::Run()
 {
   NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
 
   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);
--- a/dom/indexedDB/IndexedDatabaseManager.h
+++ b/dom/indexedDB/IndexedDatabaseManager.h
@@ -4,132 +4,58 @@
  * 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_indexeddb_indexeddatabasemanager_h__
 #define mozilla_dom_indexeddb_indexeddatabasemanager_h__
 
 #include "mozilla/dom/indexedDB/IndexedDatabase.h"
 
-#include "mozilla/Mutex.h"
+#include "nsIIndexedDatabaseManager.h"
 
-#include "nsIIndexedDatabaseManager.h"
-#include "nsIObserver.h"
-#include "nsIRunnable.h"
-#include "nsIThread.h"
-#include "nsIURI.h"
-
+#include "mozilla/Mutex.h"
 #include "nsClassHashtable.h"
-#include "nsRefPtrHashtable.h"
 #include "nsHashKeys.h"
 
 #define INDEXEDDB_MANAGER_CONTRACTID "@mozilla.org/dom/indexeddb/manager;1"
 
 class nsIAtom;
-class nsIFile;
-class nsITimer;
 class nsPIDOMWindow;
 class nsEventChainPostVisitor;
 
 namespace mozilla {
 namespace dom {
 class TabContext;
 }
 }
 
-
 BEGIN_INDEXEDDB_NAMESPACE
 
-class AsyncConnectionHelper;
 class FileManager;
-class IDBDatabase;
-
-class IndexedDatabaseManager MOZ_FINAL : public nsIIndexedDatabaseManager,
-                                         public nsIObserver
-{
-  friend class IDBDatabase;
 
+class IndexedDatabaseManager MOZ_FINAL : public nsIIndexedDatabaseManager
+{
 public:
-  static already_AddRefed<IndexedDatabaseManager> GetOrCreate();
-
-  // Returns a non-owning reference.
-  static IndexedDatabaseManager* Get();
-
-  // Returns an owning reference! No one should call this but the factory.
-  static IndexedDatabaseManager* FactoryCreate();
-
   NS_DECL_ISUPPORTS
   NS_DECL_NSIINDEXEDDATABASEMANAGER
-  NS_DECL_NSIOBSERVER
 
-  // Waits for databases to be cleared and for version change transactions to
-  // complete before dispatching the given runnable.
-  nsresult WaitForOpenAllowed(const OriginOrPatternString& aOriginOrPattern,
-                              nsIAtom* aId,
-                              nsIRunnable* aRunnable);
-
-  void AllowNextSynchronizedOp(const OriginOrPatternString& aOriginOrPattern,
-                               nsIAtom* aId);
-
-  nsIThread* IOThread()
-  {
-    NS_ASSERTION(mIOThread, "This should never be null!");
-    return mIOThread;
-  }
-
-  // Returns true if we've begun the shutdown process.
-  static bool IsShuttingDown();
-
-  static bool IsClosed();
-
-  typedef void
-  (*WaitingOnDatabasesCallback)(nsTArray<nsRefPtr<IDBDatabase> >&, void*);
+  // Returns a non-owning reference.
+  static IndexedDatabaseManager*
+  GetOrCreate();
 
-  // Acquire exclusive access to the database given (waits for all others to
-  // close).  If databases need to close first, the callback will be invoked
-  // with an array of said databases.
-  nsresult AcquireExclusiveAccess(IDBDatabase* aDatabase,
-                                  const nsACString& aOrigin,
-                                  AsyncConnectionHelper* aHelper,
-                                  WaitingOnDatabasesCallback aCallback,
-                                  void* aClosure)
-  {
-    NS_ASSERTION(aDatabase, "Need a DB here!");
-    return AcquireExclusiveAccess(aOrigin, aDatabase, aHelper, nullptr,
-                                  aCallback, aClosure);
-  }
+  // Returns a non-owning reference.
+  static IndexedDatabaseManager*
+  Get();
 
-  nsresult AcquireExclusiveAccess(const nsACString& aOrigin,
-                                  nsIRunnable* aRunnable,
-                                  WaitingOnDatabasesCallback aCallback,
-                                  void* aClosure)
-  {
-    return AcquireExclusiveAccess(aOrigin, nullptr, nullptr, aRunnable, aCallback,
-                                  aClosure);
-  }
-
-  // Called when a window is being purged from the bfcache or the user leaves
-  // a page which isn't going into the bfcache. Forces any live database
-  // objects to close themselves and aborts any running transactions.
-  void AbortCloseDatabasesForWindow(nsPIDOMWindow* aWindow);
+  // Returns an owning reference! No one should call this but the factory.
+  static IndexedDatabaseManager*
+  FactoryCreate();
 
-  // Used to check if there are running transactions in a given window.
-  bool HasOpenTransactions(nsPIDOMWindow* aWindow);
-
-  static uint32_t
-  GetIndexedDBQuotaMB();
-
-  nsresult EnsureOriginIsInitialized(const nsACString& aOrigin,
-                                     FactoryPrivilege aPrivilege,
-                                     nsIFile** aDirectory);
-
-  void UninitializeOriginsByPattern(const nsACString& aPattern);
-
-  static nsresult
-  GetASCIIOriginFromWindow(nsPIDOMWindow* aWindow, nsCString& aASCIIOrigin);
+  static bool
+  IsClosed();
 
   static bool
   IsMainProcess()
 #ifdef DEBUG
   ;
 #else
   {
     return sIsMainProcess;
@@ -138,338 +64,65 @@ public:
 
   already_AddRefed<FileManager>
   GetFileManager(const nsACString& aOrigin,
                  const nsAString& aDatabaseName);
 
   void
   AddFileManager(FileManager* aFileManager);
 
-  void InvalidateFileManagersForPattern(const nsACString& aPattern);
-
-  void InvalidateFileManager(const nsACString& aOrigin,
-                             const nsAString& aDatabaseName);
+  void
+  InvalidateAllFileManagers();
 
-  nsresult AsyncDeleteFile(FileManager* aFileManager,
-                           int64_t aFileId);
+  void
+  InvalidateFileManagersForPattern(const nsACString& aPattern);
 
-  const nsString&
-  GetBaseDirectory() const
-  {
-    return mDatabaseBasePath;
-  }
+  void
+  InvalidateFileManager(const nsACString& aOrigin,
+                        const nsAString& aDatabaseName);
 
   nsresult
-  GetDirectoryForOrigin(const nsACString& aASCIIOrigin,
-                        nsIFile** aDirectory) const;
+  AsyncDeleteFile(FileManager* aFileManager,
+                  int64_t aFileId);
 
-  static mozilla::Mutex& FileMutex()
+  static mozilla::Mutex&
+  FileMutex()
   {
     IndexedDatabaseManager* mgr = Get();
     NS_ASSERTION(mgr, "Must have a manager here!");
 
     return mgr->mFileMutex;
   }
 
-  static already_AddRefed<nsIAtom>
-  GetDatabaseId(const nsACString& aOrigin,
-                const nsAString& aName);
-
   static nsresult
   FireWindowOnError(nsPIDOMWindow* aOwner,
                     nsEventChainPostVisitor& aVisitor);
 
   static bool
   TabContextMayAccessOrigin(const mozilla::dom::TabContext& aContext,
                             const nsACString& aOrigin);
 
 private:
   IndexedDatabaseManager();
   ~IndexedDatabaseManager();
 
-  nsresult AcquireExclusiveAccess(const nsACString& aOrigin,
-                                  IDBDatabase* aDatabase,
-                                  AsyncConnectionHelper* aHelper,
-                                  nsIRunnable* aRunnable,
-                                  WaitingOnDatabasesCallback aCallback,
-                                  void* aClosure);
-
-  // Called when a database is created.
-  bool RegisterDatabase(IDBDatabase* aDatabase);
-
-  // Called when a database is being unlinked or destroyed.
-  void UnregisterDatabase(IDBDatabase* aDatabase);
-
-  // Called when a database has been closed.
-  void OnDatabaseClosed(IDBDatabase* aDatabase);
-
-  nsresult ClearDatabasesForApp(uint32_t aAppId, bool aBrowserOnly);
-
-  // Responsible for clearing the database files for a particular origin on the
-  // IO thread. Created when nsIIDBIndexedDatabaseManager::ClearDatabasesForURI
-  // is called. Runs three times, first on the main thread, next on the IO
-  // thread, and then finally again on the main thread. While on the IO thread
-  // the runnable will actually remove the origin's database files and the
-  // directory that contains them before dispatching itself back to the main
-  // thread. When back on the main thread the runnable will notify the
-  // IndexedDatabaseManager that the job has been completed.
-  class OriginClearRunnable MOZ_FINAL : public nsIRunnable
-  {
-    enum CallbackState {
-      // Not yet run.
-      Pending = 0,
-
-      // Running on the main thread in the callback for OpenAllowed.
-      OpenAllowed,
-
-      // Running on the IO thread.
-      IO,
-
-      // Running on the main thread after all work is done.
-      Complete
-    };
-
-  public:
-    NS_DECL_ISUPPORTS
-    NS_DECL_NSIRUNNABLE
-
-    OriginClearRunnable(const OriginOrPatternString& aOriginOrPattern)
-    : mOriginOrPattern(aOriginOrPattern),
-      mCallbackState(Pending)
-    { }
-
-    void AdvanceState()
-    {
-      switch (mCallbackState) {
-        case Pending:
-          mCallbackState = OpenAllowed;
-          return;
-        case OpenAllowed:
-          mCallbackState = IO;
-          return;
-        case IO:
-          mCallbackState = Complete;
-          return;
-        default:
-          NS_NOTREACHED("Can't advance past Complete!");
-      }
-    }
-
-    static void InvalidateOpenedDatabases(
-                                   nsTArray<nsRefPtr<IDBDatabase> >& aDatabases,
-                                   void* aClosure);
-
-    void DeleteFiles(IndexedDatabaseManager* aManager);
-
-  private:
-    OriginOrPatternString mOriginOrPattern;
-    CallbackState mCallbackState;
-  };
-
-  // Responsible for calculating the amount of space taken up by databases of a
-  // certain origin. Created when nsIIDBIndexedDatabaseManager::GetUsageForURI
-  // is called. May be canceled with
-  // nsIIDBIndexedDatabaseManager::CancelGetUsageForURI. Runs twice, first on
-  // the IO thread, then again on the main thread. While on the IO thread the
-  // runnable will calculate the size of all files in the origin's directory
-  // before dispatching itself back to the main thread. When on the main thread
-  // the runnable will call the callback and then notify the
-  // IndexedDatabaseManager that the job has been completed.
-  class AsyncUsageRunnable MOZ_FINAL : public nsIRunnable
-  {
-    enum CallbackState {
-      // Not yet run.
-      Pending = 0,
-
-      // Running on the main thread in the callback for OpenAllowed.
-      OpenAllowed,
-
-      // Running on the IO thread.
-      IO,
-
-      // Running on the main thread after all work is done.
-      Complete,
-
-      // Running on the main thread after skipping the work
-      Shortcut
-    };
-
-  public:
-    NS_DECL_ISUPPORTS
-    NS_DECL_NSIRUNNABLE
-
-    AsyncUsageRunnable(uint32_t aAppId,
-                       bool aInMozBrowserOnly,
-                       const OriginOrPatternString& aOrigin,
-                       nsIURI* aURI,
-                       nsIIndexedDatabaseUsageCallback* aCallback);
-
-    // Sets the canceled flag so that the callback is never called.
-    void Cancel();
+  nsresult
+  Init();
 
-    void AdvanceState()
-    {
-      switch (mCallbackState) {
-        case Pending:
-          mCallbackState = OpenAllowed;
-          return;
-        case OpenAllowed:
-          mCallbackState = IO;
-          return;
-        case IO:
-          mCallbackState = Complete;
-          return;
-        default:
-          NS_NOTREACHED("Can't advance past Complete!");
-      }
-    }
-
-    nsresult TakeShortcut();
-
-    // Run calls the RunInternal method and makes sure that we always dispatch
-    // to the main thread in case of an error.
-    inline nsresult RunInternal();
-
-    nsresult GetUsageForDirectory(nsIFile* aDirectory,
-                                  uint64_t* aUsage);
-
-    nsCOMPtr<nsIURI> mURI;
-    nsCOMPtr<nsIIndexedDatabaseUsageCallback> mCallback;
-    uint64_t mUsage;
-    uint64_t mFileUsage;
-    uint32_t mAppId;
-    int32_t mCanceled;
-    OriginOrPatternString mOrigin;
-    CallbackState mCallbackState;
-    bool mInMozBrowserOnly;
-  };
-
-  // Called when AsyncUsageRunnable has finished its Run() method.
-  inline void OnUsageCheckComplete(AsyncUsageRunnable* aRunnable);
-
-  // A struct that contains the information corresponding to a pending or
-  // running operation that requires synchronization (e.g. opening a db,
-  // clearing dbs for an origin, etc).
-  struct SynchronizedOp
-  {
-    SynchronizedOp(const OriginOrPatternString& aOriginOrPattern,
-                   nsIAtom* aId);
-    ~SynchronizedOp();
-
-    // Test whether this SynchronizedOp needs to wait for the given op.
-    bool MustWaitFor(const SynchronizedOp& aOp);
-
-    void DelayRunnable(nsIRunnable* aRunnable);
-    void DispatchDelayedRunnables();
-
-    const OriginOrPatternString mOriginOrPattern;
-    nsCOMPtr<nsIAtom> mId;
-    nsRefPtr<AsyncConnectionHelper> mHelper;
-    nsCOMPtr<nsIRunnable> mRunnable;
-    nsTArray<nsCOMPtr<nsIRunnable> > mDelayedRunnables;
-    nsTArray<IDBDatabase*> mDatabases;
-  };
-
-  // A callback runnable used by the TransactionPool when it's safe to proceed
-  // with a SetVersion/DeleteDatabase/etc.
-  class WaitForTransactionsToFinishRunnable MOZ_FINAL : public nsIRunnable
-  {
-  public:
-    WaitForTransactionsToFinishRunnable(SynchronizedOp* aOp,
-                                        uint32_t aCountdown)
-    : mOp(aOp), mCountdown(aCountdown)
-    {
-      NS_ASSERTION(mOp, "Why don't we have a runnable?");
-      NS_ASSERTION(mOp->mDatabases.IsEmpty(), "We're here too early!");
-      NS_ASSERTION(mOp->mHelper || mOp->mRunnable,
-                   "What are we supposed to do when we're done?");
-      NS_ASSERTION(mCountdown, "Wrong countdown!");
-    }
-
-    NS_DECL_ISUPPORTS
-    NS_DECL_NSIRUNNABLE
-
-  private:
-    // The IndexedDatabaseManager holds this alive.
-    SynchronizedOp* mOp;
-    uint32_t mCountdown;
-  };
-
-  class WaitForLockedFilesToFinishRunnable MOZ_FINAL : public nsIRunnable
-  {
-  public:
-    WaitForLockedFilesToFinishRunnable()
-    : mBusy(true)
-    { }
-
-    NS_DECL_ISUPPORTS
-    NS_DECL_NSIRUNNABLE
-
-    bool IsBusy() const
-    {
-      return mBusy;
-    }
-
-  private:
-    bool mBusy;
-  };
-
-  class AsyncDeleteFileRunnable MOZ_FINAL : public nsIRunnable
-  {
-  public:
-    NS_DECL_ISUPPORTS
-    NS_DECL_NSIRUNNABLE
-    AsyncDeleteFileRunnable(FileManager* aFileManager, int64_t aFileId);
-
-  private:
-    nsRefPtr<FileManager> mFileManager;
-    int64_t mFileId;
-  };
-
-  static nsresult RunSynchronizedOp(IDBDatabase* aDatabase,
-                                    SynchronizedOp* aOp);
-
-  SynchronizedOp* FindSynchronizedOp(const nsACString& aPattern,
-                                     nsIAtom* aId);
-
-  bool IsClearOriginPending(const nsACString& aPattern)
-  {
-    return !!FindSynchronizedOp(aPattern, nullptr);
-  }
-
-  // Maintains a list of live databases per origin.
-  nsClassHashtable<nsCStringHashKey, nsTArray<IDBDatabase*> > mLiveDatabases;
+  void
+  Destroy();
 
   // Maintains a list of all file managers per origin. This list isn't
   // protected by any mutex but it is only ever touched on the IO thread.
   nsClassHashtable<nsCStringHashKey,
                    nsTArray<nsRefPtr<FileManager> > > mFileManagers;
 
-  // Maintains a list of origins that we're currently enumerating to gather
-  // usage statistics.
-  nsAutoTArray<nsRefPtr<AsyncUsageRunnable>, 1> mUsageRunnables;
-
-  // Maintains a list of synchronized operatons that are in progress or queued.
-  nsAutoTArray<nsAutoPtr<SynchronizedOp>, 5> mSynchronizedOps;
-
-  // Thread on which IO is performed.
-  nsCOMPtr<nsIThread> mIOThread;
-
-  // A timer that gets activated at shutdown to ensure we close all databases.
-  nsCOMPtr<nsITimer> mShutdownTimer;
-
-  // A list of all successfully initialized origins. This list isn't protected
-  // by any mutex but it is only ever touched on the IO thread.
-  nsTArray<nsCString> mInitializedOrigins;
-
   // Lock protecting FileManager.mFileInfos and nsDOMFileBase.mFileInfos
   // It's s also used to atomically update FileInfo.mRefCnt, FileInfo.mDBRefCnt
   // and FileInfo.mSliceRefCnt
   mozilla::Mutex mFileMutex;
 
-  nsString mDatabaseBasePath;
-
   static bool sIsMainProcess;
 };
 
 END_INDEXEDDB_NAMESPACE
 
 #endif /* mozilla_dom_indexeddb_indexeddatabasemanager_h__ */
--- a/dom/indexedDB/Makefile.in
+++ b/dom/indexedDB/Makefile.in
@@ -16,16 +16,17 @@ ifndef _MSC_VER
 FAIL_ON_WARNINGS := 1
 endif # !_MSC_VER
 
 EXPORTS_NAMESPACES = mozilla/dom/indexedDB
 
 CPPSRCS = \
   AsyncConnectionHelper.cpp \
   CheckPermissionsHelper.cpp \
+  Client.cpp \
   DatabaseInfo.cpp \
   FileInfo.cpp \
   FileManager.cpp \
   IDBCursor.cpp \
   IDBDatabase.cpp \
   IDBEvents.cpp \
   IDBFactory.cpp \
   IDBFileHandle.cpp \
@@ -38,34 +39,35 @@ CPPSRCS = \
   IndexedDatabaseManager.cpp \
   Key.cpp \
   KeyPath.cpp \
   OpenDatabaseHelper.cpp \
   TransactionThreadPool.cpp \
   $(NULL)
 
 EXPORTS_mozilla/dom/indexedDB = \
+  Client.h \
   DatabaseInfo.h \
+  FileManager.h \
+  FileInfo.h \
   IDBCursor.h \
   IDBDatabase.h \
   IDBEvents.h \
   IDBFactory.h \
   IDBFileHandle.h \
   IDBIndex.h \
   IDBKeyRange.h \
   IDBObjectStore.h \
   IDBRequest.h \
   IDBTransaction.h \
   IDBWrapperCache.h \
   IndexedDatabase.h \
   IndexedDatabaseManager.h \
   Key.h \
   KeyPath.h \
-  FileManager.h \
-  FileInfo.h \
   $(NULL)
 
 LOCAL_INCLUDES = \
   -I$(topsrcdir)/caps/include \
   -I$(topsrcdir)/content/base/src \
   -I$(topsrcdir)/content/events/src \
   -I$(topsrcdir)/db/sqlite3/src \
   -I$(topsrcdir)/dom/base \
--- a/dom/indexedDB/OpenDatabaseHelper.cpp
+++ b/dom/indexedDB/OpenDatabaseHelper.cpp
@@ -3,23 +3,26 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/DebugOnly.h"
 
 #include "OpenDatabaseHelper.h"
 
 #include "nsIFile.h"
 
+#include "mozilla/dom/quota/AcquireListener.h"
+#include "mozilla/dom/quota/OriginOrPatternString.h"
 #include "mozilla/dom/quota/QuotaManager.h"
 #include "mozilla/storage.h"
 #include "nsEscape.h"
 #include "nsNetUtil.h"
 #include "nsThreadUtils.h"
 #include "snappy/snappy.h"
 
+#include "Client.h"
 #include "nsIBFCacheEntry.h"
 #include "IDBEvents.h"
 #include "IDBFactory.h"
 #include "IndexedDatabaseManager.h"
 #include <algorithm>
 
 using namespace mozilla;
 USING_INDEXEDDB_NAMESPACE
@@ -1318,17 +1321,18 @@ UpgradeSchemaFrom11_0To12_0(mozIStorageC
   NS_ENSURE_SUCCESS(rv, rv);
 
   return NS_OK;
 }
 
 class VersionChangeEventsRunnable;
 
 class SetVersionHelper : public AsyncConnectionHelper,
-                         public IDBTransactionListener
+                         public IDBTransactionListener,
+                         public AcquireListener
 {
   friend class VersionChangeEventsRunnable;
 
 public:
   SetVersionHelper(IDBTransaction* aTransaction,
                    IDBOpenDBRequest* aRequest,
                    OpenDatabaseHelper* aHelper,
                    uint64_t aRequestedVersion,
@@ -1341,16 +1345,19 @@ public:
     mTransaction->SetTransactionListener(this);
   }
 
   NS_DECL_ISUPPORTS_INHERITED
 
   virtual nsresult GetSuccessResult(JSContext* aCx,
                                     jsval* aVal) MOZ_OVERRIDE;
 
+  virtual nsresult
+  OnExclusiveAccessAcquired() MOZ_OVERRIDE;
+
 protected:
   virtual nsresult Init() MOZ_OVERRIDE;
 
   virtual nsresult DoDatabaseWork(mozIStorageConnection* aConnection)
                                   MOZ_OVERRIDE;
 
   // SetVersionHelper never fires an error event at the request.  It hands that
   // responsibility back to the OpenDatabaseHelper
@@ -1388,42 +1395,48 @@ protected:
 private:
   // In-params
   nsRefPtr<IDBOpenDBRequest> mOpenRequest;
   nsRefPtr<OpenDatabaseHelper> mOpenHelper;
   uint64_t mRequestedVersion;
   uint64_t mCurrentVersion;
 };
 
-class DeleteDatabaseHelper : public AsyncConnectionHelper
+class DeleteDatabaseHelper : public AsyncConnectionHelper,
+                             public AcquireListener
 {
   friend class VersionChangeEventsRunnable;
 public:
   DeleteDatabaseHelper(IDBOpenDBRequest* aRequest,
                        OpenDatabaseHelper* aHelper,
                        uint64_t aCurrentVersion,
                        const nsAString& aName,
                        const nsACString& aASCIIOrigin)
   : AsyncConnectionHelper(static_cast<IDBDatabase*>(nullptr), aRequest),
     mOpenHelper(aHelper), mOpenRequest(aRequest),
     mCurrentVersion(aCurrentVersion), mName(aName),
     mASCIIOrigin(aASCIIOrigin)
   { }
 
+  NS_DECL_ISUPPORTS_INHERITED
+
   nsresult GetSuccessResult(JSContext* aCx,
                             jsval* aVal);
 
   void ReleaseMainThreadObjects()
   {
     mOpenHelper = nullptr;
     mOpenRequest = nullptr;
 
     AsyncConnectionHelper::ReleaseMainThreadObjects();
   }
 
+  virtual nsresult
+  OnExclusiveAccessAcquired() MOZ_OVERRIDE;
+
 protected:
   nsresult DoDatabaseWork(mozIStorageConnection* aConnection);
   nsresult Init();
 
   // DeleteDatabaseHelper never fires events at the request.  It hands that
   // responsibility back to the OpenDatabaseHelper
   void OnError()
   {
@@ -1465,21 +1478,21 @@ private:
 
 // Responsible for firing "versionchange" events at all live and non-closed
 // databases, and for firing a "blocked" event at the requesting database if any
 // databases fail to close.
 class VersionChangeEventsRunnable : public nsRunnable
 {
 public:
   VersionChangeEventsRunnable(
-                            IDBDatabase* aRequestingDatabase,
-                            IDBOpenDBRequest* aRequest,
-                            nsTArray<nsRefPtr<IDBDatabase> >& aWaitingDatabases,
-                            int64_t aOldVersion,
-                            int64_t aNewVersion)
+                      IDBDatabase* aRequestingDatabase,
+                      IDBOpenDBRequest* aRequest,
+                      nsTArray<nsCOMPtr<nsIOfflineStorage> >& aWaitingDatabases,
+                      int64_t aOldVersion,
+                      int64_t aNewVersion)
   : mRequestingDatabase(aRequestingDatabase),
     mRequest(aRequest),
     mOldVersion(aOldVersion),
     mNewVersion(aNewVersion)
   {
     NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
     NS_ASSERTION(aRequestingDatabase, "Null pointer!");
     NS_ASSERTION(aRequest, "Null pointer!");
@@ -1492,17 +1505,19 @@ public:
   NS_IMETHOD Run()
   {
     NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
     // Fire version change events at all of the databases that are not already
     // closed. Also kick bfcached documents out of bfcache.
     uint32_t count = mWaitingDatabases.Length();
     for (uint32_t index = 0; index < count; index++) {
-      nsRefPtr<IDBDatabase>& database = mWaitingDatabases[index];
+      IDBDatabase* database =
+        IDBDatabase::FromStorage(mWaitingDatabases[index]);
+      NS_ASSERTION(database, "This shouldn't be null!");
 
       if (database->IsClosed()) {
         continue;
       }
 
       // First check if the document the IDBDatabase is part of is bfcached.
       nsCOMPtr<nsIDocument> ownerDoc = database->GetOwnerDocument();
       nsIBFCacheEntry* bfCacheEntry;
@@ -1514,22 +1529,22 @@ public:
       }
 
       // Next check if it's in the process of being bfcached.
       nsPIDOMWindow* owner = database->GetOwner();
       if (owner && owner->IsFrozen()) {
         // We can't kick the document out of the bfcache because it's not yet
         // fully in the bfcache.  Instead we'll abort everything for the window
         // and mark it as not-bfcacheable.
-        IndexedDatabaseManager* manager = IndexedDatabaseManager::Get();
-        NS_ASSERTION(manager, "Huh?");
-        manager->AbortCloseDatabasesForWindow(owner);
+        QuotaManager* quotaManager = QuotaManager::Get();
+        NS_ASSERTION(quotaManager, "Huh?");
+        quotaManager->AbortCloseStoragesForWindow(owner);
 
         NS_ASSERTION(database->IsClosed(),
-                   "AbortCloseDatabasesForWindow should have closed database");
+                   "AbortCloseStoragesForWindow should have closed database");
         ownerDoc->DisallowBFCaching();
         continue;
       }
 
       // Otherwise fire a versionchange event.
       nsRefPtr<nsDOMEvent> event = 
         IDBVersionChangeEvent::Create(database, mOldVersion, mNewVersion);
       NS_ENSURE_TRUE(event, NS_ERROR_FAILURE);
@@ -1554,34 +1569,34 @@ public:
       }
     }
 
     return NS_OK;
   }
 
   template <class T>
   static
-  void QueueVersionChange(nsTArray<nsRefPtr<IDBDatabase> >& aDatabases,
+  void QueueVersionChange(nsTArray<nsCOMPtr<nsIOfflineStorage> >& aDatabases,
                           void* aClosure);
 private:
   nsRefPtr<IDBDatabase> mRequestingDatabase;
   nsRefPtr<IDBOpenDBRequest> mRequest;
-  nsTArray<nsRefPtr<IDBDatabase> > mWaitingDatabases;
+  nsTArray<nsCOMPtr<nsIOfflineStorage> > mWaitingDatabases;
   int64_t mOldVersion;
   int64_t mNewVersion;
 };
 
 } // anonymous namespace
 
 NS_IMPL_THREADSAFE_ISUPPORTS1(OpenDatabaseHelper, nsIRunnable)
 
 nsresult
 OpenDatabaseHelper::Init()
 {
-  mDatabaseId = IndexedDatabaseManager::GetDatabaseId(mASCIIOrigin, mName);
+  mDatabaseId = QuotaManager::GetStorageId(mASCIIOrigin, mName);
   NS_ENSURE_TRUE(mDatabaseId, NS_ERROR_FAILURE);
 
   return NS_OK;
 }
 
 nsresult
 OpenDatabaseHelper::Dispatch(nsIEventTarget* aTarget)
 {
@@ -1604,46 +1619,66 @@ OpenDatabaseHelper::RunImmediately()
 }
 
 nsresult
 OpenDatabaseHelper::DoDatabaseWork()
 {
 #ifdef DEBUG
   {
     bool correctThread;
-    NS_ASSERTION(NS_SUCCEEDED(IndexedDatabaseManager::Get()->IOThread()->
+    NS_ASSERTION(NS_SUCCEEDED(QuotaManager::Get()->IOThread()->
                               IsOnCurrentThread(&correctThread)) &&
                  correctThread,
                  "Running on the wrong thread!");
   }
 #endif
 
   mState = eFiringEvents; // In case we fail somewhere along the line.
 
-  if (IndexedDatabaseManager::IsShuttingDown()) {
+  if (QuotaManager::IsShuttingDown()) {
     return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
   }
 
   NS_ASSERTION(mOpenDBRequest, "This should never be null!");
 
   // This will be null for non-window contexts.
   nsPIDOMWindow* window = mOpenDBRequest->GetOwner();
 
   AutoEnterWindow autoWindow(window);
 
   nsCOMPtr<nsIFile> dbDirectory;
 
-  IndexedDatabaseManager* mgr = IndexedDatabaseManager::Get();
-  NS_ASSERTION(mgr, "This should never be null!");
-
-  nsresult rv = mgr->EnsureOriginIsInitialized(mASCIIOrigin,
-                                               mPrivilege,
-                                               getter_AddRefs(dbDirectory));
+  QuotaManager* quotaManager = QuotaManager::Get();
+  NS_ASSERTION(quotaManager, "This should never be null!");
+
+  nsresult rv =
+    quotaManager->EnsureOriginIsInitialized(mASCIIOrigin,
+                                            mPrivilege,
+                                            getter_AddRefs(dbDirectory));
+  NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
+
+  rv = dbDirectory->Append(NS_LITERAL_STRING(IDB_DIRECTORY_NAME));
   NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
+  bool exists;
+  rv = dbDirectory->Exists(&exists);
+  NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
+
+  if (!exists) {
+    rv = dbDirectory->Create(nsIFile::DIRECTORY_TYPE, 0755);
+    NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
+  }
+#ifdef DEBUG
+  else {
+    bool isDirectory;
+    NS_ASSERTION(NS_SUCCEEDED(dbDirectory->IsDirectory(&isDirectory)) &&
+                isDirectory, "Should have caught this earlier!");
+  }
+#endif
+
   nsAutoString filename;
   rv = GetDatabaseFilename(mName, filename);
   NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
   nsCOMPtr<nsIFile> dbFile;
   rv = dbDirectory->Clone(getter_AddRefs(dbFile));
   NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
@@ -1705,16 +1740,19 @@ OpenDatabaseHelper::DoDatabaseWork()
   if (mCurrentVersion > mRequestedVersion) {
     return NS_ERROR_DOM_INDEXEDDB_VERSION_ERR;
   }
 
   if (mCurrentVersion != mRequestedVersion) {
     mState = eSetVersionPending;
   }
 
+  IndexedDatabaseManager* mgr = IndexedDatabaseManager::Get();
+  NS_ASSERTION(mgr, "This should never be null!");
+
   nsRefPtr<FileManager> fileManager = mgr->GetFileManager(mASCIIOrigin, mName);
   if (!fileManager) {
     fileManager = new FileManager(mASCIIOrigin, mPrivilege, mName);
 
     rv = fileManager->Init(fmDirectory, connection);
     NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
     mgr->AddFileManager(fileManager);
@@ -1912,22 +1950,23 @@ OpenDatabaseHelper::StartSetVersion()
     IDBTransaction::Create(mDatabase, storesToOpen,
                            IDBTransaction::VERSION_CHANGE, true);
   NS_ENSURE_TRUE(transaction, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
   nsRefPtr<SetVersionHelper> helper =
     new SetVersionHelper(transaction, mOpenDBRequest, this, mRequestedVersion,
                          mCurrentVersion);
 
-  IndexedDatabaseManager* mgr = IndexedDatabaseManager::Get();
-  NS_ASSERTION(mgr, "This should never be null!");
-
-  rv = mgr->AcquireExclusiveAccess(mDatabase, mDatabase->Origin(), helper,
+  QuotaManager* quotaManager = QuotaManager::Get();
+  NS_ASSERTION(quotaManager, "This should never be null!");
+
+  rv = quotaManager->AcquireExclusiveAccess(
+             mDatabase, mDatabase->Origin(), helper,
              &VersionChangeEventsRunnable::QueueVersionChange<SetVersionHelper>,
-                                   helper);
+             helper);
   NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
   // The SetVersionHelper is responsible for dispatching us back to the
   // main thread again and changing the state to eSetVersionCompleted.
   mState = eSetVersionPending;
   return NS_OK;
 }
 
@@ -1941,22 +1980,23 @@ OpenDatabaseHelper::StartDelete()
 
   nsresult rv = EnsureSuccessResult();
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsRefPtr<DeleteDatabaseHelper> helper =
     new DeleteDatabaseHelper(mOpenDBRequest, this, mCurrentVersion, mName,
                              mASCIIOrigin);
 
-  IndexedDatabaseManager* mgr = IndexedDatabaseManager::Get();
-  NS_ASSERTION(mgr, "This should never be null!");
-
-  rv = mgr->AcquireExclusiveAccess(mDatabase, mDatabase->Origin(), helper,
+  QuotaManager* quotaManager = QuotaManager::Get();
+  NS_ASSERTION(quotaManager, "This should never be null!");
+
+  rv = quotaManager->AcquireExclusiveAccess(
+         mDatabase, mDatabase->Origin(), helper,
          &VersionChangeEventsRunnable::QueueVersionChange<DeleteDatabaseHelper>,
-                                   helper);
+         helper);
   NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
   // The DeleteDatabaseHelper is responsible for dispatching us back to the
   // main thread again and changing the state to eDeleteCompleted.
   mState = eDeletePending;
   return NS_OK;
 }
 
@@ -2035,20 +2075,20 @@ OpenDatabaseHelper::Run()
     NS_ASSERTION(mState == eFiringEvents, "Why are we here?");
 
     if (NS_FAILED(mResultCode)) {
       DispatchErrorEvent();
     } else {
       DispatchSuccessEvent();
     }
 
-    IndexedDatabaseManager* manager = IndexedDatabaseManager::Get();
-    NS_ASSERTION(manager, "This should never be null!");
-
-    manager->AllowNextSynchronizedOp(
+    QuotaManager* quotaManager = QuotaManager::Get();
+    NS_ASSERTION(quotaManager, "This should never be null!");
+
+    quotaManager->AllowNextSynchronizedOp(
                                 OriginOrPatternString::FromOrigin(mASCIIOrigin),
                                 mDatabaseId);
 
     ReleaseMainThreadObjects();
 
     return NS_OK;
   }
 
@@ -2293,22 +2333,31 @@ SetVersionHelper::GetSuccessResult(JSCon
   NS_ASSERTION(mTransaction, "Better have a transaction!");
 
   mOpenRequest->SetTransaction(mTransaction);
 
   return WrapNative(aCx, NS_ISUPPORTS_CAST(nsIDOMEventTarget*, mDatabase),
                     aVal);
 }
 
+nsresult
+SetVersionHelper::OnExclusiveAccessAcquired()
+{
+  nsresult rv = DispatchToTransactionPool();
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  return NS_OK;
+}
+
 // static
 template <class T>
 void
 VersionChangeEventsRunnable::QueueVersionChange(
-                                  nsTArray<nsRefPtr<IDBDatabase> >& aDatabases,
-                                  void* aClosure)
+                             nsTArray<nsCOMPtr<nsIOfflineStorage> >& aDatabases,
+                             void* aClosure)
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
   NS_ASSERTION(!aDatabases.IsEmpty(), "Why are we here?");
 
   T* closure = static_cast<T*>(aClosure);
 
   nsRefPtr<VersionChangeEventsRunnable> eventsRunnable =
     new VersionChangeEventsRunnable(closure->mOpenHelper->Database(),
@@ -2361,33 +2410,38 @@ SetVersionHelper::NotifyTransactionPostC
   mOpenRequest->SetTransaction(nullptr);
   mOpenRequest = nullptr;
 
   mOpenHelper = nullptr;
 
   return rv;
 }
 
+NS_IMPL_ISUPPORTS_INHERITED0(DeleteDatabaseHelper, AsyncConnectionHelper);
+
 nsresult
 DeleteDatabaseHelper::DoDatabaseWork(mozIStorageConnection* aConnection)
 {
   NS_ASSERTION(!aConnection, "How did we get a connection here?");
 
-  const FactoryPrivilege& privilege = mOpenHelper->Privilege();
-
-  IndexedDatabaseManager* mgr = IndexedDatabaseManager::Get();
-  NS_ASSERTION(mgr, "This should never fail!");
+  const StoragePrivilege& privilege = mOpenHelper->Privilege();
+
+  QuotaManager* quotaManager = QuotaManager::Get();
+  NS_ASSERTION(quotaManager, "This should never fail!");
 
   nsCOMPtr<nsIFile> directory;
-  nsresult rv = mgr->GetDirectoryForOrigin(mASCIIOrigin,
-                                           getter_AddRefs(directory));
+  nsresult rv = quotaManager->GetDirectoryForOrigin(mASCIIOrigin,
+                                                    getter_AddRefs(directory));
   NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
   NS_ASSERTION(directory, "What?");
 
+  rv = directory->Append(NS_LITERAL_STRING(IDB_DIRECTORY_NAME));
+  NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
+
   nsAutoString filename;
   rv = GetDatabaseFilename(mName, filename);
   NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
   nsCOMPtr<nsIFile> dbFile;
   rv = directory->Clone(getter_AddRefs(dbFile));
   NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
@@ -2456,15 +2510,27 @@ DeleteDatabaseHelper::DoDatabaseWork(moz
 
 nsresult
 DeleteDatabaseHelper::GetSuccessResult(JSContext* aCx, jsval* aVal)
 {
   return NS_OK;
 }
 
 nsresult
+DeleteDatabaseHelper::OnExclusiveAccessAcquired()
+{
+  QuotaManager* quotaManager = QuotaManager::Get();
+  NS_ASSERTION(quotaManager, "We should definitely have a manager here");
+
+  nsresult rv = Dispatch(quotaManager->IOThread());
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  return NS_OK;
+}
+
+nsresult
 DeleteDatabaseHelper::Init()
 {
   // Note that there's no need to block the database here, since the page
   // never gets to touch it, and all other databases must be closed.
 
   return NS_OK;
 }
--- a/dom/indexedDB/OpenDatabaseHelper.h
+++ b/dom/indexedDB/OpenDatabaseHelper.h
@@ -19,24 +19,26 @@ namespace dom {
 class ContentParent;
 }
 }
 
 BEGIN_INDEXEDDB_NAMESPACE
 
 class OpenDatabaseHelper : public HelperBase
 {
+  typedef mozilla::dom::quota::StoragePrivilege StoragePrivilege;
+
 public:
   OpenDatabaseHelper(IDBOpenDBRequest* aRequest,
                      const nsAString& aName,
                      const nsACString& aASCIIOrigin,
                      uint64_t aRequestedVersion,
                      bool aForDeletion,
                      mozilla::dom::ContentParent* aContentParent,
-                     FactoryPrivilege aPrivilege)
+                     StoragePrivilege aPrivilege)
     : HelperBase(aRequest), mOpenDBRequest(aRequest), mName(aName),
       mASCIIOrigin(aASCIIOrigin), mRequestedVersion(aRequestedVersion),
       mForDeletion(aForDeletion), mPrivilege(aPrivilege), mDatabaseId(nullptr),
       mContentParent(aContentParent), mCurrentVersion(0), mLastObjectStoreId(0),
       mLastIndexId(0), mState(eCreated), mResultCode(NS_OK),
       mLoadDBMetadata(false)
   {
     NS_ASSERTION(!aForDeletion || !aRequestedVersion,
@@ -72,17 +74,17 @@ public:
   }
 
   IDBDatabase* Database() const
   {
     NS_ASSERTION(mDatabase, "Calling at the wrong time!");
     return mDatabase;
   }
 
-  const FactoryPrivilege& Privilege() const
+  const StoragePrivilege& Privilege() const
   {
     return mPrivilege;
   }
 
   static
   nsresult CreateDatabaseConnection(nsIFile* aDBFile,
                                     nsIFile* aFMDirectory,
                                     const nsAString& aName,
@@ -104,17 +106,17 @@ protected:
   nsresult DoDatabaseWork();
 
   // In-params.
   nsRefPtr<IDBOpenDBRequest> mOpenDBRequest;
   nsString mName;
   nsCString mASCIIOrigin;
   uint64_t mRequestedVersion;
   bool mForDeletion;
-  FactoryPrivilege mPrivilege;
+  StoragePrivilege mPrivilege;
   nsCOMPtr<nsIAtom> mDatabaseId;
   mozilla::dom::ContentParent* mContentParent;
 
   // Out-params.
   nsTArray<nsRefPtr<ObjectStoreInfo> > mObjectStores;
   uint64_t mCurrentVersion;
   nsString mDatabaseFilePath;
   int64_t mLastObjectStoreId;
--- a/dom/indexedDB/TransactionThreadPool.cpp
+++ b/dom/indexedDB/TransactionThreadPool.cpp
@@ -274,18 +274,17 @@ TransactionThreadPool::GetQueueForTransa
   DatabaseTransactionInfo::TransactionHashtable& transactionsInProgress =
     dbTransactionInfo->transactions;
   TransactionInfo* info = transactionsInProgress.Get(aTransaction);
   if (info) {
     // We recognize this one.
     return *info->queue;
   }
 
-  TransactionInfo* transactionInfo = new TransactionInfo(aTransaction,
-                                                         objectStoreNames);
+  TransactionInfo* transactionInfo = new TransactionInfo(aTransaction);
 
   dbTransactionInfo->transactions.Put(aTransaction, transactionInfo);;
 
   for (uint32_t index = 0, count = objectStoreNames.Length(); index < count;
        index++) {
     TransactionInfoPair* blockInfo =
       dbTransactionInfo->blockingTransactions.Get(objectStoreNames[index]);
     if (!blockInfo) {
@@ -347,41 +346,33 @@ TransactionThreadPool::Dispatch(IDBTrans
 
   queue.Dispatch(aRunnable);
   if (aFinish) {
     queue.Finish(aFinishRunnable);
   }
   return NS_OK;
 }
 
-bool
-TransactionThreadPool::WaitForAllDatabasesToComplete(
-                                            nsTArray<IDBDatabase*>& aDatabases,
-                                            nsIRunnable* aCallback)
+void
+TransactionThreadPool::WaitForDatabasesToComplete(
+                                       nsTArray<IDBDatabase*>& aDatabases,
+                                       nsIRunnable* aCallback)
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
   NS_ASSERTION(!aDatabases.IsEmpty(), "No databases to wait on!");
   NS_ASSERTION(aCallback, "Null pointer!");
 
   DatabasesCompleteCallback* callback = mCompleteCallbacks.AppendElement();
-  if (!callback) {
-    NS_WARNING("Out of memory!");
-    return false;
-  }
 
   callback->mCallback = aCallback;
-  if (!callback->mDatabases.SwapElements(aDatabases)) {
-    NS_ERROR("This should never fail!");
-  }
+  callback->mDatabases.SwapElements(aDatabases);
 
   if (MaybeFireCallback(*callback)) {
     mCompleteCallbacks.RemoveElementAt(mCompleteCallbacks.Length() - 1);
   }
-
-  return true;
 }
 
 // static
 PLDHashOperator
 TransactionThreadPool::CollectTransactions(IDBTransaction* aKey,
                                            TransactionInfo* aValue,
                                            void* aUserArg)
 {
@@ -425,22 +416,22 @@ TransactionThreadPool::AbortTransactions
     // being comitted. That is expected and fine, so we ignore any returned
     // errors.
     transactions[index]->Abort();
   }
 }
 
 struct NS_STACK_CLASS TransactionSearchInfo
 {
-  TransactionSearchInfo(IDBDatabase* aDatabase)
+  TransactionSearchInfo(nsIOfflineStorage* aDatabase)
     : db(aDatabase), found(false)
   {
   }
 
-  IDBDatabase* db;
+  nsIOfflineStorage* db;
   bool found;
 };
 
 // static
 PLDHashOperator
 TransactionThreadPool::FindTransaction(IDBTransaction* aKey,
                                        TransactionInfo* aValue,
                                        void* aUserArg)
--- a/dom/indexedDB/TransactionThreadPool.h
+++ b/dom/indexedDB/TransactionThreadPool.h
@@ -11,17 +11,16 @@
 #include "IndexedDatabase.h"
 
 #include "nsIObserver.h"
 #include "nsIRunnable.h"
 
 #include "mozilla/Monitor.h"
 #include "nsClassHashtable.h"
 #include "nsHashKeys.h"
-#include "nsRefPtrHashtable.h"
 
 #include "IDBTransaction.h"
 
 class nsIThreadPool;
 
 BEGIN_INDEXEDDB_NAMESPACE
 
 class FinishTransactionRunnable;
@@ -41,18 +40,18 @@ public:
 
   static void Shutdown();
 
   nsresult Dispatch(IDBTransaction* aTransaction,
                     nsIRunnable* aRunnable,
                     bool aFinish,
                     nsIRunnable* aFinishRunnable);
 
-  bool WaitForAllDatabasesToComplete(nsTArray<IDBDatabase*>& aDatabases,
-                                     nsIRunnable* aCallback);
+  void WaitForDatabasesToComplete(nsTArray<IDBDatabase*>& aDatabases,
+                                  nsIRunnable* aCallback);
 
   // Abort all transactions, unless they are already in the process of being
   // committed, for aDatabase.
   void AbortTransactionsForDatabase(IDBDatabase* aDatabase);
 
   // Returns true if there are running or pending transactions for aDatabase.
   bool HasTransactionsForDatabase(IDBDatabase* aDatabase);
 
@@ -78,37 +77,34 @@ protected:
     nsCOMPtr<nsIRunnable> mFinishRunnable;
     bool mShouldFinish;
   };
 
   friend class TransactionQueue;
 
   struct TransactionInfo
   {
-    TransactionInfo(IDBTransaction* aTransaction,
-                    const nsTArray<nsString>& aObjectStoreNames)
+    TransactionInfo(IDBTransaction* aTransaction)
     {
       MOZ_COUNT_CTOR(TransactionInfo);
 
       blockedOn.Init();
       blocking.Init();
 
       transaction = aTransaction;
       queue = new TransactionQueue(aTransaction);
-      objectStoreNames.AppendElements(aObjectStoreNames);
     }
 
     ~TransactionInfo()
     {
       MOZ_COUNT_DTOR(TransactionInfo);
     }
 
     nsRefPtr<IDBTransaction> transaction;
     nsRefPtr<TransactionQueue> queue;
-    nsTArray<nsString> objectStoreNames;
     nsTHashtable<nsPtrHashKey<TransactionInfo> > blockedOn;
     nsTHashtable<nsPtrHashKey<TransactionInfo> > blocking;
   };
 
   struct TransactionInfoPair
   {
     TransactionInfoPair()
       : lastBlockingReads(nullptr)
--- a/dom/indexedDB/ipc/IndexedDBChild.cpp
+++ b/dom/indexedDB/ipc/IndexedDBChild.cpp
@@ -6,29 +6,30 @@
 
 #include "IndexedDBChild.h"
 
 #include "nsIAtom.h"
 #include "nsIInputStream.h"
 
 #include "mozilla/Assertions.h"
 #include "mozilla/dom/ContentChild.h"
+#include "mozilla/dom/quota/QuotaManager.h"
 
 #include "AsyncConnectionHelper.h"
 #include "DatabaseInfo.h"
 #include "IDBEvents.h"
 #include "IDBFactory.h"
 #include "IDBIndex.h"
 #include "IDBObjectStore.h"
 #include "IDBTransaction.h"
-#include "IndexedDatabaseManager.h"
 
 USING_INDEXEDDB_NAMESPACE
 
 using namespace mozilla::dom;
+using mozilla::dom::quota::QuotaManager;
 
 namespace {
 
 class IPCOpenDatabaseHelper : public AsyncConnectionHelper
 {
 public:
   IPCOpenDatabaseHelper(IDBDatabase* aDatabase, IDBOpenDBRequest* aRequest)
   : AsyncConnectionHelper(aDatabase, aRequest)
@@ -280,18 +281,17 @@ IndexedDBDatabaseChild::EnsureDatabase(
                            const DatabaseInfoGuts& aDBInfo,
                            const InfallibleTArray<ObjectStoreInfoGuts>& aOSInfo)
 {
   nsCOMPtr<nsIAtom> databaseId;
   if (mDatabase) {
     databaseId = mDatabase->Id();
   }
   else {
-    databaseId =
-      IndexedDatabaseManager::GetDatabaseId(aDBInfo.origin, aDBInfo.name);
+    databaseId = QuotaManager::GetStorageId(aDBInfo.origin, aDBInfo.name);
   }
   NS_ENSURE_TRUE(databaseId, false);
 
   nsRefPtr<DatabaseInfo> dbInfo;
   if (DatabaseInfo::Get(databaseId, getter_AddRefs(dbInfo))) {
     dbInfo->version = aDBInfo.version;
   }
   else {
--- a/dom/indexedDB/nsIIndexedDatabaseManager.idl
+++ b/dom/indexedDB/nsIIndexedDatabaseManager.idl
@@ -1,74 +1,20 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsISupports.idl"
 
-interface nsIURI;
-
-[scriptable, function, uuid(38f15cc7-2df0-4a90-8b7f-1606b2243522)]
-interface nsIIndexedDatabaseUsageCallback : nsISupports
-{
-  void onUsageResult(in nsIURI aURI,
-                     in unsigned long long aUsage,
-                     in unsigned long long aFileUsage,
-                     in unsigned long aAppId,
-                     in boolean aInMozBrowserOnly);
-};
-
-[scriptable, builtinclass, uuid(e5168115-baff-4559-887e-7c0405cc9e63)]
+[scriptable, builtinclass, uuid(538d1085-517e-405a-a0f0-eb575cb0b8e5)]
 interface nsIIndexedDatabaseManager : nsISupports
 {
   /**
-   * Schedules an asynchronous callback that will return the total amount of
-   * disk space being used by databases for the given origin.
-   *
-   * @param aURI
-   *        The URI whose usage is being queried.
-   * @param aCallback
-   *        The callback that will be called when the usage is available.
-   */
-  [optional_argc]
-  void getUsageForURI(in nsIURI aURI,
-                      in nsIIndexedDatabaseUsageCallback aCallback,
-                      [optional] in unsigned long aAppId,
-                      [optional] in boolean aInMozBrowserOnly);
-
-  /**
-   * Cancels an asynchronous usage check initiated by a previous call to
-   * getUsageForURI().
-   *
-   * @param aURI
-   *        The URI whose usage is being queried.
-   * @param aCallback
-   *        The callback that will be called when the usage is available.
-   */
-  [optional_argc]
-  void cancelGetUsageForURI(in nsIURI aURI,
-                            in nsIIndexedDatabaseUsageCallback aCallback,
-                            [optional] in unsigned long aAppId,
-                            [optional] in boolean aInMozBrowserOnly);
-
-  /**
-   * Removes all databases stored for the given URI. The files may not be
-   * deleted immediately depending on prohibitive concurrent operations.
-   *
-   * @param aURI
-   *        The URI whose databases are to be cleared.
-   */
-  [optional_argc]
-  void clearDatabasesForURI(in nsIURI aURI,
-                            [optional] in unsigned long aAppId,
-                            [optional] in boolean aInMozBrowserOnly);
-
-  /**
    * Defines indexedDB and IDBKeyrange with its static functions on 
    * aObject and initializes DOM exception providers if needed.
    *
    * @param aObject
    *        The object, indexedDB and IDBKeyrange should be defined on.
    */
   [implicit_jscontext]
   void initWindowless(in jsval aObject);
--- a/dom/indexedDB/test/bug839193.js
+++ b/dom/indexedDB/test/bug839193.js
@@ -1,25 +1,24 @@
 /* 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/. */
 
-const nsIIndexedDatabaseManager =
-  Components.interfaces.nsIIndexedDatabaseManager;
+const nsIQuotaManager = Components.interfaces.nsIQuotaManager;
 
 let gURI = Components.classes["@mozilla.org/network/io-service;1"].getService(Components.interfaces.nsIIOService).newURI("http://localhost", null, null);
 
-function onIndexedDBUsageCallback(uri, usage, fileUsage) {}
+function onUsageCallback(uri, usage, fileUsage) {}
 
 function onLoad()
 {
-  var dbManager = Components.classes["@mozilla.org/dom/indexeddb/manager;1"]
-                            .getService(nsIIndexedDatabaseManager);
-  dbManager.getUsageForURI(gURI, onIndexedDBUsageCallback);
-  dbManager.cancelGetUsageForURI(gURI, onIndexedDBUsageCallback);
+  var quotaManager = Components.classes["@mozilla.org/dom/quota/manager;1"]
+                               .getService(nsIQuotaManager);
+  var quotaRequest = quotaManager.getUsageForURI(gURI, onUsageCallback);
+  quotaRequest.cancel();
   Components.classes["@mozilla.org/observer-service;1"]
             .getService(Components.interfaces.nsIObserverService)
             .notifyObservers(window, "bug839193-loaded", null);
 }
 
 function onUnload()
 {
   Components.classes["@mozilla.org/observer-service;1"]
--- a/dom/indexedDB/test/file.js
+++ b/dom/indexedDB/test/file.js
@@ -168,27 +168,27 @@ function verifyBlobArray(blobs1, blobs2,
 function grabFileUsageAndContinueHandler(usage, fileUsage)
 {
   testGenerator.send(fileUsage);
 }
 
 function getUsage(usageHandler)
 {
   let comp = SpecialPowers.wrap(Components);
-  let idbManager = comp.classes["@mozilla.org/dom/indexeddb/manager;1"]
-                       .getService(comp.interfaces.nsIIndexedDatabaseManager);
+  let quotaManager = comp.classes["@mozilla.org/dom/quota/manager;1"]
+                         .getService(comp.interfaces.nsIQuotaManager);
 
   let uri = SpecialPowers.getDocumentURIObject(window.document);
   let callback = {
     onUsageResult: function(uri, usage, fileUsage) {
       usageHandler(usage, fileUsage);
     }
   };
 
-  idbManager.getUsageForURI(uri, callback);
+  quotaManager.getUsageForURI(uri, callback);
 }
 
 function scheduleGC()
 {
   SpecialPowers.exactGC(window, continueToNextStep);
 }
 
 function getFileId(file)
--- a/dom/indexedDB/test/helpers.js
+++ b/dom/indexedDB/test/helpers.js
@@ -35,28 +35,28 @@ function clearAllDatabases(callback) {
 
   if (!SpecialPowers.isMainProcess()) {
     runCallback();
     return;
   }
 
   let comp = SpecialPowers.wrap(Components);
 
-  let idbManager =
-    comp.classes["@mozilla.org/dom/indexeddb/manager;1"]
-        .getService(comp.interfaces.nsIIndexedDatabaseManager);
+  let quotaManager =
+    comp.classes["@mozilla.org/dom/quota/manager;1"]
+        .getService(comp.interfaces.nsIQuotaManager);
 
   let uri = SpecialPowers.getDocumentURIObject(document);
 
-  idbManager.clearDatabasesForURI(uri);
-  idbManager.getUsageForURI(uri, function(uri, usage, fileUsage) {
+  quotaManager.clearStoragesForURI(uri);
+  quotaManager.getUsageForURI(uri, function(uri, usage, fileUsage) {
     if (usage) {
       ok(false,
          "getUsageForURI returned non-zero usage after clearing all " +
-         "databases!");
+         "storages!");
     }
     runCallback();
   });
 }
 
 if (!window.runTest) {
   window.runTest = function(limitedQuota)
   {
new file mode 100644
--- /dev/null
+++ b/dom/quota/AcquireListener.h
@@ -0,0 +1,29 @@
+/* -*- 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/. */
+
+#ifndef mozilla_dom_quota_acquirelistener_h__
+#define mozilla_dom_quota_acquirelistener_h__
+
+#include "mozilla/dom/quota/QuotaCommon.h"
+
+BEGIN_QUOTA_NAMESPACE
+
+class AcquireListener
+{
+public:
+  NS_IMETHOD_(nsrefcnt)
+  AddRef() = 0;
+
+  NS_IMETHOD_(nsrefcnt)
+  Release() = 0;
+
+  virtual nsresult
+  OnExclusiveAccessAcquired() = 0;
+};
+
+END_QUOTA_NAMESPACE
+
+#endif // mozilla_dom_quota_acquirelistener_h__
new file mode 100644
--- /dev/null
+++ b/dom/quota/ArrayCluster.h
@@ -0,0 +1,106 @@
+/* -*- 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/. */
+
+#ifndef mozilla_dom_quota_arraycluster_h__
+#define mozilla_dom_quota_arraycluster_h__
+
+#include "mozilla/dom/quota/QuotaCommon.h"
+
+#include "Client.h"
+
+BEGIN_QUOTA_NAMESPACE
+
+template <class ValueType, uint32_t Length = Client::TYPE_MAX>
+class ArrayCluster
+{
+public:
+  ArrayCluster()
+  {
+    mArrays.AppendElements(Length);
+  }
+
+  nsTArray<ValueType>&
+  ArrayAt(uint32_t aIndex)
+  {
+    MOZ_ASSERT(aIndex < Length, "Bad index!");
+    return mArrays[aIndex];
+  }
+
+  nsTArray<ValueType>&
+  operator[](uint32_t aIndex)
+  {
+    return ArrayAt(aIndex);
+  }
+
+  bool
+  IsEmpty()
+  {
+    for (uint32_t index = 0; index < Length; index++) {
+      if (!mArrays[index].IsEmpty()) {
+        return false;
+      }
+    }
+    return true;
+  }
+
+  template <class T>
+  void
+  AppendElementsTo(uint32_t aIndex, nsTArray<T>& aArray)
+  {
+    NS_ASSERTION(aIndex < Length, "Bad index!");
+    aArray.AppendElements(mArrays[aIndex]);
+  }
+
+  template <class T>
+  void
+  AppendElementsTo(uint32_t aIndex, ArrayCluster<T, Length>& aArrayCluster)
+  {
+    NS_ASSERTION(aIndex < Length, "Bad index!");
+    aArrayCluster[aIndex].AppendElements(mArrays[aIndex]);
+  }
+
+  template <class T>
+  void
+  AppendElementsTo(nsTArray<T>& aArray)
+  {
+    for (uint32_t index = 0; index < Length; index++) {
+      aArray.AppendElements(mArrays[index]);
+    }
+  }
+
+  template<class T>
+  void
+  AppendElementsTo(ArrayCluster<T, Length>& aArrayCluster)
+  {
+    for (uint32_t index = 0; index < Length; index++) {
+      aArrayCluster[index].AppendElements(mArrays[index]);
+    }
+  }
+
+  template<class T>
+  void
+  SwapElements(ArrayCluster<T, Length>& aArrayCluster)
+  {
+    for (uint32_t index = 0; index < Length; index++) {
+      mArrays[index].SwapElements(aArrayCluster.mArrays[index]);
+    }
+  }
+
+  void
+  Clear()
+  {
+    for (uint32_t index = 0; index < Length; index++) {
+      mArrays[index].Clear();
+    }
+  }
+
+private:
+  nsAutoTArray<nsTArray<ValueType>, Length> mArrays;
+};
+
+END_QUOTA_NAMESPACE
+
+#endif // mozilla_dom_quota_arraycluster_h__
--- a/dom/quota/CheckQuotaHelper.cpp
+++ b/dom/quota/CheckQuotaHelper.cpp
@@ -9,32 +9,31 @@
 #include "nsIDOMWindow.h"
 #include "nsIObserverService.h"
 #include "nsIPermissionManager.h"
 #include "nsIPrincipal.h"
 #include "nsIScriptObjectPrincipal.h"
 #include "nsIURI.h"
 #include "nsPIDOMWindow.h"
 
-#include "mozilla/dom/indexedDB/IndexedDatabaseManager.h"
+#include "mozilla/dom/quota/QuotaManager.h"
 #include "mozilla/Services.h"
 #include "nsContentUtils.h"
 #include "nsNetUtil.h"
 #include "nsThreadUtils.h"
 #include "nsXULAppAPI.h"
 
 #define PERMISSION_INDEXEDDB_UNLIMITED "indexedDB-unlimited"
 
 #define TOPIC_QUOTA_PROMPT "indexedDB-quota-prompt"
 #define TOPIC_QUOTA_RESPONSE "indexedDB-quota-response"
 #define TOPIC_QUOTA_CANCEL "indexedDB-quota-cancel"
 
 USING_QUOTA_NAMESPACE
 using namespace mozilla::services;
-using mozilla::dom::indexedDB::IndexedDatabaseManager;
 using mozilla::MutexAutoLock;
 
 namespace {
 
 inline
 uint32_t
 GetQuotaPermissions(nsIDOMWindow* aWindow)
 {
@@ -162,17 +161,17 @@ CheckQuotaHelper::Run()
         rv = permissionManager->AddFromPrincipal(sop->GetPrincipal(),
                                                  PERMISSION_INDEXEDDB_UNLIMITED,
                                                  mPromptResult,
                                                  nsIPermissionManager::EXPIRE_NEVER, 0);
         NS_ENSURE_SUCCESS(rv, rv);
       }
     }
     else if (mPromptResult == nsIPermissionManager::UNKNOWN_ACTION) {
-      uint32_t quota = IndexedDatabaseManager::GetIndexedDBQuotaMB();
+      uint32_t quota = QuotaManager::GetStorageQuotaMB();
 
       nsString quotaString;
       quotaString.AppendInt(quota);
 
       nsCOMPtr<nsIObserverService> obs = GetObserverService();
       NS_ENSURE_STATE(obs);
 
       // We have to watch to make sure that the window doesn't go away without
--- a/dom/quota/CheckQuotaHelper.h
+++ b/dom/quota/CheckQuotaHelper.h
@@ -44,9 +44,9 @@ private:
   mozilla::CondVar mCondVar;
   uint32_t mPromptResult;
   bool mWaiting;
   bool mHasPrompted;
 };
 
 END_QUOTA_NAMESPACE
 
-#endif // mozilla_dom_indexeddb_checkquotahelper_h__
+#endif // mozilla_dom_quota_checkquotahelper_h__
new file mode 100644
--- /dev/null
+++ b/dom/quota/Client.h
@@ -0,0 +1,110 @@
+/* -*- 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/. */
+
+#ifndef mozilla_dom_quota_client_h__
+#define mozilla_dom_quota_client_h__
+
+#include "mozilla/dom/quota/QuotaCommon.h"
+
+class nsIOfflineStorage;
+class nsIRunnable;
+
+BEGIN_QUOTA_NAMESPACE
+
+class UsageRunnable;
+
+// An abstract interface for quota manager clients.
+// Each storage API must provide an implementation of this interface in order
+// to participate in centralized quota and storage handling.
+class Client
+{
+public:
+  NS_IMETHOD_(nsrefcnt)
+  AddRef() = 0;
+
+  NS_IMETHOD_(nsrefcnt)
+  Release() = 0;
+
+  enum Type {
+    IDB = 0,
+    //LS,
+    //APPCACHE,
+    TYPE_MAX
+  };
+
+  virtual Type
+  GetType() = 0;
+
+  static nsresult
+  TypeToText(Type aType, nsAString& aText)
+  {
+    switch (aType) {
+      case IDB:
+        aText.AssignLiteral("idb");
+        break;
+
+      case TYPE_MAX:
+      default:
+        NS_NOTREACHED("Bad id value!");
+        return NS_ERROR_UNEXPECTED;
+    }
+
+    return NS_OK;
+  }
+
+  static nsresult
+  TypeFromText(const nsAString& aText, Type& aType)
+  {
+    if (aText.EqualsLiteral("idb")) {
+      aType = IDB;
+    }
+    else {
+      return NS_ERROR_FAILURE;
+    }
+
+    return NS_OK;
+  }
+
+  virtual nsresult
+  InitOrigin(const nsACString& aOrigin, UsageRunnable* aUsageRunnable) = 0;
+
+  virtual nsresult
+  GetUsageForOrigin(const nsACString& aOrigin,
+                    UsageRunnable* aUsageRunnable) = 0;
+
+  virtual bool
+  IsFileServiceUtilized() = 0;
+
+  virtual bool
+  IsTransactionServiceActivated() = 0;
+
+  virtual void
+  WaitForStoragesToComplete(nsTArray<nsIOfflineStorage*>& aStorages,
+                            nsIRunnable* aCallback) = 0;
+
+  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/FileStreams.cpp
+++ b/dom/quota/FileStreams.cpp
@@ -1,16 +1,18 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "FileStreams.h"
 
+#include "QuotaManager.h"
+
 USING_QUOTA_NAMESPACE
 
 template <class FileStreamBase>
 NS_IMETHODIMP
 FileQuotaStream<FileStreamBase>::SetEOF()
 {
   nsresult rv = FileStreamBase::SetEOF();
   NS_ENSURE_SUCCESS(rv, rv);
--- a/dom/quota/FileStreams.h
+++ b/dom/quota/FileStreams.h
@@ -6,17 +6,17 @@
 
 #ifndef mozilla_dom_quota_filestreams_h__
 #define mozilla_dom_quota_filestreams_h__
 
 #include "QuotaCommon.h"
 
 #include "nsFileStreams.h"
 
-#include "QuotaManager.h"
+#include "QuotaObject.h"
 
 BEGIN_QUOTA_NAMESPACE
 
 template <class FileStreamBase>
 class FileQuotaStream : public FileStreamBase
 {
 public:
   // nsFileStreamBase override
--- a/dom/quota/Makefile.in
+++ b/dom/quota/Makefile.in
@@ -7,26 +7,46 @@ topsrcdir        = @top_srcdir@
 srcdir           = @srcdir@
 VPATH            = @srcdir@
 
 include $(DEPTH)/config/autoconf.mk
 
 LIBRARY_NAME     = domquota_s
 LIBXUL_LIBRARY   = 1
 FORCE_STATIC_LIB = 1
+FAIL_ON_WARNINGS := 1
 
 include $(topsrcdir)/dom/dom-config.mk
 
 EXPORTS_NAMESPACES = mozilla/dom/quota
 
 CPPSRCS = \
   CheckQuotaHelper.cpp \
   FileStreams.cpp \
   QuotaManager.cpp \
+  QuotaObject.cpp \
+  $(NULL)
+
+EXPORTS = \
+  nsIOfflineStorage.h \
   $(NULL)
 
 EXPORTS_mozilla/dom/quota = \
+  AcquireListener.h \
+  ArrayCluster.h \
+  Client.h \
   FileStreams.h \
+  OriginOrPatternString.h \
   QuotaCommon.h \
   QuotaManager.h \
+  QuotaObject.h \
+  StoragePrivilege.h \
+  UsageRunnable.h \
+  Utilities.h \
   $(NULL)
 
+LOCAL_INCLUDES = \
+  -I$(topsrcdir)/caps/include \
+  $(NULL)
+
+include $(topsrcdir)/config/config.mk
+include $(topsrcdir)/ipc/chromium/chromium-config.mk
 include $(topsrcdir)/config/rules.mk
new file mode 100644
--- /dev/null
+++ b/dom/quota/OriginOrPatternString.h
@@ -0,0 +1,54 @@
+/* -*- 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/. */
+
+#ifndef mozilla_dom_quota_originorpatternstring_h__
+#define mozilla_dom_quota_originorpatternstring_h__
+
+#include "mozilla/dom/quota/QuotaCommon.h"
+
+BEGIN_QUOTA_NAMESPACE
+
+class OriginOrPatternString : public nsCString
+{
+public:
+  static OriginOrPatternString
+  FromOrigin(const nsACString& aOrigin)
+  {
+    return OriginOrPatternString(aOrigin, true);
+  }
+
+  static OriginOrPatternString
+  FromPattern(const nsACString& aPattern)
+  {
+    return OriginOrPatternString(aPattern, false);
+  }
+
+  bool
+  IsOrigin() const
+  {
+    return mIsOrigin;
+  }
+
+  bool
+  IsPattern() const
+  {
+    return !mIsOrigin;
+  }
+
+private:
+  OriginOrPatternString(const nsACString& aOriginOrPattern, bool aIsOrigin)
+  : nsCString(aOriginOrPattern), mIsOrigin(aIsOrigin)
+  { }
+
+  bool
+  operator==(const OriginOrPatternString& aOther) MOZ_DELETE;
+
+  bool mIsOrigin;
+};
+
+END_QUOTA_NAMESPACE
+
+#endif // mozilla_dom_quota_originorpatternstring_h__
--- a/dom/quota/QuotaManager.cpp
+++ b/dom/quota/QuotaManager.cpp
@@ -1,254 +1,487 @@
 /* -*- 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 "QuotaManager.h"
 
+#include "mozIApplicationClearPrivateDataParams.h"
+#include "nsIAtom.h"
 #include "nsIFile.h"
+#include "nsIObserverService.h"
+#include "nsIOfflineStorage.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 "mozilla/ClearOnShutdown.h"
+#include <algorithm>
+#include "mozilla/dom/file/FileService.h"
+#include "mozilla/dom/indexedDB/Client.h"
+#include "mozilla/LazyIdleThread.h"
+#include "mozilla/Preferences.h"
+#include "mozilla/Services.h"
+#include "nsAppDirectoryServiceDefs.h"
 #include "nsComponentManagerUtils.h"
+#include "nsContentUtils.h"
+#include "nsCRTGlue.h"
+#include "nsDirectoryServiceUtils.h"
+#include "nsScriptSecurityManager.h"
+#include "nsThreadUtils.h"
+#include "nsXULAppAPI.h"
 #include "xpcpublic.h"
 
+#include "AcquireListener.h"
 #include "CheckQuotaHelper.h"
+#include "OriginOrPatternString.h"
+#include "QuotaObject.h"
+#include "StorageMatcher.h"
+#include "UsageRunnable.h"
+#include "Utilities.h"
+
+// 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
+
+// Amount of space that storages may use by default in megabytes.
+#define DEFAULT_QUOTA_MB 50
+
+// Preference that users can set to override DEFAULT_QUOTA_MB
+#define PREF_STORAGE_QUOTA "dom.indexedDB.warningQuota"
+
+// profile-before-change, when we need to shut down quota manager
+#define PROFILE_BEFORE_CHANGE_OBSERVER_ID "profile-before-change"
+
+#define METADATA_FILE_NAME ".metadata"
 
 USING_QUOTA_NAMESPACE
+using namespace mozilla::dom;
+using mozilla::dom::file::FileService;
+
+BEGIN_QUOTA_NAMESPACE
+
+// A struct that contains the information corresponding to a pending or
+// running operation that requires synchronization (e.g. opening a db,
+// clearing dbs for an origin, etc).
+struct SynchronizedOp
+{
+  SynchronizedOp(const OriginOrPatternString& aOriginOrPattern,
+                 nsISupports* aId);
+
+  ~SynchronizedOp();
+
+  // Test whether this SynchronizedOp needs to wait for the given op.
+  bool
+  MustWaitFor(const SynchronizedOp& aOp);
+
+  void
+  DelayRunnable(nsIRunnable* aRunnable);
+
+  void
+  DispatchDelayedRunnables();
+
+  const OriginOrPatternString mOriginOrPattern;
+  nsCOMPtr<nsISupports> mId;
+  nsRefPtr<AcquireListener> mListener;
+  nsTArray<nsCOMPtr<nsIRunnable> > mDelayedRunnables;
+  ArrayCluster<nsIOfflineStorage*> mStorages;
+};
+
+// Responsible for clearing the storage files for a particular origin on the
+// IO thread. Created when nsIQuotaManager::ClearStoragesForURI is called.
+// Runs three times, first on the main thread, next on the IO thread, and then
+// finally again on the main thread. While on the IO thread the runnable will
+// actually remove the origin's storage files and the directory that contains
+// them before dispatching itself back to the main thread. When back on the main
+// thread the runnable will notify the QuotaManager that the job has been
+// completed.
+class OriginClearRunnable MOZ_FINAL : public nsIRunnable,
+                                      public AcquireListener
+{
+  enum CallbackState {
+    // Not yet run.
+    Pending = 0,
+
+    // Running on the main thread in the callback for OpenAllowed.
+    OpenAllowed,
+
+    // Running on the IO thread.
+    IO,
+
+    // Running on the main thread after all work is done.
+    Complete
+  };
+
+public:
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIRUNNABLE
+
+  // AcquireListener override
+  virtual nsresult
+  OnExclusiveAccessAcquired() MOZ_OVERRIDE;
+
+  OriginClearRunnable(const OriginOrPatternString& aOriginOrPattern)
+  : mOriginOrPattern(aOriginOrPattern),
+    mCallbackState(Pending)
+  { }
+
+  void
+  AdvanceState()
+  {
+    switch (mCallbackState) {
+      case Pending:
+        mCallbackState = OpenAllowed;
+        return;
+      case OpenAllowed:
+        mCallbackState = IO;
+        return;
+      case IO:
+        mCallbackState = Complete;
+        return;
+      default:
+        NS_NOTREACHED("Can't advance past Complete!");
+    }
+  }
+
+  static void
+  InvalidateOpenedStorages(nsTArray<nsCOMPtr<nsIOfflineStorage> >& aStorages,
+                           void* aClosure);
+
+  void
+  DeleteFiles(QuotaManager* aQuotaManager);
+
+private:
+  OriginOrPatternString mOriginOrPattern;
+  CallbackState mCallbackState;
+};
+
+// Responsible for calculating the amount of space taken up by storages of a
+// certain origin. Created when nsIQuotaManager::GetUsageForURI is called.
+// May be canceled with nsIQuotaRequest::Cancel. Runs three times, first
+// on the main thread, next on the IO thread, and then finally again on the main
+// thread. While on the IO thread the runnable will calculate the size of all
+// files in the origin's directory before dispatching itself back to the main
+// thread. When on the main thread the runnable will call the callback and then
+// notify the QuotaManager that the job has been completed.
+class AsyncUsageRunnable MOZ_FINAL : public UsageRunnable,
+                                     public nsIRunnable,
+                                     public nsIQuotaRequest
+{
+  enum CallbackState {
+    // Not yet run.
+    Pending = 0,
+
+    // Running on the main thread in the callback for OpenAllowed.
+    OpenAllowed,
+
+    // Running on the IO thread.
+    IO,
+
+    // Running on the main thread after all work is done.
+    Complete,
+
+    // Running on the main thread after skipping the work
+    Shortcut
+  };
+
+public:
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIRUNNABLE
+  NS_DECL_NSIQUOTAREQUEST
+
+  AsyncUsageRunnable(uint32_t aAppId,
+                     bool aInMozBrowserOnly,
+                     const OriginOrPatternString& aOrigin,
+                     nsIURI* aURI,
+                     nsIUsageCallback* aCallback);
+
+  void
+  AdvanceState()
+  {
+    switch (mCallbackState) {
+      case Pending:
+        mCallbackState = OpenAllowed;
+        return;
+      case OpenAllowed:
+        mCallbackState = IO;
+        return;
+      case IO:
+        mCallbackState = Complete;
+        return;
+      default:
+        NS_NOTREACHED("Can't advance past Complete!");
+    }
+  }
+
+  nsresult
+  TakeShortcut();
+
+  // Run calls the RunInternal method and makes sure that we always dispatch
+  // to the main thread in case of an error.
+  inline nsresult
+  RunInternal();
+
+  nsCOMPtr<nsIURI> mURI;
+  nsCOMPtr<nsIUsageCallback> mCallback;
+  uint32_t mAppId;
+  OriginOrPatternString mOrigin;
+  CallbackState mCallbackState;
+  bool mInMozBrowserOnly;
+};
+
+END_QUOTA_NAMESPACE
 
 namespace {
 
-nsAutoPtr<QuotaManager> gInstance;
+QuotaManager* gInstance = nullptr;
+int32_t gShutdown = 0;
+
+int32_t gStorageQuotaMB = DEFAULT_QUOTA_MB;
+
+// A callback runnable used by the TransactionPool when it's safe to proceed
+// with a SetVersion/DeleteDatabase/etc.
+class WaitForTransactionsToFinishRunnable MOZ_FINAL : public nsIRunnable
+{
+public:
+  WaitForTransactionsToFinishRunnable(SynchronizedOp* aOp)
+  : mOp(aOp), mCountdown(1)
+  {
+    NS_ASSERTION(mOp, "Why don't we have a runnable?");
+    NS_ASSERTION(mOp->mStorages.IsEmpty(), "We're here too early!");
+    NS_ASSERTION(mOp->mListener,
+                 "What are we supposed to do when we're done?");
+    NS_ASSERTION(mCountdown, "Wrong countdown!");
+  }
+
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIRUNNABLE
+
+  void
+  AddRun()
+  {
+    mCountdown++;
+  }
+
+private:
+  // The QuotaManager holds this alive.
+  SynchronizedOp* mOp;
+  uint32_t mCountdown;
+};
+
+class WaitForLockedFilesToFinishRunnable MOZ_FINAL : public nsIRunnable
+{
+public:
+  WaitForLockedFilesToFinishRunnable()
+  : mBusy(true)
+  { }
+
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIRUNNABLE
+
+  bool
+  IsBusy() const
+  {
+    return mBusy;
+  }
+
+private:
+  bool mBusy;
+};
+
+bool
+IsMainProcess()
+{
+  return XRE_GetProcessType() == GeckoProcessType_Default;
+}
+
+void
+SanitizeOriginString(nsCString& aOrigin)
+{
+  // 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.
+  static const char kReplaceChars[] = CONTROL_CHARACTERS "/:*?\"<>|\\";
+
+#ifdef XP_WIN
+  NS_ASSERTION(!strcmp(kReplaceChars,
+                       FILE_ILLEGAL_CHARACTERS FILE_PATH_SEPARATOR),
+               "Illegal file characters have changed!");
+#endif
+
+  aOrigin.ReplaceChar(kReplaceChars, '+');
+}
 
 PLDHashOperator
 RemoveQuotaForPatternCallback(const nsACString& aKey,
                               nsRefPtr<OriginInfo>& aValue,
                               void* aUserArg)
 {
   NS_ASSERTION(!aKey.IsEmpty(), "Empty key!");
   NS_ASSERTION(aValue, "Null pointer!");
   NS_ASSERTION(aUserArg, "Null pointer!");
 
   const nsACString* pattern =
     static_cast<const nsACString*>(aUserArg);
 
-  if (StringBeginsWith(aKey, *pattern)) {
+  if (PatternMatchesOrigin(*pattern, aKey)) {
     return PL_DHASH_REMOVE;
   }
 
   return PL_DHASH_NEXT;
 }
 
 } // anonymous namespace
 
-void
-QuotaObject::AddRef()
-{
-  QuotaManager* quotaManager = QuotaManager::Get();
-  if (!quotaManager) {
-    NS_ERROR("Null quota manager, this shouldn't happen, possible leak!");
-
-    NS_AtomicIncrementRefcnt(mRefCnt);
-
-    return;
-  }
-
-  MutexAutoLock lock(quotaManager->mQuotaMutex);
-
-  ++mRefCnt;
-}
-
-void
-QuotaObject::Release()
-{
-  QuotaManager* quotaManager = QuotaManager::Get();
-  if (!quotaManager) {
-    NS_ERROR("Null quota manager, this shouldn't happen, possible leak!");
-
-    nsrefcnt count = NS_AtomicDecrementRefcnt(mRefCnt);
-    if (count == 0) {
-      mRefCnt = 1;
-      delete this;
-    }
-
-    return;
-  }
-
-  {
-    MutexAutoLock lock(quotaManager->mQuotaMutex);
-
-    --mRefCnt;
-
-    if (mRefCnt > 0) {
-      return;
-    }
-
-    if (mOriginInfo) {
-      mOriginInfo->mQuotaObjects.Remove(mPath);
-    }
-  }
-
-  delete this;
-}
-
-void
-QuotaObject::UpdateSize(int64_t aSize)
-{
-  QuotaManager* quotaManager = QuotaManager::Get();
-  NS_ASSERTION(quotaManager, "Shouldn't be null!");
-
-  MutexAutoLock lock(quotaManager->mQuotaMutex);
-
-  if (mOriginInfo) {
-    mOriginInfo->mUsage -= mSize;
-    mSize = aSize;
-    mOriginInfo->mUsage += mSize;
-  }
-}
-
-bool
-QuotaObject::MaybeAllocateMoreSpace(int64_t aOffset, int32_t aCount)
-{
-  int64_t end = aOffset + aCount;
-
-  QuotaManager* quotaManager = QuotaManager::Get();
-  NS_ASSERTION(quotaManager, "Shouldn't be null!");
-
-  MutexAutoLock lock(quotaManager->mQuotaMutex);
-
-  if (mSize >= end || !mOriginInfo) {
-    return true;
-  }
-
-  int64_t newUsage = mOriginInfo->mUsage - mSize + end;
-  if (newUsage > mOriginInfo->mLimit) {
-    // This will block the thread, but it will also drop the mutex while
-    // waiting. The mutex will be reacquired again when the waiting is finished.
-    if (!quotaManager->LockedQuotaIsLifted()) {
-      return false;
-    }
-
-    // Threads raced, the origin info removal has been done by some other
-    // thread.
-    if (!mOriginInfo) {
-      // The other thread could allocate more space.
-      if (end > mSize) {
-        mSize = end;
-      }
-
-      return true;
-    }
-
-    nsCString origin = mOriginInfo->mOrigin;
-
-    mOriginInfo->LockedClearOriginInfos();
-    NS_ASSERTION(!mOriginInfo,
-                 "Should have cleared in LockedClearOriginInfos!");
-
-    quotaManager->mOriginInfos.Remove(origin);
-
-    // Some other thread could increase the size without blocking (increasing
-    // the origin usage without hitting the limit), but no more than this one.
-    NS_ASSERTION(mSize < end, "This shouldn't happen!");
-
-    mSize = end;
-
-    return true;
-  }
-
-  mOriginInfo->mUsage = newUsage;
-  mSize = end;
-
-  return true;
-}
-
-#ifdef DEBUG
-void
-OriginInfo::LockedClearOriginInfos()
-{
-  QuotaManager* quotaManager = QuotaManager::Get();
-  NS_ASSERTION(quotaManager, "Shouldn't be null!");
-
-  quotaManager->mQuotaMutex.AssertCurrentThreadOwns();
-
-  mQuotaObjects.EnumerateRead(ClearOriginInfoCallback, nullptr);
-}
-#endif
-
-// static
-PLDHashOperator
-OriginInfo::ClearOriginInfoCallback(const nsAString& aKey,
-                                    QuotaObject* aValue,
-                                    void* aUserArg)
-{
-  NS_ASSERTION(!aKey.IsEmpty(), "Empty key!");
-  NS_ASSERTION(aValue, "Null pointer!");
-
-  aValue->mOriginInfo = nullptr;
-
-  return PL_DHASH_NEXT;
-}
-
 QuotaManager::QuotaManager()
 : mCurrentWindowIndex(BAD_TLS_INDEX),
   mQuotaMutex("QuotaManager.mQuotaMutex")
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+  NS_ASSERTION(!gInstance, "More than one instance!");
 }
 
 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!");
+
+  if (IsShuttingDown()) {
+    NS_ERROR("Calling GetOrCreate() after shutdown!");
+    return nullptr;
+  }
+
   if (!gInstance) {
-    NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
-
-    nsAutoPtr<QuotaManager> instance(new QuotaManager());
+    nsRefPtr<QuotaManager> instance(new QuotaManager());
 
-    NS_ENSURE_TRUE(instance->Init(), nullptr);
+    nsresult rv = instance->Init();
+    NS_ENSURE_SUCCESS(rv, nullptr);
+
+    nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
+    NS_ENSURE_TRUE(obs, nullptr);
 
-    gInstance = instance.forget();
+    // 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);
 
-    ClearOnShutdown(&gInstance);
+    // The observer service will hold our last reference, don't AddRef here.
+    gInstance = instance;
   }
 
   return gInstance;
 }
 
 // static
 QuotaManager*
 QuotaManager::Get()
 {
   // 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;
+}
+
+nsresult
 QuotaManager::Init()
 {
   // We need a thread-local to hold the current window.
   NS_ASSERTION(mCurrentWindowIndex == BAD_TLS_INDEX, "Huh?");
 
   if (PR_NewThreadPrivateIndex(&mCurrentWindowIndex, nullptr) != PR_SUCCESS) {
     NS_ERROR("PR_NewThreadPrivateIndex failed, QuotaManager disabled");
     mCurrentWindowIndex = BAD_TLS_INDEX;
-    return false;
+    return NS_ERROR_FAILURE;
+  }
+
+  nsresult rv;
+  if (IsMainProcess()) {
+    nsCOMPtr<nsIFile> dbBaseDirectory;
+    rv = NS_GetSpecialDirectory(NS_APP_INDEXEDDB_PARENT_DIR,
+                                getter_AddRefs(dbBaseDirectory));
+    if (NS_FAILED(rv)) {
+      rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR,
+                                  getter_AddRefs(dbBaseDirectory));
+    }
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    rv = dbBaseDirectory->Append(NS_LITERAL_STRING("indexedDB"));
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    rv = dbBaseDirectory->GetPath(mStorageBasePath);
+    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(&gStorageQuotaMB,
+                                            PREF_STORAGE_QUOTA,
+                                            DEFAULT_QUOTA_MB))) {
+    NS_WARNING("Unable to respond to quota pref changes!");
+    gStorageQuotaMB = DEFAULT_QUOTA_MB;
   }
 
   mOriginInfos.Init();
   mCheckQuotaHelpers.Init();
+  mLiveStorages.Init();
 
-  return true;
+  MOZ_STATIC_ASSERT(Client::IDB == 0 && Client::TYPE_MAX == 1,
+                    "Fix the registration!");
+
+  NS_ASSERTION(mClients.Capacity() == Client::TYPE_MAX,
+               "Should be using an auto array with correct capacity!");
+
+  // Register IndexedDB
+  mClients.AppendElement(new indexedDB::Client());
+
+  return NS_OK;
 }
 
 void
 QuotaManager::InitQuotaForOrigin(const nsACString& aOrigin,
                                  int64_t aLimit,
                                  int64_t aUsage)
 {
   OriginInfo* info = new OriginInfo(aOrigin, aLimit * 1024 * 1024, aUsage);
@@ -340,16 +573,722 @@ QuotaManager::GetQuotaObject(const nsACS
   NS_ENSURE_SUCCESS(rv, nullptr);
 
   rv = file->InitWithPath(aPath);
   NS_ENSURE_SUCCESS(rv, nullptr);
 
   return GetQuotaObject(aOrigin, file);
 }
 
+bool
+QuotaManager::RegisterStorage(nsIOfflineStorage* aStorage)
+{
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+  NS_ASSERTION(aStorage, "Null pointer!");
+
+  // Don't allow any new storages to be created after shutdown.
+  if (IsShuttingDown()) {
+    return false;
+  }
+
+  // Add this storage to its origin info if it exists, create it otherwise.
+  const nsACString& origin = aStorage->Origin();
+  ArrayCluster<nsIOfflineStorage*>* cluster;
+  if (!mLiveStorages.Get(origin, &cluster)) {
+    cluster = new ArrayCluster<nsIOfflineStorage*>();
+    mLiveStorages.Put(origin, cluster);
+  }
+  (*cluster)[aStorage->GetClient()->GetType()].AppendElement(aStorage);
+
+  return true;
+}
+
+void
+QuotaManager::UnregisterStorage(nsIOfflineStorage* aStorage)
+{
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+  NS_ASSERTION(aStorage, "Null pointer!");
+
+  // Remove this storage from its origin array, maybe remove the array if it
+  // is then empty.
+  const nsACString& origin = aStorage->Origin();
+  ArrayCluster<nsIOfflineStorage*>* cluster;
+  if (mLiveStorages.Get(origin, &cluster) &&
+      (*cluster)[aStorage->GetClient()->GetType()].RemoveElement(aStorage)) {
+    if (cluster->IsEmpty()) {
+      mLiveStorages.Remove(origin);
+    }
+    return;
+  }
+  NS_ERROR("Didn't know anything about this storage!");
+}
+
+void
+QuotaManager::OnStorageClosed(nsIOfflineStorage* aStorage)
+{
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+  NS_ASSERTION(aStorage, "Null pointer!");
+
+  // Check through the list of SynchronizedOps to see if any are waiting for
+  // this storage to close before proceeding.
+  SynchronizedOp* op =
+    FindSynchronizedOp(aStorage->Origin(), aStorage->Id());
+  if (op) {
+    Client::Type clientType = aStorage->GetClient()->GetType();
+
+    // This storage is in the scope of this SynchronizedOp.  Remove it
+    // from the list if necessary.
+    if (op->mStorages[clientType].RemoveElement(aStorage)) {
+      // Now set up the helper if there are no more live storages.
+      NS_ASSERTION(op->mListener,
+                   "How did we get rid of the listener before removing the "
+                    "last storage?");
+      if (op->mStorages[clientType].IsEmpty()) {
+        // At this point, all storages are closed, so no new transactions
+        // can be started.  There may, however, still be outstanding
+        // transactions that have not completed.  We need to wait for those
+        // before we dispatch the helper.
+        if (NS_FAILED(RunSynchronizedOp(aStorage, op))) {
+          NS_WARNING("Failed to run synchronized op!");
+        }
+      }
+    }
+  }
+}
+
+void
+QuotaManager::AbortCloseStoragesForWindow(nsPIDOMWindow* aWindow)
+{
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+  NS_ASSERTION(aWindow, "Null pointer!");
+
+  FileService* service = FileService::Get();
+
+  StorageMatcher<ArrayCluster<nsIOfflineStorage*> > liveStorages;
+  liveStorages.Find(mLiveStorages);
+
+  for (uint32_t i = 0; i < Client::TYPE_MAX; i++) {
+    nsRefPtr<Client>& client = mClients[i];
+    bool utilized = service && client->IsFileServiceUtilized();
+    bool activated = client->IsTransactionServiceActivated();
+
+    nsTArray<nsIOfflineStorage*>& array = liveStorages[i];
+    for (uint32_t j = 0; j < array.Length(); j++) {
+      nsIOfflineStorage*& storage = array[j];
+
+      if (storage->IsOwned(aWindow)) {
+        if (NS_FAILED(storage->Close())) {
+          NS_WARNING("Failed to close storage for dying window!");
+        }
+
+        if (utilized) {
+          service->AbortLockedFilesForStorage(storage);
+        }
+
+        if (activated) {
+          client->AbortTransactionsForStorage(storage);
+        }
+      }
+    }
+  }
+}
+
+bool
+QuotaManager::HasOpenTransactions(nsPIDOMWindow* aWindow)
+{
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+  NS_ASSERTION(aWindow, "Null pointer!");
+
+  FileService* service = FileService::Get();
+
+  nsAutoPtr<StorageMatcher<ArrayCluster<nsIOfflineStorage*> > > liveStorages;
+
+  for (uint32_t i = 0; i < Client::TYPE_MAX; i++) {
+    nsRefPtr<Client>& client = mClients[i];
+    bool utilized = service && client->IsFileServiceUtilized();
+    bool activated = client->IsTransactionServiceActivated();
+
+    if (utilized || activated) {
+      if (!liveStorages) {
+        liveStorages = new StorageMatcher<ArrayCluster<nsIOfflineStorage*> >();
+        liveStorages->Find(mLiveStorages);
+      }
+
+      nsTArray<nsIOfflineStorage*>& storages = liveStorages->ArrayAt(i);
+      for (uint32_t j = 0; j < storages.Length(); j++) {
+        nsIOfflineStorage*& storage = storages[j];
+
+        if (storage->IsOwned(aWindow) &&
+            ((utilized && service->HasLockedFilesForStorage(storage)) ||
+             (activated && client->HasTransactionsForStorage(storage)))) {
+          return true;
+        }
+      }
+    }
+  }
+
+  return false;
+}
+
+nsresult
+QuotaManager::WaitForOpenAllowed(const OriginOrPatternString& aOriginOrPattern,
+                                 nsIAtom* aId,
+                                 nsIRunnable* aRunnable)
+{
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+  NS_ASSERTION(!aOriginOrPattern.IsEmpty(), "Empty pattern!");
+  NS_ASSERTION(aRunnable, "Null pointer!");
+
+  nsAutoPtr<SynchronizedOp> op(new SynchronizedOp(aOriginOrPattern, aId));
+
+  // See if this runnable needs to wait.
+  bool delayed = false;
+  for (uint32_t index = mSynchronizedOps.Length(); index > 0; index--) {
+    nsAutoPtr<SynchronizedOp>& existingOp = mSynchronizedOps[index - 1];
+    if (op->MustWaitFor(*existingOp)) {
+      existingOp->DelayRunnable(aRunnable);
+      delayed = true;
+      break;
+    }
+  }
+
+  // Otherwise, dispatch it immediately.
+  if (!delayed) {
+    nsresult rv = NS_DispatchToCurrentThread(aRunnable);
+    NS_ENSURE_SUCCESS(rv, rv);
+  }
+
+  // Adding this to the synchronized ops list will block any additional
+  // ops from proceeding until this one is done.
+  mSynchronizedOps.AppendElement(op.forget());
+
+  return NS_OK;
+}
+
+void
+QuotaManager::AllowNextSynchronizedOp(
+                                  const OriginOrPatternString& aOriginOrPattern,
+                                  nsIAtom* aId)
+{
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+  NS_ASSERTION(!aOriginOrPattern.IsEmpty(), "Empty origin/pattern!");
+
+  uint32_t count = mSynchronizedOps.Length();
+  for (uint32_t index = 0; index < count; index++) {
+    nsAutoPtr<SynchronizedOp>& op = mSynchronizedOps[index];
+    if (op->mOriginOrPattern.IsOrigin() == aOriginOrPattern.IsOrigin() &&
+        op->mOriginOrPattern == aOriginOrPattern) {
+      if (op->mId == aId) {
+        NS_ASSERTION(op->mStorages.IsEmpty(), "How did this happen?");
+
+        op->DispatchDelayedRunnables();
+
+        mSynchronizedOps.RemoveElementAt(index);
+        return;
+      }
+
+      // If one or the other is for an origin clear, we should have matched
+      // solely on origin.
+      NS_ASSERTION(op->mId && aId, "Why didn't we match earlier?");
+    }
+  }
+
+  NS_NOTREACHED("Why didn't we find a SynchronizedOp?");
+}
+
+nsresult
+QuotaManager::GetDirectoryForOrigin(const nsACString& aASCIIOrigin,
+                                    nsIFile** aDirectory) const
+{
+  nsresult rv;
+  nsCOMPtr<nsIFile> directory =
+    do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  rv = directory->InitWithPath(mStorageBasePath);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsAutoCString originSanitized(aASCIIOrigin);
+  SanitizeOriginString(originSanitized);
+
+  rv = directory->Append(NS_ConvertASCIItoUTF16(originSanitized));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  directory.forget(aDirectory);
+  return NS_OK;
+}
+
+nsresult
+QuotaManager::EnsureOriginIsInitialized(const nsACString& aOrigin,
+                                        StoragePrivilege aPrivilege,
+                                        nsIFile** aDirectory)
+{
+#ifdef DEBUG
+  {
+    bool correctThread;
+    NS_ASSERTION(NS_SUCCEEDED(mIOThread->IsOnCurrentThread(&correctThread)) &&
+                 correctThread,
+                 "Running on the wrong thread!");
+  }
+#endif
+
+  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);
+
+  if (exists) {
+    bool isDirectory;
+    rv = directory->IsDirectory(&isDirectory);
+    NS_ENSURE_SUCCESS(rv, rv);
+    NS_ENSURE_TRUE(isDirectory, NS_ERROR_UNEXPECTED);
+  }
+  else {
+    rv = directory->Create(nsIFile::DIRECTORY_TYPE, 0755);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    nsCOMPtr<nsIFile> metadataFile;
+    rv = directory->Clone(getter_AddRefs(metadataFile));
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    rv = metadataFile->Append(NS_LITERAL_STRING(METADATA_FILE_NAME));
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    rv = metadataFile->Create(nsIFile::NORMAL_FILE_TYPE, 0644);
+    NS_ENSURE_SUCCESS(rv, rv);
+  }
+
+  if (mInitializedOrigins.Contains(aOrigin)) {
+    NS_ADDREF(*aDirectory = directory);
+    return NS_OK;
+  }
+
+  rv = MaybeUpgradeOriginDirectory(directory);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  // We need to initialize directories of all clients if they exists and also
+  // get the total usage to initialize the quota.
+  nsAutoPtr<UsageRunnable> runnable;
+  if (aPrivilege != Chrome) {
+    runnable = new UsageRunnable();
+  }
+
+  nsCOMPtr<nsISimpleEnumerator> entries;
+  rv = directory->GetDirectoryEntries(getter_AddRefs(entries));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  bool hasMore;
+  while (NS_SUCCEEDED((rv = entries->HasMoreElements(&hasMore))) && hasMore) {
+    nsCOMPtr<nsISupports> entry;
+    rv = entries->GetNext(getter_AddRefs(entry));
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    nsCOMPtr<nsIFile> file = do_QueryInterface(entry);
+    NS_ENSURE_TRUE(file, NS_NOINTERFACE);
+
+    nsString leafName;
+    rv = file->GetLeafName(leafName);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    if (leafName.EqualsLiteral(METADATA_FILE_NAME)) {
+      continue;
+    }
+
+    bool isDirectory;
+    rv = file->IsDirectory(&isDirectory);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    if (!isDirectory) {
+      NS_WARNING("Unknown file found!");
+      return NS_ERROR_UNEXPECTED;
+    }
+
+    Client::Type clientType;
+    rv = Client::TypeFromText(leafName, clientType);
+    if (NS_FAILED(rv)) {
+      NS_WARNING("Unknown directory found!");
+      return NS_ERROR_UNEXPECTED;
+    }
+
+    rv = mClients[clientType]->InitOrigin(aOrigin, runnable);
+    NS_ENSURE_SUCCESS(rv, rv);
+  }
+
+  if (aPrivilege != Chrome) {
+    QuotaManager* quotaManager = QuotaManager::Get();
+    NS_ASSERTION(quotaManager, "Shouldn't be null!");
+
+    quotaManager->InitQuotaForOrigin(aOrigin, GetStorageQuotaMB(),
+                                     runnable->TotalUsage());
+  }
+
+  mInitializedOrigins.AppendElement(aOrigin);
+
+  NS_ADDREF(*aDirectory = directory);
+  return NS_OK;
+}
+
+void
+QuotaManager::UninitializeOriginsByPattern(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--) {
+    if (PatternMatchesOrigin(aPattern, mInitializedOrigins[i])) {
+      mInitializedOrigins.RemoveElementAt(i);
+    }
+  }
+}
+
+already_AddRefed<mozilla::dom::quota::Client>
+QuotaManager::GetClient(Client::Type aClientType)
+{
+  nsRefPtr<Client> client = mClients.SafeElementAt(aClientType);
+  return client.forget();
+}
+
+// static
+uint32_t
+QuotaManager::GetStorageQuotaMB()
+{
+  return uint32_t(std::max(gStorageQuotaMB, 0));
+}
+
+// static
+already_AddRefed<nsIAtom>
+QuotaManager::GetStorageId(const nsACString& aOrigin,
+                           const nsAString& aName)
+{
+  nsCString str(aOrigin);
+  str.Append("*");
+  str.Append(NS_ConvertUTF16toUTF8(aName));
+
+  nsCOMPtr<nsIAtom> atom = do_GetAtom(str);
+  NS_ENSURE_TRUE(atom, nullptr);
+
+  return atom.forget();
+}
+
+// static
+nsresult
+QuotaManager::GetASCIIOriginFromURI(nsIURI* aURI,
+                                    uint32_t aAppId,
+                                    bool aInMozBrowser,
+                                    nsACString& aASCIIOrigin)
+{
+  NS_ASSERTION(aURI, "Null uri!");
+
+  nsCString origin;
+  mozilla::GetExtendedOrigin(aURI, aAppId, aInMozBrowser, origin);
+
+  if (origin.IsEmpty()) {
+    NS_WARNING("GetExtendedOrigin returned empty string!");
+    return NS_ERROR_FAILURE;
+  }
+
+  aASCIIOrigin.Assign(origin);
+  return NS_OK;
+}
+
+// static
+nsresult
+QuotaManager::GetASCIIOriginFromPrincipal(nsIPrincipal* aPrincipal,
+                                          nsACString& aASCIIOrigin)
+{
+  NS_ASSERTION(aPrincipal, "Don't hand me a null principal!");
+
+  static const char kChromeOrigin[] = "chrome";
+
+  nsCString origin;
+  if (nsContentUtils::IsSystemPrincipal(aPrincipal)) {
+    origin.AssignLiteral(kChromeOrigin);
+  }
+  else {
+    bool isNullPrincipal;
+    nsresult rv = aPrincipal->GetIsNullPrincipal(&isNullPrincipal);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    if (isNullPrincipal) {
+      NS_WARNING("IndexedDB not supported from this principal!");
+      return NS_ERROR_FAILURE;
+    }
+
+    rv = aPrincipal->GetExtendedOrigin(origin);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    if (origin.EqualsLiteral(kChromeOrigin)) {
+      NS_WARNING("Non-chrome principal can't use chrome origin!");
+      return NS_ERROR_FAILURE;
+    }
+  }
+
+  aASCIIOrigin.Assign(origin);
+  return NS_OK;
+}
+
+// static
+nsresult
+QuotaManager::GetASCIIOriginFromWindow(nsPIDOMWindow* aWindow,
+                                       nsACString& aASCIIOrigin)
+{
+  NS_ASSERTION(NS_IsMainThread(),
+               "We're about to touch a window off the main thread!");
+
+  if (!aWindow) {
+    aASCIIOrigin.AssignLiteral("chrome");
+    NS_ASSERTION(nsContentUtils::IsCallerChrome(),
+                 "Null window but not chrome!");
+    return NS_OK;
+  }
+
+  nsCOMPtr<nsIScriptObjectPrincipal> sop = do_QueryInterface(aWindow);
+  NS_ENSURE_TRUE(sop, NS_ERROR_FAILURE);
+
+  nsCOMPtr<nsIPrincipal> principal = sop->GetPrincipal();
+  NS_ENSURE_TRUE(principal, NS_ERROR_FAILURE);
+
+  nsresult rv = GetASCIIOriginFromPrincipal(principal, aASCIIOrigin);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  return NS_OK;
+}
+
+NS_IMPL_ISUPPORTS2(QuotaManager, nsIQuotaManager, nsIObserver)
+
+NS_IMETHODIMP
+QuotaManager::GetUsageForURI(nsIURI* aURI,
+                             nsIUsageCallback* aCallback,
+                             uint32_t aAppId,
+                             bool aInMozBrowserOnly,
+                             uint8_t aOptionalArgCount,
+                             nsIQuotaRequest** _retval)
+{
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+
+  NS_ENSURE_ARG_POINTER(aURI);
+  NS_ENSURE_ARG_POINTER(aCallback);
+
+  // This only works from the main process.
+  NS_ENSURE_TRUE(IsMainProcess(), NS_ERROR_NOT_AVAILABLE);
+
+  if (!aOptionalArgCount) {
+    aAppId = nsIScriptSecurityManager::NO_APP_ID;
+  }
+
+  // Figure out which origin we're dealing with.
+  nsCString origin;
+  nsresult rv = GetASCIIOriginFromURI(aURI, aAppId, aInMozBrowserOnly, origin);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  OriginOrPatternString oops = OriginOrPatternString::FromOrigin(origin);
+
+  nsRefPtr<AsyncUsageRunnable> runnable =
+    new AsyncUsageRunnable(aAppId, aInMozBrowserOnly, oops, aURI, aCallback);
+
+  // Put the computation runnable in the queue.
+  rv = WaitForOpenAllowed(oops, nullptr, runnable);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  runnable->AdvanceState();
+
+  runnable.forget(_retval);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+QuotaManager::ClearStoragesForURI(nsIURI* aURI,
+                                  uint32_t aAppId,
+                                  bool aInMozBrowserOnly,
+                                  uint8_t aOptionalArgCount)
+{
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+
+  NS_ENSURE_ARG_POINTER(aURI);
+
+  // This only works from the main process.
+  NS_ENSURE_TRUE(IsMainProcess(), NS_ERROR_NOT_AVAILABLE);
+
+  if (!aOptionalArgCount) {
+    aAppId = nsIScriptSecurityManager::NO_APP_ID;
+  }
+
+  // Figure out which origin we're dealing with.
+  nsCString origin;
+  nsresult rv = GetASCIIOriginFromURI(aURI, aAppId, aInMozBrowserOnly, origin);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsAutoCString pattern;
+  GetOriginPatternString(aAppId, aInMozBrowserOnly, origin, pattern);
+
+  // If there is a pending or running clear operation for this origin, return
+  // immediately.
+  if (IsClearOriginPending(pattern)) {
+    return NS_OK;
+  }
+
+  OriginOrPatternString oops = OriginOrPatternString::FromPattern(pattern);
+
+  // Queue up the origin clear runnable.
+  nsRefPtr<OriginClearRunnable> runnable = new OriginClearRunnable(oops);
+
+  rv = WaitForOpenAllowed(oops, nullptr, runnable);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  runnable->AdvanceState();
+
+  // Give the runnable some help by invalidating any storages in the way.
+  StorageMatcher<nsAutoTArray<nsIOfflineStorage*, 20> > matches;
+  matches.Find(mLiveStorages, pattern);
+
+  for (uint32_t index = 0; index < matches.Length(); index++) {
+    // We need to grab references to any live storages here to prevent them
+    // from dying while we invalidate them.
+    nsCOMPtr<nsIOfflineStorage> storage = matches[index];
+    storage->Invalidate();
+  }
+
+  // After everything has been invalidated the helper should be dispatched to
+  // the end of the event queue.
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+QuotaManager::Observe(nsISupports* aSubject,
+                      const char* aTopic,
+                      const PRUnichar* 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 (PR_ATOMIC_SET(&gShutdown, 1)) {
+      NS_ERROR("Shutdown more than once?!");
+    }
+
+    if (IsMainProcess()) {
+      FileService* service = FileService::Get();
+      if (service) {
+        // This should only wait for storages registered in this manager
+        // to complete. Other storages may still have running locked files.
+        // If the necko service (thread pool) gets the shutdown notification
+        // first then the sync loop won't be processed at all, otherwise it will
+        // lock the main thread until all storages registered in this manager
+        // are finished.
+
+        nsTArray<uint32_t> indexes;
+        for (uint32_t index = 0; index < Client::TYPE_MAX; index++) {
+          if (mClients[index]->IsFileServiceUtilized()) {
+            indexes.AppendElement(index);
+          }
+        }
+
+        StorageMatcher<nsTArray<nsCOMPtr<nsIFileStorage> > > liveStorages;
+        liveStorages.Find(mLiveStorages, &indexes);
+
+        if (!liveStorages.IsEmpty()) {
+          nsRefPtr<WaitForLockedFilesToFinishRunnable> runnable =
+            new WaitForLockedFilesToFinishRunnable();
+
+          service->WaitForStoragesToComplete(liveStorages, runnable);
+
+          nsIThread* thread = NS_GetCurrentThread();
+          while (runnable->IsBusy()) {
+            if (!NS_ProcessNextEvent(thread)) {
+              NS_ERROR("Failed to process next event!");
+              break;
+            }
+          }
+        }
+      }
+
+      // 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))) {
+        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]->ShutdownTransactionService();
+      }
+
+      // 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!");
+
+    // Grab all live storages, for all origins.
+    StorageMatcher<nsAutoTArray<nsIOfflineStorage*, 50> > liveStorages;
+    liveStorages.Find(mLiveStorages);
+
+    // Invalidate them all.
+    if (!liveStorages.IsEmpty()) {
+      uint32_t count = liveStorages.Length();
+      for (uint32_t index = 0; index < count; index++) {
+        liveStorages[index]->Invalidate();
+      }
+    }
+
+    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;
+  }
+
+  NS_NOTREACHED("Unknown topic!");
+  return NS_ERROR_UNEXPECTED;
+}
+
 void
 QuotaManager::SetCurrentWindowInternal(nsPIDOMWindow* aWindow)
 {
   NS_ASSERTION(mCurrentWindowIndex != BAD_TLS_INDEX,
                "Should have a valid TLS storage index!");
 
   if (aWindow) {
     NS_ASSERTION(!PR_GetThreadPrivate(mCurrentWindowIndex),
@@ -419,8 +1358,820 @@ QuotaManager::LockedQuotaIsLifted()
   // If this thread created the helper and added it to the hash, this thread
   // must remove it.
   if (createdHelper) {
     mCheckQuotaHelpers.Remove(window);
   }
 
   return result;
 }
+
+nsresult
+QuotaManager::AcquireExclusiveAccess(const nsACString& aPattern,
+                                     nsIOfflineStorage* aStorage,
+                                     AcquireListener* aListener,
+                                     WaitingOnStoragesCallback aCallback,
+                                     void* aClosure)
+{
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+  NS_ASSERTION(aListener, "Need a listener!");
+
+  // Find the right SynchronizedOp.
+  SynchronizedOp* op =
+    FindSynchronizedOp(aPattern, aStorage ? aStorage->Id() : nullptr);
+
+  NS_ASSERTION(op, "We didn't find a SynchronizedOp?");
+  NS_ASSERTION(!op->mListener, "SynchronizedOp already has a listener?!?");
+
+  nsTArray<nsCOMPtr<nsIOfflineStorage> > liveStorages;
+
+  if (aStorage) {
+    // We need to wait for the storages to go away.
+    // Hold on to all storage objects that represent the same storage file
+    // (except the one that is requesting this version change).
+
+    Client::Type clientType = aStorage->GetClient()->GetType();
+
+    StorageMatcher<nsAutoTArray<nsIOfflineStorage*, 20> > matches;
+    matches.Find(mLiveStorages, aPattern, clientType);
+
+    if (!matches.IsEmpty()) {
+      // Grab all storages that are not yet closed but whose storage id match
+      // the one we're looking for.
+      for (uint32_t index = 0; index < matches.Length(); index++) {
+        nsIOfflineStorage*& storage = matches[index];
+        if (!storage->IsClosed() &&
+            storage != aStorage &&
+            storage->Id() == aStorage->Id()) {
+          liveStorages.AppendElement(storage);
+        }
+      }
+    }
+
+    if (!liveStorages.IsEmpty()) {
+      NS_ASSERTION(op->mStorages[clientType].IsEmpty(),
+                   "How do we already have storages here?");
+      op->mStorages[clientType].AppendElements(liveStorages);
+    }
+  }
+  else {
+    StorageMatcher<ArrayCluster<nsIOfflineStorage*> > matches;
+    matches.Find(mLiveStorages, aPattern);
+
+    if (!matches.IsEmpty()) {
+      // We want *all* storages, even those that are closed, when we're going to
+      // clear the origin.
+      matches.AppendElementsTo(liveStorages);
+
+      NS_ASSERTION(op->mStorages.IsEmpty(),
+                   "How do we already have storages here?");
+      matches.SwapElements(op->mStorages);
+    }
+  }
+
+  op->mListener = aListener;
+
+  if (!liveStorages.IsEmpty()) {
+    // Give our callback the storages so it can decide what to do with them.
+    aCallback(liveStorages, aClosure);
+
+    NS_ASSERTION(liveStorages.IsEmpty(),
+                 "Should have done something with the array!");
+
+    if (aStorage) {
+      // Wait for those storages to close.
+      return NS_OK;
+    }
+  }
+
+  // If we're trying to open a storage and nothing blocks it, or if we're
+  // clearing an origin, then go ahead and schedule the op.
+  nsresult rv = RunSynchronizedOp(aStorage, op);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  return NS_OK;
+}
+
+nsresult
+QuotaManager::RunSynchronizedOp(nsIOfflineStorage* aStorage,
+                                SynchronizedOp* aOp)
+{
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+  NS_ASSERTION(aOp, "Null pointer!");
+  NS_ASSERTION(aOp->mListener, "No listener on this op!");
+  NS_ASSERTION(!aStorage ||
+               aOp->mStorages[aStorage->GetClient()->GetType()].IsEmpty(),
+               "This op isn't ready to run!");
+
+  ArrayCluster<nsIOfflineStorage*> storages;
+
+  uint32_t startIndex;
+  uint32_t endIndex;
+
+  if (aStorage) {
+    Client::Type clientType = aStorage->GetClient()->GetType();
+
+    storages[clientType].AppendElement(aStorage);
+
+    startIndex = clientType;
+    endIndex = clientType + 1;
+  }
+  else {
+    aOp->mStorages.SwapElements(storages);
+
+    startIndex = 0;
+    endIndex = Client::TYPE_MAX;
+  }
+
+  nsRefPtr<WaitForTransactionsToFinishRunnable> runnable =
+    new WaitForTransactionsToFinishRunnable(aOp);
+
+  // Ask the file service to call us back when it's done with this storage.
+  FileService* service = FileService::Get();
+
+  if (service) {
+    // Have to copy here in case a transaction service needs a list too.
+    nsTArray<nsCOMPtr<nsIFileStorage> > array;
+
+    for (uint32_t index = startIndex; index < endIndex; index++)  {
+      if (!storages[index].IsEmpty() &&
+          mClients[index]->IsFileServiceUtilized()) {
+        array.AppendElements(storages[index]);
+      }
+    }
+
+    if (!array.IsEmpty()) {
+      runnable->AddRun();
+
+      service->WaitForStoragesToComplete(array, runnable);
+    }
+  }
+
+  // Ask each transaction service to call us back when they're done with this
+  // storage.
+  for (uint32_t index = startIndex; index < endIndex; index++)  {
+    nsRefPtr<Client>& client = mClients[index];
+    if (!storages[index].IsEmpty() && client->IsTransactionServiceActivated()) {
+      runnable->AddRun();
+
+      client->WaitForStoragesToComplete(storages[index], runnable);
+    }
+  }
+
+  nsresult rv = runnable->Run();
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  return NS_OK;
+}
+
+SynchronizedOp*
+QuotaManager::FindSynchronizedOp(const nsACString& aPattern,
+                                 nsISupports* aId)
+{
+  for (uint32_t index = 0; index < mSynchronizedOps.Length(); index++) {
+    const nsAutoPtr<SynchronizedOp>& currentOp = mSynchronizedOps[index];
+    if (PatternMatchesOrigin(aPattern, currentOp->mOriginOrPattern) &&
+        (!currentOp->mId || currentOp->mId == aId)) {
+      return currentOp;
+    }
+  }
+
+  return nullptr;
+}
+
+nsresult
+QuotaManager::ClearStoragesForApp(uint32_t aAppId, bool aBrowserOnly)
+{
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+  NS_ASSERTION(aAppId != nsIScriptSecurityManager::UNKNOWN_APP_ID,
+               "Bad appId!");
+
+  // This only works from the main process.
+  NS_ENSURE_TRUE(IsMainProcess(), NS_ERROR_NOT_AVAILABLE);
+
+  nsAutoCString pattern;
+  GetOriginPatternStringMaybeIgnoreBrowser(aAppId, aBrowserOnly, pattern);
+
+  // If there is a pending or running clear operation for this app, return
+  // immediately.
+  if (IsClearOriginPending(pattern)) {
+    return NS_OK;
+  }
+
+  OriginOrPatternString oops = OriginOrPatternString::FromPattern(pattern);
+
+  // Queue up the origin clear runnable.
+  nsRefPtr<OriginClearRunnable> runnable = new OriginClearRunnable(oops);
+
+  nsresult rv = WaitForOpenAllowed(oops, nullptr, runnable);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  runnable->AdvanceState();
+
+  // Give the runnable some help by invalidating any storages in the way.
+  StorageMatcher<nsAutoTArray<nsIOfflineStorage*, 20> > matches;
+  matches.Find(mLiveStorages, pattern);
+
+  for (uint32_t index = 0; index < matches.Length(); index++) {
+    // We need to grab references here to prevent the storage from dying while
+    // we invalidate it.
+    nsCOMPtr<nsIOfflineStorage> storage = matches[index];
+    storage->Invalidate();
+  }
+
+  return NS_OK;
+}
+
+nsresult
+QuotaManager::MaybeUpgradeOriginDirectory(nsIFile* aDirectory)
+{
+  NS_ASSERTION(aDirectory, "Null pointer!");
+
+  nsCOMPtr<nsIFile> metadataFile;
+  nsresult rv = aDirectory->Clone(getter_AddRefs(metadataFile));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  rv = metadataFile->Append(NS_LITERAL_STRING(METADATA_FILE_NAME));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  bool exists;
+  rv = metadataFile->Exists(&exists);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  if (!exists) {
+    // Directory structure upgrade needed.
+    // Move all files to IDB specific directory.
+
+    nsString idbDirectoryName;
+    rv = Client::TypeToText(Client::IDB, idbDirectoryName);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    nsCOMPtr<nsIFile> idbDirectory;
+    rv = aDirectory->Clone(getter_AddRefs(idbDirectory));
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    rv = idbDirectory->Append(idbDirectoryName);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    rv = idbDirectory->Create(nsIFile::DIRECTORY_TYPE, 0755);
+    if (NS_FAILED(rv)) {
+      NS_WARNING("IDB directory already exists!");
+    }
+
+    nsCOMPtr<nsISimpleEnumerator> entries;
+    rv = aDirectory->GetDirectoryEntries(getter_AddRefs(entries));
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    bool hasMore;
+    while (NS_SUCCEEDED((rv = entries->HasMoreElements(&hasMore))) && hasMore) {
+      nsCOMPtr<nsISupports> entry;
+      rv = entries->GetNext(getter_AddRefs(entry));
+      NS_ENSURE_SUCCESS(rv, rv);
+
+      nsCOMPtr<nsIFile> file = do_QueryInterface(entry);
+      NS_ENSURE_TRUE(file, NS_NOINTERFACE);
+
+      nsString leafName;
+      rv = file->GetLeafName(leafName);
+      NS_ENSURE_SUCCESS(rv, rv);
+
+      if (!leafName.Equals(idbDirectoryName)) {
+        rv = file->MoveTo(idbDirectory, EmptyString());
+        NS_ENSURE_SUCCESS(rv, rv);
+      }
+    }
+
+    rv = metadataFile->Create(nsIFile::NORMAL_FILE_TYPE, 0644);
+    NS_ENSURE_SUCCESS(rv, rv);
+  }
+
+  return NS_OK;
+}
+
+void
+QuotaManager::GetOriginPatternString(uint32_t aAppId,
+                                     MozBrowserPatternFlag aBrowserFlag,
+                                     const nsACString& aOrigin,
+                                     nsAutoCString& _retval)
+{
+  NS_ASSERTION(aAppId != nsIScriptSecurityManager::UNKNOWN_APP_ID,
+               "Bad appId!");
+  NS_ASSERTION(aOrigin.IsEmpty() || aBrowserFlag != IgnoreMozBrowser,
+               "Bad args!");
+
+  if (aOrigin.IsEmpty()) {
+    _retval.Truncate();
+
+    _retval.AppendInt(aAppId);
+    _retval.Append('+');
+
+    if (aBrowserFlag != IgnoreMozBrowser) {
+      if (aBrowserFlag == MozBrowser) {
+        _retval.Append('t');
+      }
+      else {
+        _retval.Append('f');
+      }
+      _retval.Append('+');
+    }
+
+    return;
+  }
+
+#ifdef DEBUG
+  if (aAppId != nsIScriptSecurityManager::NO_APP_ID ||
+      aBrowserFlag == MozBrowser) {
+    nsAutoCString pattern;
+    GetOriginPatternString(aAppId, aBrowserFlag, EmptyCString(), pattern);
+    NS_ASSERTION(PatternMatchesOrigin(pattern, aOrigin),
+                 "Origin doesn't match parameters!");
+  }
+#endif
+
+  _retval = aOrigin;
+}
+
+SynchronizedOp::SynchronizedOp(const OriginOrPatternString& aOriginOrPattern,
+                               nsISupports* aId)
+: mOriginOrPattern(aOriginOrPattern), mId(aId)
+{
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+  MOZ_COUNT_CTOR(SynchronizedOp);
+}
+
+SynchronizedOp::~SynchronizedOp()
+{
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+  MOZ_COUNT_DTOR(SynchronizedOp);
+}
+
+bool
+SynchronizedOp::MustWaitFor(const SynchronizedOp& aExistingOp)
+{
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+
+  bool match;
+
+  if (aExistingOp.mOriginOrPattern.IsOrigin()) {
+    if (mOriginOrPattern.IsOrigin()) {
+      match = aExistingOp.mOriginOrPattern.Equals(mOriginOrPattern);
+    }
+    else {
+      match = PatternMatchesOrigin(mOriginOrPattern, aExistingOp.mOriginOrPattern);
+    }
+  }
+  else if (mOriginOrPattern.IsOrigin()) {
+    match = PatternMatchesOrigin(aExistingOp.mOriginOrPattern, mOriginOrPattern);
+  }
+  else {
+    match = PatternMatchesOrigin(mOriginOrPattern, aExistingOp.mOriginOrPattern) ||
+            PatternMatchesOrigin(aExistingOp.mOriginOrPattern, mOriginOrPattern);
+  }
+
+  // If the origins don't match, the second can proceed.
+  if (!match) {
+    return false;
+  }
+
+  // If the origins and the ids match, the second must wait.
+  if (aExistingOp.mId == mId) {
+    return true;
+  }
+
+  // Waiting is required if either one corresponds to an origin clearing
+  // (a null Id).
+  if (!aExistingOp.mId || !mId) {
+    return true;
+  }
+
+  // Otherwise, things for the same origin but different storages can proceed
+  // independently.
+  return false;
+}
+
+void
+SynchronizedOp::DelayRunnable(nsIRunnable* aRunnable)
+{
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+  NS_ASSERTION(mDelayedRunnables.IsEmpty() || !mId,
+               "Only ClearOrigin operations can delay multiple runnables!");
+
+  mDelayedRunnables.AppendElement(aRunnable);
+}
+
+void
+SynchronizedOp::DispatchDelayedRunnables()
+{
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+  NS_ASSERTION(!mListener, "Any listener should be gone by now!");
+
+  uint32_t count = mDelayedRunnables.Length();
+  for (uint32_t index = 0; index < count; index++) {
+    NS_DispatchToCurrentThread(mDelayedRunnables[index]);
+  }
+
+  mDelayedRunnables.Clear();
+}
+
+nsresult
+OriginClearRunnable::OnExclusiveAccessAcquired()
+{
+  QuotaManager* quotaManager = QuotaManager::Get();
+  NS_ASSERTION(quotaManager, "This should never fail!");
+
+  nsresult rv = quotaManager->IOThread()->Dispatch(this, NS_DISPATCH_NORMAL);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  return NS_OK;
+}
+
+// static
+void
+OriginClearRunnable::InvalidateOpenedStorages(
+                              nsTArray<nsCOMPtr<nsIOfflineStorage> >& aStorages,
+                              void* aClosure)
+{
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+
+  nsTArray<nsCOMPtr<nsIOfflineStorage> > storages;
+  storages.SwapElements(aStorages);
+
+  for (uint32_t index = 0; index < storages.Length(); index++) {
+    storages[index]->Invalidate();
+  }
+}
+
+void
+OriginClearRunnable::DeleteFiles(QuotaManager* aQuotaManager)
+{
+  NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
+  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);
+
+  rv = directory->InitWithPath(aQuotaManager->GetBaseDirectory());
+  NS_ENSURE_SUCCESS_VOID(rv);
+
+  nsCOMPtr<nsISimpleEnumerator> entries;
+  if (NS_FAILED(directory->GetDirectoryEntries(getter_AddRefs(entries))) ||
+      !entries) {
+    return;
+  }
+
+  nsCString originSanitized(mOriginOrPattern);
+  SanitizeOriginString(originSanitized);
+
+  bool hasMore;
+  while (NS_SUCCEEDED((rv = entries->HasMoreElements(&hasMore))) && hasMore) {
+    nsCOMPtr<nsISupports> entry;
+    rv = entries->GetNext(getter_AddRefs(entry));
+    NS_ENSURE_SUCCESS_VOID(rv);
+
+    nsCOMPtr<nsIFile> file = do_QueryInterface(entry);
+    NS_ASSERTION(file, "Don't know what this is!");
+
+    bool isDirectory;
+    rv = file->IsDirectory(&isDirectory);
+    NS_ENSURE_SUCCESS_VOID(rv);
+
+    if (!isDirectory) {
+      NS_WARNING("Something in the IndexedDB directory that doesn't belong!");
+      continue;
+    }
+
+    nsString leafName;
+    rv = file->GetLeafName(leafName);
+    NS_ENSURE_SUCCESS_VOID(rv);
+
+    // Skip storages for other apps.
+    if (!PatternMatchesOrigin(originSanitized,
+                              NS_ConvertUTF16toUTF8(leafName))) {
+      continue;
+    }
+
+    if (NS_FAILED(file->Remove(true))) {
+      // This should never fail if we've closed all storage connections
+      // correctly...
+      NS_ERROR("Failed to remove directory!");
+    }
+  }
+
+  aQuotaManager->RemoveQuotaForPattern(mOriginOrPattern);
+
+  aQuotaManager->UninitializeOriginsByPattern(mOriginOrPattern);
+}
+
+NS_IMPL_THREADSAFE_ISUPPORTS1(OriginClearRunnable, nsIRunnable)
+
+NS_IMETHODIMP
+OriginClearRunnable::Run()
+{
+  QuotaManager* quotaManager = QuotaManager::Get();
+  NS_ASSERTION(quotaManager, "This should never fail!");
+
+  switch (mCallbackState) {
+    case Pending: {
+      NS_NOTREACHED("Should never get here without being dispatched!");
+      return NS_ERROR_UNEXPECTED;
+    }
+
+    case OpenAllowed: {
+      NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+
+      AdvanceState();
+
+      // Now we have to wait until the thread pool is done with all of the
+      // storages we care about.
+      nsresult rv =
+        quotaManager->AcquireExclusiveAccess(mOriginOrPattern, this,
+                                             InvalidateOpenedStorages, nullptr);
+      NS_ENSURE_SUCCESS(rv, rv);
+
+      return NS_OK;
+    }
+
+    case IO: {
+      NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
+
+      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!");
+        return NS_ERROR_FAILURE;
+      }
+
+      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!");
+      return NS_ERROR_UNEXPECTED;
+  }
+
+  NS_NOTREACHED("Should never get here!");
+  return NS_ERROR_UNEXPECTED;
+}
+
+AsyncUsageRunnable::AsyncUsageRunnable(uint32_t aAppId,
+                                       bool aInMozBrowserOnly,
+                                       const OriginOrPatternString& aOrigin,
+                                       nsIURI* aURI,
+                                       nsIUsageCallback* aCallback)
+: mURI(aURI),
+  mCallback(aCallback),
+  mAppId(aAppId),
+  mOrigin(aOrigin),
+  mCallbackState(Pending),
+  mInMozBrowserOnly(aInMozBrowserOnly)
+{
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+  NS_ASSERTION(aURI, "Null pointer!");
+  NS_ASSERTION(aOrigin.IsOrigin(), "Expect origin only here!");
+  NS_ASSERTION(!aOrigin.IsEmpty(), "Empty origin!");
+  NS_ASSERTION(aCallback, "Null pointer!");
+}
+
+nsresult
+AsyncUsageRunnable::TakeShortcut()
+{
+  NS_ASSERTION(mCallbackState == Pending, "Huh?");
+
+  nsresult rv = NS_DispatchToCurrentThread(this);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  mCallbackState = Shortcut;
+  return NS_OK;
+}
+
+nsresult
+AsyncUsageRunnable::RunInternal()
+{
+  QuotaManager* quotaManager = QuotaManager::Get();
+  NS_ASSERTION(quotaManager, "This should never fail!");
+
+  nsresult rv;
+
+  switch (mCallbackState) {
+    case Pending: {
+      NS_NOTREACHED("Should never get here without being dispatched!");
+      return NS_ERROR_UNEXPECTED;
+    }
+
+    case OpenAllowed: {
+      NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+
+      AdvanceState();
+
+      rv = quotaManager->IOThread()->Dispatch(this, NS_DISPATCH_NORMAL);
+      if (NS_FAILED(rv)) {
+        NS_WARNING("Failed to dispatch to the IO thread!");
+      }
+
+      return NS_OK;
+    }
+
+    case IO: {
+      NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
+
+      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);
+
+      bool exists;
+      rv = directory->Exists(&exists);
+      NS_ENSURE_SUCCESS(rv, rv);
+
+      // If the directory exists then enumerate all the files inside, adding up
+      // the sizes to get the final usage statistic.
+      if (exists && !mCanceled) {
+        bool initialized = quotaManager->mInitializedOrigins.Contains(mOrigin);
+
+        if (!initialized) {
+          rv = quotaManager->MaybeUpgradeOriginDirectory(directory);
+          NS_ENSURE_SUCCESS(rv, rv);
+        }
+
+        nsCOMPtr<nsISimpleEnumerator> entries;
+        rv = directory->GetDirectoryEntries(getter_AddRefs(entries));
+        NS_ENSURE_SUCCESS(rv, rv);
+
+        bool hasMore;
+        while (NS_SUCCEEDED((rv = entries->HasMoreElements(&hasMore))) &&
+               hasMore && !mCanceled) {
+          nsCOMPtr<nsISupports> entry;
+          rv = entries->GetNext(getter_AddRefs(entry));
+          NS_ENSURE_SUCCESS(rv, rv);
+
+          nsCOMPtr<nsIFile> file = do_QueryInterface(entry);
+          NS_ENSURE_TRUE(file, NS_NOINTERFACE);
+
+          nsString leafName;
+          rv = file->GetLeafName(leafName);
+          NS_ENSURE_SUCCESS(rv, rv);
+
+          if (leafName.EqualsLiteral(METADATA_FILE_NAME)) {
+            continue;
+          }
+
+          if (!initialized) {
+            bool isDirectory;
+            rv = file->IsDirectory(&isDirectory);
+            NS_ENSURE_SUCCESS(rv, rv);
+
+            if (!isDirectory) {
+              NS_WARNING("Unknown file found!");
+              return NS_ERROR_UNEXPECTED;
+            }
+          }
+
+          Client::Type clientType;
+          rv = Client::TypeFromText(leafName, clientType);
+          if (NS_FAILED(rv)) {
+            NS_WARNING("Unknown directory found!");
+            if (!initialized) {
+              return NS_ERROR_UNEXPECTED;
+            }
+            continue;
+          }
+
+          nsRefPtr<Client>& client = quotaManager->mClients[clientType];
+
+          if (!initialized) {
+            rv = client->InitOrigin(mOrigin, this);
+          }
+          else {
+            rv = client->GetUsageForOrigin(mOrigin, this);
+          }
+          NS_ENSURE_SUCCESS(rv, rv);
+        }
+      }
+
+      // Run dispatches us back to the main thread.
+      return NS_OK;
+    }
+
+    case Complete: // Fall through
+    case Shortcut: {
+      NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+
+      // Call the callback unless we were canceled.
+      if (!mCanceled) {
+        mCallback->OnUsageResult(mURI, TotalUsage(), FileUsage(), mAppId,
+                                 mInMozBrowserOnly);
+      }
+
+      // Clean up.
+      mURI = nullptr;
+      mCallback = nullptr;
+
+      // And tell the QuotaManager that we're done.
+      if (mCallbackState == Complete) {
+        quotaManager->AllowNextSynchronizedOp(mOrigin, nullptr);
+      }
+
+      return NS_OK;
+    }
+
+    default:
+      NS_ERROR("Unknown state value!");
+      return NS_ERROR_UNEXPECTED;
+  }
+
+  NS_NOTREACHED("Should never get here!");
+  return NS_ERROR_UNEXPECTED;
+}
+
+NS_IMPL_THREADSAFE_ISUPPORTS2(AsyncUsageRunnable,
+                              nsIRunnable,
+                              nsIQuotaRequest)
+
+NS_IMETHODIMP
+AsyncUsageRunnable::Run()
+{
+  nsresult rv = RunInternal();
+
+  if (!NS_IsMainThread()) {
+    if (NS_FAILED(rv)) {
+      ResetUsage();
+    }
+
+    if (NS_FAILED(NS_DispatchToMainThread(this, NS_DISPATCH_NORMAL))) {
+      NS_WARNING("Failed to dispatch to main thread!");
+    }
+  }
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+AsyncUsageRunnable::Cancel()
+{
+  if (PR_ATOMIC_SET(&mCanceled, 1)) {
+    NS_WARNING("Canceled more than once?!");
+    return NS_ERROR_UNEXPECTED;
+  }
+
+  return NS_OK;
+}
+
+NS_IMPL_THREADSAFE_ISUPPORTS1(WaitForTransactionsToFinishRunnable, nsIRunnable)
+
+NS_IMETHODIMP
+WaitForTransactionsToFinishRunnable::Run()
+{
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+  NS_ASSERTION(mOp, "Null op!");
+  NS_ASSERTION(mOp->mListener, "Nothing to run!");
+  NS_ASSERTION(mCountdown, "Wrong countdown!");
+
+  if (--mCountdown) {
+    return NS_OK;
+  }
+
+  // Don't hold the listener alive longer than necessary.
+  nsRefPtr<AcquireListener> listener;
+  listener.swap(mOp->mListener);
+
+  mOp = nullptr;
+
+  nsresult rv = listener->OnExclusiveAccessAcquired();
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  // The listener is responsible for calling
+  // QuotaManager::AllowNextSynchronizedOp.
+  return NS_OK;
+}
+
+NS_IMPL_THREADSAFE_ISUPPORTS1(WaitForLockedFilesToFinishRunnable, nsIRunnable)
+
+NS_IMETHODIMP
+WaitForLockedFilesToFinishRunnable::Run()
+{
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+
+  mBusy = false;
+
+  return NS_OK;
+}
--- a/dom/quota/QuotaManager.h
+++ b/dom/quota/QuotaManager.h
@@ -4,113 +4,87 @@
  * 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/Mutex.h"
-#include "nsDataHashtable.h"
+#include "nsClassHashtable.h"
 #include "nsRefPtrHashtable.h"
 #include "nsThreadUtils.h"
 
+#include "ArrayCluster.h"
+#include "Client.h"
+#include "StoragePrivilege.h"
+
+#define QUOTA_MANAGER_CONTRACTID "@mozilla.org/dom/quota/manager;1"
+
+class nsIAtom;
+class nsIOfflineStorage;
+class nsIPrincipal;
+class nsIThread;
+class nsITimer;
+class nsIURI;
 class nsPIDOMWindow;
 
 BEGIN_QUOTA_NAMESPACE
 
+class AcquireListener;
+class AsyncUsageRunnable;
 class CheckQuotaHelper;
+class OriginClearRunnable;
 class OriginInfo;
-class QuotaManager;
-
-class QuotaObject
-{
-  friend class OriginInfo;
-  friend class QuotaManager;
-
-public:
-  void
-  AddRef();
-
-  void
-  Release();
-
-  void
-  UpdateSize(int64_t aSize);
-
-  bool
-  MaybeAllocateMoreSpace(int64_t aOffset, int32_t aCount);
-
-private:
-  QuotaObject(OriginInfo* aOriginInfo, const nsAString& aPath, int64_t aSize)
-  : mOriginInfo(aOriginInfo), mPath(aPath), mSize(aSize)
-  { }
-
-  virtual ~QuotaObject()
-  { }
-
-  nsAutoRefCnt mRefCnt;
-
-  OriginInfo* mOriginInfo;
-  nsString mPath;
-  int64_t mSize;
-};
+class OriginOrPatternString;
+class QuotaObject;
+struct SynchronizedOp;
 
-class OriginInfo
+class QuotaManager MOZ_FINAL : public nsIQuotaManager,
+                               public nsIObserver
 {
-  friend class QuotaManager;
-  friend class QuotaObject;
-
-public:
-  OriginInfo(const nsACString& aOrigin, int64_t aLimit, int64_t aUsage)
-  : mOrigin(aOrigin), mLimit(aLimit), mUsage(aUsage)
-  {
-    mQuotaObjects.Init();
-  }
-
-  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(OriginInfo)
-
-private:
-  void
-#ifdef DEBUG
-  LockedClearOriginInfos();
-#else
-  LockedClearOriginInfos()
-  {
-    mQuotaObjects.EnumerateRead(ClearOriginInfoCallback, nullptr);
-  }
-#endif
-
-  static PLDHashOperator
-  ClearOriginInfoCallback(const nsAString& aKey,
-                          QuotaObject* aValue, void* aUserArg);
-
-  nsDataHashtable<nsStringHashKey, QuotaObject*> mQuotaObjects;
-
-  nsCString mOrigin;
-  int64_t mLimit;
-  int64_t mUsage;
-};
-
-class QuotaManager
-{
-  friend class nsAutoPtr<QuotaManager>;
+  friend class AsyncUsageRunnable;
+  friend class OriginClearRunnable;
   friend class OriginInfo;
   friend class QuotaObject;
 
+  enum MozBrowserPatternFlag
+  {
+    MozBrowser = 0,
+    NotMozBrowser,
+    IgnoreMozBrowser
+  };
+
+  typedef void
+  (*WaitingOnStoragesCallback)(nsTArray<nsCOMPtr<nsIOfflineStorage> >&, void*);
+
 public:
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIQUOTAMANAGER
+  NS_DECL_NSIOBSERVER
+
   // Returns a non-owning reference.
   static QuotaManager*
   GetOrCreate();
 
   // 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();
+
   void
   InitQuotaForOrigin(const nsACString& aOrigin,
                      int64_t aLimit,
                      int64_t aUsage);
 
   void
   DecreaseUsageForOrigin(const nsACString& aOrigin,
                          int64_t aSize);
@@ -143,45 +117,224 @@ public:
     NS_ASSERTION(aWindow, "Passed null window!");
 
     QuotaManager* quotaManager = Get();
     NS_ASSERTION(quotaManager, "Must have a manager here!");
 
     quotaManager->CancelPromptsForWindowInternal(aWindow);
   }
 
+  // Called when a storage is created.
+  bool
+  RegisterStorage(nsIOfflineStorage* aStorage);
+
+  // Called when a storage is being unlinked or destroyed.
+  void
+  UnregisterStorage(nsIOfflineStorage* aStorage);
+
+  // Called when a storage has been closed.
+  void
+  OnStorageClosed(nsIOfflineStorage* aStorage);
+
+  // Called when a window is being purged from the bfcache or the user leaves
+  // a page which isn't going into the bfcache. Forces any live storage
+  // objects to close themselves and aborts any running transactions.
+  void
+  AbortCloseStoragesForWindow(nsPIDOMWindow* aWindow);
+
+  // Used to check if there are running transactions in a given window.
+  bool
+  HasOpenTransactions(nsPIDOMWindow* aWindow);
+
+  // Waits for storages to be cleared and for version change transactions to
+  // complete before dispatching the given runnable.
+  nsresult
+  WaitForOpenAllowed(const OriginOrPatternString& aOriginOrPattern,
+                     nsIAtom* aId,
+                     nsIRunnable* aRunnable);
+
+  // Acquire exclusive access to the storage given (waits for all others to
+  // close).  If storages need to close first, the callback will be invoked
+  // with an array of said storages.
+  nsresult
+  AcquireExclusiveAccess(nsIOfflineStorage* aStorage,
+                         const nsACString& aOrigin,
+                         AcquireListener* aListener,
+                         WaitingOnStoragesCallback aCallback,
+                         void* aClosure)
+  {
+    NS_ASSERTION(aStorage, "Need a storage here!");
+    return AcquireExclusiveAccess(aOrigin, aStorage, aListener, aCallback,
+                                  aClosure);
+  }
+
+  nsresult
+  AcquireExclusiveAccess(const nsACString& aOrigin,
+                         AcquireListener* aListener,
+                         WaitingOnStoragesCallback aCallback,
+                         void* aClosure)
+  {
+    return AcquireExclusiveAccess(aOrigin, nullptr, aListener, aCallback,
+                                  aClosure);
+  }
+
+  void
+  AllowNextSynchronizedOp(const OriginOrPatternString& aOriginOrPattern,
+                          nsIAtom* aId);
+
+  bool
+  IsClearOriginPending(const nsACString& aPattern)
+  {
+    return !!FindSynchronizedOp(aPattern, nullptr);
+  }
+
+  nsresult
+  GetDirectoryForOrigin(const nsACString& aASCIIOrigin,
+                        nsIFile** aDirectory) const;
+
+  nsresult
+  EnsureOriginIsInitialized(const nsACString& aOrigin,
+                            StoragePrivilege aPrivilege,
+                            nsIFile** aDirectory);
+
+  void
+  UninitializeOriginsByPattern(const nsACString& aPattern);
+
+  nsIThread*
+  IOThread()
+  {
+    NS_ASSERTION(mIOThread, "This should never be null!");
+    return mIOThread;
+  }
+
+  already_AddRefed<Client>
+  GetClient(Client::Type aClientType);
+
+  const nsString&
+  GetBaseDirectory() const
+  {
+    return mStorageBasePath;
+  }
+
+  static uint32_t
+  GetStorageQuotaMB();
+
+  static already_AddRefed<nsIAtom>
+  GetStorageId(const nsACString& aOrigin,
+               const nsAString& aName);
+
+  static nsresult
+  GetASCIIOriginFromURI(nsIURI* aURI,
+                        uint32_t aAppId,
+                        bool aInMozBrowser,
+                        nsACString& aASCIIOrigin);
+
+  static nsresult
+  GetASCIIOriginFromPrincipal(nsIPrincipal* aPrincipal,
+                              nsACString& aASCIIOrigin);
+
+  static nsresult
+  GetASCIIOriginFromWindow(nsPIDOMWindow* aWindow,
+                           nsACString& aASCIIOrigin);
+
+  static void
+  GetOriginPatternString(uint32_t aAppId, bool aBrowserOnly,
+                         const nsACString& aOrigin, nsAutoCString& _retval)
+  {
+    return GetOriginPatternString(aAppId,
+                                  aBrowserOnly ? MozBrowser : NotMozBrowser,
+                                  aOrigin, _retval);
+  }
+
+  static void
+  GetOriginPatternStringMaybeIgnoreBrowser(uint32_t aAppId, bool aBrowserOnly,
+                                           nsAutoCString& _retval)
+  {
+    return GetOriginPatternString(aAppId,
+                                  aBrowserOnly ? MozBrowser : IgnoreMozBrowser,
+                                  EmptyCString(), _retval);
+  }
+
 private:
   QuotaManager();
 
   virtual ~QuotaManager();
 
-  bool
+  nsresult
   Init();
 
   void
   SetCurrentWindowInternal(nsPIDOMWindow* aWindow);
 
   void
   CancelPromptsForWindowInternal(nsPIDOMWindow* aWindow);
 
   // Determine if the quota is lifted for the Window the current thread is
   // using.
   bool
   LockedQuotaIsLifted();
 
+  nsresult
+  AcquireExclusiveAccess(const nsACString& aOrigin,
+                         nsIOfflineStorage* aStorage,
+                         AcquireListener* aListener,
+                         WaitingOnStoragesCallback aCallback,
+                         void* aClosure);
+
+  nsresult
+  RunSynchronizedOp(nsIOfflineStorage* aStorage,
+                    SynchronizedOp* aOp);
+
+  SynchronizedOp*
+  FindSynchronizedOp(const nsACString& aPattern,
+                     nsISupports* aId);
+
+  nsresult
+  ClearStoragesForApp(uint32_t aAppId, bool aBrowserOnly);
+
+  nsresult
+  MaybeUpgradeOriginDirectory(nsIFile* aDirectory);
+
+  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;
 
   mozilla::Mutex mQuotaMutex;
 
   nsRefPtrHashtable<nsCStringHashKey, OriginInfo> mOriginInfos;
 
   // A map of Windows to the corresponding quota helper.
   nsRefPtrHashtable<nsPtrHashKey<nsPIDOMWindow>,
                     CheckQuotaHelper> mCheckQuotaHelpers;
+
+  // Maintains a list of live storages per origin.
+  nsClassHashtable<nsCStringHashKey,
+                   ArrayCluster<nsIOfflineStorage*> > mLiveStorages;
+
+  // Maintains a list of synchronized operatons that are in progress or queued.
+  nsAutoTArray<nsAutoPtr<SynchronizedOp>, 5> mSynchronizedOps;
+
+  // Thread on which IO is performed.
+  nsCOMPtr<nsIThread> mIOThread;
+
+  // A timer that gets activated at shutdown to ensure we close all storages.
+  nsCOMPtr<nsITimer> mShutdownTimer;
+
+  // A list of all successfully initialized origins. This list isn't protected
+  // by any mutex but it is only ever touched on the IO thread.
+  nsTArray<nsCString> mInitializedOrigins;
+
+  nsAutoTArray<nsRefPtr<Client>, Client::TYPE_MAX> mClients;
+
+  nsString mStorageBasePath;
 };
 
 class AutoEnterWindow
 {
 public:
   AutoEnterWindow(nsPIDOMWindow* aWindow)
   {
     QuotaManager::SetCurrentWindow(aWindow);
new file mode 100644
--- /dev/null
+++ b/dom/quota/QuotaObject.cpp
@@ -0,0 +1,159 @@
+/* -*- 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 "QuotaObject.h"
+
+#include "QuotaManager.h"
+
+USING_QUOTA_NAMESPACE
+
+void
+QuotaObject::AddRef()
+{
+  QuotaManager* quotaManager = QuotaManager::Get();
+  if (!quotaManager) {
+    NS_ERROR("Null quota manager, this shouldn't happen, possible leak!");
+
+    NS_AtomicIncrementRefcnt(mRefCnt);
+
+    return;
+  }
+
+  MutexAutoLock lock(quotaManager->mQuotaMutex);
+
+  ++mRefCnt;
+}
+
+void
+QuotaObject::Release()
+{
+  QuotaManager* quotaManager = QuotaManager::Get();
+  if (!quotaManager) {
+    NS_ERROR("Null quota manager, this shouldn't happen, possible leak!");
+
+    nsrefcnt count = NS_AtomicDecrementRefcnt(mRefCnt);
+    if (count == 0) {
+      mRefCnt = 1;
+      delete this;
+    }
+
+    return;
+  }
+
+  {
+    MutexAutoLock lock(quotaManager->mQuotaMutex);
+
+    --mRefCnt;
+
+    if (mRefCnt > 0) {
+      return;
+    }
+
+    if (mOriginInfo) {
+      mOriginInfo->mQuotaObjects.Remove(mPath);
+    }
+  }
+
+  delete this;
+}
+
+void
+QuotaObject::UpdateSize(int64_t aSize)
+{
+  QuotaManager* quotaManager = QuotaManager::Get();
+  NS_ASSERTION(quotaManager, "Shouldn't be null!");
+
+  MutexAutoLock lock(quotaManager->mQuotaMutex);
+
+  if (mOriginInfo) {
+    mOriginInfo->mUsage -= mSize;
+    mSize = aSize;
+    mOriginInfo->mUsage += mSize;
+  }
+}
+
+bool
+QuotaObject::MaybeAllocateMoreSpace(int64_t aOffset, int32_t aCount)
+{
+  int64_t end = aOffset + aCount;
+
+  QuotaManager* quotaManager = QuotaManager::Get();
+  NS_ASSERTION(quotaManager, "Shouldn't be null!");
+
+  MutexAutoLock lock(quotaManager->mQuotaMutex);
+
+  if (mSize >= end || !mOriginInfo) {
+    return true;
+  }
+
+  int64_t newUsage = mOriginInfo->mUsage - mSize + end;
+  if (newUsage > mOriginInfo->mLimit) {
+    // This will block the thread, but it will also drop the mutex while
+    // waiting. The mutex will be reacquired again when the waiting is finished.
+    if (!quotaManager->LockedQuotaIsLifted()) {
+      return false;
+    }
+
+    // Threads raced, the origin info removal has been done by some other
+    // thread.
+    if (!mOriginInfo) {
+      // The other thread could allocate more space.
+      if (end > mSize) {
+        mSize = end;
+      }
+
+      return true;
+    }
+
+    nsCString origin = mOriginInfo->mOrigin;
+
+    mOriginInfo->LockedClearOriginInfos();
+    NS_ASSERTION(!mOriginInfo,
+                 "Should have cleared in LockedClearOriginInfos!");
+
+    quotaManager->mOriginInfos.Remove(origin);
+
+    // Some other thread could increase the size without blocking (increasing
+    // the origin usage without hitting the limit), but no more than this one.
+    NS_ASSERTION(mSize < end, "This shouldn't happen!");
+
+    mSize = end;
+
+    return true;
+  }
+
+  mOriginInfo->mUsage = newUsage;
+  mSize = end;
+
+  return true;
+}
+
+#ifdef DEBUG
+void
+OriginInfo::LockedClearOriginInfos()
+{
+  QuotaManager* quotaManager = QuotaManager::Get();
+  NS_ASSERTION(quotaManager, "Shouldn't be null!");
+
+  quotaManager->mQuotaMutex.AssertCurrentThreadOwns();
+
+  mQuotaObjects.EnumerateRead(ClearOriginInfoCallback, nullptr);
+}
+#endif
+
+// static
+PLDHashOperator
+OriginInfo::ClearOriginInfoCallback(const nsAString& aKey,
+                                    QuotaObject* aValue,
+                                    void* aUserArg)
+{
+  NS_ASSERTION(!aKey.IsEmpty(), "Empty key!");
+  NS_ASSERTION(aValue, "Null pointer!");
+
+  aValue->mOriginInfo = nullptr;
+
+  return PL_DHASH_NEXT;
+}
new file mode 100644
--- /dev/null
+++ b/dom/quota/QuotaObject.h
@@ -0,0 +1,90 @@
+/* -*- 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/. */
+
+#ifndef mozilla_dom_quota_quotaobject_h__
+#define mozilla_dom_quota_quotaobject_h__
+
+#include "mozilla/dom/quota/QuotaCommon.h"
+
+#include "nsDataHashtable.h"
+
+BEGIN_QUOTA_NAMESPACE
+
+class OriginInfo;
+class QuotaManager;
+
+class QuotaObject
+{
+  friend class OriginInfo;
+  friend class QuotaManager;
+
+public:
+  void
+  AddRef();
+
+  void
+  Release();
+
+  void
+  UpdateSize(int64_t aSize);
+
+  bool
+  MaybeAllocateMoreSpace(int64_t aOffset, int32_t aCount);
+
+private:
+  QuotaObject(OriginInfo* aOriginInfo, const nsAString& aPath, int64_t aSize)
+  : mOriginInfo(aOriginInfo), mPath(aPath), mSize(aSize)
+  { }
+
+  virtual ~QuotaObject()
+  { }
+
+  nsAutoRefCnt mRefCnt;
+
+  OriginInfo* mOriginInfo;
+  nsString mPath;
+  int64_t mSize;
+};
+
+class OriginInfo
+{
+  friend class QuotaManager;
+  friend class QuotaObject;
+
+public:
+  OriginInfo(const nsACString& aOrigin, int64_t aLimit, int64_t aUsage)
+  : mOrigin(aOrigin), mLimit(aLimit), mUsage(aUsage)
+  {
+    mQuotaObjects.Init();
+  }
+
+  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(OriginInfo)
+
+private:
+  void
+#ifdef DEBUG
+  LockedClearOriginInfos();
+#else
+  LockedClearOriginInfos()
+  {
+    mQuotaObjects.EnumerateRead(ClearOriginInfoCallback, nullptr);
+  }
+#endif
+
+  static PLDHashOperator
+  ClearOriginInfoCallback(const nsAString& aKey,
+                          QuotaObject* aValue, void* aUserArg);
+
+  nsDataHashtable<nsStringHashKey, QuotaObject*> mQuotaObjects;
+
+  nsCString mOrigin;
+  int64_t mLimit;
+  int64_t mUsage;
+};
+
+END_QUOTA_NAMESPACE
+
+#endif // mozilla_dom_quota_quotaobject_h__
new file mode 100644
--- /dev/null
+++ b/dom/quota/StorageMatcher.h
@@ -0,0 +1,191 @@
+/* -*- 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/. */
+
+#ifndef mozilla_dom_quota_patternmatcher_h__
+#define mozilla_dom_quota_patternmatcher_h__
+
+#include "mozilla/dom/quota/QuotaCommon.h"
+
+#include "ArrayCluster.h"
+#include "Utilities.h"
+
+BEGIN_QUOTA_NAMESPACE
+
+template <class ValueType, class BaseType = ArrayCluster<nsIOfflineStorage*>>
+class StorageMatcher : public ValueType
+{
+  typedef StorageMatcher<ValueType, BaseType> SelfType;
+
+  struct Closure
+  {
+    Closure(SelfType& aSelf)
+    : mSelf(aSelf), mPattern(EmptyCString()), mIndexes(nullptr)
+    { }
+
+    Closure(SelfType& aSelf, const nsACString& aPattern)
+    : mSelf(aSelf), mPattern(aPattern), mIndexes(nullptr)
+    { }
+
+    Closure(SelfType& aSelf, const nsTArray<uint32_t>* aIndexes)
+    : mSelf(aSelf), mPattern(EmptyCString()), mIndexes(aIndexes)
+    { }
+
+    Closure(SelfType& aSelf, const nsACString& aPattern,
+            const nsTArray<uint32_t>* aIndexes)
+    : mSelf(aSelf), mPattern(aPattern), mIndexes(aIndexes)
+    { }
+
+    SelfType& mSelf;
+    const nsACString& mPattern;
+    const nsTArray<uint32_t>* mIndexes;
+  };
+
+public:
+  template <class T, class U, class V>
+  void
+  Find(const nsBaseHashtable<T, U, V>& aHashtable,
+       const nsACString& aPattern)
+  {
+    SelfType::Clear();
+
+    Closure closure(*this, aPattern);
+    aHashtable.EnumerateRead(SelfType::MatchPattern, &closure);
+  }
+
+  template <class T, class U, class V>
+  void
+  Find(const nsBaseHashtable<T, U, V>& aHashtable,
+       const nsTArray<uint32_t>* aIndexes)
+  {
+    SelfType::Clear();
+
+    Closure closure(*this, aIndexes);
+    aHashtable.EnumerateRead(SelfType::MatchIndexes, &closure);
+  }
+
+  template <class T, class U, class V>
+  void
+  Find(const nsBaseHashtable<T, U, V>& aHashtable,
+       uint32_t aIndex)
+  {
+    nsAutoTArray<uint32_t, 1> indexes;
+    indexes.AppendElement(aIndex);
+
+    Find(aHashtable, &indexes);
+  }
+
+  template <class T, class U, class V>
+  void
+  Find(const nsBaseHashtable<T, U, V>& aHashtable,
+       const nsACString& aPattern,
+       const nsTArray<uint32_t>* aIndexes)
+  {
+    SelfType::Clear();
+
+    Closure closure(*this, aPattern, aIndexes);
+    aHashtable.EnumerateRead(SelfType::MatchPatternAndIndexes, &closure);
+  }
+
+  template <class T, class U, class V>
+  void
+  Find(const nsBaseHashtable<T, U, V>& aHashtable,
+       const nsACString& aPattern,
+       uint32_t aIndex)
+  {
+    nsAutoTArray<uint32_t, 1> indexes;
+    indexes.AppendElement(aIndex);
+
+    Find(aHashtable, aPattern, &indexes);
+  }
+
+  template <class T, class U, class V>
+  void
+  Find(const nsBaseHashtable<T, U, V>& aHashtable)
+  {
+    SelfType::Clear();
+
+    Closure closure(*this);
+    aHashtable.EnumerateRead(SelfType::MatchAll, &closure);
+  }
+
+private:
+  static PLDHashOperator
+  MatchPattern(const nsACString& aKey,
+               BaseType* aValue,
+               void* aUserArg)
+  {
+    MOZ_ASSERT(!aKey.IsEmpty(), "Empty key!");
+    MOZ_ASSERT(aValue, "Null pointer!");
+    MOZ_ASSERT(aUserArg, "Null pointer!");
+
+    Closure* closure = static_cast<Closure*>(aUserArg);
+
+    if (PatternMatchesOrigin(closure->mPattern, aKey)) {
+      aValue->AppendElementsTo(closure->mSelf);
+    }
+
+    return PL_DHASH_NEXT;
+  }
+
+  static PLDHashOperator
+  MatchIndexes(const nsACString& aKey,
+               BaseType* aValue,
+               void* aUserArg)
+  {
+    MOZ_ASSERT(!aKey.IsEmpty(), "Empty key!");
+    MOZ_ASSERT(aValue, "Null pointer!");
+    MOZ_ASSERT(aUserArg, "Null pointer!");
+
+    Closure* closure = static_cast<Closure*>(aUserArg);
+
+    for (uint32_t index = 0; index < closure->mIndexes->Length(); index++) {
+      aValue->AppendElementsTo(closure->mIndexes->ElementAt(index),
+                               closure->mSelf);
+    }
+
+    return PL_DHASH_NEXT;
+  }
+
+  static PLDHashOperator
+  MatchPatternAndIndexes(const nsACString& aKey,
+                         BaseType* aValue,
+                         void* aUserArg)
+  {
+    MOZ_ASSERT(!aKey.IsEmpty(), "Empty key!");
+    MOZ_ASSERT(aValue, "Null pointer!");
+    MOZ_ASSERT(aUserArg, "Null pointer!");
+
+    Closure* closure = static_cast<Closure*>(aUserArg);
+
+    if (PatternMatchesOrigin(closure->mPattern, aKey)) {
+      for (uint32_t index = 0; index < closure->mIndexes->Length(); index++) {
+        aValue->AppendElementsTo(closure->mIndexes->ElementAt(index),
+                                 closure->mSelf);
+      }
+    }
+
+    return PL_DHASH_NEXT;
+  }
+
+  static PLDHashOperator
+  MatchAll(const nsACString& aKey,
+           BaseType* aValue,
+           void* aUserArg)
+  {
+    MOZ_ASSERT(!aKey.IsEmpty(), "Empty key!");
+    MOZ_ASSERT(aValue, "Null pointer!");
+    MOZ_ASSERT(aUserArg, "Null pointer!");
+
+    Closure* closure = static_cast<Closure*>(aUserArg);
+    aValue->AppendElementsTo(closure->mSelf);
+
+    return PL_DHASH_NEXT;
+  }
+};
+
+END_QUOTA_NAMESPACE
+
+#endif // mozilla_dom_quota_patternmatcher_h__
new file mode 100644
--- /dev/null
+++ b/dom/quota/StoragePrivilege.h
@@ -0,0 +1,21 @@
+/* -*- 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/. */
+
+#ifndef mozilla_dom_quota_storageprivilege_h__
+#define mozilla_dom_quota_storageprivilege_h__
+
+#include "mozilla/dom/quota/QuotaCommon.h"
+
+BEGIN_QUOTA_NAMESPACE
+
+enum StoragePrivilege {
+  Content,
+  Chrome
+};
+
+END_QUOTA_NAMESPACE
+
+#endif // mozilla_dom_quota_storageprivilege_h__
new file mode 100644
--- /dev/null
+++ b/dom/quota/UsageRunnable.h
@@ -0,0 +1,81 @@
+/* -*- 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/. */
+
+#ifndef mozilla_dom_quota_usagerunnable_h__
+#define mozilla_dom_quota_usagerunnable_h__
+
+#include "mozilla/dom/quota/QuotaCommon.h"
+
+#include "Utilities.h"
+
+BEGIN_QUOTA_NAMESPACE
+
+class UsageRunnable
+{
+public:
+  UsageRunnable()
+  : mCanceled(0), mDatabaseUsage(0), mFileUsage(0)
+  { }
+
+  virtual ~UsageRunnable()
+  { }
+
+  bool
+  Canceled()
+  {
+    return mCanceled;
+  }
+
+  void
+  AppendToDatabaseUsage(uint64_t aUsage)
+  {
+    IncrementUsage(&mDatabaseUsage, aUsage);
+  }
+
+  void
+  AppendToFileUsage(uint64_t aUsage)
+  {
+    IncrementUsage(&mFileUsage, aUsage);
+  }
+
+  uint64_t
+  DatabaseUsage()
+  {
+    return mDatabaseUsage;
+  }
+
+  uint64_t
+  FileUsage()
+  {
+    return mFileUsage;
+  }
+
+  uint64_t
+  TotalUsage()
+  {
+    uint64_t totalUsage = mDatabaseUsage;
+    IncrementUsage(&totalUsage, mFileUsage);
+    return totalUsage;
+  }
+
+  void
+  ResetUsage()
+  {
+    mDatabaseUsage = 0;
+    mFileUsage = 0;
+  }
+
+protected:
+  int32_t mCanceled;
+
+private:
+  uint64_t mDatabaseUsage;
+  uint64_t mFileUsage;
+};
+
+END_QUOTA_NAMESPACE
+
+#endif // mozilla_dom_quota_usagerunnable_h__
new file mode 100644
--- /dev/null
+++ b/dom/quota/Utilities.h
@@ -0,0 +1,36 @@
+/* -*- 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/. */
+
+#ifndef mozilla_dom_quota_utilities_h__
+#define mozilla_dom_quota_utilities_h__
+
+#include "mozilla/dom/quota/QuotaCommon.h"
+
+BEGIN_QUOTA_NAMESPACE
+
+inline void
+IncrementUsage(uint64_t* aUsage, uint64_t aDelta)
+{
+  // Watch for overflow!
+  if ((UINT64_MAX - *aUsage) < aDelta) {
+    NS_WARNING("Usage exceeds the maximum!");
+    *aUsage = UINT64_MAX;
+  }
+  else {
+    *aUsage += aDelta;
+  }
+}
+
+inline bool
+PatternMatchesOrigin(const nsACString& aPatternString, const nsACString& aOrigin)
+{
+  // Aren't we smart!
+  return StringBeginsWith(aOrigin, aPatternString);
+}
+
+END_QUOTA_NAMESPACE
+
+#endif // mozilla_dom_quota_utilities_h__
--- a/dom/quota/moz.build
+++ b/dom/quota/moz.build
@@ -1,9 +1,15 @@
 # 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',
+]
+
 XPIDL_MODULE = 'dom_quota'
 
 MODULE = 'dom'
 
new file mode 100644
--- /dev/null
+++ b/dom/quota/nsIOfflineStorage.h
@@ -0,0 +1,92 @@
+/* -*- 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/. */
+
+#ifndef nsIOfflineStorage_h__
+#define nsIOfflineStorage_h__
+
+#include "nsIFileStorage.h"
+
+#define NS_OFFLINESTORAGE_IID \
+  {0xe531b6e0, 0x55b8, 0x4f39, \
+  { 0x95, 0xbb, 0x97, 0x21, 0x4c, 0xb0, 0xf6, 0x1a } }
+
+namespace mozilla {
+namespace dom {
+namespace quota {
+class Client;
+}
+}
+}
+
+class nsIOfflineStorage : public nsIFileStorage
+{
+public:
+  typedef mozilla::dom::quota::Client Client;
+
+  NS_DECLARE_STATIC_IID_ACCESSOR(NS_OFFLINESTORAGE_IID)
+
+  NS_IMETHOD_(Client*)
+  GetClient() = 0;
+
+  NS_IMETHOD_(bool)
+  IsOwned(nsPIDOMWindow* aOwner) = 0;
+
+  NS_IMETHOD_(const nsACString&)
+  Origin() = 0;
+
+  // Implementation of this method should close the storage (without aborting
+  // running operations nor discarding pending operations).
+  NS_IMETHOD_(nsresult)
+  Close() = 0;
+
+  // Whether or not the storage has had Close called on it.
+  NS_IMETHOD_(bool)
+  IsClosed() = 0;
+
+  // Implementation of this method should close the storage, all running
+  // operations should be aborted and pending operations should be discarded.
+  NS_IMETHOD_(void)
+  Invalidate() = 0;
+};
+
+NS_DEFINE_STATIC_IID_ACCESSOR(nsIOfflineStorage, NS_OFFLINESTORAGE_IID)
+
+#define NS_DECL_NSIOFFLINESTORAGE                                              \
+  NS_IMETHOD_(Client*)                                                         \
+  GetClient() MOZ_OVERRIDE;                                                    \
+                                                                               \
+  NS_IMETHOD_(bool)                                                            \
+  IsOwned(nsPIDOMWindow* aOwner) MOZ_OVERRIDE;                                 \
+                                                                               \
+  NS_IMETHOD_(const nsACString&)                                               \
+  Origin() MOZ_OVERRIDE;                                                       \
+                                                                               \
+  NS_IMETHOD_(nsresult)                                                        \
+  Close() MOZ_OVERRIDE;                                                        \
+                                                                               \
+  NS_IMETHOD_(bool)                                                            \
+  IsClosed() MOZ_OVERRIDE;                                                     \
+                                                                               \
+  NS_IMETHOD_(void)                                                            \
+  Invalidate() MOZ_OVERRIDE;
+
+#define NS_DECL_NSIOFFLINESTORAGE_NOCLOSE                                      \
+  NS_IMETHOD_(Client*)                                                         \
+  GetClient() MOZ_OVERRIDE;                                                    \
+                                                                               \
+  NS_IMETHOD_(bool)                                                            \
+  IsOwned(nsPIDOMWindow* aOwner) MOZ_OVERRIDE;                                 \
+                                                                               \
+  NS_IMETHOD_(const nsACString&)                                               \
+  Origin() MOZ_OVERRIDE;                                                       \
+                                                                               \
+  NS_IMETHOD_(bool)                                                            \
+  IsClosed() MOZ_OVERRIDE;                                                     \
+                                                                               \
+  NS_IMETHOD_(void)                                                            \
+  Invalidate() MOZ_OVERRIDE;
+
+#endif // nsIOfflineStorage_h__
new file mode 100644
--- /dev/null
+++ b/dom/quota/nsIQuotaManager.idl
@@ -0,0 +1,44 @@
+/* -*- 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 nsIQuotaRequest;
+interface nsIURI;
+interface nsIUsageCallback;
+
+[scriptable, builtinclass, uuid(8d74e6f8-81c3-4045-9bb7-70bdb7b11b25)]
+interface nsIQuotaManager : nsISupports
+{
+  /**
+   * Schedules an asynchronous callback that will return the total amount of
+   * disk space being used by storages for the given origin.
+   *
+   * @param aURI
+   *        The URI whose usage is being queried.
+   * @param aCallback
+   *        The callback that will be called when the usage is available.
+   */
+  [optional_argc]
+  nsIQuotaRequest
+  getUsageForURI(in nsIURI aURI,
+                 in nsIUsageCallback aCallback,
+                 [optional] in unsigned long aAppId,
+                 [optional] in boolean aInMozBrowserOnly);
+
+  /**
+   * Removes all storages stored for the given URI. The files may not be
+   * deleted immediately depending on prohibitive concurrent operations.
+   *
+   * @param aURI
+   *        The URI whose storages are to be cleared.
+   */
+  [optional_argc]
+  void
+  clearStoragesForURI(in nsIURI aURI,
+                      [optional] in unsigned long aAppId,
+                      [optional] in boolean aInMozBrowserOnly);
+};
new file mode 100644
--- /dev/null
+++ b/dom/quota/nsIQuotaRequest.idl
@@ -0,0 +1,14 @@
+/* -*- 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
+{
+  void
+  cancel();
+};
new file mode 100644
--- /dev/null
+++ b/dom/quota/nsIUsageCallback.idl
@@ -0,0 +1,20 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsISupports.idl"
+
+interface nsIURI;
+
+[scriptable, function, uuid(7b0f9928-0ddc-42c7-b9f2-6b2308b90b18)]
+interface nsIUsageCallback : nsISupports
+{
+  void
+  onUsageResult(in nsIURI aURI,
+                in unsigned long long aUsage,
+                in unsigned long long aFileUsage,
+                in unsigned long aAppId,
+                in boolean aInMozBrowserOnly);
+};
--- a/js/xpconnect/src/dom_quickstubs.qsconf
+++ b/js/xpconnect/src/dom_quickstubs.qsconf
@@ -210,23 +210,27 @@ members = [
     'nsIIDBFileHandle.*',
     'nsIIDBIndex.*',
     'nsIIDBKeyRange.*',
     'nsIIDBObjectStore.*',
     'nsIIDBRequest.*',
     'nsIIDBTransaction.*',
     'nsIIDBOpenDBRequest.*',
     'nsIIDBVersionChangeEvent.*',
-    'nsIIndexedDatabaseUsageCallback.*',
     'nsIIndexedDatabaseManager.*',
 
     'nsIDOMDOMError.*',
 
     # dom/file
     'nsIDOMLockedFile.*',
+
+    # dom/quota
+    'nsIQuotaManager.*',
+    'nsIQuotaRequest.*',
+    'nsIUsageCallback.*',
     ]
 
 # Most interfaces can be found by searching the includePath; to find
 # nsIDOMEvent, for example, just look for nsIDOMEvent.idl.  But IDL filenames
 # for very long interface names are slightly abbreviated, and many interfaces
 # don't have their own files, just for extra wackiness.  So qsgen.py needs
 # a little help.
 #
--- a/layout/build/nsLayoutCID.h
+++ b/layout/build/nsLayoutCID.h
@@ -90,9 +90,13 @@
 // {1A26A7B7-D06E-4F45-8B45-D7AD60F7A9AB}
 #define INDEXEDDB_MANAGER_CID \
 { 0x1a26a7b7, 0xd06e, 0x4f45, { 0x8b, 0x45, 0xd7, 0xad, 0x60, 0xf7, 0xa9, 0xab } }
 
 // {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 \
+{ 0x5a75c25a, 0x5e7e, 0x4d90, { 0x8f, 0x7c, 0x07, 0xeb, 0x15, 0xcc, 0x0a, 0xa8 } }
+
 #endif /* nsLayoutCID_h__ */
--- a/layout/build/nsLayoutModule.cpp
+++ b/layout/build/nsLayoutModule.cpp
@@ -85,16 +85,17 @@
 #include "nsJSON.h"
 #include "mozIApplicationClearPrivateDataParams.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/dom/Activity.h"
 #include "mozilla/dom/DOMRequest.h"
 #include "mozilla/dom/EventSource.h"
 #include "mozilla/dom/indexedDB/IndexedDatabaseManager.h"
 #include "mozilla/dom/network/TCPSocketChild.h"
+#include "mozilla/dom/quota/QuotaManager.h"
 #include "mozilla/OSFileConstants.h"
 #include "mozilla/Services.h"
 
 #ifdef MOZ_B2G_RIL
 #include "SystemWorkerManager.h"
 using mozilla::dom::gonk::SystemWorkerManager;
 #define SYSTEMWORKERMANAGER_CID \
   {0xd53b6524, 0x6ac3, 0x42b0, {0xae, 0xca, 0x62, 0xb3, 0xc4, 0xe5, 0x2b, 0x04}}
@@ -243,16 +244,17 @@ static void Shutdown();
 #include "MediaManager.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 using namespace mozilla::dom::mobilemessage;
 using mozilla::dom::alarm::AlarmHalService;
 using mozilla::dom::indexedDB::IndexedDatabaseManager;
 using mozilla::dom::power::PowerManagerService;
+using mozilla::dom::quota::QuotaManager;
 using mozilla::dom::TCPSocketChild;
 using mozilla::dom::time::TimeService;
 
 // Transformiix
 /* 5d5d92cd-6bf8-11d9-bf4a-000a95dc234c */
 #define TRANSFORMIIX_NODESET_CID \
 { 0x5d5d92cd, 0x6bf8, 0x11d9, { 0xbf, 0x4a, 0x0, 0x0a, 0x95, 0xdc, 0x23, 0x4c } }
 
@@ -275,16 +277,18 @@ NS_GENERIC_FACTORY_CONSTRUCTOR(nsHostObj
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsDOMParser)
 NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(nsDOMStorageManager,
                                          nsDOMStorageManager::GetInstance)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsChannelPolicy)
 NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(IndexedDatabaseManager,
                                          IndexedDatabaseManager::FactoryCreate)
 NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(DOMRequestService,
                                          DOMRequestService::FactoryCreate)
+NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(QuotaManager,
+                                         QuotaManager::FactoryCreate)
 #ifdef MOZ_B2G_RIL
 NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(SystemWorkerManager,
                                          SystemWorkerManager::FactoryCreate)
 #endif
 #ifdef MOZ_B2G_BT
 NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(BluetoothService,
                                          BluetoothService::FactoryCreate)
 #endif
@@ -778,16 +782,17 @@ NS_DEFINE_NAMED_CID(NS_EVENTSOURCE_CID);
 NS_DEFINE_NAMED_CID(NS_DOMACTIVITY_CID);
 NS_DEFINE_NAMED_CID(NS_DOMPARSER_CID);
 NS_DEFINE_NAMED_CID(NS_DOMSTORAGE2_CID);
 NS_DEFINE_NAMED_CID(NS_DOMSTORAGEMANAGER_CID);
 NS_DEFINE_NAMED_CID(NS_DOMJSON_CID);
 NS_DEFINE_NAMED_CID(NS_TEXTEDITOR_CID);
 NS_DEFINE_NAMED_CID(INDEXEDDB_MANAGER_CID);
 NS_DEFINE_NAMED_CID(DOMREQUEST_SERVICE_CID);
+NS_DEFINE_NAMED_CID(QUOTA_MANAGER_CID);
 #ifdef MOZ_B2G_RIL
 NS_DEFINE_NAMED_CID(SYSTEMWORKERMANAGER_CID);
 #endif
 #ifdef MOZ_B2G_BT
 NS_DEFINE_NAMED_CID(BLUETOOTHSERVICE_CID);
 #endif
 #ifdef MOZ_WIDGET_GONK
 NS_DEFINE_NAMED_CID(NS_AUDIOMANAGER_CID);
@@ -1064,16 +1069,17 @@ static const mozilla::Module::CIDEntry k
   { &kNS_DOMACTIVITY_CID, false, NULL, ActivityConstructor },
   { &kNS_DOMPARSER_CID, false, NULL, nsDOMParserConstructor },
   { &kNS_DOMSTORAGE2_CID, false, NULL, NS_NewDOMStorage2 },
   { &kNS_DOMSTORAGEMANAGER_CID, false, NULL, nsDOMStorageManagerConstructor },
   { &kNS_DOMJSON_CID, false, NULL, NS_NewJSON },
   { &kNS_TEXTEDITOR_CID, false, NULL, nsPlaintextEditorConstructor },
   { &kINDEXEDDB_MANAGER_CID, false, NULL, IndexedDatabaseManagerConstructor },
   { &kDOMREQUEST_SERVICE_CID, false, NULL, DOMRequestServiceConstructor },
+  { &kQUOTA_MANAGER_CID, false, NULL, QuotaManagerConstructor },
 #ifdef MOZ_B2G_RIL
   { &kSYSTEMWORKERMANAGER_CID, true, NULL, SystemWorkerManagerConstructor },
 #endif
 #ifdef MOZ_B2G_BT
   { &kBLUETOOTHSERVICE_CID, true, NULL, BluetoothServiceConstructor },
 #endif
 #ifdef MOZ_WIDGET_GONK
   { &kNS_AUDIOMANAGER_CID, true, NULL, AudioManagerConstructor },
@@ -1211,16 +1217,17 @@ static const mozilla::Module::ContractID
   { NS_DOMACTIVITY_CONTRACTID, &kNS_DOMACTIVITY_CID },
   { NS_DOMPARSER_CONTRACTID, &kNS_DOMPARSER_CID },
   { "@mozilla.org/dom/storage;2", &kNS_DOMSTORAGE2_CID },
   { "@mozilla.org/dom/storagemanager;1", &kNS_DOMSTORAGEMANAGER_CID },
   { "@mozilla.org/dom/json;1", &kNS_DOMJSON_CID },
   { "@mozilla.org/editor/texteditor;1", &kNS_TEXTEDITOR_CID },
   { INDEXEDDB_MANAGER_CONTRACTID, &kINDEXEDDB_MANAGER_CID },
   { DOMREQUEST_SERVICE_CONTRACTID, &kDOMREQUEST_SERVICE_CID },
+  { QUOTA_MANAGER_CONTRACTID, &kQUOTA_MANAGER_CID },
 #ifdef MOZ_B2G_RIL
   { SYSTEMWORKERMANAGER_CONTRACTID, &kSYSTEMWORKERMANAGER_CID },
 #endif
 #ifdef MOZ_B2G_BT
   { BLUETOOTHSERVICE_CONTRACTID, &kBLUETOOTHSERVICE_CID },
 #endif
 #ifdef MOZ_WIDGET_GONK
   { NS_AUDIOMANAGER_CONTRACTID, &kNS_AUDIOMANAGER_CID },
@@ -1289,17 +1296,17 @@ static const mozilla::Module::CategoryEn
 #endif
   { "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 },
   { JAVASCRIPT_GLOBAL_STATIC_NAMESET_CATEGORY, "PrivilegeManager", NS_SECURITYNAMESET_CONTRACTID },
   { "app-startup", "Script Security Manager", "service," NS_SCRIPTSECURITYMANAGER_CONTRACTID },
-  { TOPIC_WEB_APP_CLEAR_DATA, "IndexedDatabaseManager", "service," INDEXEDDB_MANAGER_CONTRACTID },
+  { TOPIC_WEB_APP_CLEAR_DATA, "QuotaManager", "service," QUOTA_MANAGER_CONTRACTID },
 #ifdef MOZ_WIDGET_GONK
   { "app-startup", "Volume Service", "service," NS_VOLUMESERVICE_CONTRACTID },
 #endif
   CONTENTDLF_CATEGORIES
 #ifdef MOZ_B2G_RIL
   { "profile-after-change", "Telephony System Worker Manager", SYSTEMWORKERMANAGER_CONTRACTID },
 #endif
 #ifdef MOZ_B2G_BT
--- a/mobile/android/installer/package-manifest.in
+++ b/mobile/android/installer/package-manifest.in
@@ -126,16 +126,17 @@
 @BINPATH@/components/dom_network.xpt
 @BINPATH@/components/dom_notification.xpt
 @BINPATH@/components/dom_html.xpt
 @BINPATH@/components/dom_indexeddb.xpt
 @BINPATH@/components/dom_offline.xpt
 @BINPATH@/components/dom_json.xpt
 @BINPATH@/components/dom_browserelement.xpt
 @BINPATH@/components/dom_power.xpt
+@BINPATH@/components/dom_quota.xpt
 @BINPATH@/components/dom_range.xpt
 @BINPATH@/components/dom_settings.xpt
 @BINPATH@/components/dom_sidebar.xpt
 @BINPATH@/components/dom_mobilemessage.xpt
 @BINPATH@/components/dom_storage.xpt
 @BINPATH@/components/dom_stylesheets.xpt
 @BINPATH@/components/dom_system.xpt
 @BINPATH@/components/dom_threads.xpt
--- a/storage/src/TelemetryVFS.cpp
+++ b/storage/src/TelemetryVFS.cpp
@@ -6,16 +6,17 @@
 
 #include <string.h>
 #include "mozilla/Telemetry.h"
 #include "mozilla/Preferences.h"
 #include "sqlite3.h"
 #include "nsThreadUtils.h"
 #include "mozilla/Util.h"
 #include "mozilla/dom/quota/QuotaManager.h"
+#include "mozilla/dom/quota/QuotaObject.h"
 
 /**
  * This preference is a workaround to allow users/sysadmins to identify
  * that the profile exists on an NFS share whose implementation
  * is incompatible with SQLite's default locking implementation.
  * Bug 433129 attempted to automatically identify such file-systems, 
  * but a reliable way was not found and it was determined that the fallback 
  * locking is slower than POSIX locking, so we do not want to do it by default.
--- a/toolkit/forgetaboutsite/ForgetAboutSite.jsm
+++ b/toolkit/forgetaboutsite/ForgetAboutSite.jsm
@@ -204,27 +204,27 @@ this.ForgetAboutSite = {
         let enumerator = cp.getPrefs(uri, null).enumerator;
         while (enumerator.hasMoreElements()) {
           let pref = enumerator.getNext().QueryInterface(Ci.nsIProperty);
           cp.removePref(uri, pref.name, null);
         }
       }
     }
 
-    // Indexed DB
-    let (idbm = Cc["@mozilla.org/dom/indexeddb/manager;1"].
-                getService(Ci.nsIIndexedDatabaseManager)) {
+    // Offline Storages
+    let (qm = Cc["@mozilla.org/dom/quota/manager;1"].
+              getService(Ci.nsIQuotaManager)) {
       // 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);
-      idbm.clearDatabasesForURI(httpURI);
-      idbm.clearDatabasesForURI(httpsURI);
+      qm.clearStoragesForURI(httpURI);
+      qm.clearStoragesForURI(httpsURI);
     }
 
     // Everybody else (including extensions)
     Services.obs.notifyObservers(null, "browser:purge-domain-data", aDomain);
   }
 };