Bug 820715 - Move quota related pieces from IndexedDatabaseManager to QuotaManager. r=bent
authorJan Varga <jan.varga@gmail.com>
Wed, 19 Dec 2012 18:45:57 +0100
changeset 116517 f0e43f3770fbb2223eebe432e67d52bc1e40ae67
parent 116516 b645cf8f8796b8cba1ee7986f64d518ae289e080
child 116518 541c69c0b116d949fcb4566580569717797aa7b0
push id19976
push userJan.Varga@gmail.com
push dateWed, 19 Dec 2012 17:47:00 +0000
treeherdermozilla-inbound@f0e43f3770fb [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbent
bugs820715
milestone20.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 820715 - Move quota related pieces from IndexedDatabaseManager to QuotaManager. r=bent
dom/indexedDB/AsyncConnectionHelper.cpp
dom/indexedDB/CheckQuotaHelper.cpp
dom/indexedDB/CheckQuotaHelper.h
dom/indexedDB/IDBDatabase.cpp
dom/indexedDB/IDBTransaction.cpp
dom/indexedDB/IndexedDatabaseManager.cpp
dom/indexedDB/IndexedDatabaseManager.h
dom/indexedDB/Makefile.in
dom/quota/CheckQuotaHelper.cpp
dom/quota/CheckQuotaHelper.h
dom/quota/Makefile.in
dom/quota/QuotaManager.cpp
dom/quota/QuotaManager.h
--- a/dom/indexedDB/AsyncConnectionHelper.cpp
+++ b/dom/indexedDB/AsyncConnectionHelper.cpp
@@ -3,32 +3,34 @@
 /* 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 "base/basictypes.h"
 
 #include "AsyncConnectionHelper.h"
 
+#include "mozilla/dom/quota/QuotaManager.h"
 #include "mozilla/storage.h"
 #include "nsComponentManagerUtils.h"
 #include "nsContentUtils.h"
 #include "nsProxyRelease.h"
 #include "nsThreadUtils.h"
 #include "nsWrapperCacheInlines.h"
 
 #include "IDBEvents.h"
 #include "IDBTransaction.h"
 #include "IndexedDatabaseManager.h"
 #include "TransactionThreadPool.h"
 
 #include "ipc/IndexedDBChild.h"
 #include "ipc/IndexedDBParent.h"
 
 USING_INDEXEDDB_NAMESPACE
+using mozilla::dom::quota::QuotaManager;
 
 namespace {
 
 IDBTransaction* gCurrentTransaction = nullptr;
 
 const uint32_t kProgressHandlerGranularity = 1000;
 
 class TransactionPoolEventTarget : public StackBasedEventTarget
@@ -282,17 +284,17 @@ AsyncConnectionHelper::Run()
     if (NS_SUCCEEDED(rv)) {
       setProgressHandler = true;
     }
   }
 
   if (NS_SUCCEEDED(rv)) {
     bool hasSavepoint = false;
     if (mDatabase) {
-      IndexedDatabaseManager::SetCurrentWindow(mDatabase->GetOwner());
+      QuotaManager::SetCurrentWindow(mDatabase->GetOwner());
 
       // Make the first savepoint.
       if (mTransaction) {
         if (!(hasSavepoint = mTransaction->StartSavepoint())) {
           NS_WARNING("Failed to make savepoint!");
         }
       }
     }
@@ -308,17 +310,17 @@ AsyncConnectionHelper::Run()
         }
         else {
           mTransaction->RollbackSavepoint();
         }
       }
 
       // Don't unset this until we're sure that all SQLite activity has
       // completed!
-      IndexedDatabaseManager::SetCurrentWindow(nullptr);
+      QuotaManager::SetCurrentWindow(nullptr);
     }
   }
   else {
     // NS_ERROR_NOT_AVAILABLE is our special code for "database is invalidated"
     // and we should fail with RECOVERABLE_ERR.
     if (rv == NS_ERROR_NOT_AVAILABLE) {
       mResultCode = NS_ERROR_DOM_INDEXEDDB_RECOVERABLE_ERR;
     }
--- a/dom/indexedDB/IDBDatabase.cpp
+++ b/dom/indexedDB/IDBDatabase.cpp
@@ -6,24 +6,24 @@
 
 #include "base/basictypes.h"
 
 #include "IDBDatabase.h"
 
 #include "mozilla/Mutex.h"
 #include "mozilla/storage.h"
 #include "mozilla/dom/ContentParent.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 "CheckQuotaHelper.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"
@@ -32,16 +32,17 @@
 #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::QuotaManager;
 
 namespace {
 
 class NoRequestDatabaseHelper : public AsyncConnectionHelper
 {
 public:
   NoRequestDatabaseHelper(IDBTransaction* aTransaction)
   : AsyncConnectionHelper(aTransaction, nullptr)
@@ -253,21 +254,21 @@ IDBDatabase::Invalidate()
   }
 
   mInvalidated = true;
 
   // Make sure we're closed too.
   Close();
 
   // When the IndexedDatabaseManager needs to invalidate databases, all it has
-  // is an origin, so we call back into the manager to cancel any prompts for
-  // our owner.
+  // is an origin, so we call into the quota manager here to cancel any prompts
+  // for our owner.
   nsPIDOMWindow* owner = GetOwner();
   if (owner) {
-    IndexedDatabaseManager::CancelPromptsForWindow(owner);
+    QuotaManager::CancelPromptsForWindow(owner);
   }
 
   DatabaseInfo::Remove(mDatabaseId);
 
   // And let the child process know as well.
   if (mActorParent) {
     NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
     mActorParent->Invalidate();
@@ -281,17 +282,17 @@ IDBDatabase::DisconnectFromActorParent()
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
   // Make sure we're closed too.
   Close();
 
   // Kill any outstanding prompts.
   nsPIDOMWindow* owner = GetOwner();
   if (owner) {
-    IndexedDatabaseManager::CancelPromptsForWindow(owner);
+    QuotaManager::CancelPromptsForWindow(owner);
   }
 }
 
 void
 IDBDatabase::CloseInternal(bool aIsDead)
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
@@ -802,23 +803,23 @@ IDBDatabase::IsStorageShuttingDown()
 {
   return IndexedDatabaseManager::IsShuttingDown();
 }
 
 void
 IDBDatabase::SetThreadLocals()
 {
   NS_ASSERTION(GetOwner(), "Should have owner!");
-  IndexedDatabaseManager::SetCurrentWindow(GetOwner());
+  QuotaManager::SetCurrentWindow(GetOwner());
 }
 
 void
 IDBDatabase::UnsetThreadLocals()
 {
-  IndexedDatabaseManager::SetCurrentWindow(nullptr);
+  QuotaManager::SetCurrentWindow(nullptr);
 }
 
 nsresult
 IDBDatabase::PostHandleEvent(nsEventChainPostVisitor& aVisitor)
 {
   return IndexedDatabaseManager::FireWindowOnError(GetOwner(), aVisitor);
 }
 
--- a/dom/indexedDB/IDBTransaction.cpp
+++ b/dom/indexedDB/IDBTransaction.cpp
@@ -7,16 +7,17 @@
 #include "base/basictypes.h"
 
 #include "IDBTransaction.h"
 
 #include "nsIAppShell.h"
 #include "nsIScriptContext.h"
 
 #include "DOMError.h"
+#include "mozilla/dom/quota/QuotaManager.h"
 #include "mozilla/storage.h"
 #include "nsContentUtils.h"
 #include "nsDOMClassInfoID.h"
 #include "nsDOMLists.h"
 #include "nsEventDispatcher.h"
 #include "nsPIDOMWindow.h"
 #include "nsProxyRelease.h"
 #include "nsThreadUtils.h"
@@ -31,16 +32,17 @@
 #include "IndexedDatabaseManager.h"
 #include "TransactionThreadPool.h"
 
 #include "ipc/IndexedDBChild.h"
 
 #define SAVEPOINT_NAME "savepoint"
 
 USING_INDEXEDDB_NAMESPACE
+using mozilla::dom::quota::QuotaManager;
 
 namespace {
 
 NS_DEFINE_CID(kAppShellCID, NS_APPSHELL_CID);
 
 PLDHashOperator
 DoomCachedStatements(const nsACString& aQuery,
                      nsCOMPtr<mozIStorageStatement>& aStatement,
@@ -888,17 +890,17 @@ CommitHelper::Run()
   }
 
   IDBDatabase* database = mTransaction->Database();
   if (database->IsInvalidated()) {
     mAbortCode = NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
   }
 
   if (mConnection) {
-    IndexedDatabaseManager::SetCurrentWindow(database->GetOwner());
+    QuotaManager::SetCurrentWindow(database->GetOwner());
 
     if (NS_SUCCEEDED(mAbortCode) && mUpdateFileRefcountFunction &&
         NS_FAILED(mUpdateFileRefcountFunction->WillCommit(mConnection))) {
       mAbortCode = NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
     }
 
     if (NS_SUCCEEDED(mAbortCode) && NS_FAILED(WriteAutoIncrementCounts())) {
       mAbortCode = NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
@@ -944,17 +946,17 @@ CommitHelper::Run()
       if (NS_FAILED(rv)) {
         NS_WARNING("Failed to remove function!");
       }
     }
 
     mConnection->Close();
     mConnection = nullptr;
 
-    IndexedDatabaseManager::SetCurrentWindow(nullptr);
+    QuotaManager::SetCurrentWindow(nullptr);
   }
 
   return NS_OK;
 }
 
 nsresult
 CommitHelper::WriteAutoIncrementCounts()
 {
--- a/dom/indexedDB/IndexedDatabaseManager.cpp
+++ b/dom/indexedDB/IndexedDatabaseManager.cpp
@@ -32,20 +32,18 @@
 #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 "xpcpublic.h"
 
 #include "AsyncConnectionHelper.h"
-#include "CheckQuotaHelper.h"
 #include "IDBDatabase.h"
 #include "IDBEvents.h"
 #include "IDBFactory.h"
 #include "IDBKeyRange.h"
 #include "OpenDatabaseHelper.h"
 #include "TransactionThreadPool.h"
 
 #include "IndexedDatabaseInlines.h"
@@ -341,19 +339,17 @@ GetASCIIOriginFromPrincipal(nsIPrincipal
 
   aOrigin.Assign(origin);
   return NS_OK;
 }
 
 } // anonymous namespace
 
 IndexedDatabaseManager::IndexedDatabaseManager()
-: mCurrentWindowIndex(BAD_TLS_INDEX),
-  mQuotaHelperMutex("IndexedDatabaseManager.mQuotaHelperMutex"),
-  mFileMutex("IndexedDatabaseManager.mFileMutex")
+: mFileMutex("IndexedDatabaseManager.mFileMutex")
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
   NS_ASSERTION(!gInstance, "More than one instance!");
 }
 
 IndexedDatabaseManager::~IndexedDatabaseManager()
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
@@ -377,29 +373,18 @@ IndexedDatabaseManager::GetOrCreate()
   nsRefPtr<IndexedDatabaseManager> instance(gInstance);
 
   if (!instance) {
     sIsMainProcess = XRE_GetProcessType() == GeckoProcessType_Default;
 
     instance = new IndexedDatabaseManager();
 
     instance->mLiveDatabases.Init();
-    instance->mQuotaHelperHash.Init();
     instance->mFileManagers.Init();
 
-    // We need a thread-local to hold the current window.
-    NS_ASSERTION(instance->mCurrentWindowIndex == BAD_TLS_INDEX, "Huh?");
-
-    if (PR_NewThreadPrivateIndex(&instance->mCurrentWindowIndex, nullptr) !=
-        PR_SUCCESS) {
-      NS_ERROR("PR_NewThreadPrivateIndex failed, IndexedDB disabled");
-      instance->mCurrentWindowIndex = BAD_TLS_INDEX;
-      return nullptr;
-    }
-
     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,
@@ -415,25 +400,25 @@ IndexedDatabaseManager::GetOrCreate()
 
       // 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 sure that the quota manager is up.
-      NS_ENSURE_TRUE(QuotaManager::GetOrCreate(), nullptr);
-
       // 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);
     }
 
+    // Make sure that the quota manager is up.
+    NS_ENSURE_TRUE(QuotaManager::GetOrCreate(), nullptr);
+
     nsCOMPtr<nsIObserverService> obs = GetObserverService();
     NS_ENSURE_TRUE(obs, nullptr);
 
     // We need this callback to know when to shut down all our threads.
     rv = obs->AddObserver(instance, PROFILE_BEFORE_CHANGE_OBSERVER_ID, false);
     NS_ENSURE_SUCCESS(rv, nullptr);
 
     if (NS_FAILED(Preferences::AddIntVarCache(&gIndexedDBQuotaMB,
@@ -907,35 +892,16 @@ IndexedDatabaseManager::OnDatabaseClosed
         if (NS_FAILED(RunSynchronizedOp(aDatabase, op))) {
           NS_WARNING("Failed to run synchronized op!");
         }
       }
     }
   }
 }
 
-void
-IndexedDatabaseManager::SetCurrentWindowInternal(nsPIDOMWindow* aWindow)
-{
-  if (aWindow) {
-#ifdef DEBUG
-    NS_ASSERTION(!PR_GetThreadPrivate(mCurrentWindowIndex),
-                 "Somebody forgot to clear the current window!");
-#endif
-    PR_SetThreadPrivate(mCurrentWindowIndex, aWindow);
-  }
-  else {
-    // We cannot assert PR_GetThreadPrivate(mCurrentWindowIndex) here
-    // because we cannot distinguish between the thread private became
-    // null and that it was set to null on the first place, 
-    // because we didn't have a window.
-    PR_SetThreadPrivate(mCurrentWindowIndex, nullptr);
-  }
-}
-
 // static
 uint32_t
 IndexedDatabaseManager::GetIndexedDBQuotaMB()
 {
   return uint32_t(NS_MAX(gIndexedDBQuotaMB, 0));
 }
 
 nsresult
@@ -1111,81 +1077,16 @@ IndexedDatabaseManager::UninitializeOrig
 
   for (int32_t i = mInitializedOrigins.Length() - 1; i >= 0; i--) {
     if (PatternMatchesOrigin(aPattern, mInitializedOrigins[i])) {
       mInitializedOrigins.RemoveElementAt(i);
     }
   }
 }
 
-bool
-IndexedDatabaseManager::QuotaIsLiftedInternal()
-{
-  nsPIDOMWindow* window = nullptr;
-  nsRefPtr<CheckQuotaHelper> helper = nullptr;
-  bool createdHelper = false;
-
-  window =
-    static_cast<nsPIDOMWindow*>(PR_GetThreadPrivate(mCurrentWindowIndex));
-
-  // Once IDB is supported outside of Windows this should become an early
-  // return true.
-  NS_ASSERTION(window, "Why don't we have a Window here?");
-
-  // Hold the lock from here on.
-  MutexAutoLock autoLock(mQuotaHelperMutex);
-
-  mQuotaHelperHash.Get(window, getter_AddRefs(helper));
-
-  if (!helper) {
-    helper = new CheckQuotaHelper(window, mQuotaHelperMutex);
-    createdHelper = true;
-
-    mQuotaHelperHash.Put(window, helper);
-
-    // Unlock while calling out to XPCOM
-    {
-      MutexAutoUnlock autoUnlock(mQuotaHelperMutex);
-
-      nsresult rv = NS_DispatchToMainThread(helper);
-      NS_ENSURE_SUCCESS(rv, false);
-    }
-
-    // Relocked.  If any other threads hit the quota limit on the same Window,
-    // they are using the helper we created here and are now blocking in
-    // PromptAndReturnQuotaDisabled.
-  }
-
-  bool result = helper->PromptAndReturnQuotaIsDisabled();
-
-  // If this thread created the helper and added it to the hash, this thread
-  // must remove it.
-  if (createdHelper) {
-    mQuotaHelperHash.Remove(window);
-  }
-
-  return result;
-}
-
-void
-IndexedDatabaseManager::CancelPromptsForWindowInternal(nsPIDOMWindow* aWindow)
-{
-  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
-
-  nsRefPtr<CheckQuotaHelper> helper;
-
-  MutexAutoLock autoLock(mQuotaHelperMutex);
-
-  mQuotaHelperHash.Get(aWindow, getter_AddRefs(helper));
-
-  if (helper) {
-    helper->Cancel();
-  }
-}
-
 // static
 nsresult
 IndexedDatabaseManager::GetASCIIOriginFromWindow(nsPIDOMWindow* aWindow,
                                                  nsCString& aASCIIOrigin)
 {
   NS_ASSERTION(NS_IsMainThread(),
                "We're about to touch a window off the main thread!");
 
--- a/dom/indexedDB/IndexedDatabaseManager.h
+++ b/dom/indexedDB/IndexedDatabaseManager.h
@@ -34,17 +34,16 @@ namespace dom {
 class TabContext;
 }
 }
 
 
 BEGIN_INDEXEDDB_NAMESPACE
 
 class AsyncConnectionHelper;
-class CheckQuotaHelper;
 class FileManager;
 class IDBDatabase;
 
 class IndexedDatabaseManager MOZ_FINAL : public nsIIndexedDatabaseManager,
                                          public nsIObserver
 {
   friend class IDBDatabase;
 
@@ -110,56 +109,25 @@ public:
   // 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);
 
   // Used to check if there are running transactions in a given window.
   bool HasOpenTransactions(nsPIDOMWindow* aWindow);
 
-  // Set the Window that the current thread is doing operations for.
-  // The caller is responsible for ensuring that aWindow is held alive.
-  static inline void
-  SetCurrentWindow(nsPIDOMWindow* aWindow)
-  {
-    IndexedDatabaseManager* mgr = Get();
-    NS_ASSERTION(mgr, "Must have a manager here!");
-
-    return mgr->SetCurrentWindowInternal(aWindow);
-  }
-
   static uint32_t
   GetIndexedDBQuotaMB();
 
   nsresult EnsureOriginIsInitialized(const nsACString& aOrigin,
                                      FactoryPrivilege aPrivilege,
                                      nsIFile** aDirectory);
 
   void UninitializeOriginsByPattern(const nsACString& aPattern);
 
-  // Determine if the quota is lifted for the Window the current thread is
-  // using.
-  static inline bool
-  QuotaIsLifted()
-  {
-    IndexedDatabaseManager* mgr = Get();
-    NS_ASSERTION(mgr, "Must have a manager here!");
-
-    return mgr->QuotaIsLiftedInternal();
-  }
-
-  static inline void
-  CancelPromptsForWindow(nsPIDOMWindow* aWindow)
-  {
-    IndexedDatabaseManager* mgr = Get();
-    NS_ASSERTION(mgr, "Must have a manager here!");
-
-    mgr->CancelPromptsForWindowInternal(aWindow);
-  }
-
   static nsresult
   GetASCIIOriginFromWindow(nsPIDOMWindow* aWindow, nsCString& aASCIIOrigin);
 
   static bool
   IsMainProcess()
 #ifdef DEBUG
   ;
 #else
@@ -219,20 +187,16 @@ private:
 
   nsresult AcquireExclusiveAccess(const nsACString& aOrigin,
                                   IDBDatabase* aDatabase,
                                   AsyncConnectionHelper* aHelper,
                                   nsIRunnable* aRunnable,
                                   WaitingOnDatabasesCallback aCallback,
                                   void* aClosure);
 
-  void SetCurrentWindowInternal(nsPIDOMWindow* aWindow);
-  bool QuotaIsLiftedInternal();
-  void CancelPromptsForWindowInternal(nsPIDOMWindow* aWindow);
-
   // 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);
@@ -469,25 +433,16 @@ private:
   bool IsClearOriginPending(const nsACString& aPattern)
   {
     return !!FindSynchronizedOp(aPattern, nullptr);
   }
 
   // Maintains a list of live databases per origin.
   nsClassHashtable<nsCStringHashKey, nsTArray<IDBDatabase*> > mLiveDatabases;
 
-  // TLS storage index for the current thread's window
-  unsigned mCurrentWindowIndex;
-
-  // Lock protecting mQuotaHelperHash
-  mozilla::Mutex mQuotaHelperMutex;
-
-  // A map of Windows to the corresponding quota helper.
-  nsRefPtrHashtable<nsPtrHashKey<nsPIDOMWindow>, CheckQuotaHelper> mQuotaHelperHash;
-
   // 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;
@@ -510,25 +465,11 @@ private:
   // and FileInfo.mSliceRefCnt
   mozilla::Mutex mFileMutex;
 
   nsString mDatabaseBasePath;
 
   static bool sIsMainProcess;
 };
 
-class AutoEnterWindow
-{
-public:
-  AutoEnterWindow(nsPIDOMWindow* aWindow)
-  {
-    IndexedDatabaseManager::SetCurrentWindow(aWindow);
-  }
-
-  ~AutoEnterWindow()
-  {
-    IndexedDatabaseManager::SetCurrentWindow(nullptr);
-  }
-};
-
 END_INDEXEDDB_NAMESPACE
 
 #endif /* mozilla_dom_indexeddb_indexeddatabasemanager_h__ */
--- a/dom/indexedDB/Makefile.in
+++ b/dom/indexedDB/Makefile.in
@@ -16,17 +16,16 @@ XPIDL_MODULE = dom_indexeddb
 LIBXUL_LIBRARY = 1
 FORCE_STATIC_LIB = 1
 
 EXPORTS_NAMESPACES = mozilla/dom/indexedDB
 
 CPPSRCS = \
   AsyncConnectionHelper.cpp \
   CheckPermissionsHelper.cpp \
-  CheckQuotaHelper.cpp \
   DatabaseInfo.cpp \
   FileInfo.cpp \
   FileManager.cpp \
   IDBCursor.cpp \
   IDBDatabase.cpp \
   IDBEvents.cpp \
   IDBFactory.cpp \
   IDBFileHandle.cpp \
rename from dom/indexedDB/CheckQuotaHelper.cpp
rename to dom/quota/CheckQuotaHelper.cpp
--- a/dom/indexedDB/CheckQuotaHelper.cpp
+++ b/dom/quota/CheckQuotaHelper.cpp
@@ -7,33 +7,33 @@
 #include "CheckQuotaHelper.h"
 
 #include "nsIDOMWindow.h"
 #include "nsIObserverService.h"
 #include "nsIPermissionManager.h"
 #include "nsIPrincipal.h"
 #include "nsIScriptObjectPrincipal.h"
 #include "nsIURI.h"
-#include "nsXULAppAPI.h"
 
+#include "mozilla/dom/indexedDB/IndexedDatabaseManager.h"
+#include "mozilla/Services.h"
 #include "nsContentUtils.h"
 #include "nsNetUtil.h"
 #include "nsThreadUtils.h"
-#include "mozilla/Services.h"
-
-#include "IndexedDatabaseManager.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_INDEXEDDB_NAMESPACE
+USING_QUOTA_NAMESPACE
 using namespace mozilla::services;
+using mozilla::dom::indexedDB::IndexedDatabaseManager;
 using mozilla::MutexAutoLock;
 
 namespace {
 
 inline
 uint32_t
 GetQuotaPermissions(nsIDOMWindow* aWindow)
 {
rename from dom/indexedDB/CheckQuotaHelper.h
rename to dom/quota/CheckQuotaHelper.h
--- a/dom/indexedDB/CheckQuotaHelper.h
+++ b/dom/quota/CheckQuotaHelper.h
@@ -1,31 +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_indexeddb_checkquotahelper_h__
-#define mozilla_dom_indexeddb_checkquotahelper_h__
+#ifndef mozilla_dom_quota_checkquotahelper_h__
+#define mozilla_dom_quota_checkquotahelper_h__
 
-// Only meant to be included in IndexedDB source files, not exported.
-#include "IndexedDatabase.h"
-#include "IDBDatabase.h"
+#include "QuotaCommon.h"
 
 #include "nsIInterfaceRequestor.h"
 #include "nsIObserver.h"
 #include "nsIRunnable.h"
 
 #include "mozilla/Mutex.h"
 #include "mozilla/CondVar.h"
 
 class nsPIDOMWindow;
 
-BEGIN_INDEXEDDB_NAMESPACE
+BEGIN_QUOTA_NAMESPACE
 
 class CheckQuotaHelper MOZ_FINAL : public nsIRunnable,
                                    public nsIInterfaceRequestor,
                                    public nsIObserver
 {
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSIRUNNABLE
@@ -44,11 +42,11 @@ private:
 
   mozilla::Mutex& mMutex;
   mozilla::CondVar mCondVar;
   uint32_t mPromptResult;
   bool mWaiting;
   bool mHasPrompted;
 };
 
-END_INDEXEDDB_NAMESPACE
+END_QUOTA_NAMESPACE
 
 #endif // mozilla_dom_indexeddb_checkquotahelper_h__
--- a/dom/quota/Makefile.in
+++ b/dom/quota/Makefile.in
@@ -15,16 +15,17 @@ XPIDL_MODULE     = dom_quota
 LIBXUL_LIBRARY   = 1
 FORCE_STATIC_LIB = 1
 
 include $(topsrcdir)/dom/dom-config.mk
 
 EXPORTS_NAMESPACES = mozilla/dom/quota
 
 CPPSRCS = \
+  CheckQuotaHelper.cpp \
   FileStreams.cpp \
   QuotaManager.cpp \
   $(NULL)
 
 EXPORTS_mozilla/dom/quota = \
   FileStreams.h \
   QuotaCommon.h \
   QuotaManager.h \
--- a/dom/quota/QuotaManager.cpp
+++ b/dom/quota/QuotaManager.cpp
@@ -5,18 +5,19 @@
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "QuotaManager.h"
 
 #include "nsIFile.h"
 
 #include "mozilla/ClearOnShutdown.h"
 #include "nsComponentManagerUtils.h"
+#include "xpcpublic.h"
 
-#include "mozilla/dom/indexedDB/IndexedDatabaseManager.h"
+#include "CheckQuotaHelper.h"
 
 USING_QUOTA_NAMESPACE
 
 namespace {
 
 nsAutoPtr<QuotaManager> gInstance;
 
 PLDHashOperator
@@ -116,28 +117,45 @@ QuotaObject::MaybeAllocateMoreSpace(int6
   MutexAutoLock lock(quotaManager->mQuotaMutex);
 
   if (mSize >= end || !mOriginInfo) {
     return true;
   }
 
   int64_t newUsage = mOriginInfo->mUsage - mSize + end;
   if (newUsage > mOriginInfo->mLimit) {
-    if (!indexedDB::IndexedDatabaseManager::QuotaIsLifted()) {
+    // 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;
 
@@ -166,39 +184,73 @@ OriginInfo::ClearOriginInfoCallback(cons
   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!");
+}
+
+QuotaManager::~QuotaManager()
+{
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+}
+
 // static
 QuotaManager*
 QuotaManager::GetOrCreate()
 {
   if (!gInstance) {
     NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
-    gInstance = new QuotaManager();
+    nsAutoPtr<QuotaManager> instance(new QuotaManager());
+
+    NS_ENSURE_TRUE(instance->Init(), nullptr);
+
+    gInstance = instance.forget();
 
     ClearOnShutdown(&gInstance);
   }
 
   return gInstance;
 }
 
 // static
 QuotaManager*
 QuotaManager::Get()
 {
   // Does not return an owning reference.
   return gInstance;
 }
 
+bool
+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;
+  }
+
+  mOriginInfos.Init();
+  mCheckQuotaHelpers.Init();
+
+  return true;
+}
+
 void
 QuotaManager::InitQuotaForOrigin(const nsACString& aOrigin,
                                  int64_t aLimit,
                                  int64_t aUsage)
 {
   OriginInfo* info = new OriginInfo(aOrigin, aLimit * 1024 * 1024, aUsage);
 
   MutexAutoLock lock(mQuotaMutex);
@@ -287,8 +339,88 @@ QuotaManager::GetQuotaObject(const nsACS
   nsCOMPtr<nsIFile> file = do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv);
   NS_ENSURE_SUCCESS(rv, nullptr);
 
   rv = file->InitWithPath(aPath);
   NS_ENSURE_SUCCESS(rv, nullptr);
 
   return GetQuotaObject(aOrigin, file);
 }
+
+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),
+                 "Somebody forgot to clear the current window!");
+    PR_SetThreadPrivate(mCurrentWindowIndex, aWindow);
+  }
+  else {
+    // We cannot assert PR_GetThreadPrivate(mCurrentWindowIndex) here because
+    // there are some cases where we did not already have a window.
+    PR_SetThreadPrivate(mCurrentWindowIndex, nullptr);
+  }
+}
+
+void
+QuotaManager::CancelPromptsForWindowInternal(nsPIDOMWindow* aWindow)
+{
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+
+  nsRefPtr<CheckQuotaHelper> helper;
+
+  MutexAutoLock autoLock(mQuotaMutex);
+
+  if (mCheckQuotaHelpers.Get(aWindow, getter_AddRefs(helper))) {
+    helper->Cancel();
+  }
+}
+
+bool
+QuotaManager::LockedQuotaIsLifted()
+{
+  mQuotaMutex.AssertCurrentThreadOwns();
+
+  NS_ASSERTION(mCurrentWindowIndex != BAD_TLS_INDEX,
+               "Should have a valid TLS storage index!");
+
+  nsPIDOMWindow* window =
+    static_cast<nsPIDOMWindow*>(PR_GetThreadPrivate(mCurrentWindowIndex));
+
+  // Quota is not enforced in chrome contexts (e.g. for components and JSMs)
+  // so we must have a window here.
+  NS_ASSERTION(window, "Why don't we have a Window here?");
+
+  bool createdHelper = false;
+
+  nsRefPtr<CheckQuotaHelper> helper;
+  if (!mCheckQuotaHelpers.Get(window, getter_AddRefs(helper))) {
+    helper = new CheckQuotaHelper(window, mQuotaMutex);
+    createdHelper = true;
+
+    mCheckQuotaHelpers.Put(window, helper);
+
+    // Unlock while calling out to XPCOM
+    {
+      MutexAutoUnlock autoUnlock(mQuotaMutex);
+
+      nsresult rv = NS_DispatchToMainThread(helper);
+      NS_ENSURE_SUCCESS(rv, false);
+    }
+
+    // Relocked.  If any other threads hit the quota limit on the same Window,
+    // they are using the helper we created here and are now blocking in
+    // PromptAndReturnQuotaDisabled.
+  }
+
+  bool result = helper->PromptAndReturnQuotaIsDisabled();
+
+  // If this thread created the helper and added it to the hash, this thread
+  // must remove it.
+  if (createdHelper) {
+    mCheckQuotaHelpers.Remove(window);
+  }
+
+  return result;
+}
--- a/dom/quota/QuotaManager.h
+++ b/dom/quota/QuotaManager.h
@@ -9,18 +9,21 @@
 
 #include "QuotaCommon.h"
 
 #include "mozilla/Mutex.h"
 #include "nsDataHashtable.h"
 #include "nsRefPtrHashtable.h"
 #include "nsThreadUtils.h"
 
+class nsPIDOMWindow;
+
 BEGIN_QUOTA_NAMESPACE
 
+class CheckQuotaHelper;
 class OriginInfo;
 class QuotaManager;
 
 class QuotaObject
 {
   friend class OriginInfo;
   friend class QuotaManager;
 
@@ -118,30 +121,78 @@ public:
   already_AddRefed<QuotaObject>
   GetQuotaObject(const nsACString& aOrigin,
                  nsIFile* aFile);
 
   already_AddRefed<QuotaObject>
   GetQuotaObject(const nsACString& aOrigin,
                  const nsAString& aPath);
 
-private:
-  QuotaManager()
-  : mQuotaMutex("QuotaManager.mQuotaMutex")
+  // Set the Window that the current thread is doing operations for.
+  // The caller is responsible for ensuring that aWindow is held alive.
+  static void
+  SetCurrentWindow(nsPIDOMWindow* aWindow)
   {
-    NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+    QuotaManager* quotaManager = Get();
+    NS_ASSERTION(quotaManager, "Must have a manager here!");
+
+    quotaManager->SetCurrentWindowInternal(aWindow);
+  }
 
-    mOriginInfos.Init();
+  static void
+  CancelPromptsForWindow(nsPIDOMWindow* aWindow)
+  {
+    NS_ASSERTION(aWindow, "Passed null window!");
+
+    QuotaManager* quotaManager = Get();
+    NS_ASSERTION(quotaManager, "Must have a manager here!");
+
+    quotaManager->CancelPromptsForWindowInternal(aWindow);
   }
 
-  virtual ~QuotaManager()
-  {
-    NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
-  }
+private:
+  QuotaManager();
+
+  virtual ~QuotaManager();
+
+  bool
+  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();
+
+  // 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;
+};
+
+class AutoEnterWindow
+{
+public:
+  AutoEnterWindow(nsPIDOMWindow* aWindow)
+  {
+    QuotaManager::SetCurrentWindow(aWindow);
+  }
+
+  ~AutoEnterWindow()
+  {
+    QuotaManager::SetCurrentWindow(nullptr);
+  }
 };
 
 END_QUOTA_NAMESPACE
 
 #endif /* mozilla_dom_quota_quotamanager_h__ */