Bug 785884 - Implement support for temporary storage (aka shared pool). r=ehsan, r=bent
authorJan Varga <jan.varga@gmail.com>
Wed, 11 Sep 2013 06:18:36 +0200
changeset 159430 8734ea74053b8cbcce4085cd1b6972e80b30b600
parent 159429 45975a8112078e3a80c57de34f3bcfebdd2ef031
child 159431 78f1f02b6b360a537451274aa1a7f95284a77cd2
push id2961
push userlsblakk@mozilla.com
push dateMon, 28 Oct 2013 21:59:28 +0000
treeherdermozilla-beta@73ef4f13486f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersehsan, bent
bugs785884
milestone26.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 785884 - Implement support for temporary storage (aka shared pool). r=ehsan, r=bent
browser/components/sessionstore/src/SessionStorage.jsm
caps/idl/nsIPrincipal.idl
caps/idl/nsIScriptSecurityManager.idl
caps/include/nsPrincipal.h
caps/include/nsScriptSecurityManager.h
caps/src/nsNullPrincipal.cpp
caps/src/nsPrincipal.cpp
caps/src/nsScriptSecurityManager.cpp
caps/src/nsSystemPrincipal.cpp
caps/tests/mochitest/Makefile.in
caps/tests/mochitest/test_principal_extendedorigin_appid_appstatus.html
caps/tests/mochitest/test_principal_jarprefix_origin_appid_appstatus.html
dom/apps/src/PermissionsTable.jsm
dom/apps/src/Webapps.jsm
dom/base/nsDOMWindowUtils.cpp
dom/bindings/Nullable.h
dom/indexedDB/CheckPermissionsHelper.cpp
dom/indexedDB/CheckPermissionsHelper.h
dom/indexedDB/Client.cpp
dom/indexedDB/Client.h
dom/indexedDB/DatabaseInfo.cpp
dom/indexedDB/DatabaseInfo.h
dom/indexedDB/FileManager.cpp
dom/indexedDB/FileManager.h
dom/indexedDB/IDBDatabase.cpp
dom/indexedDB/IDBDatabase.h
dom/indexedDB/IDBFactory.cpp
dom/indexedDB/IDBFactory.h
dom/indexedDB/IDBFileHandle.cpp
dom/indexedDB/IDBObjectStore.cpp
dom/indexedDB/IDBTransaction.cpp
dom/indexedDB/IndexedDatabaseManager.cpp
dom/indexedDB/IndexedDatabaseManager.h
dom/indexedDB/OpenDatabaseHelper.cpp
dom/indexedDB/OpenDatabaseHelper.h
dom/indexedDB/ipc/IndexedDBChild.cpp
dom/indexedDB/ipc/IndexedDBChild.h
dom/indexedDB/ipc/IndexedDBParent.cpp
dom/indexedDB/ipc/IndexedDBParent.h
dom/indexedDB/ipc/PIndexedDB.ipdl
dom/indexedDB/ipc/SerializationHelpers.h
dom/indexedDB/test/Makefile.in
dom/indexedDB/test/file.js
dom/indexedDB/test/helpers.js
dom/indexedDB/test/test_file_replace.html
dom/indexedDB/test/test_persistenceType.html
dom/indexedDB/test/unit/Makefile.in
dom/indexedDB/test/unit/head.js
dom/indexedDB/test/unit/test_invalid_version.js
dom/indexedDB/test/unit/test_persistenceType.js
dom/indexedDB/test/unit/test_temporary_storage.js
dom/indexedDB/test/unit/xpcshell.ini
dom/interfaces/base/nsIDOMWindowUtils.idl
dom/ipc/PBrowser.ipdl
dom/ipc/TabChild.cpp
dom/ipc/TabChild.h
dom/ipc/TabParent.cpp
dom/ipc/TabParent.h
dom/permission/tests/unit/test_bug808734.js
dom/quota/CheckQuotaHelper.cpp
dom/quota/CheckQuotaHelper.h
dom/quota/Client.h
dom/quota/FileStreams.cpp
dom/quota/FileStreams.h
dom/quota/OriginCollection.h
dom/quota/OriginOrPatternString.h
dom/quota/PersistenceType.h
dom/quota/QuotaCommon.h
dom/quota/QuotaManager.cpp
dom/quota/QuotaManager.h
dom/quota/QuotaObject.cpp
dom/quota/QuotaObject.h
dom/quota/StoragePrivilege.h
dom/quota/UsageInfo.h
dom/quota/UsageRunnable.h
dom/quota/moz.build
dom/quota/nsIOfflineStorage.h
dom/quota/nsIQuotaManager.idl
dom/webidl/IDBDatabase.webidl
dom/webidl/IDBFactory.webidl
dom/webidl/StorageType.webidl
dom/webidl/moz.build
modules/libpref/src/init/all.js
storage/src/TelemetryVFS.cpp
testing/specialpowers/content/specialpowersAPI.js
--- a/browser/components/sessionstore/src/SessionStorage.jsm
+++ b/browser/components/sessionstore/src/SessionStorage.jsm
@@ -56,17 +56,17 @@ let DomStorage = {
     for (let i = 0; i < shistory.count; i++) {
       let principal = History.getPrincipalForEntry(shistory, i, aDocShell);
       if (!principal)
         continue;
 
       // Check if we're allowed to store sessionStorage data.
       let isHTTPS = principal.URI && principal.URI.schemeIs("https");
       if (aFullData || SessionStore.checkPrivacyLevel(isHTTPS, isPinned)) {
-        let origin = principal.extendedOrigin;
+        let origin = principal.jarPrefix + principal.origin;
 
         // Don't read a host twice.
         if (!(origin in data)) {
           let originData = this._readEntry(principal, aDocShell);
           if (Object.keys(originData).length) {
             data[origin] = originData;
           }
         }
--- a/caps/idl/nsIPrincipal.idl
+++ b/caps/idl/nsIPrincipal.idl
@@ -15,17 +15,17 @@ struct JSPrincipals;
 
 interface nsIURI;
 interface nsIContentSecurityPolicy;
 
 [ptr] native JSContext(JSContext);
 [ptr] native JSPrincipals(JSPrincipals);
 [ptr] native PrincipalArray(nsTArray<nsCOMPtr<nsIPrincipal> >);
 
-[scriptable, builtinclass, uuid(dbda8bb0-3023-4aec-ad98-8e9931a29d70)]
+[scriptable, builtinclass, uuid(551bf53d-203c-4ac4-8c0b-40aa7b5f1ad6)]
 interface nsIPrincipal : nsISerializable
 {
     /**
      * Returns whether the other principal is equivalent to this principal.
      * Principals are considered equal if they are the same principal, or
      * they have the same origin.
      */
     boolean equals(in nsIPrincipal other);
@@ -157,29 +157,29 @@ interface nsIPrincipal : nsISerializable
                       in boolean allowIfInheritsPrincipal);
 
     /**
      * A Content Security Policy associated with this principal.
      */
     [noscript] attribute nsIContentSecurityPolicy csp;
 
     /**
-     * Returns the extended origin of the principal.
-     * The extended origin is a string that has more information than the origin
-     * and can be used to isolate data or permissions between different
-     * principals while taking into account parameters like the app id or the
-     * fact that the principal is embedded in a mozbrowser.
-     * Some principals will return the origin for extendedOrigin.
-     * Some principals will assert if you try to access the extendedOrigin.
+     * Returns the jar prefix of the principal.
+     * The jar prefix is a string that can be used to isolate data or
+     * permissions between different principals while taking into account
+     * parameters like the app id or the fact that the principal is embedded in
+     * a mozbrowser.
+     * Some principals will return an empty string.
+     * Some principals will assert if you try to access the jarPrefix.
      *
-     * The extendedOrigin is intended to be an opaque identifier. It is
-     * currently "human-readable" but no callers should assume it will stay
-     * as is and it might be crypto-hashed at some point.
+     * The jarPrefix is intended to be an opaque identifier. It is currently
+     * "human-readable" but no callers should assume it will stay as is and
+     * it might be crypto-hashed at some point.
      */
-    readonly attribute AUTF8String extendedOrigin;
+    readonly attribute AUTF8String jarPrefix;
 
     /**
      * The base domain of the codebase URI to which this principal pertains
      * (generally the document URI), handling null principals and
      * non-hierarchical schemes correctly.
      */
     readonly attribute ACString baseDomain;
 
--- a/caps/idl/nsIScriptSecurityManager.idl
+++ b/caps/idl/nsIScriptSecurityManager.idl
@@ -5,17 +5,17 @@
 
 #include "nsISupports.idl"
 #include "nsIPrincipal.idl"
 #include "nsIXPCSecurityManager.idl"
 interface nsIURI;
 interface nsIChannel;
 interface nsIDocShell;
 
-[scriptable, uuid(ae486501-ec57-4ec8-a565-6880ca4ae6c4)]
+[scriptable, uuid(d6475e53-9ece-4dc0-940c-095ac3d85363)]
 interface nsIScriptSecurityManager : nsIXPCSecurityManager
 {
     ///////////////// Security Checks //////////////////
     /**
      * Checks whether the running script is allowed to access aProperty.
      */
     [noscript] void checkPropertyAccess(in JSContextPtr aJSContext,
                                         in JSObjectPtr aJSObject,
@@ -224,20 +224,19 @@ interface nsIScriptSecurityManager : nsI
      * principal.
      */
     [noscript,notxpcom] nsIPrincipal getCxSubjectPrincipal(in JSContextPtr cx);
 
     const unsigned long NO_APP_ID = 0;
     const unsigned long UNKNOWN_APP_ID = 4294967295; // UINT32_MAX
 
     /**
-     * Returns the extended origin for the uri.
+     * Returns the jar prefix for the app.
      * appId can be NO_APP_ID or a valid app id. appId should not be
      * UNKNOWN_APP_ID.
-     * inMozBrowser has to be true if the uri is inside a mozbrowser iframe.
+     * inMozBrowser has to be true if the app is inside a mozbrowser iframe.
      */
-    AUTF8String getExtendedOrigin(in nsIURI uri, in unsigned long appId,
-                                  in boolean inMozBrowser);
+    AUTF8String getJarPrefix(in unsigned long appId, in boolean inMozBrowser);
 };
 
 %{C++
 #define NS_SCRIPTSECURITYMANAGER_CONTRACTID "@mozilla.org/scriptsecuritymanager;1"
 %}
--- a/caps/include/nsPrincipal.h
+++ b/caps/include/nsPrincipal.h
@@ -60,17 +60,17 @@ public:
   NS_IMETHOD GetHashValue(uint32_t* aHashValue);
   NS_IMETHOD GetURI(nsIURI** aURI);
   NS_IMETHOD GetDomain(nsIURI** aDomain);
   NS_IMETHOD SetDomain(nsIURI* aDomain);
   NS_IMETHOD GetOrigin(char** aOrigin);
   NS_IMETHOD Subsumes(nsIPrincipal* other, bool* _retval);
   NS_IMETHOD SubsumesIgnoringDomain(nsIPrincipal* other, bool* _retval);
   NS_IMETHOD CheckMayLoad(nsIURI* uri, bool report, bool allowIfInheritsPrincipal);
-  NS_IMETHOD GetExtendedOrigin(nsACString& aExtendedOrigin);
+  NS_IMETHOD GetJarPrefix(nsACString& aJarPrefix);
   NS_IMETHOD GetAppStatus(uint16_t* aAppStatus);
   NS_IMETHOD GetAppId(uint32_t* aAppStatus);
   NS_IMETHOD GetIsInBrowserElement(bool* aIsInBrowserElement);
   NS_IMETHOD GetUnknownAppId(bool* aUnknownAppId);
   NS_IMETHOD GetIsNullPrincipal(bool* aIsNullPrincipal);
   NS_IMETHOD GetBaseDomain(nsACString& aBaseDomain);
 #ifdef DEBUG
   virtual void dumpImpl();
@@ -143,17 +143,17 @@ public:
   NS_IMETHOD GetHashValue(uint32_t* aHashValue);
   NS_IMETHOD GetURI(nsIURI** aURI);
   NS_IMETHOD GetDomain(nsIURI** aDomain);
   NS_IMETHOD SetDomain(nsIURI* aDomain);
   NS_IMETHOD GetOrigin(char** aOrigin);
   NS_IMETHOD Subsumes(nsIPrincipal* other, bool* _retval);
   NS_IMETHOD SubsumesIgnoringDomain(nsIPrincipal* other, bool* _retval);
   NS_IMETHOD CheckMayLoad(nsIURI* uri, bool report, bool allowIfInheritsPrincipal);
-  NS_IMETHOD GetExtendedOrigin(nsACString& aExtendedOrigin);
+  NS_IMETHOD GetJarPrefix(nsACString& aJarPrefix);
   NS_IMETHOD GetAppStatus(uint16_t* aAppStatus);
   NS_IMETHOD GetAppId(uint32_t* aAppStatus);
   NS_IMETHOD GetIsInBrowserElement(bool* aIsInBrowserElement);
   NS_IMETHOD GetUnknownAppId(bool* aUnknownAppId);
   NS_IMETHOD GetIsNullPrincipal(bool* aIsNullPrincipal);
   NS_IMETHOD GetBaseDomain(nsACString& aBaseDomain);
 #ifdef DEBUG
   virtual void dumpImpl();
--- a/caps/include/nsScriptSecurityManager.h
+++ b/caps/include/nsScriptSecurityManager.h
@@ -521,15 +521,15 @@ public:
     NS_DECL_ISUPPORTS
 
     NS_IMETHOD InitializeNameSet(nsIScriptContext* aScriptContext);
 };
 
 namespace mozilla {
 
 void
-GetExtendedOrigin(nsIURI* aURI, uint32_t aAppid,
-                  bool aInMozBrowser,
-                  nsACString& aExtendedOrigin);
+GetJarPrefix(uint32_t aAppid,
+             bool aInMozBrowser,
+             nsACString& aJarPrefix);
 
 } // namespace mozilla
 
 #endif // nsScriptSecurityManager_h__
--- a/caps/src/nsNullPrincipal.cpp
+++ b/caps/src/nsNullPrincipal.cpp
@@ -254,19 +254,20 @@ nsNullPrincipal::CheckMayLoad(nsIURI* aU
     nsScriptSecurityManager::ReportError(
       nullptr, NS_LITERAL_STRING("CheckSameOriginError"), mURI, aURI);
   }
 
   return NS_ERROR_DOM_BAD_URI;
 }
 
 NS_IMETHODIMP
-nsNullPrincipal::GetExtendedOrigin(nsACString& aExtendedOrigin)
+nsNullPrincipal::GetJarPrefix(nsACString& aJarPrefix)
 {
-  return GetOrigin(getter_Copies(aExtendedOrigin));
+  aJarPrefix.Truncate();
+  return NS_OK;
 }
 
 NS_IMETHODIMP
 nsNullPrincipal::GetAppStatus(uint16_t* aAppStatus)
 {
   *aAppStatus = nsIPrincipal::APP_STATUS_NOT_INSTALLED;
   return NS_OK;
 }
--- a/caps/src/nsPrincipal.cpp
+++ b/caps/src/nsPrincipal.cpp
@@ -419,21 +419,21 @@ nsPrincipal::SetDomain(nsIURI* aDomain)
   success = js::RecomputeWrappers(cx, js::CompartmentsWithPrincipals(principals),
                                   js::ContentCompartmentsOnly());
   NS_ENSURE_TRUE(success, NS_ERROR_FAILURE);
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
-nsPrincipal::GetExtendedOrigin(nsACString& aExtendedOrigin)
+nsPrincipal::GetJarPrefix(nsACString& aJarPrefix)
 {
   MOZ_ASSERT(mAppId != nsIScriptSecurityManager::UNKNOWN_APP_ID);
 
-  mozilla::GetExtendedOrigin(mCodebase, mAppId, mInMozBrowser, aExtendedOrigin);
+  mozilla::GetJarPrefix(mAppId, mInMozBrowser, aJarPrefix);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsPrincipal::GetAppStatus(uint16_t* aAppStatus)
 {
   *aAppStatus = GetAppStatus();
   return NS_OK;
@@ -768,19 +768,20 @@ nsExpandedPrincipal::GetURI(nsIURI** aUR
 NS_IMETHODIMP
 nsExpandedPrincipal::GetWhiteList(nsTArray<nsCOMPtr<nsIPrincipal> >** aWhiteList)
 {
   *aWhiteList = &mPrincipals;
   return NS_OK;
 }
 
 NS_IMETHODIMP
-nsExpandedPrincipal::GetExtendedOrigin(nsACString& aExtendedOrigin)
+nsExpandedPrincipal::GetJarPrefix(nsACString& aJarPrefix)
 {
-  return GetOrigin(getter_Copies(aExtendedOrigin));
+  aJarPrefix.Truncate();
+  return NS_OK;
 }
 
 NS_IMETHODIMP
 nsExpandedPrincipal::GetAppStatus(uint16_t* aAppStatus)
 {
   *aAppStatus = nsIPrincipal::APP_STATUS_NOT_INSTALLED;
   return NS_OK;
 }
--- a/caps/src/nsScriptSecurityManager.cpp
+++ b/caps/src/nsScriptSecurityManager.cpp
@@ -2773,51 +2773,44 @@ nsScriptSecurityManager::InitPrefs()
     Preferences::AddStrongObservers(this, kObservedPrefs);
 
     return NS_OK;
 }
 
 namespace mozilla {
 
 void
-GetExtendedOrigin(nsIURI* aURI, uint32_t aAppId, bool aInMozBrowser,
-                  nsACString& aExtendedOrigin)
+GetJarPrefix(uint32_t aAppId, bool aInMozBrowser, nsACString& aJarPrefix)
 {
-  MOZ_ASSERT(aURI);
   MOZ_ASSERT(aAppId != nsIScriptSecurityManager::UNKNOWN_APP_ID);
 
   if (aAppId == nsIScriptSecurityManager::UNKNOWN_APP_ID) {
     aAppId = nsIScriptSecurityManager::NO_APP_ID;
   }
 
-  nsAutoCString origin;
-  nsPrincipal::GetOriginForURI(aURI, getter_Copies(origin));
+  aJarPrefix.Truncate();
 
   // Fallback.
   if (aAppId == nsIScriptSecurityManager::NO_APP_ID && !aInMozBrowser) {
-    aExtendedOrigin.Assign(origin);
     return;
   }
 
-  // aExtendedOrigin = appId + "+" + { 't', 'f' } "+" + origin;
-  aExtendedOrigin.Truncate();
-  aExtendedOrigin.AppendInt(aAppId);
-  aExtendedOrigin.Append('+');
-  aExtendedOrigin.Append(aInMozBrowser ? 't' : 'f');
-  aExtendedOrigin.Append('+');
-  aExtendedOrigin.Append(origin);
+  // aJarPrefix = appId + "+" + { 't', 'f' } + "+";
+  aJarPrefix.AppendInt(aAppId);
+  aJarPrefix.Append('+');
+  aJarPrefix.Append(aInMozBrowser ? 't' : 'f');
+  aJarPrefix.Append('+');
 
   return;
 }
 
 } // namespace mozilla
 
 NS_IMETHODIMP
-nsScriptSecurityManager::GetExtendedOrigin(nsIURI* aURI,
-                                           uint32_t aAppId,
-                                           bool aInMozBrowser,
-                                           nsACString& aExtendedOrigin)
+nsScriptSecurityManager::GetJarPrefix(uint32_t aAppId,
+                                      bool aInMozBrowser,
+                                      nsACString& aJarPrefix)
 {
   MOZ_ASSERT(aAppId != nsIScriptSecurityManager::UNKNOWN_APP_ID);
 
-  mozilla::GetExtendedOrigin(aURI, aAppId, aInMozBrowser, aExtendedOrigin);
+  mozilla::GetJarPrefix(aAppId, aInMozBrowser, aJarPrefix);
   return NS_OK;
 }
--- a/caps/src/nsSystemPrincipal.cpp
+++ b/caps/src/nsSystemPrincipal.cpp
@@ -161,19 +161,20 @@ nsSystemPrincipal::GetSecurityPolicy(voi
 
 NS_IMETHODIMP
 nsSystemPrincipal::SetSecurityPolicy(void* aSecurityPolicy)
 {
     return NS_OK;
 }
 
 NS_IMETHODIMP
-nsSystemPrincipal::GetExtendedOrigin(nsACString& aExtendedOrigin)
+nsSystemPrincipal::GetJarPrefix(nsACString& aJarPrefix)
 {
-  return GetOrigin(getter_Copies(aExtendedOrigin));
+  aJarPrefix.Truncate();
+  return NS_OK;
 }
 
 NS_IMETHODIMP
 nsSystemPrincipal::GetAppStatus(uint16_t* aAppStatus)
 {
   *aAppStatus = nsIPrincipal::APP_STATUS_NOT_INSTALLED;
   return NS_OK;
 }
--- a/caps/tests/mochitest/Makefile.in
+++ b/caps/tests/mochitest/Makefile.in
@@ -6,13 +6,13 @@
 MOCHITEST_FILES = 	test_bug423375.html \
                 test_bug246699.html \
                 test_bug292789.html \
                 test_bug470804.html \
                 test_disallowInheritPrincipal.html \
                 test_app_principal_equality.html \
                 $(NULL)
 
-# extendedOrigin test doesn't work on Windows, see bug 776296.
+# jarPrefix test doesn't work on Windows, see bug 776296.
 ifneq ($(OS_ARCH),WINNT)
-MOCHITEST_CHROME_FILES = test_principal_extendedorigin_appid_appstatus.html \
+MOCHITEST_CHROME_FILES = test_principal_jarprefix_origin_appid_appstatus.html \
                          $(NULL)
 endif
rename from caps/tests/mochitest/test_principal_extendedorigin_appid_appstatus.html
rename to caps/tests/mochitest/test_principal_jarprefix_origin_appid_appstatus.html
--- a/caps/tests/mochitest/test_principal_extendedorigin_appid_appstatus.html
+++ b/caps/tests/mochitest/test_principal_jarprefix_origin_appid_appstatus.html
@@ -1,16 +1,16 @@
 <!DOCTYPE HTML>
 <html>
 <!--
 https://bugzilla.mozilla.org/show_bug.cgi?id=758258
 -->
 <head>
   <meta charset="utf-8">
-  <title>Test for nsIPrincipal extendedOrigin, appStatus and appId</title>
+  <title>Test for nsIPrincipal jarPrefix, origin, appStatus and appId</title>
   <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"?>
 </head>
 <body>
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=758258">Mozilla Bug 758258</a>
 <p id="display"></p>
 <div id="content">
 
@@ -289,30 +289,30 @@ function checkIFrame(aFrame, data) {
   } else {
     is(principal.appStatus, Ci.nsIPrincipal.APP_STATUS_NOT_INSTALLED,
        'this should not be an installed app');
     is(principal.appId, Ci.nsIScriptSecurityManager.NO_APP_ID,
        "principals from non-installed app should have NO_APP_ID");
   }
 
   if (!data.isapp && !data.browser) {
-    is(principal.extendedOrigin, principal.origin,
-       'extendedOrigin should return the origin for non-app and non-browsers principals');
+    is(principal.jarPrefix, "",
+       'jarPrefix should return an empty string for non-app and non-browsers principals');
   } else {
-    isnot(principal.extendedOrigin, principal.origin,
-          'extendedOrigin should not return the origin for apps or mozbrowsers');
+    isnot(principal.jarPrefix, "",
+          'jarPrefix should not return an empty string for apps or mozbrowsers');
   }
 
   if (data.test.indexOf("eo-unique") != -1) {
-    is(eoList.indexOf(principal.extendedOrigin), -1,
-       "extendedOrigin should be unique");
+    is(eoList.indexOf(principal.jarPrefix + principal.origin), -1,
+       "extended origin should be unique");
   }
   if (data.test.indexOf("eo-as-last") != -1) {
-    is(principal.extendedOrigin, eoList[eoList.length-1],
-       "extendedOrigin should be the same as the last inserted one");
+    is(principal.jarPrefix + principal.origin, eoList[eoList.length-1],
+       "extended origin should be the same as the last inserted one");
   }
 
   is(principal.isInBrowserElement, !!data.browser,
      "check principal.isInBrowserElement");
 
   if (data.child) {
     let childPrincipal = aFrame.contentWindow.frames[0].document.nodePrincipal;
 
@@ -320,27 +320,29 @@ function checkIFrame(aFrame, data) {
       is(childPrincipal.appStatus, Ci.nsIPrincipal.APP_STATUS_INSTALLED,
          "child should be an installed app");
     }
 
     is(childPrincipal.isInBrowserElement, !!data.child.browser || !!data.child.inBrowser,
        "check childPrincipal.isInBrowserElement");
 
     if (data.test.indexOf("child-has-same-eo") != -1) {
-      is(childPrincipal.extendedOrigin, principal.extendedOrigin,
-         "child should have the same extendedOrigin as parent");
+      is(childPrincipal.jarPrefix + childPrincipal.origin,
+         principal.jarPrefix + principal.origin,
+         "child should have the same extended origin as parent");
       is(childPrincipal.appStatus, principal.appStatus,
-         "child should have the same appStatus if it has the same extendedOrigin");
+         "child should have the same appStatus if it has the same extended origin");
       is(childPrincipal.appId, principal.appId,
-         "child should have the same appId if it has the same extendedOrigin");
+         "child should have the same appId if it has the same extended origin");
     }
 
     if (data.test.indexOf("child-has-different-eo") != -1) {
-      isnot(childPrincipal.extendedOrigin, principal.extendedOrigin,
-            "child should not have the same extendedOrigin as parent");
+      isnot(childPrincipal.jarPrefix + childPrincipal.origin,
+            principal.jarPrefix + principal.origin,
+            "child should not have the same extended origin as parent");
     }
 
     if (data.test.indexOf("child-has-same-appstatus") != -1) {
       is(childPrincipal.appStatus, principal.appStatus,
          "childPrincipal and parent principal should have the same appStatus");
     }
 
     if (data.test.indexOf("child-has-different-appstatus") != -1) {
@@ -354,32 +356,34 @@ function checkIFrame(aFrame, data) {
     }
 
     if (data.test.indexOf("child-has-different-appid") != -1) {
       isnot(childPrincipal.appId, principal.appId,
             "childPrincipal and parent principal should have different appId");
     }
   }
 
-  eoList.push(principal.extendedOrigin);
+  eoList.push(principal.jarPrefix + principal.origin);
 
   checkedCount++;
   if (checkedCount == checksTodo) {
     SpecialPowers.removePermission("browser", "http://example.org");
     SpecialPowers.removePermission("embed-apps", "http://example.org");
     SimpleTest.finish();
   } else {
     gTestRunner.next();
   }
 }
 
 is('appStatus' in document.nodePrincipal, true,
    'appStatus should be present in nsIPrincipal');
-is('extendedOrigin' in document.nodePrincipal, true,
-   'extendedOrigin should be present in nsIPrincipal');
+is('jarPrefix' in document.nodePrincipal, true,
+   'jarPrefix should be present in nsIPrincipal');
+is('origin' in document.nodePrincipal, true,
+   'origin should be present in nsIPrincipal');
 is('appId' in document.nodePrincipal, true,
    'appId should be present in nsIPrincipal');
 
 function runTest() {
   // We want to use a generator. Those only work in a one level stack so we
   // can't use .forEach() here.
   for (var i=0; i<gData.length; ++i) {
     let data = gData[i];
--- a/dom/apps/src/PermissionsTable.jsm
+++ b/dom/apps/src/PermissionsTable.jsm
@@ -223,17 +223,18 @@ this.PermissionsTable =  { geolocation: 
                            },
                            "storage": {
                              app: ALLOW_ACTION,
                              privileged: ALLOW_ACTION,
                              certified: ALLOW_ACTION,
                              substitute: [
                                "indexedDB-unlimited",
                                "offline-app",
-                               "pin-app"
+                               "pin-app",
+                               "default-persistent-storage"
                              ]
                            },
                            "background-sensors": {
                              app: DENY_ACTION,
                              privileged: DENY_ACTION,
                              certified: ALLOW_ACTION
                            },
                            cellbroadcast: {
--- a/dom/apps/src/Webapps.jsm
+++ b/dom/apps/src/Webapps.jsm
@@ -454,17 +454,17 @@ this.DOMApplicationRegistry = {
     }
   },
 
 #ifdef MOZ_WIDGET_GONK
   fixIndexedDb: function fixIndexedDb() {
     debug("Fixing indexedDb folder names");
     let idbDir = FileUtils.getDir("indexedDBPDir", ["indexedDB"]);
 
-    if (!idbDir.isDirectory()) {
+    if (!idbDir.exists() || !idbDir.isDirectory()) {
       return;
     }
 
     let re = /^(\d+)\+(.*)\+(f|t)$/;
 
     let entries = idbDir.directoryEntries;
     while (entries.hasMoreElements()) {
       let entry = entries.getNext().QueryInterface(Ci.nsIFile);
--- a/dom/base/nsDOMWindowUtils.cpp
+++ b/dom/base/nsDOMWindowUtils.cpp
@@ -54,17 +54,19 @@
 #include <gdk/gdk.h>
 #include <gdk/gdkx.h>
 #endif
 
 #include "Layers.h"
 #include "mozilla/layers/ShadowLayers.h"
 
 #include "mozilla/dom/Element.h"
+#include "mozilla/dom/IDBFactoryBinding.h"
 #include "mozilla/dom/indexedDB/IndexedDatabaseManager.h"
+#include "mozilla/dom/quota/PersistenceType.h"
 #include "mozilla/dom/quota/QuotaManager.h"
 #include "nsDOMBlobBuilder.h"
 #include "nsIDOMFileHandle.h"
 #include "nsPrintfCString.h"
 #include "nsViewportInfo.h"
 #include "nsIFormControl.h"
 #include "nsIScriptError.h"
 #include "nsIAppShell.h"
@@ -2821,38 +2823,52 @@ nsDOMWindowUtils::GetFileId(const JS::Va
     }
   }
 
   *aResult = -1;
   return NS_OK;
 }
 
 NS_IMETHODIMP
-nsDOMWindowUtils::GetFileReferences(const nsAString& aDatabaseName,
-                                    int64_t aId, int32_t* aRefCnt,
-                                    int32_t* aDBRefCnt, int32_t* aSliceRefCnt,
+nsDOMWindowUtils::GetFileReferences(const nsAString& aDatabaseName, int64_t aId,
+                                    const jsval& aOptions,
+                                    int32_t* aRefCnt, int32_t* aDBRefCnt,
+                                    int32_t* aSliceRefCnt, JSContext* aCx,
                                     bool* aResult)
 {
   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 = quota::QuotaManager::GetASCIIOriginFromWindow(window, origin);
+  quota::PersistenceType defaultPersistenceType;
+  nsresult rv =
+    quota::QuotaManager::GetInfoFromWindow(window, nullptr, &origin, nullptr,
+                                           &defaultPersistenceType);
   NS_ENSURE_SUCCESS(rv, rv);
 
+  IDBOpenDBOptions options;
+  JS::Rooted<JS::Value> optionsVal(aCx, aOptions);
+  if (!options.Init(aCx, optionsVal)) {
+    return NS_ERROR_TYPE_ERR;
+  }
+
+  quota::PersistenceType persistenceType =
+    quota::PersistenceTypeFromStorage(options.mStorage, defaultPersistenceType);
+
   nsRefPtr<indexedDB::IndexedDatabaseManager> mgr =
     indexedDB::IndexedDatabaseManager::Get();
 
   if (mgr) {
-    rv = mgr->BlockAndGetFileReferences(origin, aDatabaseName, aId, aRefCnt,
-                                        aDBRefCnt, aSliceRefCnt, aResult);
+    rv = mgr->BlockAndGetFileReferences(persistenceType, origin, aDatabaseName,
+                                        aId, aRefCnt, aDBRefCnt, aSliceRefCnt,
+                                        aResult);
     NS_ENSURE_SUCCESS(rv, rv);
   }
   else {
     *aRefCnt = *aDBRefCnt = *aSliceRefCnt = -1;
     *aResult = false;
   }
 
   return NS_OK;
--- a/dom/bindings/Nullable.h
+++ b/dom/bindings/Nullable.h
@@ -59,16 +59,38 @@ public:
     MOZ_ASSERT(!mIsNull);
     return mValue;
   }
 
   bool IsNull() const {
     return mIsNull;
   }
 
+  bool Equals(const Nullable<T>& aOtherNullable) const
+  {
+    return (mIsNull && aOtherNullable.mIsNull) ||
+           (!mIsNull && !aOtherNullable.mIsNull &&
+            mValue == aOtherNullable.mValue);
+  }
+
+  bool operator==(const Nullable<T>& aOtherNullable) const
+  {
+    return Equals(aOtherNullable);
+  }
+
+  bool operator!=(const Nullable<T>& aOtherNullable) const
+  {
+    return !Equals(aOtherNullable);
+  }
+
+  operator bool() const
+  {
+    return !mIsNull;
+  }
+
   // Make it possible to use a const Nullable of an array type with other
   // array types.
   template<typename U>
   operator const Nullable< nsTArray<U> >&() const {
     // Make sure that T is ok to reinterpret to nsTArray<U>
     const nsTArray<U>& arr = mValue;
     (void)arr;
     return *reinterpret_cast<const Nullable< nsTArray<U> >*>(this);
--- a/dom/indexedDB/CheckPermissionsHelper.cpp
+++ b/dom/indexedDB/CheckPermissionsHelper.cpp
@@ -14,17 +14,16 @@
 #include "nsIPrincipal.h"
 #include "nsIScriptObjectPrincipal.h"
 #include "nsIURI.h"
 
 #include "CheckQuotaHelper.h"
 #include "nsContentUtils.h"
 #include "nsNetUtil.h"
 #include "nsThreadUtils.h"
-#include "mozilla/dom/quota/QuotaManager.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/Services.h"
 
 #include "IndexedDatabaseManager.h"
 
 #define PERMISSION_INDEXEDDB "indexedDB"
 #define PREF_INDEXEDDB_ENABLED "dom.indexedDB.enabled"
 #define TOPIC_PERMISSIONS_PROMPT "indexedDB-permissions-prompt"
@@ -160,20 +159,17 @@ CheckPermissionsHelper::Run()
       uint32_t quotaPermission =
         CheckQuotaHelper::GetQuotaPermission(windowPrincipal);
 
       if (quotaPermission == nsIPermissionManager::ALLOW_ACTION) {
         helper->SetUnlimitedQuotaAllowed();
       }
     }
 
-    quota::QuotaManager* quotaManager = quota::QuotaManager::Get();
-    NS_ASSERTION(quotaManager, "This should never be null!");
-
-    return helper->Dispatch(quotaManager->IOThread());
+    return helper->DispatchToIOThread();
   }
 
   NS_ASSERTION(permission == PERMISSION_PROMPT ||
                permission == PERMISSION_DENIED,
                "Unknown permission!");
 
   helper->SetError(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR);
 
--- a/dom/indexedDB/CheckPermissionsHelper.h
+++ b/dom/indexedDB/CheckPermissionsHelper.h
@@ -25,27 +25,28 @@ class CheckPermissionsHelper MOZ_FINAL :
 {
 public:
   NS_DECL_THREADSAFE_ISUPPORTS
   NS_DECL_NSIRUNNABLE
   NS_DECL_NSIINTERFACEREQUESTOR
   NS_DECL_NSIOBSERVER
 
   CheckPermissionsHelper(OpenDatabaseHelper* aHelper,
-                         nsIDOMWindow* aWindow,
-                         bool aForDeletion)
+                         nsIDOMWindow* aWindow)
   : mHelper(aHelper),
     mWindow(aWindow),
     // If we're trying to delete the database, we should never prompt the user.
     // Anything that would prompt is translated to denied.
-    mPromptAllowed(!aForDeletion),
+    mPromptAllowed(!aHelper->mForDeletion),
     mHasPrompted(false),
     mPromptResult(0)
   {
     NS_ASSERTION(aHelper, "Null pointer!");
+    NS_ASSERTION(aHelper->mPersistenceType == quota::PERSISTENCE_TYPE_PERSISTENT,
+                 "Checking permission for non persistent databases?!");
   }
 
 private:
   nsRefPtr<OpenDatabaseHelper> mHelper;
   nsCOMPtr<nsIDOMWindow> mWindow;
   bool mPromptAllowed;
   bool mHasPrompted;
   uint32_t mPromptResult;
--- a/dom/indexedDB/Client.cpp
+++ b/dom/indexedDB/Client.cpp
@@ -2,17 +2,17 @@
 /* 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/UsageInfo.h"
 #include "mozilla/dom/quota/Utilities.h"
 
 #include "IDBDatabase.h"
 #include "IndexedDatabaseManager.h"
 #include "TransactionThreadPool.h"
 
 USING_INDEXEDDB_NAMESPACE
 using mozilla::dom::quota::AssertIsOnIOThread;
@@ -40,59 +40,59 @@ GetDatabaseBaseFilename(const nsAString&
 
 } // 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)
+Client::InitOrigin(PersistenceType aPersistenceType, const nsACString& aGroup,
+                   const nsACString& aOrigin, UsageInfo* aUsageInfo)
 {
   AssertIsOnIOThread();
 
   nsCOMPtr<nsIFile> directory;
-  nsresult rv = GetDirectory(aOrigin, getter_AddRefs(directory));
+  nsresult rv =
+    GetDirectory(aPersistenceType, 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(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())) {
+         hasMore && (!aUsageInfo || !aUsageInfo->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;
     }
 
-#ifdef XP_MACOSX
     if (leafName.EqualsLiteral(DSSTORE_FILE_NAME)) {
       continue;
     }
-#endif
 
     bool isDirectory;
     rv = file->IsDirectory(&isDirectory);
     NS_ENSURE_SUCCESS(rv, rv);
 
     if (isDirectory) {
       if (!validSubdirs.GetEntry(leafName)) {
         subdirsToProcess.AppendElement(leafName);
@@ -108,31 +108,34 @@ Client::InitOrigin(const nsACString& aOr
 
     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);
+    rv = FileManager::InitDirectory(fmDirectory, file, aPersistenceType, aGroup,
+                                    aOrigin);
     NS_ENSURE_SUCCESS(rv, rv);
 
-    if (aUsageRunnable) {
+    if (aUsageInfo) {
       int64_t fileSize;
       rv = file->GetFileSize(&fileSize);
       NS_ENSURE_SUCCESS(rv, rv);
 
-      aUsageRunnable->AppendToDatabaseUsage(uint64_t(fileSize));
+      NS_ASSERTION(fileSize >= 0, "Negative size?!");
+
+      aUsageInfo->AppendToDatabaseUsage(uint64_t(fileSize));
 
       uint64_t usage;
       rv = FileManager::GetUsage(fmDirectory, &usage);
       NS_ENSURE_SUCCESS(rv, rv);
 
-      aUsageRunnable->AppendToFileUsage(usage);
+      aUsageInfo->AppendToFileUsage(usage);
     }
 
     validSubdirs.PutEntry(dbBaseFilename);
   }
   NS_ENSURE_SUCCESS(rv, rv);
 
   for (uint32_t i = 0; i < subdirsToProcess.Length(); i++) {
     const nsString& subdir = subdirsToProcess[i];
@@ -162,40 +165,43 @@ Client::InitOrigin(const nsACString& aOr
       }
     }
   }
 
   return NS_OK;
 }
 
 nsresult
-Client::GetUsageForOrigin(const nsACString& aOrigin,
-                          UsageRunnable* aUsageRunnable)
+Client::GetUsageForOrigin(PersistenceType aPersistenceType,
+                          const nsACString& aGroup, const nsACString& aOrigin,
+                          UsageInfo* aUsageInfo)
 {
   AssertIsOnIOThread();
-  NS_ASSERTION(aUsageRunnable, "Null pointer!");
+  NS_ASSERTION(aUsageInfo, "Null pointer!");
 
   nsCOMPtr<nsIFile> directory;
-  nsresult rv = GetDirectory(aOrigin, getter_AddRefs(directory));
+  nsresult rv =
+    GetDirectory(aPersistenceType, aOrigin, getter_AddRefs(directory));
   NS_ENSURE_SUCCESS(rv, rv);
 
-  rv = GetUsageForDirectoryInternal(directory, aUsageRunnable, true);
+  rv = GetUsageForDirectoryInternal(directory, aUsageInfo, true);
   NS_ENSURE_SUCCESS(rv, rv);
 
   return NS_OK;
 }
 
 void
-Client::OnOriginClearCompleted(const nsACString& aPattern)
+Client::OnOriginClearCompleted(PersistenceType aPersistenceType,
+                               const OriginOrPatternString& aOriginOrPattern)
 {
   AssertIsOnIOThread();
 
   IndexedDatabaseManager* mgr = IndexedDatabaseManager::Get();
   if (mgr) {
-    mgr->InvalidateFileManagersForPattern(aPattern);
+    mgr->InvalidateFileManagers(aPersistenceType, aOriginOrPattern);
   }
 }
 
 void
 Client::ReleaseIOThreadObjects()
 {
   AssertIsOnIOThread();
 
@@ -270,68 +276,69 @@ void
 Client::ShutdownTransactionService()
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
   TransactionThreadPool::Shutdown();
 }
 
 nsresult
-Client::GetDirectory(const nsACString& aOrigin, nsIFile** aDirectory)
+Client::GetDirectory(PersistenceType aPersistenceType,
+                     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));
+  nsresult rv = quotaManager->GetDirectoryForOrigin(aPersistenceType, 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,
+                                     UsageInfo* aUsageInfo,
                                      bool aDatabaseFiles)
 {
   NS_ASSERTION(aDirectory, "Null pointer!");
-  NS_ASSERTION(aUsageRunnable, "Null pointer!");
+  NS_ASSERTION(aUsageInfo, "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()) {
+         hasMore && !aUsageInfo->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);
+        rv = GetUsageForDirectoryInternal(file, aUsageInfo, false);
         NS_ENSURE_SUCCESS(rv, rv);
       }
       else {
         nsString leafName;
         rv = file->GetLeafName(leafName);
         NS_ENSURE_SUCCESS(rv, rv);
 
         if (!leafName.EqualsLiteral(JOURNAL_DIRECTORY_NAME)) {
@@ -344,18 +351,18 @@ Client::GetUsageForDirectoryInternal(nsI
 
     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));
+      aUsageInfo->AppendToDatabaseUsage(uint64_t(fileSize));
     }
     else {
-      aUsageRunnable->AppendToFileUsage(uint64_t(fileSize));
+      aUsageInfo->AppendToFileUsage(uint64_t(fileSize));
     }
   }
   NS_ENSURE_SUCCESS(rv, rv);
 
   return NS_OK;
 }
--- a/dom/indexedDB/Client.h
+++ b/dom/indexedDB/Client.h
@@ -13,41 +13,49 @@
 
 #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;
+  typedef mozilla::dom::quota::OriginOrPatternString OriginOrPatternString;
+  typedef mozilla::dom::quota::PersistenceType PersistenceType;
+  typedef mozilla::dom::quota::UsageInfo UsageInfo;
 
 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;
+  InitOrigin(PersistenceType aPersistenceType,
+             const nsACString& aGroup,
+             const nsACString& aOrigin,
+             UsageInfo* aUsageInfo) MOZ_OVERRIDE;
 
   virtual nsresult
-  GetUsageForOrigin(const nsACString& aOrigin,
-                    UsageRunnable* aUsageRunnable) MOZ_OVERRIDE;
+  GetUsageForOrigin(PersistenceType aPersistenceType,
+                    const nsACString& aGroup,
+                    const nsACString& aOrigin,
+                    UsageInfo* aUsageInfo) MOZ_OVERRIDE;
 
   virtual void
-  OnOriginClearCompleted(const nsACString& aPattern) MOZ_OVERRIDE;
+  OnOriginClearCompleted(PersistenceType aPersistenceType,
+                         const OriginOrPatternString& aOriginOrPattern)
+                         MOZ_OVERRIDE;
 
   virtual void
   ReleaseIOThreadObjects() MOZ_OVERRIDE;
 
   virtual bool
   IsFileServiceUtilized() MOZ_OVERRIDE
   {
     return true;
@@ -66,21 +74,22 @@ public:
   virtual bool
   HasTransactionsForStorage(nsIOfflineStorage* aStorage) MOZ_OVERRIDE;
 
   virtual void
   ShutdownTransactionService() MOZ_OVERRIDE;
 
 private:
   nsresult
-  GetDirectory(const nsACString& aOrigin, nsIFile** aDirectory);
+  GetDirectory(PersistenceType aPersistenceType, const nsACString& aOrigin,
+               nsIFile** aDirectory);
 
   nsresult
   GetUsageForDirectoryInternal(nsIFile* aDirectory,
-                               UsageRunnable* aUsageRunnable,
+                               UsageInfo* aUsageInfo,
                                bool aDatabaseFiles);
 
   nsAutoRefCnt mRefCnt;
   NS_DECL_OWNINGTHREAD
 };
 
 END_INDEXEDDB_NAMESPACE
 
--- a/dom/indexedDB/DatabaseInfo.cpp
+++ b/dom/indexedDB/DatabaseInfo.cpp
@@ -245,18 +245,20 @@ DatabaseInfo::RemoveObjectStore(const ns
 
 already_AddRefed<DatabaseInfo>
 DatabaseInfo::Clone()
 {
   nsRefPtr<DatabaseInfo> dbInfo(new DatabaseInfo());
 
   dbInfo->cloned = true;
   dbInfo->name = name;
+  dbInfo->group = group;
   dbInfo->origin = origin;
   dbInfo->version = version;
+  dbInfo->persistenceType = persistenceType;
   dbInfo->id = id;
   dbInfo->filePath = filePath;
   dbInfo->nextObjectStoreId = nextObjectStoreId;
   dbInfo->nextIndexId = nextIndexId;
 
   if (objectStoreHash) {
     dbInfo->objectStoreHash = new ObjectStoreInfoHash();
     objectStoreHash->EnumerateRead(CloneObjectStoreInfo,
--- a/dom/indexedDB/DatabaseInfo.h
+++ b/dom/indexedDB/DatabaseInfo.h
@@ -4,50 +4,57 @@
  * 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_databaseinfo_h__
 #define mozilla_dom_indexeddb_databaseinfo_h__
 
 #include "mozilla/dom/indexedDB/IndexedDatabase.h"
 
+#include "mozilla/dom/quota/PersistenceType.h"
+#include "nsRefPtrHashtable.h"
+#include "nsHashKeys.h"
+
 #include "mozilla/dom/indexedDB/Key.h"
 #include "mozilla/dom/indexedDB/KeyPath.h"
 #include "mozilla/dom/indexedDB/IDBObjectStore.h"
 
-#include "nsRefPtrHashtable.h"
-#include "nsHashKeys.h"
-
 BEGIN_INDEXEDDB_NAMESPACE
 
 class IndexedDBDatabaseChild;
 struct ObjectStoreInfo;
 
 typedef nsRefPtrHashtable<nsStringHashKey, ObjectStoreInfo>
         ObjectStoreInfoHash;
 
 struct DatabaseInfoGuts
 {
+  typedef mozilla::dom::quota::PersistenceType PersistenceType;
+
   DatabaseInfoGuts()
   : nextObjectStoreId(1), nextIndexId(1)
   { }
 
   bool operator==(const DatabaseInfoGuts& aOther) const
   {
     return this->name == aOther.name &&
+           this->group == aOther.group &&
            this->origin == aOther.origin &&
            this->version == aOther.version &&
+           this->persistenceType == aOther.persistenceType &&
            this->nextObjectStoreId == aOther.nextObjectStoreId &&
            this->nextIndexId == aOther.nextIndexId;
   };
 
   // Make sure to update ipc/SerializationHelpers.h when changing members here!
   nsString name;
+  nsCString group;
   nsCString origin;
   uint64_t version;
+  PersistenceType persistenceType;
   int64_t nextObjectStoreId;
   int64_t nextIndexId;
 };
 
 struct DatabaseInfo : public DatabaseInfoGuts
 {
   DatabaseInfo()
   : cloned(false)
--- a/dom/indexedDB/FileManager.cpp
+++ b/dom/indexedDB/FileManager.cpp
@@ -261,16 +261,18 @@ FileManager::GetFileForId(nsIFile* aDire
 
   return file.forget();
 }
 
 // static
 nsresult
 FileManager::InitDirectory(nsIFile* aDirectory,
                            nsIFile* aDatabaseFile,
+                           PersistenceType aPersistenceType,
+                           const nsACString& aGroup,
                            const nsACString& aOrigin)
 {
   AssertIsOnIOThread();
   NS_ASSERTION(aDirectory, "Null directory!");
   NS_ASSERTION(aDatabaseFile, "Null database file!");
 
   bool exists;
   nsresult rv = aDirectory->Exists(&exists);
@@ -306,17 +308,18 @@ FileManager::InitDirectory(nsIFile* aDir
 
     bool hasElements;
     rv = entries->HasMoreElements(&hasElements);
     NS_ENSURE_SUCCESS(rv, rv);
 
     if (hasElements) {
       nsCOMPtr<mozIStorageConnection> connection;
       rv = OpenDatabaseHelper::CreateDatabaseConnection(aDatabaseFile,
-        aDirectory, NullString(), aOrigin, getter_AddRefs(connection));
+        aDirectory, NullString(), aPersistenceType, aGroup, aOrigin,
+        getter_AddRefs(connection));
       NS_ENSURE_SUCCESS(rv, rv);
 
       mozStorageTransaction transaction(connection, false);
 
       rv = connection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
         "CREATE VIRTUAL TABLE fs USING filesystem;"
       ));
       NS_ENSURE_SUCCESS(rv, rv);
--- a/dom/indexedDB/FileManager.h
+++ b/dom/indexedDB/FileManager.h
@@ -7,43 +7,57 @@
 #ifndef mozilla_dom_indexeddb_filemanager_h__
 #define mozilla_dom_indexeddb_filemanager_h__
 
 #include "IndexedDatabase.h"
 
 #include "nsIDOMFile.h"
 #include "nsIFile.h"
 
+#include "mozilla/dom/quota/PersistenceType.h"
 #include "mozilla/dom/quota/StoragePrivilege.h"
 #include "nsDataHashtable.h"
 
 class mozIStorageConnection;
 
 BEGIN_INDEXEDDB_NAMESPACE
 
 class FileInfo;
 
 class FileManager
 {
   friend class FileInfo;
 
+  typedef mozilla::dom::quota::PersistenceType PersistenceType;
   typedef mozilla::dom::quota::StoragePrivilege StoragePrivilege;
 
 public:
-  FileManager(const nsACString& aOrigin, StoragePrivilege aPrivilege,
+  FileManager(PersistenceType aPersistenceType, const nsACString& aGroup,
+              const nsACString& aOrigin, StoragePrivilege aPrivilege,
               const nsAString& aDatabaseName)
-  : mOrigin(aOrigin), mPrivilege(aPrivilege), mDatabaseName(aDatabaseName),
-    mLastFileId(0), mInvalidated(false)
+  : mPersistenceType(aPersistenceType), mGroup(aGroup), mOrigin(aOrigin),
+    mPrivilege(aPrivilege), mDatabaseName(aDatabaseName), mLastFileId(0),
+    mInvalidated(false)
   { }
 
   ~FileManager()
   { }
 
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(FileManager)
 
+  PersistenceType Type()
+  {
+    return mPersistenceType;
+  }
+
+  const nsACString& Group() const
+  {
+    return mGroup;
+  }
+
   const nsACString& Origin() const
   {
     return mOrigin;
   }
 
   const StoragePrivilege& Privilege() const
   {
     return mPrivilege;
@@ -74,21 +88,25 @@ public:
 
   already_AddRefed<FileInfo> GetNewFileInfo();
 
   static already_AddRefed<nsIFile> GetFileForId(nsIFile* aDirectory,
                                                 int64_t aId);
 
   static nsresult InitDirectory(nsIFile* aDirectory,
                                 nsIFile* aDatabaseFile,
+                                PersistenceType aPersistenceType,
+                                const nsACString& aGroup,
                                 const nsACString& aOrigin);
 
   static nsresult GetUsage(nsIFile* aDirectory, uint64_t* aUsage);
 
 private:
+  PersistenceType mPersistenceType;
+  nsCString mGroup;
   nsCString mOrigin;
   StoragePrivilege mPrivilege;
   nsString mDatabaseName;
 
   nsString mDirectoryPath;
   nsString mJournalDirectoryPath;
 
   int64_t mLastFileId;
--- a/dom/indexedDB/IDBDatabase.cpp
+++ b/dom/indexedDB/IDBDatabase.cpp
@@ -192,16 +192,18 @@ IDBDatabase::Create(IDBWrapperCache* aOw
   nsRefPtr<IDBDatabase> db(new IDBDatabase());
 
   db->BindToOwner(aOwnerCache);
   db->SetScriptOwner(aOwnerCache->GetScriptOwner());
   db->mFactory = aFactory;
   db->mDatabaseId = databaseInfo->id;
   db->mName = databaseInfo->name;
   db->mFilePath = databaseInfo->filePath;
+  db->mPersistenceType = databaseInfo->persistenceType;
+  db->mGroup = databaseInfo->group;
   databaseInfo.swap(db->mDatabaseInfo);
   db->mASCIIOrigin = aASCIIOrigin;
   db->mFileManager = aFileManager;
   db->mContentParent = aContentParent;
 
   QuotaManager* quotaManager = QuotaManager::Get();
   NS_ASSERTION(quotaManager, "This should never be null!");
 
@@ -222,18 +224,17 @@ IDBDatabase::Create(IDBWrapperCache* aOw
 IDBDatabase*
 IDBDatabase::FromStorage(nsIOfflineStorage* aStorage)
 {
   return aStorage->GetClient()->GetType() == Client::IDB ?
          static_cast<IDBDatabase*>(aStorage) : nullptr;
 }
 
 IDBDatabase::IDBDatabase()
-: mDatabaseId(0),
-  mActorChild(nullptr),
+: mActorChild(nullptr),
   mActorParent(nullptr),
   mContentParent(nullptr),
   mInvalidated(false),
   mRegistered(false),
   mClosed(false),
   mRunningVersionChange(false)
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
--- a/dom/indexedDB/IDBDatabase.h
+++ b/dom/indexedDB/IDBDatabase.h
@@ -11,16 +11,17 @@
 
 #include "nsIDocument.h"
 #include "nsIFileStorage.h"
 #include "nsIOfflineStorage.h"
 
 #include "mozilla/Attributes.h"
 #include "mozilla/dom/IDBObjectStoreBinding.h"
 #include "mozilla/dom/IDBTransactionBinding.h"
+#include "mozilla/dom/quota/PersistenceType.h"
 #include "nsDOMEventTargetHelper.h"
 
 #include "mozilla/dom/indexedDB/FileManager.h"
 #include "mozilla/dom/indexedDB/IDBRequest.h"
 #include "mozilla/dom/indexedDB/IDBWrapperCache.h"
 
 class nsIScriptContext;
 class nsPIDOMWindow;
@@ -205,16 +206,22 @@ public:
   already_AddRefed<indexedDB::IDBTransaction>
   Transaction(const Sequence<nsString>& aStoreNames, IDBTransactionMode aMode,
               ErrorResult& aRv);
 
   IMPL_EVENT_HANDLER(abort)
   IMPL_EVENT_HANDLER(error)
   IMPL_EVENT_HANDLER(versionchange)
 
+  mozilla::dom::StorageType
+  Storage() const
+  {
+    return PersistenceTypeToStorage(mPersistenceType);
+  }
+
   already_AddRefed<IDBRequest>
   MozCreateFileHandle(const nsAString& aName, const Optional<nsAString>& aType,
                       ErrorResult& aRv);
 
   virtual void LastRelease() MOZ_OVERRIDE;
 private:
   IDBDatabase();
   ~IDBDatabase();
--- a/dom/indexedDB/IDBFactory.cpp
+++ b/dom/indexedDB/IDBFactory.cpp
@@ -4,30 +4,30 @@
  * 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 "IDBFactory.h"
 
 #include "nsIFile.h"
 #include "nsIPrincipal.h"
 #include "nsIScriptContext.h"
+#include "nsIScriptSecurityManager.h"
 #include "nsIXPConnect.h"
 #include "nsIXPCScriptable.h"
 
 #include <algorithm>
 #include "mozilla/dom/ContentParent.h"
 #include "mozilla/dom/ContentChild.h"
 #include "mozilla/dom/IDBFactoryBinding.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 "nsCxPusher.h"
 #include "nsDOMClassInfoID.h"
 #include "nsGlobalWindow.h"
 #include "nsHashKeys.h"
 #include "nsPIDOMWindow.h"
 #include "nsServiceManagerUtils.h"
@@ -46,16 +46,17 @@
 
 #include "ipc/IndexedDBChild.h"
 
 USING_INDEXEDDB_NAMESPACE
 USING_QUOTA_NAMESPACE
 
 using mozilla::dom::ContentChild;
 using mozilla::dom::ContentParent;
+using mozilla::dom::IDBOpenDBOptions;
 using mozilla::dom::NonNull;
 using mozilla::dom::Optional;
 using mozilla::dom::TabChild;
 using mozilla::ErrorResult;
 
 namespace {
 
 struct ObjectStoreInfoMap
@@ -65,17 +66,18 @@ struct ObjectStoreInfoMap
 
   int64_t id;
   ObjectStoreInfo* info;
 };
 
 } // anonymous namespace
 
 IDBFactory::IDBFactory()
-: mOwningObject(nullptr), mActorChild(nullptr), mActorParent(nullptr),
+: mPrivilege(Content), mDefaultPersistenceType(PERSISTENCE_TYPE_TEMPORARY),
+  mOwningObject(nullptr), mActorChild(nullptr), mActorParent(nullptr),
   mContentParent(nullptr), mRootedOwningObject(false)
 {
   SetIsDOMBinding();
 }
 
 IDBFactory::~IDBFactory()
 {
   NS_ASSERTION(!mActorParent, "Actor parent owns us, how can we be dying?!");
@@ -88,16 +90,17 @@ IDBFactory::~IDBFactory()
     mOwningObject = nullptr;
     mozilla::DropJSObjects(this);
   }
 }
 
 // static
 nsresult
 IDBFactory::Create(nsPIDOMWindow* aWindow,
+                   const nsACString& aGroup,
                    const nsACString& aASCIIOrigin,
                    ContentParent* aContentParent,
                    IDBFactory** aFactory)
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
   NS_ASSERTION(aASCIIOrigin.IsEmpty() || nsContentUtils::IsCallerChrome(),
                "Non-chrome may not supply their own origin!");
 
@@ -111,39 +114,52 @@ IDBFactory::Create(nsPIDOMWindow* aWindo
   // Make sure that the manager is up before we do anything here since lots of
   // decisions depend on which process we're running in.
   indexedDB::IndexedDatabaseManager* mgr =
     indexedDB::IndexedDatabaseManager::GetOrCreate();
   NS_ENSURE_TRUE(mgr, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
   nsresult rv;
 
+  nsCString group(aGroup);
   nsCString origin(aASCIIOrigin);
+  StoragePrivilege privilege;
+  PersistenceType defaultPersistenceType;
   if (origin.IsEmpty()) {
-    rv = QuotaManager::GetASCIIOriginFromWindow(aWindow, origin);
-    if (NS_FAILED(rv)) {
-      // Not allowed.
-      *aFactory = nullptr;
-      return NS_OK;
-    }
+    NS_ASSERTION(aGroup.IsEmpty(), "Should be empty too!");
+
+    rv = QuotaManager::GetInfoFromWindow(aWindow, &group, &origin, &privilege,
+                                         &defaultPersistenceType);
+  }
+  else {
+    rv = QuotaManager::GetInfoFromWindow(aWindow, nullptr, nullptr, &privilege,
+                                         &defaultPersistenceType);
+  }
+  if (NS_FAILED(rv)) {
+    // Not allowed.
+    *aFactory = nullptr;
+    return NS_OK;
   }
 
   nsRefPtr<IDBFactory> factory = new IDBFactory();
+  factory->mGroup = group;
   factory->mASCIIOrigin = origin;
+  factory->mPrivilege = privilege;
+  factory->mDefaultPersistenceType = defaultPersistenceType;
   factory->mWindow = aWindow;
   factory->mContentParent = aContentParent;
 
   if (!IndexedDatabaseManager::IsMainProcess()) {
     TabChild* tabChild = GetTabChildFrom(aWindow);
     NS_ENSURE_TRUE(tabChild, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
     IndexedDBChild* actor = new IndexedDBChild(origin);
 
     bool allowed;
-    tabChild->SendPIndexedDBConstructor(actor, origin, &allowed);
+    tabChild->SendPIndexedDBConstructor(actor, group, origin, &allowed);
 
     if (!allowed) {
       actor->Send__delete__(actor);
       *aFactory = nullptr;
       return NS_OK;
     }
 
     actor->SetFactory(factory);
@@ -162,22 +178,30 @@ 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 group;
   nsCString origin;
-  nsresult rv = QuotaManager::GetASCIIOriginFromWindow(nullptr, origin);
+  StoragePrivilege privilege;
+  PersistenceType defaultPersistenceType;
+  nsresult rv =
+    QuotaManager::GetInfoFromWindow(nullptr, &group, &origin, &privilege,
+                                    &defaultPersistenceType);
   NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
   nsRefPtr<IDBFactory> factory = new IDBFactory();
+  factory->mGroup = group;
   factory->mASCIIOrigin = origin;
+  factory->mPrivilege = privilege;
+  factory->mDefaultPersistenceType = defaultPersistenceType;
   factory->mOwningObject = aOwningObject;
   factory->mContentParent = aContentParent;
 
   if (!IndexedDatabaseManager::IsMainProcess()) {
     ContentChild* contentChild = ContentChild::GetSingleton();
     NS_ENSURE_TRUE(contentChild, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
     IndexedDBChild* actor = new IndexedDBChild(origin);
@@ -233,34 +257,44 @@ IDBFactory::Create(ContentParent* aConte
   factory->mRootedOwningObject = true;
 
   factory.forget(aFactory);
   return NS_OK;
 }
 
 // static
 already_AddRefed<nsIFileURL>
-IDBFactory::GetDatabaseFileURL(nsIFile* aDatabaseFile, const nsACString& aOrigin)
+IDBFactory::GetDatabaseFileURL(nsIFile* aDatabaseFile,
+                               PersistenceType aPersistenceType,
+                               const nsACString& aGroup,
+                               const nsACString& aOrigin)
 {
   nsCOMPtr<nsIURI> uri;
   nsresult rv = NS_NewFileURI(getter_AddRefs(uri), aDatabaseFile);
   NS_ENSURE_SUCCESS(rv, nullptr);
 
   nsCOMPtr<nsIFileURL> fileUrl = do_QueryInterface(uri);
   NS_ASSERTION(fileUrl, "This should always succeed!");
 
-  rv = fileUrl->SetQuery(NS_LITERAL_CSTRING("origin=") + aOrigin);
+  nsAutoCString type;
+  PersistenceTypeToText(aPersistenceType, type);
+
+  rv = fileUrl->SetQuery(NS_LITERAL_CSTRING("persistenceType=") + type +
+                         NS_LITERAL_CSTRING("&group=") + aGroup +
+                         NS_LITERAL_CSTRING("&origin=") + aOrigin);
   NS_ENSURE_SUCCESS(rv, nullptr);
 
   return fileUrl.forget();
 }
 
 // static
 already_AddRefed<mozIStorageConnection>
 IDBFactory::GetConnection(const nsAString& aDatabaseFilePath,
+                          PersistenceType aPersistenceType,
+                          const nsACString& aGroup,
                           const nsACString& aOrigin)
 {
   NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
   NS_ASSERTION(StringEndsWith(aDatabaseFilePath, NS_LITERAL_STRING(".sqlite")),
                "Bad file path!");
 
   nsCOMPtr<nsIFile> dbFile(do_CreateInstance(NS_LOCAL_FILE_CONTRACTID));
   NS_ENSURE_TRUE(dbFile, nullptr);
@@ -268,17 +302,18 @@ IDBFactory::GetConnection(const nsAStrin
   nsresult rv = dbFile->InitWithPath(aDatabaseFilePath);
   NS_ENSURE_SUCCESS(rv, nullptr);
 
   bool exists;
   rv = dbFile->Exists(&exists);
   NS_ENSURE_SUCCESS(rv, nullptr);
   NS_ENSURE_TRUE(exists, nullptr);
 
-  nsCOMPtr<nsIFileURL> dbFileUrl = GetDatabaseFileURL(dbFile, aOrigin);
+  nsCOMPtr<nsIFileURL> dbFileUrl =
+    GetDatabaseFileURL(dbFile, aPersistenceType, aGroup, aOrigin);
   NS_ENSURE_TRUE(dbFileUrl, nullptr);
 
   nsCOMPtr<mozIStorageService> ss =
     do_GetService(MOZ_STORAGE_SERVICE_CONTRACTID);
   NS_ENSURE_TRUE(ss, nullptr);
 
   nsCOMPtr<mozIStorageConnection> connection;
   rv = ss->OpenDatabaseWithFileURL(dbFileUrl, getter_AddRefs(connection));
@@ -519,81 +554,97 @@ NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(IDB
   NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mOwningObject)
 NS_IMPL_CYCLE_COLLECTION_TRACE_END
 
 DOMCI_DATA(IDBFactory, IDBFactory)
 
 nsresult
 IDBFactory::OpenInternal(const nsAString& aName,
                          int64_t aVersion,
+                         PersistenceType aPersistenceType,
+                         const nsACString& aGroup,
                          const nsACString& aASCIIOrigin,
+                         StoragePrivilege aPrivilege,
                          bool aDeleting,
                          IDBOpenDBRequest** _retval)
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
   NS_ASSERTION(mWindow || mOwningObject, "Must have one of these!");
 
   AutoJSContext cx;
   nsCOMPtr<nsPIDOMWindow> window;
   JS::Rooted<JSObject*> scriptOwner(cx);
-  StoragePrivilege privilege;
 
   if (mWindow) {
     window = mWindow;
     scriptOwner =
       static_cast<nsGlobalWindow*>(window.get())->FastGetGlobalJSObject();
-    privilege = Content;
   }
   else {
     scriptOwner = mOwningObject;
-    privilege = Chrome;
+  }
+
+  if (aPrivilege == Chrome) {
+    // Chrome privilege, ignore the persistence type parameter.
+    aPersistenceType = PERSISTENCE_TYPE_PERSISTENT;
   }
 
   nsRefPtr<IDBOpenDBRequest> request =
     IDBOpenDBRequest::Create(this, window, scriptOwner);
   NS_ENSURE_TRUE(request, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
   nsresult rv;
 
   if (IndexedDatabaseManager::IsMainProcess()) {
     nsRefPtr<OpenDatabaseHelper> openHelper =
-      new OpenDatabaseHelper(request, aName, aASCIIOrigin, aVersion, aDeleting,
-                             mContentParent, privilege);
+      new OpenDatabaseHelper(request, aName, aGroup, aASCIIOrigin, aVersion,
+                             aPersistenceType, aDeleting, mContentParent,
+                             aPrivilege);
 
     rv = openHelper->Init();
     NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
-    nsRefPtr<CheckPermissionsHelper> permissionHelper =
-      new CheckPermissionsHelper(openHelper, window, aDeleting);
+    if (aPersistenceType == PERSISTENCE_TYPE_PERSISTENT) {
+      nsRefPtr<CheckPermissionsHelper> permissionHelper =
+        new CheckPermissionsHelper(openHelper, window);
+
+      QuotaManager* quotaManager = QuotaManager::Get();
+      NS_ASSERTION(quotaManager, "This should never be null!");
 
-    QuotaManager* quotaManager = QuotaManager::Get();
-    NS_ASSERTION(quotaManager, "This should never be null!");
+      rv = quotaManager->
+        WaitForOpenAllowed(OriginOrPatternString::FromOrigin(aASCIIOrigin),
+                           Nullable<PersistenceType>(aPersistenceType),
+                           openHelper->Id(), permissionHelper);
+    }
+    else {
+      NS_ASSERTION(aPersistenceType == PERSISTENCE_TYPE_TEMPORARY, "Huh?");
 
-    rv = quotaManager->WaitForOpenAllowed(OriginOrPatternString::FromOrigin(
-                                          aASCIIOrigin), openHelper->Id(),
-                                          permissionHelper);
+      rv = openHelper->WaitForOpenAllowed();
+    }
     NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
   }
   else if (aDeleting) {
     nsCOMPtr<nsIAtom> databaseId =
-      QuotaManager::GetStorageId(aASCIIOrigin, aName);
+      QuotaManager::GetStorageId(aPersistenceType, aASCIIOrigin, aName);
     NS_ENSURE_TRUE(databaseId, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
     IndexedDBDeleteDatabaseRequestChild* actor =
       new IndexedDBDeleteDatabaseRequestChild(this, request, databaseId);
 
     mActorChild->SendPIndexedDBDeleteDatabaseRequestConstructor(
-                                                               actor,
-                                                               nsString(aName));
+                                                              actor,
+                                                              nsString(aName),
+                                                              aPersistenceType);
   }
   else {
     IndexedDBDatabaseChild* dbActor =
       static_cast<IndexedDBDatabaseChild*>(
         mActorChild->SendPIndexedDBDatabaseConstructor(nsString(aName),
-                                                       aVersion));
+                                                       aVersion,
+                                                       aPersistenceType));
 
     dbActor->SetRequest(request);
   }
 
 #ifdef IDB_PROFILER_USE_MARKS
   {
     NS_ConvertUTF16toUTF8 profilerName(aName);
     if (aDeleting) {
@@ -615,16 +666,32 @@ IDBFactory::OpenInternal(const nsAString
 }
 
 JSObject*
 IDBFactory::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aScope)
 {
   return IDBFactoryBinding::Wrap(aCx, aScope, this);
 }
 
+already_AddRefed<IDBOpenDBRequest>
+IDBFactory::Open(const nsAString& aName, const IDBOpenDBOptions& aOptions,
+                 ErrorResult& aRv)
+{
+  return Open(nullptr, aName, aOptions.mVersion, aOptions.mStorage, false, aRv);
+}
+
+already_AddRefed<IDBOpenDBRequest>
+IDBFactory::DeleteDatabase(const nsAString& aName,
+                           const IDBOpenDBOptions& aOptions,
+                           ErrorResult& aRv)
+{
+  return Open(nullptr, aName, Optional<uint64_t>(), aOptions.mStorage, true,
+              aRv);
+}
+
 int16_t
 IDBFactory::Cmp(JSContext* aCx, JS::Handle<JS::Value> aFirst,
                 JS::Handle<JS::Value> aSecond, ErrorResult& aRv)
 {
   Key first, second;
   nsresult rv = first.SetFromJSVal(aCx, aFirst);
   if (NS_FAILED(rv)) {
     aRv.Throw(rv);
@@ -641,75 +708,97 @@ IDBFactory::Cmp(JSContext* aCx, JS::Hand
     aRv.Throw(NS_ERROR_DOM_INDEXEDDB_DATA_ERR);
     return 0;
   }
 
   return Key::CompareKeys(first, second);
 }
 
 already_AddRefed<IDBOpenDBRequest>
-IDBFactory::OpenForPrincipal(nsIPrincipal* aPrincipal,
-                             const nsAString& aName,
-                             const Optional<uint64_t>& aVersion,
-                             ErrorResult& aRv)
+IDBFactory::OpenForPrincipal(nsIPrincipal* aPrincipal, const nsAString& aName,
+                             uint64_t aVersion, ErrorResult& aRv)
 {
   // Just to be on the extra-safe side
   if (!nsContentUtils::IsCallerChrome()) {
     MOZ_CRASH();
   }
 
-  return Open(aPrincipal, aName, aVersion, false, aRv);
+  return Open(aPrincipal, aName, Optional<uint64_t>(aVersion),
+              Optional<mozilla::dom::StorageType>(), false, aRv);
 }
 
 already_AddRefed<IDBOpenDBRequest>
-IDBFactory::DeleteForPrincipal(nsIPrincipal* aPrincipal,
-                               const nsAString& aName,
+IDBFactory::OpenForPrincipal(nsIPrincipal* aPrincipal, const nsAString& aName,
+                             const IDBOpenDBOptions& aOptions, ErrorResult& aRv)
+{
+  // Just to be on the extra-safe side
+  if (!nsContentUtils::IsCallerChrome()) {
+    MOZ_CRASH();
+  }
+
+  return Open(aPrincipal, aName, aOptions.mVersion, aOptions.mStorage, false,
+              aRv);
+}
+
+already_AddRefed<IDBOpenDBRequest>
+IDBFactory::DeleteForPrincipal(nsIPrincipal* aPrincipal, const nsAString& aName,
+                               const IDBOpenDBOptions& aOptions,
                                ErrorResult& aRv)
 {
   // Just to be on the extra-safe side
   if (!nsContentUtils::IsCallerChrome()) {
     MOZ_CRASH();
   }
 
-  return Open(aPrincipal, aName, Optional<uint64_t>(), true, aRv);
+  return Open(aPrincipal, aName, Optional<uint64_t>(), aOptions.mStorage, true,
+              aRv);
 }
 
 already_AddRefed<IDBOpenDBRequest>
-IDBFactory::Open(nsIPrincipal* aPrincipal,
-                 const nsAString& aName, const Optional<uint64_t>& aVersion,
+IDBFactory::Open(nsIPrincipal* aPrincipal, const nsAString& aName,
+                 const Optional<uint64_t>& aVersion,
+                 const Optional<mozilla::dom::StorageType>& aStorageType,
                  bool aDelete, ErrorResult& aRv)
 {
   nsresult rv;
 
+  nsCString group;
   nsCString origin;
+  StoragePrivilege privilege;
+  PersistenceType defaultPersistenceType;
   if (aPrincipal) {
-    rv = QuotaManager::GetASCIIOriginFromPrincipal(aPrincipal, origin);
+    rv = QuotaManager::GetInfoFromPrincipal(aPrincipal, &group, &origin,
+                                            &privilege,
+                                            &defaultPersistenceType);
     if (NS_FAILED(rv)) {
-      aRv.Throw(rv);
+      aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
       return nullptr;
     }
   }
   else {
+    group = mGroup;
     origin = mASCIIOrigin;
+    privilege = mPrivilege;
+    defaultPersistenceType = mDefaultPersistenceType;
   }
 
-  uint64_t version;
+  uint64_t version = 0;
   if (!aDelete && aVersion.WasPassed()) {
-    version = aVersion.Value();
-    if (version < 1) {
+    if (aVersion.Value() < 1) {
       aRv.ThrowTypeError(MSG_INVALID_VERSION);
       return nullptr;
     }
-  }
-  else {
-    version = 0;
+    version = aVersion.Value();
   }
 
+  PersistenceType persistenceType =
+    PersistenceTypeFromStorage(aStorageType, defaultPersistenceType);
+
   nsRefPtr<IDBOpenDBRequest> request;
-  rv = OpenInternal(aName, version, origin, aDelete,
-                    getter_AddRefs(request));
+  rv = OpenInternal(aName, version, persistenceType, group, origin, privilege,
+                    aDelete, getter_AddRefs(request));
   if (NS_FAILED(rv)) {
     aRv.Throw(rv);
     return nullptr;
   }
 
   return request.forget();
 }
--- a/dom/indexedDB/IDBFactory.h
+++ b/dom/indexedDB/IDBFactory.h
@@ -3,16 +3,19 @@
 /* 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_idbfactory_h__
 #define mozilla_dom_indexeddb_idbfactory_h__
 
 #include "mozilla/dom/BindingDeclarations.h" // for Optional
+#include "mozilla/dom/StorageTypeBinding.h"
+#include "mozilla/dom/quota/PersistenceType.h"
+#include "mozilla/dom/quota/StoragePrivilege.h"
 #include "nsCOMPtr.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsWrapperCache.h"
 
 class mozIStorageConnection;
 class nsIAtom;
 class nsIFile;
 class nsIFileURL;
@@ -20,68 +23,78 @@ class nsIPrincipal;
 class nsPIDOMWindow;
 template<typename> class nsRefPtr;
 
 namespace mozilla {
 class ErrorResult;
 
 namespace dom {
 class ContentParent;
+class IDBOpenDBOptions;
 
 namespace indexedDB {
 
 struct DatabaseInfo;
 class IDBDatabase;
 class IDBOpenDBRequest;
 class IndexedDBChild;
 class IndexedDBParent;
 
 struct ObjectStoreInfo;
 
 class IDBFactory MOZ_FINAL : public nsISupports,
                              public nsWrapperCache
 {
   typedef mozilla::dom::ContentParent ContentParent;
+  typedef mozilla::dom::quota::PersistenceType PersistenceType;
   typedef nsTArray<nsRefPtr<ObjectStoreInfo> > ObjectStoreInfoArray;
+  typedef mozilla::dom::quota::StoragePrivilege StoragePrivilege;
 
 public:
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(IDBFactory)
 
   // Called when using IndexedDB from a window in a different process.
   static nsresult Create(nsPIDOMWindow* aWindow,
+                         const nsACString& aGroup,
                          const nsACString& aASCIIOrigin,
                          ContentParent* aContentParent,
                          IDBFactory** aFactory);
 
   // Called when using IndexedDB from a window in the current process.
   static nsresult Create(nsPIDOMWindow* aWindow,
                          ContentParent* aContentParent,
                          IDBFactory** aFactory)
   {
-    return Create(aWindow, EmptyCString(), aContentParent, aFactory);
+    return Create(aWindow, EmptyCString(), EmptyCString(), aContentParent,
+                  aFactory);
   }
 
   // Called when using IndexedDB from a JS component or a JSM in the current
   // process.
   static nsresult Create(JSContext* aCx,
                          JS::Handle<JSObject*> aOwningObject,
                          ContentParent* aContentParent,
                          IDBFactory** aFactory);
 
   // Called when using IndexedDB from a JS component or a JSM in a different
   // process.
   static nsresult Create(ContentParent* aContentParent,
                          IDBFactory** aFactory);
 
   static already_AddRefed<nsIFileURL>
-  GetDatabaseFileURL(nsIFile* aDatabaseFile, const nsACString& aOrigin);
+  GetDatabaseFileURL(nsIFile* aDatabaseFile,
+                     PersistenceType aPersistenceType,
+                     const nsACString& aGroup,
+                     const nsACString& aOrigin);
 
   static already_AddRefed<mozIStorageConnection>
   GetConnection(const nsAString& aDatabaseFilePath,
+                PersistenceType aPersistenceType,
+                const nsACString& aGroup,
                 const nsACString& aOrigin);
 
   static nsresult
   SetDefaultPragmas(mozIStorageConnection* aConnection);
 
   static nsresult
   LoadDatabaseInformation(mozIStorageConnection* aConnection,
                           nsIAtom* aDatabaseId,
@@ -91,27 +104,32 @@ public:
   static nsresult
   SetDatabaseMetadata(DatabaseInfo* aDatabaseInfo,
                       uint64_t aVersion,
                       ObjectStoreInfoArray& aObjectStores);
 
   nsresult
   OpenInternal(const nsAString& aName,
                int64_t aVersion,
+               PersistenceType aPersistenceType,
+               const nsACString& aGroup,
                const nsACString& aASCIIOrigin,
+               StoragePrivilege aStoragePrivilege,
                bool aDeleting,
                IDBOpenDBRequest** _retval);
 
   nsresult
   OpenInternal(const nsAString& aName,
                int64_t aVersion,
+               PersistenceType aPersistenceType,
                bool aDeleting,
                IDBOpenDBRequest** _retval)
   {
-    return OpenInternal(aName, aVersion, mASCIIOrigin, aDeleting, _retval);
+    return OpenInternal(aName, aVersion, aPersistenceType, mGroup, mASCIIOrigin,
+                        mPrivilege, aDeleting, _retval);
   }
 
   void
   SetActor(IndexedDBChild* aActorChild)
   {
     NS_ASSERTION(!aActorChild || !mActorChild, "Shouldn't have more than one!");
     mActorChild = aActorChild;
   }
@@ -124,60 +142,72 @@ public:
   }
 
   const nsCString&
   GetASCIIOrigin() const
   {
     return mASCIIOrigin;
   }
 
-  // WrapperCache
-  nsPIDOMWindow* GetParentObject() const
+  // nsWrapperCache
+  virtual JSObject*
+  WrapObject(JSContext* aCx, JS::Handle<JSObject*> aScope) MOZ_OVERRIDE;
+
+  // WebIDL
+  nsPIDOMWindow*
+  GetParentObject() const
   {
     return mWindow;
   }
 
-  virtual JSObject* WrapObject(JSContext* aCx,
-                               JS::Handle<JSObject*> aScope) MOZ_OVERRIDE;
-
-  // WebIDL
   already_AddRefed<IDBOpenDBRequest>
-  Open(const nsAString& aName, const Optional<uint64_t>& aVersion,
-       ErrorResult& aRv)
+  Open(const nsAString& aName, uint64_t aVersion, ErrorResult& aRv)
   {
-    return Open(nullptr, aName, aVersion, false, aRv);
+    return Open(nullptr, aName, Optional<uint64_t>(aVersion),
+                Optional<mozilla::dom::StorageType>(), false, aRv);
   }
 
   already_AddRefed<IDBOpenDBRequest>
-  DeleteDatabase(const nsAString& aName, ErrorResult& aRv)
-  {
-    return Open(nullptr, aName, Optional<uint64_t>(), true, aRv);
-  }
+  Open(const nsAString& aName, const IDBOpenDBOptions& aOptions,
+       ErrorResult& aRv);
+
+  already_AddRefed<IDBOpenDBRequest>
+  DeleteDatabase(const nsAString& aName, const IDBOpenDBOptions& aOptions,
+                 ErrorResult& aRv);
 
   int16_t
   Cmp(JSContext* aCx, JS::Handle<JS::Value> aFirst,
       JS::Handle<JS::Value> aSecond, ErrorResult& aRv);
 
   already_AddRefed<IDBOpenDBRequest>
   OpenForPrincipal(nsIPrincipal* aPrincipal, const nsAString& aName,
-                   const Optional<uint64_t>& aVersion, ErrorResult& aRv);
+                   uint64_t aVersion, ErrorResult& aRv);
+
+  already_AddRefed<IDBOpenDBRequest>
+  OpenForPrincipal(nsIPrincipal* aPrincipal, const nsAString& aName,
+                   const IDBOpenDBOptions& aOptions, ErrorResult& aRv);
 
   already_AddRefed<IDBOpenDBRequest>
   DeleteForPrincipal(nsIPrincipal* aPrincipal, const nsAString& aName,
-                     ErrorResult& aRv);
+                     const IDBOpenDBOptions& aOptions, ErrorResult& aRv);
 
 private:
   IDBFactory();
   ~IDBFactory();
 
   already_AddRefed<IDBOpenDBRequest>
   Open(nsIPrincipal* aPrincipal, const nsAString& aName,
-       const Optional<uint64_t>& aVersion, bool aDelete, ErrorResult& aRv);
+       const Optional<uint64_t>& aVersion,
+       const Optional<mozilla::dom::StorageType>& aStorageType, bool aDelete,
+       ErrorResult& aRv);
 
+  nsCString mGroup;
   nsCString mASCIIOrigin;
+  StoragePrivilege mPrivilege;
+  PersistenceType mDefaultPersistenceType;
 
   // If this factory lives on a window then mWindow must be non-null. Otherwise
   // mOwningObject must be non-null.
   nsCOMPtr<nsPIDOMWindow> mWindow;
   JS::Heap<JSObject*> mOwningObject;
 
   IndexedDBChild* mActorChild;
   IndexedDBParent* mActorParent;
--- a/dom/indexedDB/IDBFileHandle.cpp
+++ b/dom/indexedDB/IDBFileHandle.cpp
@@ -72,28 +72,32 @@ IDBFileHandle::Create(IDBDatabase* aData
 }
 
 already_AddRefed<nsISupports>
 IDBFileHandle::CreateStream(nsIFile* aFile, bool aReadOnly)
 {
   nsCOMPtr<nsIOfflineStorage> storage = do_QueryInterface(mFileStorage);
   NS_ASSERTION(storage, "This should always succeed!");
 
+  PersistenceType persistenceType = storage->Type();
+  const nsACString& group = storage->Group();
   const nsACString& origin = storage->Origin();
 
   nsCOMPtr<nsISupports> result;
 
   if (aReadOnly) {
-    nsRefPtr<FileInputStream> stream = FileInputStream::Create(
-      origin, aFile, -1, -1, nsIFileInputStream::DEFER_OPEN);
+    nsRefPtr<FileInputStream> stream =
+      FileInputStream::Create(persistenceType, group, origin, aFile, -1, -1,
+                              nsIFileInputStream::DEFER_OPEN);
     result = NS_ISUPPORTS_CAST(nsIFileInputStream*, stream);
   }
   else {
-    nsRefPtr<FileStream> stream = FileStream::Create(
-      origin, aFile, -1, -1, nsIFileStream::DEFER_OPEN);
+    nsRefPtr<FileStream> stream =
+      FileStream::Create(persistenceType, group, origin, aFile, -1, -1,
+                         nsIFileStream::DEFER_OPEN);
     result = NS_ISUPPORTS_CAST(nsIFileStream*, stream);
   }
   NS_ENSURE_TRUE(result, nullptr);
 
   return result.forget();
 }
 
 already_AddRefed<nsIDOMFile>
--- a/dom/indexedDB/IDBObjectStore.cpp
+++ b/dom/indexedDB/IDBObjectStore.cpp
@@ -3010,18 +3010,20 @@ AddHelper::DoDatabaseWork(mozIStorageCon
 
         rv = nativeFile->Create(nsIFile::NORMAL_FILE_TYPE, 0644);
         NS_ENSURE_TRUE(nativeFile, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
         // Now we can copy the blob
         nativeFile = fileManager->GetFileForId(directory, id);
         NS_ENSURE_TRUE(nativeFile, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
-        nsRefPtr<FileOutputStream> outputStream = FileOutputStream::Create(
-          mObjectStore->Transaction()->Database()->Origin(), nativeFile);
+        IDBDatabase* database = mObjectStore->Transaction()->Database();
+        nsRefPtr<FileOutputStream> outputStream =
+          FileOutputStream::Create(database->Type(), database->Group(),
+                                   database->Origin(), nativeFile);
         NS_ENSURE_TRUE(outputStream, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
         rv = CopyData(inputStream, outputStream);
         NS_ENSURE_SUCCESS(rv, rv);
 
         cloneFile.mFile->AddFileInfo(fileInfo);
       }
 
--- a/dom/indexedDB/IDBTransaction.cpp
+++ b/dom/indexedDB/IDBTransaction.cpp
@@ -356,18 +356,18 @@ IDBTransaction::GetOrCreateConnection(mo
   PROFILER_LABEL("IndexedDB", "IDBTransaction::GetOrCreateConnection");
 
   if (mDatabase->IsInvalidated()) {
     return NS_ERROR_NOT_AVAILABLE;
   }
 
   if (!mConnection) {
     nsCOMPtr<mozIStorageConnection> connection =
-      IDBFactory::GetConnection(mDatabase->FilePath(),
-                                mDatabase->Origin());
+      IDBFactory::GetConnection(mDatabase->FilePath(), mDatabase->Type(),
+                                mDatabase->Group(), mDatabase->Origin());
     NS_ENSURE_TRUE(connection, NS_ERROR_FAILURE);
 
     nsresult rv;
 
     nsRefPtr<UpdateRefcountFunction> function;
     nsCString beginTransaction;
     if (mMode != IDBTransaction::READ_ONLY) {
       function = new UpdateRefcountFunction(Database()->Manager());
--- a/dom/indexedDB/IndexedDatabaseManager.cpp
+++ b/dom/indexedDB/IndexedDatabaseManager.cpp
@@ -11,16 +11,17 @@
 #include "nsIDOMScriptObjectFactory.h"
 #include "nsIFile.h"
 #include "nsIFileStorage.h"
 #include "nsIObserverService.h"
 #include "nsIScriptError.h"
 
 #include "mozilla/ClearOnShutdown.h"
 #include "mozilla/CondVar.h"
+#include "mozilla/dom/quota/OriginOrPatternString.h"
 #include "mozilla/dom/quota/QuotaManager.h"
 #include "mozilla/dom/quota/Utilities.h"
 #include "mozilla/dom/TabContext.h"
 #include "mozilla/Services.h"
 #include "mozilla/storage.h"
 #include "nsContentUtils.h"
 #include "nsEventDispatcher.h"
 #include "nsThreadUtils.h"
@@ -36,16 +37,63 @@
 #define LOW_DISK_SPACE_DATA_FREE "free"
 
 USING_INDEXEDDB_NAMESPACE
 using namespace mozilla::dom;
 USING_QUOTA_NAMESPACE
 
 static NS_DEFINE_CID(kDOMSOF_CID, NS_DOM_SCRIPT_OBJECT_FACTORY_CID);
 
+BEGIN_INDEXEDDB_NAMESPACE
+
+class FileManagerInfo
+{
+public:
+  already_AddRefed<FileManager>
+  GetFileManager(PersistenceType aPersistenceType,
+                 const nsAString& aName) const;
+
+  void
+  AddFileManager(FileManager* aFileManager);
+
+  bool
+  HasFileManagers() const
+  {
+    AssertIsOnIOThread();
+
+    return !mPersistentStorageFileManagers.IsEmpty() ||
+           !mTemporaryStorageFileManagers.IsEmpty();
+  }
+
+  void
+  InvalidateAllFileManagers() const;
+
+  void
+  InvalidateAndRemoveFileManagers(PersistenceType aPersistenceType);
+
+  void
+  InvalidateAndRemoveFileManager(PersistenceType aPersistenceType,
+                                 const nsAString& aName);
+
+private:
+  nsTArray<nsRefPtr<FileManager> >&
+  GetArray(PersistenceType aPersistenceType);
+
+  const nsTArray<nsRefPtr<FileManager> >&
+  GetImmutableArray(PersistenceType aPersistenceType) const
+  {
+    return const_cast<FileManagerInfo*>(this)->GetArray(aPersistenceType);
+  }
+
+  nsTArray<nsRefPtr<FileManager> > mPersistentStorageFileManagers;
+  nsTArray<nsRefPtr<FileManager> > mTemporaryStorageFileManagers;
+};
+
+END_INDEXEDDB_NAMESPACE
+
 namespace {
 
 mozilla::StaticRefPtr<IndexedDatabaseManager> gInstance;
 
 mozilla::Atomic<int32_t> gInitialized(0);
 mozilla::Atomic<int32_t> gClosed(0);
 
 class AsyncDeleteFileRunnable MOZ_FINAL : public nsIRunnable
@@ -62,72 +110,63 @@ private:
 };
 
 class GetFileReferencesHelper MOZ_FINAL : public nsIRunnable
 {
 public:
   NS_DECL_THREADSAFE_ISUPPORTS
   NS_DECL_NSIRUNNABLE
 
-  GetFileReferencesHelper(const nsACString& aOrigin,
+  GetFileReferencesHelper(PersistenceType aPersistenceType,
+                          const nsACString& aOrigin,
                           const nsAString& aDatabaseName,
                           int64_t aFileId)
-  : mOrigin(aOrigin), mDatabaseName(aDatabaseName), mFileId(aFileId),
+  : mPersistenceType(aPersistenceType),
+    mOrigin(aOrigin),
+    mDatabaseName(aDatabaseName),
+    mFileId(aFileId),
     mMutex(IndexedDatabaseManager::FileMutex()),
     mCondVar(mMutex, "GetFileReferencesHelper::mCondVar"),
     mMemRefCnt(-1),
     mDBRefCnt(-1),
     mSliceRefCnt(-1),
     mResult(false),
     mWaiting(true)
   { }
 
   nsresult
   DispatchAndReturnFileReferences(int32_t* aMemRefCnt,
                                   int32_t* aDBRefCnt,
                                   int32_t* aSliceRefCnt,
                                   bool* aResult);
 
 private:
+  PersistenceType mPersistenceType;
   nsCString mOrigin;
   nsString mDatabaseName;
   int64_t mFileId;
 
   mozilla::Mutex& mMutex;
   mozilla::CondVar mCondVar;
   int32_t mMemRefCnt;
   int32_t mDBRefCnt;
   int32_t mSliceRefCnt;
   bool mResult;
   bool mWaiting;
 };
 
-PLDHashOperator
-InvalidateAndRemoveFileManagers(
-                           const nsACString& aKey,
-                           nsAutoPtr<nsTArray<nsRefPtr<FileManager> > >& aValue,
-                           void* aUserArg)
+struct MOZ_STACK_CLASS InvalidateInfo
 {
-  AssertIsOnIOThread();
-  NS_ASSERTION(!aKey.IsEmpty(), "Empty key!");
-  NS_ASSERTION(aValue, "Null pointer!");
+  InvalidateInfo(PersistenceType aPersistenceType, const nsACString& aPattern)
+  : persistenceType(aPersistenceType), pattern(aPattern)
+  { }
 
-  const nsACString* pattern =
-    static_cast<const nsACString*>(aUserArg);
-
-  if (!pattern || PatternMatchesOrigin(*pattern, aKey)) {
-    for (uint32_t i = 0; i < aValue->Length(); i++) {
-      nsRefPtr<FileManager>& fileManager = aValue->ElementAt(i);
-      fileManager->Invalidate();
-    }
-    return PL_DHASH_REMOVE;
-  }
-
-  return PL_DHASH_NEXT;
-}
+  PersistenceType persistenceType;
+  const nsACString& pattern;
+};
 
 } // anonymous namespace
 
 IndexedDatabaseManager::IndexedDatabaseManager()
 : mFileMutex("IndexedDatabaseManager.mFileMutex")
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 }
@@ -376,95 +415,127 @@ IndexedDatabaseManager::InLowDiskSpaceMo
   NS_ASSERTION(gInstance,
                "InLowDiskSpaceMode() called before indexedDB has been "
                "initialized!");
   return !!sLowDiskSpaceMode;
 }
 #endif
 
 already_AddRefed<FileManager>
-IndexedDatabaseManager::GetFileManager(const nsACString& aOrigin,
+IndexedDatabaseManager::GetFileManager(PersistenceType aPersistenceType,
+                                       const nsACString& aOrigin,
                                        const nsAString& aDatabaseName)
 {
   AssertIsOnIOThread();
 
-  nsTArray<nsRefPtr<FileManager> >* array;
-  if (!mFileManagers.Get(aOrigin, &array)) {
+  FileManagerInfo* info;
+  if (!mFileManagerInfos.Get(aOrigin, &info)) {
     return nullptr;
   }
 
-  for (uint32_t i = 0; i < array->Length(); i++) {
-    nsRefPtr<FileManager>& fileManager = array->ElementAt(i);
+  nsRefPtr<FileManager> fileManager =
+    info->GetFileManager(aPersistenceType, aDatabaseName);
 
-    if (fileManager->DatabaseName().Equals(aDatabaseName)) {
-      nsRefPtr<FileManager> result = fileManager;
-      return result.forget();
-    }
-  }
-
-  return nullptr;
+  return fileManager.forget();
 }
 
 void
 IndexedDatabaseManager::AddFileManager(FileManager* aFileManager)
 {
   AssertIsOnIOThread();
   NS_ASSERTION(aFileManager, "Null file manager!");
 
-  nsTArray<nsRefPtr<FileManager> >* array;
-  if (!mFileManagers.Get(aFileManager->Origin(), &array)) {
-    array = new nsTArray<nsRefPtr<FileManager> >();
-    mFileManagers.Put(aFileManager->Origin(), array);
+  FileManagerInfo* info;
+  if (!mFileManagerInfos.Get(aFileManager->Origin(), &info)) {
+    info = new FileManagerInfo();
+    mFileManagerInfos.Put(aFileManager->Origin(), info);
   }
 
-  array->AppendElement(aFileManager);
+  info->AddFileManager(aFileManager);
+}
+
+// static
+PLDHashOperator
+IndexedDatabaseManager::InvalidateAndRemoveFileManagers(
+                                             const nsACString& aKey,
+                                             nsAutoPtr<FileManagerInfo>& aValue,
+                                             void* aUserArg)
+{
+  AssertIsOnIOThread();
+  NS_ASSERTION(!aKey.IsEmpty(), "Empty key!");
+  NS_ASSERTION(aValue, "Null pointer!");
+
+  if (!aUserArg) {
+    aValue->InvalidateAllFileManagers();
+    return PL_DHASH_REMOVE;
+  }
+
+  InvalidateInfo* info = static_cast<InvalidateInfo*>(aUserArg);
+
+  if (PatternMatchesOrigin(info->pattern, aKey)) {
+    aValue->InvalidateAndRemoveFileManagers(info->persistenceType);
+
+    if (!aValue->HasFileManagers()) {
+      return PL_DHASH_REMOVE;
+    }
+  }
+
+  return PL_DHASH_NEXT;
 }
 
 void
 IndexedDatabaseManager::InvalidateAllFileManagers()
 {
   AssertIsOnIOThread();
 
-  mFileManagers.Enumerate(InvalidateAndRemoveFileManagers, nullptr);
+  mFileManagerInfos.Enumerate(InvalidateAndRemoveFileManagers, nullptr);
 }
 
 void
-IndexedDatabaseManager::InvalidateFileManagersForPattern(
-                                                     const nsACString& aPattern)
+IndexedDatabaseManager::InvalidateFileManagers(
+                                  PersistenceType aPersistenceType,
+                                  const OriginOrPatternString& aOriginOrPattern)
 {
   AssertIsOnIOThread();
-  NS_ASSERTION(!aPattern.IsEmpty(), "Empty pattern!");
+  NS_ASSERTION(!aOriginOrPattern.IsEmpty(), "Empty pattern!");
+
+  if (aOriginOrPattern.IsOrigin()) {
+    FileManagerInfo* info;
+    if (!mFileManagerInfos.Get(aOriginOrPattern, &info)) {
+      return;
+    }
 
-  mFileManagers.Enumerate(InvalidateAndRemoveFileManagers,
-                          const_cast<nsACString*>(&aPattern));
+    info->InvalidateAndRemoveFileManagers(aPersistenceType);
+
+    if (!info->HasFileManagers()) {
+      mFileManagerInfos.Remove(aOriginOrPattern);
+    }
+  }
+  else {
+    InvalidateInfo info(aPersistenceType, aOriginOrPattern);
+    mFileManagerInfos.Enumerate(InvalidateAndRemoveFileManagers, &info);
+  }
 }
 
 void
-IndexedDatabaseManager::InvalidateFileManager(const nsACString& aOrigin,
+IndexedDatabaseManager::InvalidateFileManager(PersistenceType aPersistenceType,
+                                              const nsACString& aOrigin,
                                               const nsAString& aDatabaseName)
 {
   AssertIsOnIOThread();
 
-  nsTArray<nsRefPtr<FileManager> >* array;
-  if (!mFileManagers.Get(aOrigin, &array)) {
+  FileManagerInfo* info;
+  if (!mFileManagerInfos.Get(aOrigin, &info)) {
     return;
   }
 
-  for (uint32_t i = 0; i < array->Length(); i++) {
-    nsRefPtr<FileManager> fileManager = array->ElementAt(i);
-    if (fileManager->DatabaseName().Equals(aDatabaseName)) {
-      fileManager->Invalidate();
-      array->RemoveElementAt(i);
+  info->InvalidateAndRemoveFileManager(aPersistenceType, aDatabaseName);
 
-      if (array->IsEmpty()) {
-        mFileManagers.Remove(aOrigin);
-      }
-
-      break;
-    }
+  if (!info->HasFileManagers()) {
+    mFileManagerInfos.Remove(aOrigin);
   }
 }
 
 nsresult
 IndexedDatabaseManager::AsyncDeleteFile(FileManager* aFileManager,
                                         int64_t aFileId)
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
@@ -487,26 +558,28 @@ IndexedDatabaseManager::AsyncDeleteFile(
     quotaManager->IOThread()->Dispatch(runnable, NS_DISPATCH_NORMAL);
   NS_ENSURE_SUCCESS(rv, rv);
 
   return NS_OK;
 }
 
 nsresult
 IndexedDatabaseManager::BlockAndGetFileReferences(
-                                                 const nsACString& aOrigin,
-                                                 const nsAString& aDatabaseName,
-                                                 int64_t aFileId,
-                                                 int32_t* aRefCnt,
-                                                 int32_t* aDBRefCnt,
-                                                 int32_t* aSliceRefCnt,
-                                                 bool* aResult)
+                                               PersistenceType aPersistenceType,
+                                               const nsACString& aOrigin,
+                                               const nsAString& aDatabaseName,
+                                               int64_t aFileId,
+                                               int32_t* aRefCnt,
+                                               int32_t* aDBRefCnt,
+                                               int32_t* aSliceRefCnt,
+                                               bool* aResult)
 {
   nsRefPtr<GetFileReferencesHelper> helper =
-    new GetFileReferencesHelper(aOrigin, aDatabaseName, aFileId);
+    new GetFileReferencesHelper(aPersistenceType, aOrigin, aDatabaseName,
+                                aFileId);
 
   nsresult rv = helper->DispatchAndReturnFileReferences(aRefCnt, aDBRefCnt,
                                                         aSliceRefCnt, aResult);
   NS_ENSURE_SUCCESS(rv, rv);
 
   return NS_OK;
 }
 
@@ -597,16 +670,115 @@ IndexedDatabaseManager::Observe(nsISuppo
 
     return NS_OK;
   }
 
    NS_NOTREACHED("Unknown topic!");
    return NS_ERROR_UNEXPECTED;
  }
 
+already_AddRefed<FileManager>
+FileManagerInfo::GetFileManager(PersistenceType aPersistenceType,
+                                const nsAString& aName) const
+{
+  AssertIsOnIOThread();
+
+  const nsTArray<nsRefPtr<FileManager> >& managers =
+    GetImmutableArray(aPersistenceType);
+
+  for (uint32_t i = 0; i < managers.Length(); i++) {
+    const nsRefPtr<FileManager>& fileManager = managers[i];
+
+    if (fileManager->DatabaseName() == aName) {
+      nsRefPtr<FileManager> result = fileManager;
+      return result.forget();
+    }
+  }
+
+  return nullptr;
+}
+
+void
+FileManagerInfo::AddFileManager(FileManager* aFileManager)
+{
+  AssertIsOnIOThread();
+
+  nsTArray<nsRefPtr<FileManager> >& managers = GetArray(aFileManager->Type());
+
+  NS_ASSERTION(!managers.Contains(aFileManager), "Adding more than once?!");
+
+  managers.AppendElement(aFileManager);
+}
+
+void
+FileManagerInfo::InvalidateAllFileManagers() const
+{
+  AssertIsOnIOThread();
+
+  uint32_t i;
+
+  for (i = 0; i < mPersistentStorageFileManagers.Length(); i++) {
+    mPersistentStorageFileManagers[i]->Invalidate();
+  }
+
+  for (i = 0; i < mTemporaryStorageFileManagers.Length(); i++) {
+    mTemporaryStorageFileManagers[i]->Invalidate();
+  }
+}
+
+void
+FileManagerInfo::InvalidateAndRemoveFileManagers(
+                                               PersistenceType aPersistenceType)
+{
+  AssertIsOnIOThread();
+
+  nsTArray<nsRefPtr<FileManager > >& managers = GetArray(aPersistenceType);
+
+  for (uint32_t i = 0; i < managers.Length(); i++) {
+    managers[i]->Invalidate();
+  }
+
+  managers.Clear();
+}
+
+void
+FileManagerInfo::InvalidateAndRemoveFileManager(
+                                               PersistenceType aPersistenceType,
+                                               const nsAString& aName)
+{
+  AssertIsOnIOThread();
+
+  nsTArray<nsRefPtr<FileManager > >& managers = GetArray(aPersistenceType);
+
+  for (uint32_t i = 0; i < managers.Length(); i++) {
+    nsRefPtr<FileManager>& fileManager = managers[i];
+    if (fileManager->DatabaseName() == aName) {
+      fileManager->Invalidate();
+      managers.RemoveElementAt(i);
+      return;
+    }
+  }
+}
+
+nsTArray<nsRefPtr<FileManager> >&
+FileManagerInfo::GetArray(PersistenceType aPersistenceType)
+{
+  switch (aPersistenceType) {
+    case PERSISTENCE_TYPE_PERSISTENT:
+      return mPersistentStorageFileManagers;
+    case PERSISTENCE_TYPE_TEMPORARY:
+      return mTemporaryStorageFileManagers;
+
+    case PERSISTENCE_TYPE_INVALID:
+    default:
+      MOZ_CRASH("Bad storage type value!");
+      return mPersistentStorageFileManagers;
+  }
+}
+
 AsyncDeleteFileRunnable::AsyncDeleteFileRunnable(FileManager* aFileManager,
                                                  int64_t aFileId)
 : mFileManager(aFileManager), mFileId(aFileId)
 {
 }
 
 NS_IMPL_ISUPPORTS1(AsyncDeleteFileRunnable,
                    nsIRunnable)
@@ -632,17 +804,19 @@ AsyncDeleteFileRunnable::Run()
 
   rv = file->Remove(false);
   NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
 
   if (mFileManager->Privilege() != Chrome) {
     QuotaManager* quotaManager = QuotaManager::Get();
     NS_ASSERTION(quotaManager, "Shouldn't be null!");
 
-    quotaManager->DecreaseUsageForOrigin(mFileManager->Origin(), fileSize);
+    quotaManager->DecreaseUsageForOrigin(mFileManager->Type(),
+                                         mFileManager->Group(),
+                                         mFileManager->Origin(), fileSize);
   }
 
   directory = mFileManager->GetJournalDirectory();
   NS_ENSURE_TRUE(directory, NS_ERROR_FAILURE);
 
   file = mFileManager->GetFileForId(directory, mFileId);
   NS_ENSURE_TRUE(file, NS_ERROR_FAILURE);
 
@@ -687,17 +861,17 @@ NS_IMETHODIMP
 GetFileReferencesHelper::Run()
 {
   AssertIsOnIOThread();
 
   IndexedDatabaseManager* mgr = IndexedDatabaseManager::Get();
   NS_ASSERTION(mgr, "This should never fail!");
 
   nsRefPtr<FileManager> fileManager =
-    mgr->GetFileManager(mOrigin, mDatabaseName);
+    mgr->GetFileManager(mPersistenceType, mOrigin, mDatabaseName);
 
   if (fileManager) {
     nsRefPtr<FileInfo> fileInfo = fileManager->GetFileInfo(mFileId);
 
     if (fileInfo) {
       fileInfo->GetReferences(&mMemRefCnt, &mDBRefCnt, &mSliceRefCnt);
 
       if (mMemRefCnt != -1) {
--- a/dom/indexedDB/IndexedDatabaseManager.h
+++ b/dom/indexedDB/IndexedDatabaseManager.h
@@ -8,39 +8,47 @@
 #define mozilla_dom_indexeddb_indexeddatabasemanager_h__
 
 #include "mozilla/dom/indexedDB/IndexedDatabase.h"
 
 #include "nsIIndexedDatabaseManager.h"
 #include "nsIObserver.h"
 
 #include "mozilla/Atomics.h"
+#include "mozilla/dom/quota/PersistenceType.h"
 #include "mozilla/Mutex.h"
 #include "nsClassHashtable.h"
 #include "nsHashKeys.h"
 
 #define INDEXEDDB_MANAGER_CONTRACTID "@mozilla.org/dom/indexeddb/manager;1"
 
 class nsIAtom;
 class nsPIDOMWindow;
 class nsEventChainPostVisitor;
 
 namespace mozilla {
 namespace dom {
 class TabContext;
+namespace quota {
+class OriginOrPatternString;
+}
 }
 }
 
 BEGIN_INDEXEDDB_NAMESPACE
 
 class FileManager;
+class FileManagerInfo;
 
 class IndexedDatabaseManager MOZ_FINAL : public nsIIndexedDatabaseManager,
                                          public nsIObserver
 {
+  typedef mozilla::dom::quota::OriginOrPatternString OriginOrPatternString;
+  typedef mozilla::dom::quota::PersistenceType PersistenceType;
+
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSIINDEXEDDATABASEMANAGER
   NS_DECL_NSIOBSERVER
 
   // Returns a non-owning reference.
   static IndexedDatabaseManager*
   GetOrCreate();
@@ -72,41 +80,45 @@ public:
   ;
 #else
   {
     return !!sLowDiskSpaceMode;
   }
 #endif
 
   already_AddRefed<FileManager>
-  GetFileManager(const nsACString& aOrigin,
+  GetFileManager(PersistenceType aPersistenceType,
+                 const nsACString& aOrigin,
                  const nsAString& aDatabaseName);
 
   void
   AddFileManager(FileManager* aFileManager);
 
   void
   InvalidateAllFileManagers();
 
   void
-  InvalidateFileManagersForPattern(const nsACString& aPattern);
+  InvalidateFileManagers(PersistenceType aPersistenceType,
+                         const OriginOrPatternString& aOriginOrPattern);
 
   void
-  InvalidateFileManager(const nsACString& aOrigin,
+  InvalidateFileManager(PersistenceType aPersistenceType,
+                        const nsACString& aOrigin,
                         const nsAString& aDatabaseName);
 
   nsresult
   AsyncDeleteFile(FileManager* aFileManager,
                   int64_t aFileId);
 
   // Don't call this method in real code, it blocks the main thread!
   // It is intended to be used by mochitests to test correctness of the special
   // reference counting of stored blobs/files.
   nsresult
-  BlockAndGetFileReferences(const nsACString& aOrigin,
+  BlockAndGetFileReferences(PersistenceType aPersistenceType,
+                            const nsACString& aOrigin,
                             const nsAString& aDatabaseName,
                             int64_t aFileId,
                             int32_t* aRefCnt,
                             int32_t* aDBRefCnt,
                             int32_t* aSliceRefCnt,
                             bool* aResult);
 
   static mozilla::Mutex&
@@ -131,20 +143,24 @@ private:
   ~IndexedDatabaseManager();
 
   nsresult
   Init();
 
   void
   Destroy();
 
+  static PLDHashOperator
+  InvalidateAndRemoveFileManagers(const nsACString& aKey,
+                                  nsAutoPtr<FileManagerInfo>& aValue,
+                                  void* aUserArg);
+
   // 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;
+  nsClassHashtable<nsCStringHashKey, FileManagerInfo> mFileManagerInfos;
 
   // 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;
 
   static bool sIsMainProcess;
   static mozilla::Atomic<int32_t> sLowDiskSpaceMode;
--- a/dom/indexedDB/OpenDatabaseHelper.cpp
+++ b/dom/indexedDB/OpenDatabaseHelper.cpp
@@ -1499,21 +1499,24 @@ class DeleteDatabaseHelper : public Asyn
                              public AcquireListener
 {
   friend class VersionChangeEventsRunnable;
 public:
   DeleteDatabaseHelper(IDBOpenDBRequest* aRequest,
                        OpenDatabaseHelper* aHelper,
                        uint64_t aCurrentVersion,
                        const nsAString& aName,
-                       const nsACString& aASCIIOrigin)
+                       const nsACString& aGroup,
+                       const nsACString& aASCIIOrigin,
+                       PersistenceType aPersistenceType)
   : AsyncConnectionHelper(static_cast<IDBDatabase*>(nullptr), aRequest),
     mOpenHelper(aHelper), mOpenRequest(aRequest),
     mCurrentVersion(aCurrentVersion), mName(aName),
-    mASCIIOrigin(aASCIIOrigin)
+    mGroup(aGroup), mASCIIOrigin(aASCIIOrigin),
+    mPersistenceType(aPersistenceType)
   { }
 
   NS_DECL_ISUPPORTS_INHERITED
 
   nsresult GetSuccessResult(JSContext* aCx,
                             JS::MutableHandle<JS::Value> aVal);
 
   void ReleaseMainThreadObjects()
@@ -1562,17 +1565,19 @@ protected:
   }
 
 private:
   // In-params
   nsRefPtr<OpenDatabaseHelper> mOpenHelper;
   nsRefPtr<IDBOpenDBRequest> mOpenRequest;
   uint64_t mCurrentVersion;
   nsString mName;
+  nsCString mGroup;
   nsCString mASCIIOrigin;
+  PersistenceType mPersistenceType;
 };
 
 // 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:
@@ -1680,40 +1685,70 @@ private:
 
 } // anonymous namespace
 
 NS_IMPL_ISUPPORTS1(OpenDatabaseHelper, nsIRunnable)
 
 nsresult
 OpenDatabaseHelper::Init()
 {
-  mDatabaseId = QuotaManager::GetStorageId(mASCIIOrigin, mName);
+  mDatabaseId =
+    QuotaManager::GetStorageId(mPersistenceType, mASCIIOrigin, mName);
   NS_ENSURE_TRUE(mDatabaseId, NS_ERROR_FAILURE);
 
   return NS_OK;
 }
 
 nsresult
+OpenDatabaseHelper::WaitForOpenAllowed()
+{
+  NS_ASSERTION(mState == eCreated, "We've already been dispatched?");
+  NS_ASSERTION(NS_IsMainThread(), "All hell is about to break lose!");
+
+  mState = eOpenPending;
+
+  QuotaManager* quotaManager = QuotaManager::Get();
+  NS_ASSERTION(quotaManager, "This should never be null!");
+
+  return quotaManager->
+    WaitForOpenAllowed(OriginOrPatternString::FromOrigin(mASCIIOrigin),
+                       Nullable<PersistenceType>(mPersistenceType), mDatabaseId,
+                       this);
+}
+
+nsresult
 OpenDatabaseHelper::Dispatch(nsIEventTarget* aTarget)
 {
-  NS_ASSERTION(mState == eCreated, "We've already been dispatched?");
+  NS_ASSERTION(mState == eCreated || mState == eOpenPending,
+               "We've already been dispatched?");
+
   mState = eDBWork;
 
   return aTarget->Dispatch(this, NS_DISPATCH_NORMAL);
 }
 
 nsresult
+OpenDatabaseHelper::DispatchToIOThread()
+{
+  QuotaManager* quotaManager = QuotaManager::Get();
+  NS_ASSERTION(quotaManager, "This should never be null!");
+
+  return Dispatch(quotaManager->IOThread());
+}
+
+nsresult
 OpenDatabaseHelper::RunImmediately()
 {
   NS_ASSERTION(mState == eCreated, "We've already been dispatched?");
   NS_ASSERTION(NS_FAILED(mResultCode),
                "Should only be short-circuiting if we failed!");
   NS_ASSERTION(NS_IsMainThread(), "All hell is about to break lose!");
 
   mState = eFiringEvents;
+
   return this->Run();
 }
 
 nsresult
 OpenDatabaseHelper::DoDatabaseWork()
 {
   AssertIsOnIOThread();
   NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
@@ -1734,18 +1769,18 @@ OpenDatabaseHelper::DoDatabaseWork()
   AutoEnterWindow autoWindow(window);
 
   nsCOMPtr<nsIFile> dbDirectory;
 
   QuotaManager* quotaManager = QuotaManager::Get();
   NS_ASSERTION(quotaManager, "This should never be null!");
 
   nsresult rv =
-    quotaManager->EnsureOriginIsInitialized(mASCIIOrigin,
-                                            mTrackingQuota,
+    quotaManager->EnsureOriginIsInitialized(mPersistenceType, mGroup,
+                                            mASCIIOrigin, mTrackingQuota,
                                             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);
@@ -1780,17 +1815,18 @@ OpenDatabaseHelper::DoDatabaseWork()
   nsCOMPtr<nsIFile> fmDirectory;
   rv = dbDirectory->Clone(getter_AddRefs(fmDirectory));
   NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
   rv = fmDirectory->Append(filename);
   NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
   nsCOMPtr<mozIStorageConnection> connection;
-  rv = CreateDatabaseConnection(dbFile, fmDirectory, mName, mASCIIOrigin,
+  rv = CreateDatabaseConnection(dbFile, fmDirectory, mName, mPersistenceType,
+                                mGroup, mASCIIOrigin,
                                 getter_AddRefs(connection));
   if (NS_FAILED(rv) &&
       NS_ERROR_GET_MODULE(rv) != NS_ERROR_MODULE_DOM_INDEXEDDB) {
     rv = NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
   }
   NS_ENSURE_SUCCESS(rv, rv);
 
   rv = IDBFactory::LoadDatabaseInformation(connection, mDatabaseId,
@@ -1832,19 +1868,21 @@ OpenDatabaseHelper::DoDatabaseWork()
 
   if (mCurrentVersion != mRequestedVersion) {
     mState = eSetVersionPending;
   }
 
   IndexedDatabaseManager* mgr = IndexedDatabaseManager::Get();
   NS_ASSERTION(mgr, "This should never be null!");
 
-  nsRefPtr<FileManager> fileManager = mgr->GetFileManager(mASCIIOrigin, mName);
+  nsRefPtr<FileManager> fileManager =
+    mgr->GetFileManager(mPersistenceType, mASCIIOrigin, mName);
   if (!fileManager) {
-    fileManager = new FileManager(mASCIIOrigin, mPrivilege, mName);
+    fileManager = new FileManager(mPersistenceType, mGroup, mASCIIOrigin,
+                                  mPrivilege, mName);
 
     rv = fileManager->Init(fmDirectory, connection);
     NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
     mgr->AddFileManager(fileManager);
   }
 
   mFileManager = fileManager.forget();
@@ -1853,16 +1891,18 @@ OpenDatabaseHelper::DoDatabaseWork()
 }
 
 // static
 nsresult
 OpenDatabaseHelper::CreateDatabaseConnection(
                                         nsIFile* aDBFile,
                                         nsIFile* aFMDirectory,
                                         const nsAString& aName,
+                                        PersistenceType aPersistenceType,
+                                        const nsACString& aGroup,
                                         const nsACString& aOrigin,
                                         mozIStorageConnection** aConnection)
 {
   AssertIsOnIOThread();
   NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
 
   PROFILER_LABEL("IndexedDB", "OpenDatabaseHelper::CreateDatabaseConnection");
 
@@ -1875,17 +1915,17 @@ OpenDatabaseHelper::CreateDatabaseConnec
 
     if (!exists) {
       NS_WARNING("Refusing to create database because disk space is low!");
       return NS_ERROR_DOM_INDEXEDDB_QUOTA_ERR;
     }
   }
 
   nsCOMPtr<nsIFileURL> dbFileUrl =
-    IDBFactory::GetDatabaseFileURL(aDBFile, aOrigin);
+    IDBFactory::GetDatabaseFileURL(aDBFile, aPersistenceType, aGroup, aOrigin);
   NS_ENSURE_TRUE(dbFileUrl, NS_ERROR_FAILURE);
 
   nsCOMPtr<mozIStorageService> ss =
     do_GetService(MOZ_STORAGE_SERVICE_CONTRACTID);
   NS_ENSURE_TRUE(ss, NS_ERROR_FAILURE);
 
   nsCOMPtr<mozIStorageConnection> connection;
   rv = ss->OpenDatabaseWithFileURL(dbFileUrl, getter_AddRefs(connection));
@@ -1945,17 +1985,22 @@ OpenDatabaseHelper::CreateDatabaseConnec
   if (schemaVersion != kSQLiteSchemaVersion) {
 #if defined(MOZ_WIDGET_ANDROID) || defined(MOZ_WIDGET_GONK)
     if (!schemaVersion) {
       // Have to do this before opening a transaction.
       rv = connection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
         // Turn on auto_vacuum mode to reclaim disk space on mobile devices.
         "PRAGMA auto_vacuum = FULL; "
       ));
-      NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
+      if (rv == NS_ERROR_FILE_NO_DEVICE_SPACE) {
+        // mozstorage translates SQLITE_FULL to NS_ERROR_FILE_NO_DEVICE_SPACE,
+        // which we know better as NS_ERROR_DOM_INDEXEDDB_QUOTA_ERR.
+        rv = NS_ERROR_DOM_INDEXEDDB_QUOTA_ERR;
+      }
+      NS_ENSURE_SUCCESS(rv, rv);
     }
 #endif
 
     mozStorageTransaction transaction(connection, false,
                                   mozIStorageConnection::TRANSACTION_IMMEDIATE);
 
     if (!schemaVersion) {
       // Brand new file, initialize our tables.
@@ -2092,17 +2137,17 @@ OpenDatabaseHelper::StartDelete()
   // In case we fail, fire error events
   mState = eFiringEvents;
 
   nsresult rv = EnsureSuccessResult();
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsRefPtr<DeleteDatabaseHelper> helper =
     new DeleteDatabaseHelper(mOpenDBRequest, this, mCurrentVersion, mName,
-                             mASCIIOrigin);
+                             mGroup, mASCIIOrigin, mPersistenceType);
 
   QuotaManager* quotaManager = QuotaManager::Get();
   NS_ASSERTION(quotaManager, "This should never be null!");
 
   rv = quotaManager->AcquireExclusiveAccess(
          mDatabase, mDatabase->Origin(), helper,
          &VersionChangeEventsRunnable::QueueVersionChange<DeleteDatabaseHelper>,
          helper);
@@ -2117,16 +2162,20 @@ OpenDatabaseHelper::StartDelete()
 NS_IMETHODIMP
 OpenDatabaseHelper::Run()
 {
   NS_ASSERTION(mState != eCreated, "Dispatch was not called?!?");
 
   if (NS_IsMainThread()) {
     PROFILER_MAIN_THREAD_LABEL("IndexedDB", "OpenDatabaseHelper::Run");
 
+    if (mState == eOpenPending) {
+      return DispatchToIOThread();
+    }
+
     // If we need to queue up a SetVersionHelper, do that here.
     if (mState == eSetVersionPending) {
       nsresult rv = StartSetVersion();
 
       if (NS_SUCCEEDED(rv)) {
         return rv;
       }
 
@@ -2194,19 +2243,20 @@ OpenDatabaseHelper::Run()
       DispatchErrorEvent();
     } else {
       DispatchSuccessEvent();
     }
 
     QuotaManager* quotaManager = QuotaManager::Get();
     NS_ASSERTION(quotaManager, "This should never be null!");
 
-    quotaManager->AllowNextSynchronizedOp(
-                                OriginOrPatternString::FromOrigin(mASCIIOrigin),
-                                mDatabaseId);
+    quotaManager->
+      AllowNextSynchronizedOp(OriginOrPatternString::FromOrigin(mASCIIOrigin),
+                              Nullable<PersistenceType>(mPersistenceType),
+                              mDatabaseId);
 
     ReleaseMainThreadObjects();
 
     return NS_OK;
   }
 
   PROFILER_LABEL("IndexedDB", "OpenDatabaseHelper::Run");
 
@@ -2237,16 +2287,17 @@ OpenDatabaseHelper::EnsureSuccessResult(
 
   nsRefPtr<DatabaseInfo> dbInfo;
   if (DatabaseInfo::Get(mDatabaseId, getter_AddRefs(dbInfo))) {
 
 #ifdef DEBUG
     {
       NS_ASSERTION(dbInfo->name == mName &&
                    dbInfo->version == mCurrentVersion &&
+                   dbInfo->persistenceType == mPersistenceType &&
                    dbInfo->id == mDatabaseId &&
                    dbInfo->filePath == mDatabaseFilePath,
                    "Metadata mismatch!");
 
       uint32_t objectStoreCount = mObjectStores.Length();
       for (uint32_t index = 0; index < objectStoreCount; index++) {
         nsRefPtr<ObjectStoreInfo>& info = mObjectStores[index];
 
@@ -2279,17 +2330,19 @@ OpenDatabaseHelper::EnsureSuccessResult(
     }
 #endif
 
   }
   else {
     nsRefPtr<DatabaseInfo> newInfo(new DatabaseInfo());
 
     newInfo->name = mName;
+    newInfo->group = mGroup;
     newInfo->origin = mASCIIOrigin;
+    newInfo->persistenceType = mPersistenceType;
     newInfo->id = mDatabaseId;
     newInfo->filePath = mDatabaseFilePath;
 
     if (!DatabaseInfo::Put(newInfo)) {
       NS_ERROR("Failed to add to hash!");
       return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
     }
 
@@ -2571,17 +2624,18 @@ DeleteDatabaseHelper::DoDatabaseWork(moz
   PROFILER_LABEL("IndexedDB", "DeleteDatabaseHelper::DoDatabaseWork");
 
   const StoragePrivilege& privilege = mOpenHelper->Privilege();
 
   QuotaManager* quotaManager = QuotaManager::Get();
   NS_ASSERTION(quotaManager, "This should never fail!");
 
   nsCOMPtr<nsIFile> directory;
-  nsresult rv = quotaManager->GetDirectoryForOrigin(mASCIIOrigin,
+  nsresult rv = quotaManager->GetDirectoryForOrigin(mPersistenceType,
+                                                    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);
 
@@ -2610,17 +2664,18 @@ DeleteDatabaseHelper::DoDatabaseWork(moz
 
     rv = dbFile->Remove(false);
     NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
     if (privilege != Chrome) {
       QuotaManager* quotaManager = QuotaManager::Get();
       NS_ASSERTION(quotaManager, "Shouldn't be null!");
 
-      quotaManager->DecreaseUsageForOrigin(mASCIIOrigin, fileSize);
+      quotaManager->DecreaseUsageForOrigin(mPersistenceType, mGroup,
+                                           mASCIIOrigin, fileSize);
     }
   }
 
   nsCOMPtr<nsIFile> dbJournalFile;
   rv = directory->Clone(getter_AddRefs(dbJournalFile));
   NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
   rv = dbJournalFile->Append(filename + NS_LITERAL_STRING(".sqlite-journal"));
@@ -2659,24 +2714,25 @@ DeleteDatabaseHelper::DoDatabaseWork(moz
 
     rv = fmDirectory->Remove(true);
     NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
     if (privilege != Chrome) {
       QuotaManager* quotaManager = QuotaManager::Get();
       NS_ASSERTION(quotaManager, "Shouldn't be null!");
 
-      quotaManager->DecreaseUsageForOrigin(mASCIIOrigin, usage);
+      quotaManager->DecreaseUsageForOrigin(mPersistenceType, mGroup,
+                                           mASCIIOrigin, usage);
     }
   }
 
   IndexedDatabaseManager* mgr = IndexedDatabaseManager::Get();
   NS_ASSERTION(mgr, "This should never fail!");
 
-  mgr->InvalidateFileManager(mASCIIOrigin, mName);
+  mgr->InvalidateFileManager(mPersistenceType, mASCIIOrigin, mName);
 
   return NS_OK;
 }
 
 nsresult
 DeleteDatabaseHelper::GetSuccessResult(JSContext* aCx, JS::MutableHandle<JS::Value> aVal)
 {
   return NS_OK;
--- a/dom/indexedDB/OpenDatabaseHelper.h
+++ b/dom/indexedDB/OpenDatabaseHelper.h
@@ -26,44 +26,50 @@ class ContentParent;
 BEGIN_INDEXEDDB_NAMESPACE
 
 class CheckPermissionsHelper;
 
 class OpenDatabaseHelper : public HelperBase
 {
   friend class CheckPermissionsHelper;
 
+  typedef mozilla::dom::quota::PersistenceType PersistenceType;
   typedef mozilla::dom::quota::StoragePrivilege StoragePrivilege;
 
 public:
   OpenDatabaseHelper(IDBOpenDBRequest* aRequest,
                      const nsAString& aName,
+                     const nsACString& aGroup,
                      const nsACString& aASCIIOrigin,
                      uint64_t aRequestedVersion,
+                     PersistenceType aPersistenceType,
                      bool aForDeletion,
                      mozilla::dom::ContentParent* aContentParent,
                      StoragePrivilege aPrivilege)
     : HelperBase(aRequest), mOpenDBRequest(aRequest), mName(aName),
-      mASCIIOrigin(aASCIIOrigin), mRequestedVersion(aRequestedVersion),
+      mGroup(aGroup), mASCIIOrigin(aASCIIOrigin),
+      mRequestedVersion(aRequestedVersion), mPersistenceType(aPersistenceType),
       mForDeletion(aForDeletion), mPrivilege(aPrivilege), mDatabaseId(nullptr),
       mContentParent(aContentParent), mCurrentVersion(0), mLastObjectStoreId(0),
       mLastIndexId(0), mState(eCreated), mResultCode(NS_OK),
       mLoadDBMetadata(false),
       mTrackingQuota(aPrivilege != mozilla::dom::quota::Chrome)
   {
     NS_ASSERTION(!aForDeletion || !aRequestedVersion,
                  "Can't be for deletion and request a version!");
   }
 
   NS_DECL_THREADSAFE_ISUPPORTS
   NS_DECL_NSIRUNNABLE
 
   nsresult Init();
 
+  nsresult WaitForOpenAllowed();
   nsresult Dispatch(nsIEventTarget* aDatabaseThread);
+  nsresult DispatchToIOThread();
   nsresult RunImmediately();
 
   void SetError(nsresult rv)
   {
     NS_ASSERTION(NS_FAILED(rv), "Why are you telling me?");
     mResultCode = rv;
   }
 
@@ -91,16 +97,18 @@ public:
   {
     return mPrivilege;
   }
 
   static
   nsresult CreateDatabaseConnection(nsIFile* aDBFile,
                                     nsIFile* aFMDirectory,
                                     const nsAString& aName,
+                                    PersistenceType aPersistenceType,
+                                    const nsACString& aGroup,
                                     const nsACString& aOrigin,
                                     mozIStorageConnection** aConnection);
 
 protected:
   // Methods only called on the main thread
   nsresult EnsureSuccessResult();
   nsresult StartSetVersion();
   nsresult StartDelete();
@@ -117,34 +125,37 @@ protected:
   }
 
   // Methods only called on the DB thread
   nsresult DoDatabaseWork();
 
   // In-params.
   nsRefPtr<IDBOpenDBRequest> mOpenDBRequest;
   nsString mName;
+  nsCString mGroup;
   nsCString mASCIIOrigin;
   uint64_t mRequestedVersion;
+  PersistenceType mPersistenceType;
   bool mForDeletion;
   StoragePrivilege mPrivilege;
   nsCOMPtr<nsIAtom> mDatabaseId;
   mozilla::dom::ContentParent* mContentParent;
 
   // Out-params.
   nsTArray<nsRefPtr<ObjectStoreInfo> > mObjectStores;
   uint64_t mCurrentVersion;
   nsString mDatabaseFilePath;
   int64_t mLastObjectStoreId;
   int64_t mLastIndexId;
   nsRefPtr<IDBDatabase> mDatabase;
 
   // State variables
   enum OpenDatabaseState {
     eCreated = 0, // Not yet dispatched to the DB thread
+    eOpenPending, // Waiting for open allowed/open allowed
     eDBWork, // Waiting to do/doing work on the DB thread
     eFiringEvents, // Waiting to fire/firing events on the main thread
     eSetVersionPending, // Waiting on a SetVersionHelper
     eSetVersionCompleted, // SetVersionHelper is done
     eDeletePending, // Waiting on a DeleteDatabaseHelper
     eDeleteCompleted, // DeleteDatabaseHelper is done
   };
   OpenDatabaseState mState;
--- a/dom/indexedDB/ipc/IndexedDBChild.cpp
+++ b/dom/indexedDB/ipc/IndexedDBChild.cpp
@@ -205,31 +205,35 @@ IndexedDBChild::ActorDestroy(ActorDestro
     mFactory->SetActor(static_cast<IndexedDBChild*>(NULL));
 #ifdef DEBUG
     mFactory = NULL;
 #endif
   }
 }
 
 PIndexedDBDatabaseChild*
-IndexedDBChild::AllocPIndexedDBDatabaseChild(const nsString& aName,
-                                             const uint64_t& aVersion)
+IndexedDBChild::AllocPIndexedDBDatabaseChild(
+                                        const nsString& aName,
+                                        const uint64_t& aVersion,
+                                        const PersistenceType& aPersistenceType)
 {
   return new IndexedDBDatabaseChild(aName, aVersion);
 }
 
 bool
 IndexedDBChild::DeallocPIndexedDBDatabaseChild(PIndexedDBDatabaseChild* aActor)
 {
   delete aActor;
   return true;
 }
 
 PIndexedDBDeleteDatabaseRequestChild*
-IndexedDBChild::AllocPIndexedDBDeleteDatabaseRequestChild(const nsString& aName)
+IndexedDBChild::AllocPIndexedDBDeleteDatabaseRequestChild(
+                                        const nsString& aName,
+                                        const PersistenceType& aPersistenceType)
 {
   MOZ_CRASH("Caller is supposed to manually construct a request!");
 }
 
 bool
 IndexedDBChild::DeallocPIndexedDBDeleteDatabaseRequestChild(
                                    PIndexedDBDeleteDatabaseRequestChild* aActor)
 {
@@ -280,17 +284,18 @@ IndexedDBDatabaseChild::EnsureDatabase(
                            const DatabaseInfoGuts& aDBInfo,
                            const InfallibleTArray<ObjectStoreInfoGuts>& aOSInfo)
 {
   nsCOMPtr<nsIAtom> databaseId;
   if (mDatabase) {
     databaseId = mDatabase->Id();
   }
   else {
-    databaseId = QuotaManager::GetStorageId(aDBInfo.origin, aDBInfo.name);
+    databaseId = QuotaManager::GetStorageId(aDBInfo.persistenceType,
+                                            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/ipc/IndexedDBChild.h
+++ b/dom/indexedDB/ipc/IndexedDBChild.h
@@ -60,24 +60,28 @@ public:
   void
   Disconnect();
 
 protected:
   virtual void
   ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE;
 
   virtual PIndexedDBDatabaseChild*
-  AllocPIndexedDBDatabaseChild(const nsString& aName, const uint64_t& aVersion)
+  AllocPIndexedDBDatabaseChild(const nsString& aName, const uint64_t& aVersion,
+                               const PersistenceType& aPersistenceType)
                                MOZ_OVERRIDE;
 
   virtual bool
   DeallocPIndexedDBDatabaseChild(PIndexedDBDatabaseChild* aActor) MOZ_OVERRIDE;
 
   virtual PIndexedDBDeleteDatabaseRequestChild*
-  AllocPIndexedDBDeleteDatabaseRequestChild(const nsString& aName) MOZ_OVERRIDE;
+  AllocPIndexedDBDeleteDatabaseRequestChild(
+                                        const nsString& aName,
+                                        const PersistenceType& aPersistenceType)
+                                        MOZ_OVERRIDE;
 
   virtual bool
   DeallocPIndexedDBDeleteDatabaseRequestChild(
                                    PIndexedDBDeleteDatabaseRequestChild* aActor)
                                    MOZ_OVERRIDE;
 };
 
 /*******************************************************************************
--- a/dom/indexedDB/ipc/IndexedDBParent.cpp
+++ b/dom/indexedDB/ipc/IndexedDBParent.cpp
@@ -139,51 +139,53 @@ IndexedDBParent::CheckPermissionInternal
 void
 IndexedDBParent::ActorDestroy(ActorDestroyReason aWhy)
 {
   // Nothing really needs to be done here...
 }
 
 bool
 IndexedDBParent::RecvPIndexedDBDatabaseConstructor(
-                                               PIndexedDBDatabaseParent* aActor,
-                                               const nsString& aName,
-                                               const uint64_t& aVersion)
+                                        PIndexedDBDatabaseParent* aActor,
+                                        const nsString& aName,
+                                        const uint64_t& aVersion,
+                                        const PersistenceType& aPersistenceType)
 {
   if (!CheckReadPermission(aName)) {
     return false;
   }
 
   if (IsDisconnected()) {
     // We're shutting down, ignore this request.
     return true;
   }
 
   if (!mFactory) {
     return true;
   }
 
   nsRefPtr<IDBOpenDBRequest> request;
-  nsresult rv =
-    mFactory->OpenInternal(aName, aVersion, false, getter_AddRefs(request));
+  nsresult rv = mFactory->OpenInternal(aName, aVersion, aPersistenceType, false,
+                                       getter_AddRefs(request));
   NS_ENSURE_SUCCESS(rv, false);
 
   IndexedDBDatabaseParent* actor =
     static_cast<IndexedDBDatabaseParent*>(aActor);
 
   rv = actor->SetOpenRequest(request);
   NS_ENSURE_SUCCESS(rv, false);
 
   return true;
 }
 
 bool
 IndexedDBParent::RecvPIndexedDBDeleteDatabaseRequestConstructor(
                                   PIndexedDBDeleteDatabaseRequestParent* aActor,
-                                  const nsString& aName)
+                                  const nsString& aName,
+                                  const PersistenceType& aPersistenceType)
 {
   if (!CheckWritePermission(aName)) {
     return false;
   }
 
   if (IsDisconnected()) {
     // We're shutting down, ignore this request.
     return true;
@@ -193,42 +195,46 @@ IndexedDBParent::RecvPIndexedDBDeleteDat
     return true;
   }
 
   IndexedDBDeleteDatabaseRequestParent* actor =
     static_cast<IndexedDBDeleteDatabaseRequestParent*>(aActor);
 
   nsRefPtr<IDBOpenDBRequest> request;
 
-  nsresult rv =
-    mFactory->OpenInternal(aName, 0, true, getter_AddRefs(request));
+  nsresult rv = mFactory->OpenInternal(aName, 0, aPersistenceType, true,
+                                       getter_AddRefs(request));
   NS_ENSURE_SUCCESS(rv, false);
 
   rv = actor->SetOpenRequest(request);
   NS_ENSURE_SUCCESS(rv, false);
 
   return true;
 }
 
 PIndexedDBDatabaseParent*
-IndexedDBParent::AllocPIndexedDBDatabaseParent(const nsString& aName,
-                                               const uint64_t& aVersion)
+IndexedDBParent::AllocPIndexedDBDatabaseParent(
+                                        const nsString& aName,
+                                        const uint64_t& aVersion,
+                                        const PersistenceType& aPersistenceType)
 {
   return new IndexedDBDatabaseParent();
 }
 
 bool
 IndexedDBParent::DeallocPIndexedDBDatabaseParent(PIndexedDBDatabaseParent* aActor)
 {
   delete aActor;
   return true;
 }
 
 PIndexedDBDeleteDatabaseRequestParent*
-IndexedDBParent::AllocPIndexedDBDeleteDatabaseRequestParent(const nsString& aName)
+IndexedDBParent::AllocPIndexedDBDeleteDatabaseRequestParent(
+                                        const nsString& aName,
+                                        const PersistenceType& aPersistenceType)
 {
   return new IndexedDBDeleteDatabaseRequestParent(mFactory);
 }
 
 bool
 IndexedDBParent::DeallocPIndexedDBDeleteDatabaseRequestParent(
                                   PIndexedDBDeleteDatabaseRequestParent* aActor)
 {
--- a/dom/indexedDB/ipc/IndexedDBParent.h
+++ b/dom/indexedDB/ipc/IndexedDBParent.h
@@ -200,32 +200,40 @@ protected:
                           const nsDependentCString& aPermission);
 
   virtual void
   ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE;
 
   virtual bool
   RecvPIndexedDBDatabaseConstructor(PIndexedDBDatabaseParent* aActor,
                                     const nsString& aName,
-                                    const uint64_t& aVersion) MOZ_OVERRIDE;
+                                    const uint64_t& aVersion,
+                                    const PersistenceType& aPersistenceType)
+                                    MOZ_OVERRIDE;
 
   virtual bool
   RecvPIndexedDBDeleteDatabaseRequestConstructor(
                                   PIndexedDBDeleteDatabaseRequestParent* aActor,
-                                  const nsString& aName) MOZ_OVERRIDE;
+                                  const nsString& aName,
+                                  const PersistenceType& aPersistenceType)
+                                  MOZ_OVERRIDE;
 
   virtual PIndexedDBDatabaseParent*
-  AllocPIndexedDBDatabaseParent(const nsString& aName, const uint64_t& aVersion)
+  AllocPIndexedDBDatabaseParent(const nsString& aName, const uint64_t& aVersion,
+                                const PersistenceType& aPersistenceType)
                                 MOZ_OVERRIDE;
 
   virtual bool
   DeallocPIndexedDBDatabaseParent(PIndexedDBDatabaseParent* aActor) MOZ_OVERRIDE;
 
   virtual PIndexedDBDeleteDatabaseRequestParent*
-  AllocPIndexedDBDeleteDatabaseRequestParent(const nsString& aName) MOZ_OVERRIDE;
+  AllocPIndexedDBDeleteDatabaseRequestParent(
+                                        const nsString& aName,
+                                        const PersistenceType& aPersistenceType)
+                                        MOZ_OVERRIDE;
 
   virtual bool
   DeallocPIndexedDBDeleteDatabaseRequestParent(
                                   PIndexedDBDeleteDatabaseRequestParent* aActor)
                                   MOZ_OVERRIDE;
 };
 
 /*******************************************************************************
--- a/dom/indexedDB/ipc/PIndexedDB.ipdl
+++ b/dom/indexedDB/ipc/PIndexedDB.ipdl
@@ -2,30 +2,36 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 include protocol PBrowser;
 include protocol PContent;
 include protocol PIndexedDBDatabase;
 include protocol PIndexedDBDeleteDatabaseRequest;
 
+include "mozilla/dom/indexedDB/SerializationHelpers.h";
+
+using mozilla::dom::quota::PersistenceType;
+
 namespace mozilla {
 namespace dom {
 namespace indexedDB {
 
 protocol PIndexedDB
 {
   manager PBrowser or PContent;
 
   manages PIndexedDBDatabase;
   manages PIndexedDBDeleteDatabaseRequest;
 
 parent:
   __delete__();
 
-  PIndexedDBDatabase(nsString name, uint64_t version);
+  PIndexedDBDatabase(nsString name, uint64_t version,
+                     PersistenceType persistenceType);
 
-  PIndexedDBDeleteDatabaseRequest(nsString name);
+  PIndexedDBDeleteDatabaseRequest(nsString name,
+                                  PersistenceType persistenceType);
 };
 
 } // namespace indexedDB
 } // namespace dom
 } // namespace mozilla
--- a/dom/indexedDB/ipc/SerializationHelpers.h
+++ b/dom/indexedDB/ipc/SerializationHelpers.h
@@ -11,16 +11,23 @@
 #include "mozilla/dom/indexedDB/Key.h"
 #include "mozilla/dom/indexedDB/KeyPath.h"
 #include "mozilla/dom/indexedDB/IDBCursor.h"
 #include "mozilla/dom/indexedDB/IDBTransaction.h"
 
 namespace IPC {
 
 template <>
+struct ParamTraits<mozilla::dom::quota::PersistenceType> :
+  public EnumSerializer<mozilla::dom::quota::PersistenceType,
+                        mozilla::dom::quota::PERSISTENCE_TYPE_PERSISTENT,
+                        mozilla::dom::quota::PERSISTENCE_TYPE_INVALID>
+{ };
+
+template <>
 struct ParamTraits<mozilla::dom::indexedDB::Key>
 {
   typedef mozilla::dom::indexedDB::Key paramType;
 
   static void Write(Message* aMsg, const paramType& aParam)
   {
     WriteParam(aMsg, aParam.mBuffer);
   }
@@ -149,34 +156,39 @@ struct ParamTraits<mozilla::dom::indexed
 template <>
 struct ParamTraits<mozilla::dom::indexedDB::DatabaseInfoGuts>
 {
   typedef mozilla::dom::indexedDB::DatabaseInfoGuts paramType;
 
   static void Write(Message* aMsg, const paramType& aParam)
   {
     WriteParam(aMsg, aParam.name);
+    WriteParam(aMsg, aParam.group);
     WriteParam(aMsg, aParam.origin);
     WriteParam(aMsg, aParam.version);
+    WriteParam(aMsg, aParam.persistenceType);
     WriteParam(aMsg, aParam.nextObjectStoreId);
     WriteParam(aMsg, aParam.nextIndexId);
   }
 
   static bool Read(const Message* aMsg, void** aIter, paramType* aResult)
   {
     return ReadParam(aMsg, aIter, &aResult->name) &&
+           ReadParam(aMsg, aIter, &aResult->group) &&
            ReadParam(aMsg, aIter, &aResult->origin) &&
            ReadParam(aMsg, aIter, &aResult->version) &&
+           ReadParam(aMsg, aIter, &aResult->persistenceType) &&
            ReadParam(aMsg, aIter, &aResult->nextObjectStoreId) &&
            ReadParam(aMsg, aIter, &aResult->nextIndexId);
   }
 
   static void Log(const paramType& aParam, std::wstring* aLog)
   {
     LogParam(aParam.name, aLog);
+    LogParam(aParam.group, aLog);
     LogParam(aParam.origin, aLog);
     LogParam(aParam.version, aLog);
     LogParam(aParam.nextObjectStoreId, aLog);
     LogParam(aParam.nextIndexId, aLog);
   }
 };
 
 template <>
--- a/dom/indexedDB/test/Makefile.in
+++ b/dom/indexedDB/test/Makefile.in
@@ -74,16 +74,17 @@ MOCHITEST_FILES = \
   test_objectStore_remove_values.html \
   test_object_identity.html \
   test_odd_result_order.html \
   test_open_empty_db.html \
   test_open_for_principal.html \
   test_open_objectStore.html \
   test_optionalArguments.html \
   test_overlapping_transactions.html \
+  test_persistenceType.html \
   test_put_get_values.html \
   test_put_get_values_autoIncrement.html \
   test_readonly_transactions.html \
   test_remove_index.html \
   test_remove_objectStore.html \
   test_request_readyState.html \
   test_success_events_after_abort.html \
   test_third_party.html \
--- a/dom/indexedDB/test/file.js
+++ b/dom/indexedDB/test/file.js
@@ -185,36 +185,31 @@ function getUsage(usageHandler)
   sb.usageHandler = usageHandler;
   var cb = SpecialPowers.Cu.evalInSandbox((function(uri, usage, fileUsage) {
                                            usageHandler(usage, fileUsage); }).toSource(), sb);
 
   let uri = SpecialPowers.wrap(window).document.documentURIObject;
   quotaManager.getUsageForURI(uri, cb);
 }
 
-function scheduleGC()
-{
-  SpecialPowers.exactGC(window, continueToNextStep);
-}
-
 function getFileId(file)
 {
   return utils.getFileId(file);
 }
 
 function hasFileInfo(name, id)
 {
   return utils.getFileReferences(name, id);
 }
 
 function getFileRefCount(name, id)
 {
   let count = {};
-  utils.getFileReferences(name, id, count);
+  utils.getFileReferences(name, id, null, count);
   return count.value;
 }
 
 function getFileDBRefCount(name, id)
 {
   let count = {};
-  utils.getFileReferences(name, id, {}, count);
+  utils.getFileReferences(name, id, null, {}, count);
   return count.value;
 }
--- a/dom/indexedDB/test/helpers.js
+++ b/dom/indexedDB/test/helpers.js
@@ -79,25 +79,27 @@ if (!window.runTest) {
 
     if (limitedQuota) {
       denyUnlimitedQuota();
     }
     else {
       allowUnlimitedQuota();
     }
 
+    enableExperimental();
     enableArchiveReader();
 
     clearAllDatabases(function () { testGenerator.next(); });
   }
 }
 
 function finishTest()
 {
   resetUnlimitedQuota();
+  resetExperimental();
   resetArchiveReader();
   SpecialPowers.notifyObserversInParentProcess(null, "disk-space-watcher",
                                                "free");
 
   SimpleTest.executeSoon(function() {
     testGenerator.close();
     //clearAllDatabases(function() { SimpleTest.finish(); });
     SimpleTest.finish();
@@ -248,13 +250,28 @@ function enableArchiveReader()
   SpecialPowers.setBoolPref("dom.archivereader.enabled", true);
 }
 
 function resetArchiveReader()
 {
   SpecialPowers.setBoolPref("dom.archivereader.enabled", archiveReaderEnabled);
 }
 
+function enableExperimental()
+{
+  SpecialPowers.setBoolPref("dom.indexedDB.experimental", true);
+}
+
+function resetExperimental()
+{
+  SpecialPowers.clearUserPref("dom.indexedDB.experimental");
+}
+
 function gc()
 {
   SpecialPowers.forceGC();
   SpecialPowers.forceCC();
 }
+
+function scheduleGC()
+{
+  SpecialPowers.exactGC(window, continueToNextStep);
+}
--- a/dom/indexedDB/test/test_file_replace.html
+++ b/dom/indexedDB/test/test_file_replace.html
@@ -41,17 +41,17 @@
 
     event = yield undefined;
 
     is(event.type, "success", "Got correct event type");
 
     for (let id = 1; id <= 100; id++) {
       let refs = {};
       let dbRefs = {};
-      let hasFileInfo = utils.getFileReferences(name, id, refs, dbRefs);
+      let hasFileInfo = utils.getFileReferences(name, id, null, refs, dbRefs);
       ok(hasFileInfo, "Has file info");
       is(refs.value, 1, "Correct ref count");
       is(dbRefs.value, id / 100 >> 0, "Correct db ref count");
     }
 
     finishTest();
     yield undefined;
   }
new file mode 100644
--- /dev/null
+++ b/dom/indexedDB/test/test_persistenceType.html
@@ -0,0 +1,108 @@
+<!--
+  Any copyright is dedicated to the Public Domain.
+  http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<html>
+<head>
+  <title>Indexed Database Property Test</title>
+
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+
+  <script type="text/javascript;version=1.7">
+    function testSteps()
+    {
+      const name = window.location.pathname;
+      const version = 1;
+
+      const objectStoreName = "Foo";
+      const data = { key: 1, value: "bar" };
+
+      try {
+        indexedDB.open(name, { version: version, storage: "unknown" });
+        ok(false, "Should have thrown!");
+      }
+      catch (e) {
+        ok(e instanceof TypeError, "Got TypeError.");
+        is(e.name, "TypeError", "Good error name.");
+      }
+
+      let request = indexedDB.open(name, { version: version,
+                                           storage: "persistent" });
+      request.onerror = errorHandler;
+      request.onupgradeneeded = grabEventAndContinueHandler;
+      request.onsuccess = grabEventAndContinueHandler;
+      let event = yield undefined;
+
+      is(event.type, "upgradeneeded", "Got correct event type");
+
+      let db = event.target.result;
+      db.onerror = errorHandler;
+
+      let objectStore = db.createObjectStore(objectStoreName, { });
+
+      event = yield undefined;
+
+      is(event.type, "success", "Got correct event type");
+
+      is(db.name, name, "Correct name");
+      is(db.version, version, "Correct version");
+      is(db.storage, "persistent", "Correct persistence type");
+
+      objectStore = db.transaction([objectStoreName], "readwrite")
+                      .objectStore(objectStoreName);
+
+      request = objectStore.get(data.key);
+      request.onsuccess = grabEventAndContinueHandler;
+      event = yield undefined;
+
+      is(event.target.result, null, "Got no data");
+
+      request = objectStore.add(data.value, data.key);
+      request.onsuccess = grabEventAndContinueHandler;
+      event = yield undefined;
+
+      is(event.target.result, data.key, "Got correct key");
+
+      request = indexedDB.open(name, { version: version,
+                                       storage: "temporary" });
+      request.onerror = errorHandler;
+      request.onupgradeneeded = grabEventAndContinueHandler;
+      request.onsuccess = grabEventAndContinueHandler;
+      let event = yield undefined;
+
+      is(event.type, "upgradeneeded", "Got correct event type");
+
+      db = event.target.result;
+      db.onerror = errorHandler;
+
+      objectStore = db.createObjectStore(objectStoreName, { });
+
+      event = yield undefined;
+
+      is(event.type, "success", "Got correct event type");
+
+      is(db.name, name, "Correct name");
+      is(db.version, version, "Correct version");
+      is(db.storage, "temporary", "Correct persistence type");
+
+      objectStore = db.transaction([objectStoreName])
+                      .objectStore(objectStoreName);
+
+      request = objectStore.get(data.key);
+      request.onsuccess = grabEventAndContinueHandler;
+      event = yield undefined;
+
+      is(event.target.result, null, "Got no data");
+
+      finishTest();
+      yield undefined;
+    }
+  </script>
+  <script type="text/javascript;version=1.7" src="helpers.js"></script>
+
+</head>
+
+<body onload="runTest();"></body>
+
+</html>
--- a/dom/indexedDB/test/unit/Makefile.in
+++ b/dom/indexedDB/test/unit/Makefile.in
@@ -41,27 +41,29 @@ MOCHITEST_FILES = \
   test_objectStore_inline_autoincrement_key_added_on_put.js \
   test_objectStore_remove_values.js \
   test_odd_result_order.js \
   test_open_empty_db.js \
   test_open_for_principal.js \
   test_open_objectStore.js \
   test_optionalArguments.js \
   test_overlapping_transactions.js \
+  test_persistenceType.js \
   test_put_get_values.js \
   test_put_get_values_autoIncrement.js \
   test_readonly_transactions.js \
   test_remove_index.js \
   test_remove_objectStore.js \
   test_request_readyState.js \
   test_setVersion.js \
   test_setVersion_abort.js \
   test_setVersion_events.js \
   test_setVersion_exclusion.js \
   test_success_events_after_abort.js \
+  test_temporary_storage.js \
   test_traffic_jam.js \
   test_transaction_abort.js \
   test_transaction_abort_hang.js \
   test_transaction_lifetimes.js \
   test_transaction_lifetimes_nested.js \
   test_transaction_ordering.js \
   test_unique_index_update.js \
   test_writer_starvation.js \
--- a/dom/indexedDB/test/unit/head.js
+++ b/dom/indexedDB/test/unit/head.js
@@ -42,26 +42,30 @@ function runTest()
 {
   // XPCShell does not get a profile by default.
   do_get_profile();
 
   var idbManager = Cc["@mozilla.org/dom/indexeddb/manager;1"].
                    getService(Ci.nsIIndexedDatabaseManager);
   idbManager.initWindowless(this);
 
+  enableExperimental();
+
   do_test_pending();
   testGenerator.next();
 }
 
 function finishTest()
 {
+  resetExperimental();
+  SpecialPowers.notifyObserversInParentProcess(null, "disk-space-watcher",
+                                               "free");
+
   do_execute_soon(function(){
     testGenerator.close();
-    SpecialPowers.notifyObserversInParentProcess(null, "disk-space-watcher",
-                                                 "free");
     do_test_finished();
   })
 }
 
 function grabEventAndContinueHandler(event)
 {
   testGenerator.send(event);
 }
@@ -174,22 +178,37 @@ function allowUnlimitedQuota(url)
   throw "allowUnlimitedQuota";
 }
 
 function disallowUnlimitedQuota(url)
 {
   throw "disallowUnlimitedQuota";
 }
 
+function enableExperimental()
+{
+  SpecialPowers.setBoolPref("dom.indexedDB.experimental", true);
+}
+
+function resetExperimental()
+{
+  SpecialPowers.clearUserPref("dom.indexedDB.experimental");
+}
+
 function gc()
 {
   Components.utils.forceGC();
   Components.utils.forceCC();
 }
 
+function scheduleGC()
+{
+  SpecialPowers.exactGC(null, continueToNextStep);
+}
+
 function setTimeout(fun, timeout) {
   let timer = Components.classes["@mozilla.org/timer;1"]
                         .createInstance(Components.interfaces.nsITimer);
   var event = {
     notify: function (timer) {
       fun();
     }
   };
@@ -209,10 +228,48 @@ var SpecialPowers = {
                    .getService(Ci.nsIObserverService);
     obsvc.notifyObservers(subject, topic, data);
   },
   notifyObserversInParentProcess: function(subject, topic, data) {
     if (subject) {
       throw new Error("Can't send subject to another process!");
     }
     return this.notifyObservers(subject, topic, data);
+  },
+  getBoolPref: function(prefName) {
+    return this._getPrefs().getBoolPref(prefName);
+  },
+  setBoolPref: function(prefName, value) {
+    this._getPrefs().setBoolPref(prefName, value);
+  },
+  setIntPref: function(prefName, value) {
+    this._getPrefs().setIntPref(prefName, value);
+  },
+  clearUserPref: function(prefName) {
+    this._getPrefs().clearUserPref(prefName);
+  },
+  // Copied (and slightly adjusted) from specialpowersAPI.js
+  exactGC: function(win, callback) {
+    let count = 0;
+
+    function doPreciseGCandCC() {
+      function scheduledGCCallback() {
+        Components.utils.forceCC();
+
+        if (++count < 2) {
+          doPreciseGCandCC();
+        } else {
+          callback();
+        }
+      }
+
+      Components.utils.schedulePreciseGC(scheduledGCCallback);
+    }
+
+    doPreciseGCandCC();
+  },
+
+  _getPrefs: function() {
+    var prefService =
+      Cc["@mozilla.org/preferences-service;1"].getService(Ci.nsIPrefService);
+    return prefService.getBranch(null);
   }
 };
--- a/dom/indexedDB/test/unit/test_invalid_version.js
+++ b/dom/indexedDB/test/unit/test_invalid_version.js
@@ -22,11 +22,29 @@ function testSteps()
     indexedDB.open(name, -1);
     ok(false, "Should have thrown!");
   }
   catch (e) {
     ok(e instanceof TypeError, "Got TypeError.");
     is(e.name, "TypeError", "Good error name.");
   }
 
+  try {
+    indexedDB.open(name, { version: 0 });
+    ok(false, "Should have thrown!");
+  }
+  catch (e) {
+    ok(e instanceof TypeError, "Got TypeError.");
+    is(e.name, "TypeError", "Good error name.");
+  }
+
+  try {
+    indexedDB.open(name, { version: -1 });
+    ok(false, "Should have thrown!");
+  }
+  catch (e) {
+    ok(e instanceof TypeError, "Got TypeError.");
+    is(e.name, "TypeError", "Good error name.");
+  }
+
   finishTest();
   yield undefined;
 }
new file mode 100644
--- /dev/null
+++ b/dom/indexedDB/test/unit/test_persistenceType.js
@@ -0,0 +1,86 @@
+/**
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+var testGenerator = testSteps();
+
+function testSteps()
+{
+  const name = "Splendid Test";
+  const version = 1;
+
+  const objectStoreName = "Foo";
+  const data = { key: 1, value: "bar" };
+
+  try {
+    indexedDB.open(name, { version: version, storage: "unknown" });
+    ok(false, "Should have thrown!");
+  }
+  catch (e) {
+    ok(e instanceof TypeError, "Got TypeError.");
+    is(e.name, "TypeError", "Good error name.");
+  }
+
+  let request = indexedDB.open(name, { version: version,
+                                       storage: "persistent" });
+  request.onerror = errorHandler;
+  request.onupgradeneeded = grabEventAndContinueHandler;
+  request.onsuccess = grabEventAndContinueHandler;
+  let event = yield undefined;
+
+  is(event.type, "upgradeneeded", "Got correct event type");
+
+  let db = event.target.result;
+  db.onerror = errorHandler;
+
+  let objectStore = db.createObjectStore(objectStoreName, { });
+
+  event = yield undefined;
+
+  is(event.type, "success", "Got correct event type");
+
+  is(db.name, name, "Correct name");
+  is(db.version, version, "Correct version");
+  is(db.storage, "persistent", "Correct persistence type");
+
+  objectStore = db.transaction([objectStoreName], "readwrite")
+                  .objectStore(objectStoreName);
+
+  request = objectStore.get(data.key);
+  request.onsuccess = grabEventAndContinueHandler;
+  event = yield undefined;
+
+  is(event.target.result, null, "Got no data");
+
+  request = objectStore.add(data.value, data.key);
+  request.onsuccess = grabEventAndContinueHandler;
+  event = yield undefined;
+
+  is(event.target.result, data.key, "Got correct key");
+
+  request = indexedDB.open(name, { version: version,
+                                   storage: "temporary" });
+  request.onerror = errorHandler;
+  request.onupgradeneeded = grabEventAndContinueHandler;
+  request.onsuccess = grabEventAndContinueHandler;
+  let event = yield undefined;
+
+  is(event.type, "success", "Got correct event type");
+
+  is(db.name, name, "Correct name");
+  is(db.version, version, "Correct version");
+  is(db.storage, "persistent", "Correct persistence type");
+
+  objectStore = db.transaction([objectStoreName])
+                  .objectStore(objectStoreName);
+
+  request = objectStore.get(data.key);
+  request.onsuccess = grabEventAndContinueHandler;
+  event = yield undefined;
+
+  is(event.target.result, data.value, "Got correct data");
+
+  finishTest();
+  yield undefined;
+}
new file mode 100644
--- /dev/null
+++ b/dom/indexedDB/test/unit/test_temporary_storage.js
@@ -0,0 +1,225 @@
+/**
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+var testGenerator = testSteps();
+
+function testSteps()
+{
+  const name = this.window ? window.location.pathname : "Splendid Test";
+
+  const urls = [
+    { url: "http://www.alpha.com",        flags: [true, true, false, false] },
+    { url: "http://www.beta.com",         flags: [true, false, false, false] },
+    { url: "http://www.gamma.com",        flags: [true, true, false, false] },
+    { url: "http://www.delta.com",        flags: [true, true, false, false] },
+    { url: "http://www.epsilon.com",      flags: [true, true, false, false] },
+    { url: "http://www2.alpha.com",       flags: [true, true, false, false] },
+    { url: "http://www2.beta.com",        flags: [true, true, false, false] },
+    { url: "http://www2.gamma.com",       flags: [true, true, true, false] },
+    { url: "http://www2.delta.com",       flags: [true, true, true, true] },
+    { url: "http://www2.epsilon.com",     flags: [true, true, true, true] },
+    { url: "http://joe.blog.alpha.com",   flags: [true, true, true, true] },
+    { url: "http://joe.blog.beta.com",    flags: [true, true, true, true] },
+    { url: "http://joe.blog.gamma.com",   flags: [true, true, true, true] },
+    { url: "http://joe.blog.delta.com",   flags: [true, true, true, true] },
+    { url: "http://joe.blog.epsilon.com", flags: [true, true, true, true] },
+    { url: "http://www.rudolf.org",       flags: [true, true, true, true] },
+    { url: "http://www.pauline.org",      flags: [true, true, true, true] },
+    { url: "http://www.marie.org",        flags: [true, true, true, true] },
+    { url: "http://www.john.org",         flags: [true, true, true, true] },
+    { url: "http://www.ema.org",          flags: [true, true, true, true] },
+    { url: "http://www.trigger.com",      flags: [false, true, true, true] }
+  ];
+  const lastIndex = urls.length - 1;
+  const lastUrl = urls[lastIndex].url;
+
+  let quotaManager =
+    Components.classes["@mozilla.org/dom/quota/manager;1"]
+              .getService(Components.interfaces.nsIQuotaManager);
+
+  let ioService = Components.classes["@mozilla.org/network/io-service;1"]
+                            .getService(Components.interfaces.nsIIOService);
+
+  let dbSize = 0;
+
+  let databases = [];
+
+  function setLimit(limit) {
+    if (limit) {
+      SpecialPowers.setIntPref("dom.quotaManager.temporaryStorage.fixedLimit",
+                               limit);
+      return;
+    }
+    SpecialPowers.clearUserPref("dom.quotaManager.temporaryStorage.fixedLimit");
+  }
+
+  function getPrincipal(url) {
+    let uri = ioService.newURI(url, null, null);
+    return Components.classes["@mozilla.org/scriptsecuritymanager;1"]
+                     .getService(Components.interfaces.nsIScriptSecurityManager)
+                     .getNoAppCodebasePrincipal(uri);
+  }
+
+  function getUsageForUrl(url, usageHandler) {
+    let uri = ioService.newURI(url, null, null);
+    function callback(uri, usage, fileUsage) {
+      usageHandler(usage, fileUsage);
+    }
+    quotaManager.getUsageForURI(uri, callback);
+  }
+
+  function grabUsageAndContinueHandler(usage, fileUsage) {
+    testGenerator.send(usage);
+  }
+
+  function checkUsage(stageIndex) {
+    let handledIndex = 0;
+
+    function usageHandler(usage, fileUsage) {
+      if (urls[handledIndex].flags[stageIndex - 1]) {
+        ok(usage > 0, "Correct usage");
+      }
+      else {
+        ok(usage == 0, "Correct usage");
+      }
+      if (++handledIndex == urls.length) {
+        continueToNextStep();
+      }
+    }
+
+    for (let i = 0; i < urls.length; i++) {
+      getUsageForUrl(urls[i].url, usageHandler);
+    }
+  }
+
+  // Enable clear() and test()
+  let testingEnabled =
+    SpecialPowers.getBoolPref("dom.quotaManager.testing");
+  SpecialPowers.setBoolPref("dom.quotaManager.testing", true)
+
+  // Calibration
+  let request = indexedDB.openForPrincipal(getPrincipal(lastUrl), name,
+                                           { storage: "temporary" });
+  request.onerror = errorHandler;
+  request.onsuccess = grabEventAndContinueHandler;
+  let event = yield undefined;
+
+  getUsageForUrl(lastUrl, grabUsageAndContinueHandler);
+  dbSize = yield undefined;
+
+  setLimit(lastIndex * dbSize / 1024);
+  quotaManager.clear();
+
+  // Stage 1
+  for (let i = 0; i < lastIndex; i++) {
+    let data = urls[i];
+
+    request = indexedDB.openForPrincipal(getPrincipal(data.url), name,
+                                         { storage: "temporary" });
+    request.onerror = errorHandler;
+    request.onupgradeneeded = grabEventAndContinueHandler;
+    request.onsuccess = grabEventAndContinueHandler;
+    event = yield undefined;
+
+    is(event.type, "upgradeneeded", "Got correct event type");
+
+    let db = event.target.result;
+    db.createObjectStore("foo", { });
+
+    event = yield undefined;
+
+    is(event.type, "success", "Got correct event type");
+
+    databases.push(event.target.result);
+  }
+
+  request = indexedDB.openForPrincipal(getPrincipal(lastUrl), name,
+                                       { storage: "temporary" });
+  request.addEventListener("error", new ExpectError("QuotaExceededError"));
+  request.onsuccess = unexpectedSuccessHandler;
+  event = yield undefined;
+
+  checkUsage(1);
+  yield undefined;
+
+  // Stage 2
+  for (let i = 1; i < urls.length; i++) {
+    databases[i] = null;
+
+    scheduleGC();
+    yield undefined;
+
+    // The origin access time is set to the current system time when the first
+    // database for an origin is registered or the last one is unregistered.
+    // The registration happens when the database object is being created and
+    // the unregistration when it is unlinked/garbage collected.
+    // Some older windows systems have the system time limited to a maximum
+    // resolution of 10 or 15 milliseconds, so without a pause here we would
+    // end up with origins with the same access time which would cause random
+    // failures.
+    setTimeout(function() { testGenerator.next(); }, 20);
+    yield undefined;
+  }
+
+  request = indexedDB.openForPrincipal(getPrincipal(lastUrl), name,
+                                       { storage: "temporary" });
+  request.onerror = errorHandler;
+  request.onupgradeneeded = grabEventAndContinueHandler;
+  request.onsuccess = grabEventAndContinueHandler;
+  event = yield undefined;
+
+  is(event.type, "upgradeneeded", "Got correct event type");
+
+  let db = event.target.result;
+  db.createObjectStore("foo", { });
+
+  event = yield undefined;
+
+  is(event.type, "success", "Got correct event type");
+
+  checkUsage(2);
+  yield undefined;
+
+  // Stage 3
+  setLimit(14 * dbSize / 1024);
+  quotaManager.reset();
+
+  request = indexedDB.openForPrincipal(getPrincipal(lastUrl), name,
+                                       { storage: "temporary" });
+  request.onerror = errorHandler;
+  request.onsuccess = grabEventAndContinueHandler;
+  event = yield undefined;
+
+  is(event.type, "success", "Got correct event type");
+
+  let db = event.target.result;
+
+  checkUsage(3);
+  yield undefined;
+
+  // Stage 4
+  let trans = db.transaction(["foo"], "readwrite");
+
+  let blob = Blob(["bar"]);
+  request = trans.objectStore("foo").add(blob, 42);
+  request.onerror = errorHandler;
+  request.onsuccess = grabEventAndContinueHandler;
+  event = yield undefined;
+
+  trans.oncomplete = grabEventAndContinueHandler;
+  event = yield undefined;
+
+  checkUsage(4);
+  yield undefined;
+
+  // Cleanup
+  setLimit();
+  quotaManager.reset();
+
+  SpecialPowers.setBoolPref("dom.quotaManager.testing", testingEnabled);
+
+  finishTest();
+  yield undefined;
+}
--- a/dom/indexedDB/test/unit/xpcshell.ini
+++ b/dom/indexedDB/test/unit/xpcshell.ini
@@ -42,27 +42,29 @@ tail =
 [test_objectStore_inline_autoincrement_key_added_on_put.js]
 [test_objectStore_remove_values.js]
 [test_odd_result_order.js]
 [test_open_empty_db.js]
 [test_open_for_principal.js]
 [test_open_objectStore.js]
 [test_optionalArguments.js]
 [test_overlapping_transactions.js]
+[test_persistenceType.js]
 [test_put_get_values.js]
 [test_put_get_values_autoIncrement.js]
 [test_readonly_transactions.js]
 [test_remove_index.js]
 [test_remove_objectStore.js]
 [test_request_readyState.js]
 [test_setVersion.js]
 [test_setVersion_abort.js]
 [test_setVersion_events.js]
 [test_setVersion_exclusion.js]
 [test_success_events_after_abort.js]
+[test_temporary_storage.js]
 [test_traffic_jam.js]
 [test_transaction_abort.js]
 [test_transaction_abort_hang.js]
 [test_transaction_lifetimes.js]
 [test_transaction_lifetimes_nested.js]
 [test_transaction_ordering.js]
 [test_unique_index_update.js]
 [test_writer_starvation.js]
--- a/dom/interfaces/base/nsIDOMWindowUtils.idl
+++ b/dom/interfaces/base/nsIDOMWindowUtils.idl
@@ -37,17 +37,17 @@ interface nsIDOMBlob;
 interface nsIDOMFile;
 interface nsIFile;
 interface nsIDOMTouch;
 interface nsIDOMClientRect;
 interface nsIURI;
 interface nsIDOMEventTarget;
 interface nsIRunnable;
 
-[scriptable, uuid(d6e733ef-492b-4e67-b723-28571c2959f0)]
+[scriptable, uuid(d18a8d69-7609-4165-ae20-af8aead36833)]
 interface nsIDOMWindowUtils : nsISupports {
 
   /**
    * Image animation mode of the window. When this attribute's value
    * is changed, the implementation should set all images in the window
    * to the given value. That is, when set to kDontAnimMode, all images
    * will stop animating. The attribute's value must be one of the
    * animationMode values from imgIContainer.
@@ -1224,17 +1224,19 @@ interface nsIDOMWindowUtils : nsISupport
    * Get internal id of the stored blob, file or file handle.
    */
   [implicit_jscontext] long long getFileId(in jsval aFile);
 
   /**
    * Get file ref count info for given database and file id.
    *
    */
+  [implicit_jscontext]
   boolean getFileReferences(in AString aDatabaseName, in long long aId,
+                            [optional] in jsval aOptions,
                             [optional] out long aRefCnt,
                             [optional] out long aDBRefCnt,
                             [optional] out long aSliceRefCnt);
 
   /**
    * Return whether incremental GC has been disabled due to a binary add-on.
    */
   [implicit_jscontext]
--- a/dom/ipc/PBrowser.ipdl
+++ b/dom/ipc/PBrowser.ipdl
@@ -246,17 +246,17 @@ parent:
      *   cache group update. But we must cache the document (identified by the
      *   documentURI). This argument will ensure that a previously uncached 
      *   document will get cached and that we don't re-cache a document that 
      *   has already been cached (stickDocument=false).
      */
     POfflineCacheUpdate(URIParams manifestURI, URIParams documentURI,
                         bool stickDocument);
 
-    sync PIndexedDB(nsCString asciiOrigin)
+    sync PIndexedDB(nsCString group, nsCString asciiOrigin)
         returns (bool allowed);
 
     /**
      * window.open from inside <iframe mozbrowser> is special.  When the child
      * process calls window.open, it creates a new PBrowser (in its own
      * process), then calls BrowserFrameOpenWindow on it.
      *
      * The parent process gets a chance to accept or reject the window.open
--- a/dom/ipc/TabChild.cpp
+++ b/dom/ipc/TabChild.cpp
@@ -2440,17 +2440,19 @@ TabChild::GetMessageManager(nsIContentFr
     NS_ADDREF(*aResult = mTabChildGlobal);
     return NS_OK;
   }
   *aResult = nullptr;
   return NS_ERROR_FAILURE;
 }
 
 PIndexedDBChild*
-TabChild::AllocPIndexedDBChild(const nsCString& aASCIIOrigin, bool* /* aAllowed */)
+TabChild::AllocPIndexedDBChild(
+                            const nsCString& aGroup,
+                            const nsCString& aASCIIOrigin, bool* /* aAllowed */)
 {
   NS_NOTREACHED("Should never get here!");
   return NULL;
 }
 
 bool
 TabChild::DeallocPIndexedDBChild(PIndexedDBChild* aActor)
 {
--- a/dom/ipc/TabChild.h
+++ b/dom/ipc/TabChild.h
@@ -346,17 +346,18 @@ protected:
                                                       TextureFactoryIdentifier* aTextureFactoryIdentifier,
                                                       uint64_t* aLayersId) MOZ_OVERRIDE;
     virtual bool DeallocPRenderFrameChild(PRenderFrameChild* aFrame) MOZ_OVERRIDE;
     virtual bool RecvDestroy() MOZ_OVERRIDE;
     virtual bool RecvSetUpdateHitRegion(const bool& aEnabled) MOZ_OVERRIDE;
 
     nsEventStatus DispatchWidgetEvent(nsGUIEvent& event);
 
-    virtual PIndexedDBChild* AllocPIndexedDBChild(const nsCString& aASCIIOrigin,
+    virtual PIndexedDBChild* AllocPIndexedDBChild(const nsCString& aGroup,
+                                                  const nsCString& aASCIIOrigin,
                                                   bool* /* aAllowed */);
 
     virtual bool DeallocPIndexedDBChild(PIndexedDBChild* aActor);
 
 private:
     /**
      * Create a new TabChild object.
      *
--- a/dom/ipc/TabParent.cpp
+++ b/dom/ipc/TabParent.cpp
@@ -1190,30 +1190,33 @@ TabParent::ReceiveMessage(const nsString
                             aCloneData,
                             aCpows,
                             aJSONRetVal);
   }
   return true;
 }
 
 PIndexedDBParent*
-TabParent::AllocPIndexedDBParent(const nsCString& aASCIIOrigin, bool* /* aAllowed */)
+TabParent::AllocPIndexedDBParent(
+                            const nsCString& aGroup,
+                            const nsCString& aASCIIOrigin, bool* /* aAllowed */)
 {
   return new IndexedDBParent(this);
 }
 
 bool
 TabParent::DeallocPIndexedDBParent(PIndexedDBParent* aActor)
 {
   delete aActor;
   return true;
 }
 
 bool
 TabParent::RecvPIndexedDBConstructor(PIndexedDBParent* aActor,
+                                     const nsCString& aGroup,
                                      const nsCString& aASCIIOrigin,
                                      bool* aAllowed)
 {
   nsRefPtr<IndexedDatabaseManager> mgr = IndexedDatabaseManager::GetOrCreate();
   NS_ENSURE_TRUE(mgr, false);
 
   if (!IndexedDatabaseManager::IsMainProcess()) {
     NS_RUNTIMEABORT("Not supported yet!");
@@ -1258,17 +1261,17 @@ TabParent::RecvPIndexedDBConstructor(PIn
     *aAllowed = false;
     return true;
   }
 
   ContentParent* contentParent = Manager();
   NS_ASSERTION(contentParent, "Null manager?!");
 
   nsRefPtr<IDBFactory> factory;
-  rv = IDBFactory::Create(window, aASCIIOrigin, contentParent,
+  rv = IDBFactory::Create(window, aGroup, aASCIIOrigin, contentParent,
                           getter_AddRefs(factory));
   NS_ENSURE_SUCCESS(rv, false);
 
   if (!factory) {
     *aAllowed = false;
     return true;
   }
 
--- a/dom/ipc/TabParent.h
+++ b/dom/ipc/TabParent.h
@@ -243,23 +243,26 @@ protected:
                         const StructuredCloneData* aCloneData,
                         CpowHolder* aCpows,
                         InfallibleTArray<nsString>* aJSONRetVal = nullptr);
 
     virtual bool Recv__delete__() MOZ_OVERRIDE;
 
     virtual void ActorDestroy(ActorDestroyReason why) MOZ_OVERRIDE;
 
-    virtual PIndexedDBParent* AllocPIndexedDBParent(const nsCString& aASCIIOrigin,
-                                                    bool* /* aAllowed */);
+    virtual PIndexedDBParent* AllocPIndexedDBParent(
+                                                  const nsCString& aGroup,
+                                                  const nsCString& aASCIIOrigin,
+                                                  bool* /* aAllowed */);
 
     virtual bool DeallocPIndexedDBParent(PIndexedDBParent* aActor);
 
     virtual bool
     RecvPIndexedDBConstructor(PIndexedDBParent* aActor,
+                              const nsCString& aGroup,
                               const nsCString& aASCIIOrigin,
                               bool* aAllowed);
 
     Element* mFrameElement;
     nsCOMPtr<nsIBrowserDOMWindow> mBrowserDOMWindow;
 
     struct DelayedDialogData
     {
--- a/dom/permission/tests/unit/test_bug808734.js
+++ b/dom/permission/tests/unit/test_bug808734.js
@@ -16,17 +16,18 @@ var gData = [
   access: READWRITE,
   expected: ["settings-read", "settings-write",
              "indexedDB-chrome-settings-read",
              "indexedDB-chrome-settings-write"] 
 },
 // test substitute
 {
   permission: "storage",
-  expected: ["indexedDB-unlimited", "offline-app", "pin-app"]
+  expected: ["indexedDB-unlimited", "offline-app", "pin-app",
+             "default-persistent-storage"]
 },
 // test unknown access
 {
   permission: "contacts",
   access: UNKNOWN,
   expected: []
 },
 // test unknown permission
--- a/dom/quota/CheckQuotaHelper.cpp
+++ b/dom/quota/CheckQuotaHelper.cpp
@@ -115,19 +115,18 @@ CheckQuotaHelper::Cancel()
 
 // static
 uint32_t
 CheckQuotaHelper::GetQuotaPermission(nsIPrincipal* aPrincipal)
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
   NS_ASSERTION(aPrincipal, "Null principal!");
 
-  if (nsContentUtils::IsSystemPrincipal(aPrincipal)) {
-    return nsIPermissionManager::ALLOW_ACTION;
-  }
+  NS_ASSERTION(!nsContentUtils::IsSystemPrincipal(aPrincipal),
+               "Chrome windows shouldn't track quota!");
 
   nsCOMPtr<nsIPermissionManager> pm =
     do_GetService(NS_PERMISSIONMANAGER_CONTRACTID);
   NS_ENSURE_TRUE(pm, nsIPermissionManager::DENY_ACTION);
 
   uint32_t permission;
   nsresult rv = pm->TestPermissionFromPrincipal(aPrincipal,
                                                 PERMISSION_INDEXEDDB_UNLIMITED,
@@ -195,17 +194,17 @@ CheckQuotaHelper::Run()
       return NS_OK;
     }
   }
 
   MutexAutoLock lock(mMutex);
 
   NS_ASSERTION(mWaiting, "Huh?!");
 
-    // This should never be used again.
+  // This should never be used again.
   mWindow = nullptr;
 
   mWaiting = false;
   mCondVar.NotifyAll();
 
   return NS_OK;
 }
 
--- a/dom/quota/CheckQuotaHelper.h
+++ b/dom/quota/CheckQuotaHelper.h
@@ -8,18 +8,18 @@
 #define mozilla_dom_quota_checkquotahelper_h__
 
 #include "QuotaCommon.h"
 
 #include "nsIInterfaceRequestor.h"
 #include "nsIObserver.h"
 #include "nsIRunnable.h"
 
+#include "mozilla/CondVar.h"
 #include "mozilla/Mutex.h"
-#include "mozilla/CondVar.h"
 
 class nsIPrincipal;
 class nsPIDOMWindow;
 
 BEGIN_QUOTA_NAMESPACE
 
 class CheckQuotaHelper MOZ_FINAL : public nsIRunnable,
                                    public nsIInterfaceRequestor,
@@ -29,21 +29,24 @@ public:
   NS_DECL_THREADSAFE_ISUPPORTS
   NS_DECL_NSIRUNNABLE
   NS_DECL_NSIINTERFACEREQUESTOR
   NS_DECL_NSIOBSERVER
 
   CheckQuotaHelper(nsPIDOMWindow* aWindow,
                    mozilla::Mutex& aMutex);
 
-  bool PromptAndReturnQuotaIsDisabled();
+  bool
+  PromptAndReturnQuotaIsDisabled();
 
-  void Cancel();
+  void
+  Cancel();
 
-  static uint32_t GetQuotaPermission(nsIPrincipal* aPrincipal);
+  static uint32_t
+  GetQuotaPermission(nsIPrincipal* aPrincipal);
 
 private:
   nsPIDOMWindow* mWindow;
 
   mozilla::Mutex& mMutex;
   mozilla::CondVar mCondVar;
   uint32_t mPromptResult;
   bool mWaiting;
--- a/dom/quota/Client.h
+++ b/dom/quota/Client.h
@@ -4,22 +4,25 @@
  * 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"
 
+#include "PersistenceType.h"
+
 class nsIOfflineStorage;
 class nsIRunnable;
 
 BEGIN_QUOTA_NAMESPACE
 
-class UsageRunnable;
+class OriginOrPatternString;
+class UsageInfo;
 
 // 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)
@@ -65,24 +68,30 @@ public:
       return NS_ERROR_FAILURE;
     }
 
     return NS_OK;
   }
 
   // Methods which are called on the IO thred.
   virtual nsresult
-  InitOrigin(const nsACString& aOrigin, UsageRunnable* aUsageRunnable) = 0;
+  InitOrigin(PersistenceType aPersistenceType,
+             const nsACString& aGroup,
+             const nsACString& aOrigin,
+             UsageInfo* aUsageInfo) = 0;
 
   virtual nsresult
-  GetUsageForOrigin(const nsACString& aOrigin,
-                    UsageRunnable* aUsageRunnable) = 0;
+  GetUsageForOrigin(PersistenceType aPersistenceType,
+                    const nsACString& aGroup,
+                    const nsACString& aOrigin,
+                    UsageInfo* aUsageInfo) = 0;
 
   virtual void
-  OnOriginClearCompleted(const nsACString& aPattern) = 0;
+  OnOriginClearCompleted(PersistenceType aPersistenceType,
+                         const OriginOrPatternString& aOriginOrPattern) = 0;
 
   virtual void
   ReleaseIOThreadObjects() = 0;
 
   // Methods which are called on the main thred.
   virtual bool
   IsFileServiceUtilized() = 0;
 
--- a/dom/quota/FileStreams.cpp
+++ b/dom/quota/FileStreams.cpp
@@ -43,17 +43,17 @@ FileQuotaStream<FileStreamBase>::Close()
 template <class FileStreamBase>
 nsresult
 FileQuotaStream<FileStreamBase>::DoOpen()
 {
   QuotaManager* quotaManager = QuotaManager::Get();
   NS_ASSERTION(quotaManager, "Shouldn't be null!");
 
   NS_ASSERTION(!mQuotaObject, "Creating quota object more than once?");
-  mQuotaObject = quotaManager->GetQuotaObject(mOrigin,
+  mQuotaObject = quotaManager->GetQuotaObject(mPersistenceType, mGroup, mOrigin,
     FileStreamBase::mOpenParams.localFile);
 
   nsresult rv = FileStreamBase::DoOpen();
   NS_ENSURE_SUCCESS(rv, rv);
 
   if (mQuotaObject && (FileStreamBase::mOpenParams.ioFlags & PR_TRUNCATE)) {
     mQuotaObject->UpdateSize(0);
   }
@@ -84,42 +84,48 @@ FileQuotaStreamWithWrite<FileStreamBase>
   NS_ENSURE_SUCCESS(rv, rv);
 
   return NS_OK;
 }
 
 NS_IMPL_ISUPPORTS_INHERITED0(FileInputStream, nsFileInputStream)
 
 already_AddRefed<FileInputStream>
-FileInputStream::Create(const nsACString& aOrigin, nsIFile* aFile,
-                        int32_t aIOFlags, int32_t aPerm,
+FileInputStream::Create(PersistenceType aPersistenceType,
+                        const nsACString& aGroup, const nsACString& aOrigin,
+                        nsIFile* aFile, int32_t aIOFlags, int32_t aPerm,
                         int32_t aBehaviorFlags)
 {
-  nsRefPtr<FileInputStream> stream = new FileInputStream(aOrigin);
+  nsRefPtr<FileInputStream> stream =
+    new FileInputStream(aPersistenceType, aGroup, aOrigin);
   nsresult rv = stream->Init(aFile, aIOFlags, aPerm, aBehaviorFlags);
   NS_ENSURE_SUCCESS(rv, nullptr);
   return stream.forget();
 }
 
 NS_IMPL_ISUPPORTS_INHERITED0(FileOutputStream, nsFileOutputStream)
 
 already_AddRefed<FileOutputStream>
-FileOutputStream::Create(const nsACString& aOrigin, nsIFile* aFile,
-                         int32_t aIOFlags, int32_t aPerm,
+FileOutputStream::Create(PersistenceType aPersistenceType,
+                         const nsACString& aGroup, const nsACString& aOrigin,
+                         nsIFile* aFile, int32_t aIOFlags, int32_t aPerm,
                          int32_t aBehaviorFlags)
 {
-  nsRefPtr<FileOutputStream> stream = new FileOutputStream(aOrigin);
+  nsRefPtr<FileOutputStream> stream =
+    new FileOutputStream(aPersistenceType, aGroup, aOrigin);
   nsresult rv = stream->Init(aFile, aIOFlags, aPerm, aBehaviorFlags);
   NS_ENSURE_SUCCESS(rv, nullptr);
   return stream.forget();
 }
 
 NS_IMPL_ISUPPORTS_INHERITED0(FileStream, nsFileStream)
 
 already_AddRefed<FileStream>
-FileStream::Create(const nsACString& aOrigin, nsIFile* aFile, int32_t aIOFlags,
+FileStream::Create(PersistenceType aPersistenceType, const nsACString& aGroup,
+                   const nsACString& aOrigin, nsIFile* aFile, int32_t aIOFlags,
                    int32_t aPerm, int32_t aBehaviorFlags)
 {
-  nsRefPtr<FileStream> stream = new FileStream(aOrigin);
+  nsRefPtr<FileStream> stream =
+    new FileStream(aPersistenceType, aGroup, aOrigin);
   nsresult rv = stream->Init(aFile, aIOFlags, aPerm, aBehaviorFlags);
   NS_ENSURE_SUCCESS(rv, nullptr);
   return stream.forget();
 }
--- a/dom/quota/FileStreams.h
+++ b/dom/quota/FileStreams.h
@@ -6,108 +6,120 @@
 
 #ifndef mozilla_dom_quota_filestreams_h__
 #define mozilla_dom_quota_filestreams_h__
 
 #include "QuotaCommon.h"
 
 #include "nsFileStreams.h"
 
+#include "PersistenceType.h"
 #include "QuotaObject.h"
 
 BEGIN_QUOTA_NAMESPACE
 
 template <class FileStreamBase>
 class FileQuotaStream : public FileStreamBase
 {
 public:
   // nsFileStreamBase override
   NS_IMETHOD
   SetEOF() MOZ_OVERRIDE;
 
   NS_IMETHOD
   Close() MOZ_OVERRIDE;
 
 protected:
-  FileQuotaStream(const nsACString& aOrigin)
-  : mOrigin(aOrigin)
+  FileQuotaStream(PersistenceType aPersistenceType, const nsACString& aGroup,
+                  const nsACString& aOrigin)
+  : mPersistenceType(aPersistenceType), mGroup(aGroup), mOrigin(aOrigin)
   { }
 
   // nsFileStreamBase override
   virtual nsresult
   DoOpen() MOZ_OVERRIDE;
 
+  PersistenceType mPersistenceType;
+  nsCString mGroup;
   nsCString mOrigin;
   nsRefPtr<QuotaObject> mQuotaObject;
 };
 
 template <class FileStreamBase>
 class FileQuotaStreamWithWrite : public FileQuotaStream<FileStreamBase>
 {
 public:
   // nsFileStreamBase override
   NS_IMETHOD
   Write(const char* aBuf, uint32_t aCount, uint32_t* _retval) MOZ_OVERRIDE;
 
 protected:
-  FileQuotaStreamWithWrite(const nsACString& aOrigin)
-  : FileQuotaStream<FileStreamBase>(aOrigin)
+  FileQuotaStreamWithWrite(PersistenceType aPersistenceType,
+                           const nsACString& aGroup, const nsACString& aOrigin)
+  : FileQuotaStream<FileStreamBase>(aPersistenceType, aGroup, aOrigin)
   { }
 };
 
 class FileInputStream : public FileQuotaStream<nsFileInputStream>
 {
 public:
   NS_DECL_ISUPPORTS_INHERITED
 
   static already_AddRefed<FileInputStream>
-  Create(const nsACString& aOrigin, nsIFile* aFile, int32_t aIOFlags = -1,
+  Create(PersistenceType aPersistenceType, const nsACString& aGroup,
+         const nsACString& aOrigin, nsIFile* aFile, int32_t aIOFlags = -1,
          int32_t aPerm = -1, int32_t aBehaviorFlags = 0);
 
 private:
-  FileInputStream(const nsACString& aOrigin)
-  : FileQuotaStream<nsFileInputStream>(aOrigin)
+  FileInputStream(PersistenceType aPersistenceType, const nsACString& aGroup,
+                  const nsACString& aOrigin)
+  : FileQuotaStream<nsFileInputStream>(aPersistenceType, aGroup, aOrigin)
   { }
 
   virtual ~FileInputStream() {
     Close();
   }
 };
 
 class FileOutputStream : public FileQuotaStreamWithWrite<nsFileOutputStream>
 {
 public:
   NS_DECL_ISUPPORTS_INHERITED
 
   static already_AddRefed<FileOutputStream>
-  Create(const nsACString& aOrigin, nsIFile* aFile, int32_t aIOFlags = -1,
+  Create(PersistenceType aPersistenceType, const nsACString& aGroup,
+         const nsACString& aOrigin, nsIFile* aFile, int32_t aIOFlags = -1,
          int32_t aPerm = -1, int32_t aBehaviorFlags = 0);
 
 private:
-  FileOutputStream(const nsACString& aOrigin)
-  : FileQuotaStreamWithWrite<nsFileOutputStream>(aOrigin)
+  FileOutputStream(PersistenceType aPersistenceType, const nsACString& aGroup,
+                   const nsACString& aOrigin)
+  : FileQuotaStreamWithWrite<nsFileOutputStream>(aPersistenceType, aGroup,
+                                                 aOrigin)
   { }
 
   virtual ~FileOutputStream() {
     Close();
   }
 };
 
 class FileStream : public FileQuotaStreamWithWrite<nsFileStream>
 {
 public:
   NS_DECL_ISUPPORTS_INHERITED
 
   static already_AddRefed<FileStream>
-  Create(const nsACString& aOrigin, nsIFile* aFile, int32_t aIOFlags = -1,
+  Create(PersistenceType aPersistenceType, const nsACString& aGroup,
+         const nsACString& aOrigin, nsIFile* aFile, int32_t aIOFlags = -1,
          int32_t aPerm = -1, int32_t aBehaviorFlags = 0);
 
 private:
-  FileStream(const nsACString& aOrigin)
-  : FileQuotaStreamWithWrite<nsFileStream>(aOrigin)
+  FileStream(PersistenceType aPersistenceType, const nsACString& aGroup,
+             const nsACString& aOrigin)
+  : FileQuotaStreamWithWrite<nsFileStream>(aPersistenceType, aGroup, aOrigin)
   { }
 
   virtual ~FileStream() {
     Close();
   }
 };
 
 END_QUOTA_NAMESPACE
new file mode 100644
--- /dev/null
+++ b/dom/quota/OriginCollection.h
@@ -0,0 +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/. */
+
+#ifndef mozilla_dom_quota_origincollection_h__
+#define mozilla_dom_quota_origincollection_h__
+
+#include "mozilla/dom/quota/QuotaCommon.h"
+
+#include "nsHashKeys.h"
+#include "nsTHashtable.h"
+
+#include "Utilities.h"
+
+BEGIN_QUOTA_NAMESPACE
+
+class OriginCollection
+{
+public:
+  bool
+  ContainsPattern(const nsACString& aPattern)
+  {
+    return mPatterns.Contains(aPattern);
+  }
+
+  void
+  AddPattern(const nsACString& aPattern)
+  {
+    MOZ_ASSERT(!mOrigins.Count());
+    MOZ_ASSERT(!ContainsPattern(aPattern));
+    mPatterns.AppendElement(aPattern);
+  }
+
+  bool
+  ContainsOrigin(const nsACString& aOrigin)
+  {
+    for (uint32_t index = 0; index < mPatterns.Length(); index++) {
+      if (PatternMatchesOrigin(mPatterns[index], aOrigin)) {
+        return true;
+      }
+    }
+
+    return mOrigins.GetEntry(aOrigin);
+  }
+
+  void
+  AddOrigin(const nsACString& aOrigin)
+  {
+    MOZ_ASSERT(!ContainsOrigin(aOrigin));
+    mOrigins.PutEntry(aOrigin);
+  }
+
+private:
+  nsTArray<nsCString> mPatterns;
+  nsTHashtable<nsCStringHashKey> mOrigins;
+};
+
+END_QUOTA_NAMESPACE
+
+#endif // mozilla_dom_quota_origincollection_h__
--- a/dom/quota/OriginOrPatternString.h
+++ b/dom/quota/OriginOrPatternString.h
@@ -21,34 +21,53 @@ public:
   }
 
   static OriginOrPatternString
   FromPattern(const nsACString& aPattern)
   {
     return OriginOrPatternString(aPattern, false);
   }
 
+  static OriginOrPatternString
+  FromNull()
+  {
+    return OriginOrPatternString();
+  }
+
   bool
   IsOrigin() const
   {
     return mIsOrigin;
   }
 
   bool
   IsPattern() const
   {
-    return !mIsOrigin;
+    return mIsPattern;
+  }
+
+  bool
+  IsNull() const
+  {
+    return mIsNull;
   }
 
 private:
   OriginOrPatternString(const nsACString& aOriginOrPattern, bool aIsOrigin)
-  : nsCString(aOriginOrPattern), mIsOrigin(aIsOrigin)
+  : nsCString(aOriginOrPattern),
+    mIsOrigin(aIsOrigin), mIsPattern(!aIsOrigin), mIsNull(false)
+  { }
+
+  OriginOrPatternString()
+  : mIsOrigin(false), mIsPattern(false), mIsNull(true)
   { }
 
   bool
   operator==(const OriginOrPatternString& aOther) MOZ_DELETE;
 
   bool mIsOrigin;
+  bool mIsPattern;
+  bool mIsNull;
 };
 
 END_QUOTA_NAMESPACE
 
 #endif // mozilla_dom_quota_originorpatternstring_h__
new file mode 100644
--- /dev/null
+++ b/dom/quota/PersistenceType.h
@@ -0,0 +1,77 @@
+/* -*- 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_persistencetype_h__
+#define mozilla_dom_quota_persistencetype_h__
+
+#include "mozilla/dom/quota/QuotaCommon.h"
+
+#include "mozilla/dom/StorageTypeBinding.h"
+
+BEGIN_QUOTA_NAMESPACE
+
+enum PersistenceType
+{
+  PERSISTENCE_TYPE_PERSISTENT = 0,
+  PERSISTENCE_TYPE_TEMPORARY,
+
+  // Only needed for IPC serialization helper, should never be used in code.
+  PERSISTENCE_TYPE_INVALID
+};
+
+inline void
+PersistenceTypeToText(PersistenceType aPersistenceType, nsACString& aText)
+{
+  switch (aPersistenceType) {
+    case PERSISTENCE_TYPE_PERSISTENT:
+      aText.AssignLiteral("persistent");
+      return;
+    case PERSISTENCE_TYPE_TEMPORARY:
+      aText.AssignLiteral("temporary");
+      return;
+
+    case PERSISTENCE_TYPE_INVALID:
+    default:
+      MOZ_CRASH("Bad persistence type value!");
+  }
+
+  MOZ_ASSUME_UNREACHABLE("Should never get here!");
+}
+
+inline PersistenceType
+PersistenceTypeFromText(const nsACString& aText)
+{
+  if (aText.EqualsLiteral("persistent")) {
+    return PERSISTENCE_TYPE_PERSISTENT;
+  }
+
+  if (aText.EqualsLiteral("temporary")) {
+    return PERSISTENCE_TYPE_TEMPORARY;
+  }
+
+  MOZ_ASSUME_UNREACHABLE("Should never get here!");
+}
+
+inline mozilla::dom::StorageType
+PersistenceTypeToStorage(PersistenceType aPersistenceType)
+{
+  return mozilla::dom::StorageType(static_cast<int>(aPersistenceType));
+}
+
+inline PersistenceType
+PersistenceTypeFromStorage(const Optional<mozilla::dom::StorageType>& aStorage,
+                           PersistenceType aDefaultPersistenceType)
+{
+  if (aStorage.WasPassed()) {
+    return PersistenceType(static_cast<int>(aStorage.Value()));
+  }
+
+  return aDefaultPersistenceType;
+}
+
+END_QUOTA_NAMESPACE
+
+#endif // mozilla_dom_quota_persistencetype_h__
--- a/dom/quota/QuotaCommon.h
+++ b/dom/quota/QuotaCommon.h
@@ -19,20 +19,20 @@
   } /* namespace quota */ } /* namespace dom */ } /* namespace mozilla */
 #define USING_QUOTA_NAMESPACE \
   using namespace mozilla::dom::quota;
 
 #define DSSTORE_FILE_NAME ".DS_Store"
 
 BEGIN_QUOTA_NAMESPACE
 
-#ifdef DEBUG
 void
 AssertIsOnIOThread();
-#else
-inline void
-AssertIsOnIOThread()
-{ }
-#endif
+
+void
+AssertCurrentThreadOwnsQuotaMutex();
+
+bool
+IsOnIOThread();
 
 END_QUOTA_NAMESPACE
 
 #endif // mozilla_dom_quota_quotacommon_h__
--- a/dom/quota/QuotaManager.cpp
+++ b/dom/quota/QuotaManager.cpp
@@ -3,116 +3,177 @@
 /* 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 "nsIBinaryInputStream.h"
+#include "nsIBinaryOutputStream.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 <algorithm>
 #include "GeckoProfiler.h"
 #include "mozilla/Atomics.h"
+#include "mozilla/CondVar.h"
 #include "mozilla/dom/file/FileService.h"
 #include "mozilla/dom/indexedDB/Client.h"
+#include "mozilla/Mutex.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 "nsNetUtil.h"
 #include "nsScriptSecurityManager.h"
 #include "nsThreadUtils.h"
 #include "nsXULAppAPI.h"
 #include "xpcpublic.h"
 
 #include "AcquireListener.h"
 #include "CheckQuotaHelper.h"
+#include "OriginCollection.h"
 #include "OriginOrPatternString.h"
 #include "QuotaObject.h"
 #include "StorageMatcher.h"
-#include "UsageRunnable.h"
+#include "UsageInfo.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"
 
+// Preference that users can set to override temporary storage smart limit
+// calculation.
+#define PREF_FIXED_LIMIT "dom.quotaManager.temporaryStorage.fixedLimit"
+
+// Preferences that are used during temporary storage smart limit calculation
+#define PREF_SMART_LIMIT_PREFIX "dom.quotaManager.temporaryStorage.smartLimit."
+#define PREF_SMART_LIMIT_MIN PREF_SMART_LIMIT_PREFIX "min"
+#define PREF_SMART_LIMIT_MAX PREF_SMART_LIMIT_PREFIX "max"
+#define PREF_SMART_LIMIT_CHUNK PREF_SMART_LIMIT_PREFIX "chunk"
+#define PREF_SMART_LIMIT_RATIO PREF_SMART_LIMIT_PREFIX "ratio"
+
+// Preference that is used to enable testing features
+#define PREF_TESTING_FEATURES "dom.quotaManager.testing"
+
 // profile-before-change, when we need to shut down quota manager
 #define PROFILE_BEFORE_CHANGE_OBSERVER_ID "profile-before-change"
 
+// The name of the file that we use to load/save the last access time of an
+// origin.
 #define METADATA_FILE_NAME ".metadata"
 
+#define PERMISSION_DEFAUT_PERSISTENT_STORAGE "default-persistent-storage"
+
 USING_QUOTA_NAMESPACE
 using namespace mozilla::dom;
 using mozilla::dom::file::FileService;
 
+static_assert(
+  static_cast<uint32_t>(StorageType::Persistent) ==
+  static_cast<uint32_t>(PERSISTENCE_TYPE_PERSISTENT),
+  "Enum values should match.");
+
+static_assert(
+  static_cast<uint32_t>(StorageType::Temporary) ==
+  static_cast<uint32_t>(PERSISTENCE_TYPE_TEMPORARY),
+  "Enum values should match.");
+
 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,
+                 Nullable<PersistenceType> aPersistenceType,
                  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;
+  Nullable<PersistenceType> mPersistenceType;
   nsCOMPtr<nsISupports> mId;
   nsRefPtr<AcquireListener> mListener;
   nsTArray<nsCOMPtr<nsIRunnable> > mDelayedRunnables;
   ArrayCluster<nsIOfflineStorage*> mStorages;
 };
 
+class CollectOriginsHelper MOZ_FINAL : public nsRunnable
+{
+public:
+  CollectOriginsHelper(mozilla::Mutex& aMutex, uint64_t aMinSizeToBeFreed);
+
+  NS_IMETHOD
+  Run();
+
+  // Blocks the current thread until origins are collected on the main thread.
+  // The returned value contains an aggregate size of those origins.
+  int64_t
+  BlockAndReturnOriginsForEviction(nsTArray<OriginInfo*>& aOriginInfos);
+
+private:
+  ~CollectOriginsHelper()
+  { }
+
+  uint64_t mMinSizeToBeFreed;
+
+  mozilla::Mutex& mMutex;
+  mozilla::CondVar mCondVar;
+
+  // The members below are protected by mMutex.
+  nsTArray<OriginInfo*> mOriginInfos;
+  uint64_t mSizeToBeFreed;
+  bool mWaiting;
+};
+
 // 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,
+class OriginClearRunnable MOZ_FINAL : public nsRunnable,
                                       public AcquireListener
 {
   enum CallbackState {
     // Not yet run.
     Pending = 0,
 
     // Running on the main thread in the callback for OpenAllowed.
     OpenAllowed,
@@ -120,28 +181,30 @@ class OriginClearRunnable MOZ_FINAL : pu
     // Running on the IO thread.
     IO,
 
     // Running on the main thread after all work is done.
     Complete
   };
 
 public:
-  NS_DECL_THREADSAFE_ISUPPORTS
-  NS_DECL_NSIRUNNABLE
-
-  // AcquireListener override
-  virtual nsresult
-  OnExclusiveAccessAcquired() MOZ_OVERRIDE;
+  NS_DECL_ISUPPORTS_INHERITED
 
   OriginClearRunnable(const OriginOrPatternString& aOriginOrPattern)
   : mOriginOrPattern(aOriginOrPattern),
     mCallbackState(Pending)
   { }
 
+  NS_IMETHOD
+  Run();
+
+  // AcquireListener override
+  virtual nsresult
+  OnExclusiveAccessAcquired() MOZ_OVERRIDE;
+
   void
   AdvanceState()
   {
     switch (mCallbackState) {
       case Pending:
         mCallbackState = OpenAllowed;
         return;
       case OpenAllowed:
@@ -155,33 +218,34 @@ public:
     }
   }
 
   static void
   InvalidateOpenedStorages(nsTArray<nsCOMPtr<nsIOfflineStorage> >& aStorages,
                            void* aClosure);
 
   void
-  DeleteFiles(QuotaManager* aQuotaManager);
+  DeleteFiles(QuotaManager* aQuotaManager,
+              PersistenceType aPersistenceType);
 
 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,
+class AsyncUsageRunnable MOZ_FINAL : public UsageInfo,
+                                     public nsRunnable,
                                      public nsIQuotaRequest
 {
   enum CallbackState {
     // Not yet run.
     Pending = 0,
 
     // Running on the main thread in the callback for OpenAllowed.
     OpenAllowed,
@@ -192,26 +256,29 @@ class AsyncUsageRunnable MOZ_FINAL : pub
     // Running on the main thread after all work is done.
     Complete,
 
     // Running on the main thread after skipping the work
     Shortcut
   };
 
 public:
-  NS_DECL_THREADSAFE_ISUPPORTS
-  NS_DECL_NSIRUNNABLE
+  NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_NSIQUOTAREQUEST
 
   AsyncUsageRunnable(uint32_t aAppId,
                      bool aInMozBrowserOnly,
+                     const nsACString& aGroup,
                      const OriginOrPatternString& aOrigin,
                      nsIURI* aURI,
                      nsIUsageCallback* aCallback);
 
+  NS_IMETHOD
+  Run();
+
   void
   AdvanceState()
   {
     switch (mCallbackState) {
       case Pending:
         mCallbackState = OpenAllowed;
         return;
       case OpenAllowed:
@@ -223,102 +290,313 @@ public:
       default:
         NS_NOTREACHED("Can't advance past Complete!");
     }
   }
 
   nsresult
   TakeShortcut();
 
+private:
   // 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
+  AddToUsage(QuotaManager* aQuotaManager,
+             PersistenceType aPersistenceType);
+
   nsCOMPtr<nsIURI> mURI;
   nsCOMPtr<nsIUsageCallback> mCallback;
   uint32_t mAppId;
+  nsCString mGroup;
   OriginOrPatternString mOrigin;
   CallbackState mCallbackState;
   bool mInMozBrowserOnly;
 };
 
-#ifdef DEBUG
-void
-AssertIsOnIOThread()
+class ResetOrClearRunnable MOZ_FINAL : public nsRunnable,
+                                       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_INHERITED
+
+  ResetOrClearRunnable(bool aClear)
+  : mCallbackState(Pending),
+    mClear(aClear)
+  { }
+
+  NS_IMETHOD
+  Run();
+
+  // AcquireListener override
+  virtual nsresult
+  OnExclusiveAccessAcquired() MOZ_OVERRIDE;
+
+  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,
+              PersistenceType aPersistenceType);
+
+private:
+  CallbackState mCallbackState;
+  bool mClear;
+};
+
+// Responsible for finalizing eviction of certian origins (storage files have
+// been already cleared, we just need to release IO thread only objects and
+// allow next synchronized ops for evicted origins). Created when
+// QuotaManager::FinalizeOriginEviction 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 release IO thread only
+// objects before dispatching itself back to the main thread. When back on the
+// main thread the runnable will call QuotaManager::AllowNextSynchronizedOp.
+// The runnable can also run in a shortened mode (runs only twice).
+class FinalizeOriginEvictionRunnable MOZ_FINAL : public nsRunnable
+{
+  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 IO work is done.
+    Complete
+  };
+
+public:
+  FinalizeOriginEvictionRunnable(nsTArray<nsCString>& aOrigins)
+  : mCallbackState(Pending)
+  {
+    mOrigins.SwapElements(aOrigins);
+  }
+
+  NS_IMETHOD
+  Run();
+
+  void
+  AdvanceState()
+  {
+    switch (mCallbackState) {
+      case Pending:
+        mCallbackState = OpenAllowed;
+        return;
+      case OpenAllowed:
+        mCallbackState = IO;
+        return;
+      case IO:
+        mCallbackState = Complete;
+        return;
+      default:
+        MOZ_ASSUME_UNREACHABLE("Can't advance past Complete!");
+    }
+  }
+
+  nsresult
+  Dispatch();
+
+  nsresult
+  RunImmediately();
+
+private:
+  CallbackState mCallbackState;
+  nsTArray<nsCString> mOrigins;
+};
+
+bool
+IsOnIOThread()
 {
   QuotaManager* quotaManager = QuotaManager::Get();
   NS_ASSERTION(quotaManager, "Must have a manager here!");
 
-  bool correctThread;
-  NS_ASSERTION(NS_SUCCEEDED(quotaManager->IOThread()->
-                            IsOnCurrentThread(&correctThread)) && correctThread,
-               "Running on the wrong thread!");
+  bool currentThread;
+  return NS_SUCCEEDED(quotaManager->IOThread()->
+                      IsOnCurrentThread(&currentThread)) && currentThread;
+}
+
+void
+AssertIsOnIOThread()
+{
+  NS_ASSERTION(IsOnIOThread(), "Running on the wrong thread!");
 }
+
+void
+AssertCurrentThreadOwnsQuotaMutex()
+{
+#ifdef DEBUG
+  QuotaManager* quotaManager = QuotaManager::Get();
+  NS_ASSERTION(quotaManager, "Must have a manager here!");
+
+  quotaManager->AssertCurrentThreadOwnsQuotaMutex();
 #endif
+}
 
 END_QUOTA_NAMESPACE
 
 namespace {
 
+// Amount of space that storages may use by default in megabytes.
+static const int32_t  kDefaultQuotaMB =             50;
+
+// Constants for temporary storage limit computing.
+static const int32_t  kDefaultFixedLimitKB =        -1;
+#ifdef ANDROID
+// On Android, smaller/older devices may have very little storage and
+// device owners may be sensitive to storage footprint: Use a smaller
+// percentage of available space and a smaller minimum/maximum.
+static const uint32_t kDefaultSmartLimitMinKB =     10 * 1024;
+static const uint32_t kDefaultSmartLimitMaxKB =    200 * 1024;
+static const uint32_t kDefaultSmartLimitChunkKB =    2 * 1024;
+static const float    kDefaultSmartLimitRatio =     .2f;
+#else
+static const uint64_t kDefaultSmartLimitMinKB =     50 * 1024;
+static const uint64_t kDefaultSmartLimitMaxKB =   1024 * 1024;
+static const uint32_t kDefaultSmartLimitChunkKB =   10 * 1024;
+static const float    kDefaultSmartLimitRatio =     .4f;
+#endif
+
 QuotaManager* gInstance = nullptr;
 mozilla::Atomic<uint32_t> gShutdown(0);
 
-int32_t gStorageQuotaMB = DEFAULT_QUOTA_MB;
+int32_t gStorageQuotaMB = kDefaultQuotaMB;
+
+int32_t gFixedLimitKB = kDefaultFixedLimitKB;
+uint32_t gSmartLimitMinKB = kDefaultSmartLimitMinKB;
+uint32_t gSmartLimitMaxKB = kDefaultSmartLimitMaxKB;
+uint32_t gSmartLimitChunkKB = kDefaultSmartLimitChunkKB;
+float gSmartLimitRatio = kDefaultSmartLimitRatio;
+
+bool gTestingEnabled = false;
 
 // A callback runnable used by the TransactionPool when it's safe to proceed
 // with a SetVersion/DeleteDatabase/etc.
-class WaitForTransactionsToFinishRunnable MOZ_FINAL : public nsIRunnable
+class WaitForTransactionsToFinishRunnable MOZ_FINAL : public nsRunnable
 {
 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_THREADSAFE_ISUPPORTS
-  NS_DECL_NSIRUNNABLE
+  NS_IMETHOD
+  Run();
 
   void
   AddRun()
   {
     mCountdown++;
   }
 
 private:
   // The QuotaManager holds this alive.
   SynchronizedOp* mOp;
   uint32_t mCountdown;
 };
 
-class WaitForLockedFilesToFinishRunnable MOZ_FINAL : public nsIRunnable
+class WaitForLockedFilesToFinishRunnable MOZ_FINAL : public nsRunnable
 {
 public:
   WaitForLockedFilesToFinishRunnable()
   : mBusy(true)
   { }
 
-  NS_DECL_THREADSAFE_ISUPPORTS
-  NS_DECL_NSIRUNNABLE
+  NS_IMETHOD
+  Run();
 
   bool
   IsBusy() const
   {
     return mBusy;
   }
 
 private:
   bool mBusy;
 };
 
+class SaveOriginAccessTimeRunnable MOZ_FINAL : public nsRunnable
+{
+public:
+  SaveOriginAccessTimeRunnable(const nsACString& aOrigin, int64_t aTimestamp)
+  : mOrigin(aOrigin), mTimestamp(aTimestamp)
+  { }
+
+  NS_IMETHOD
+  Run();
+
+private:
+  nsCString mOrigin;
+  int64_t mTimestamp;
+};
+
+struct MOZ_STACK_CLASS RemoveQuotaInfo
+{
+  RemoveQuotaInfo(PersistenceType aPersistenceType, const nsACString& aPattern)
+  : persistenceType(aPersistenceType), pattern(aPattern)
+  { }
+
+  PersistenceType persistenceType;
+  nsCString pattern;
+};
+
+struct MOZ_STACK_CLASS InactiveOriginsInfo
+{
+  InactiveOriginsInfo(OriginCollection& aCollection,
+                      nsTArray<OriginInfo*>& aOrigins)
+  : collection(aCollection), origins(aOrigins)
+  { }
+
+  OriginCollection& collection;
+  nsTArray<OriginInfo*>& origins;
+};
+
 bool
 IsMainProcess()
 {
   return XRE_GetProcessType() == GeckoProcessType_Default;
 }
 
 void
 SanitizeOriginString(nsCString& aOrigin)
@@ -333,40 +611,355 @@ SanitizeOriginString(nsCString& aOrigin)
   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)
+nsresult
+EnsureDirectory(nsIFile* aDirectory, bool* aCreated)
+{
+  AssertIsOnIOThread();
+
+  nsresult rv = aDirectory->Create(nsIFile::DIRECTORY_TYPE, 0755);
+  if (rv == NS_ERROR_FILE_ALREADY_EXISTS) {
+    bool isDirectory;
+    rv = aDirectory->IsDirectory(&isDirectory);
+    NS_ENSURE_SUCCESS(rv, rv);
+    NS_ENSURE_TRUE(isDirectory, NS_ERROR_UNEXPECTED);
+
+    *aCreated = false;
+  }
+  else {
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    *aCreated = true;
+  }
+
+  return NS_OK;
+}
+
+nsresult
+CreateDirectoryUpgradeStamp(nsIFile* aDirectory)
+{
+  AssertIsOnIOThread();
+
+  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);
+
+  rv = metadataFile->Create(nsIFile::NORMAL_FILE_TYPE, 0644);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  return NS_OK;
+}
+
+nsresult
+GetDirectoryMetadataStream(nsIFile* aDirectory, bool aUpdate,
+                           nsIBinaryOutputStream** aStream)
+{
+  AssertIsOnIOThread();
+
+  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);
+
+  nsCOMPtr<nsIOutputStream> outputStream;
+  if (aUpdate) {
+    bool exists;
+    rv = metadataFile->Exists(&exists);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    if (!exists) {
+      *aStream = nullptr;
+      return NS_OK;
+    }
+
+    nsCOMPtr<nsIFileStream> stream;
+    rv = NS_NewLocalFileStream(getter_AddRefs(stream), metadataFile);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    outputStream = do_QueryInterface(stream);
+    NS_ENSURE_TRUE(outputStream, NS_ERROR_FAILURE);
+  }
+  else {
+    rv = NS_NewLocalFileOutputStream(getter_AddRefs(outputStream),
+                                     metadataFile);
+    NS_ENSURE_SUCCESS(rv, rv);
+  }
+
+  nsCOMPtr<nsIBinaryOutputStream> binaryStream =
+    do_CreateInstance("@mozilla.org/binaryoutputstream;1");
+  NS_ENSURE_TRUE(binaryStream, NS_ERROR_FAILURE);
+
+  rv = binaryStream->SetOutputStream(outputStream);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  binaryStream.forget(aStream);
+  return NS_OK;
+}
+
+nsresult
+CreateDirectoryMetadata(nsIFile* aDirectory, int64_t aTimestamp,
+                        const nsACString& aGroup, const nsACString& aOrigin)
+{
+  AssertIsOnIOThread();
+
+  nsCOMPtr<nsIBinaryOutputStream> stream;
+  nsresult rv =
+    GetDirectoryMetadataStream(aDirectory, false, getter_AddRefs(stream));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  NS_ASSERTION(stream, "This shouldn't be null!");
+
+  rv = stream->Write64(aTimestamp);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  rv = stream->WriteStringZ(PromiseFlatCString(aGroup).get());
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  rv = stream->WriteStringZ(PromiseFlatCString(aOrigin).get());
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  return NS_OK;
+}
+
+nsresult
+GetDirectoryMetadata(nsIFile* aDirectory, int64_t* aTimestamp,
+                     nsACString& aGroup, nsACString& aOrigin)
+{
+  AssertIsOnIOThread();
+
+  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);
+
+  nsCOMPtr<nsIInputStream> stream;
+  rv = NS_NewLocalFileInputStream(getter_AddRefs(stream), metadataFile);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsCOMPtr<nsIInputStream> bufferedStream;
+  rv = NS_NewBufferedInputStream(getter_AddRefs(bufferedStream), stream, 512);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsCOMPtr<nsIBinaryInputStream> binaryStream =
+    do_CreateInstance("@mozilla.org/binaryinputstream;1");
+  NS_ENSURE_TRUE(binaryStream, NS_ERROR_FAILURE);
+
+  rv = binaryStream->SetInputStream(bufferedStream);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  uint64_t timestamp;
+  rv = binaryStream->Read64(&timestamp);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsCString group;
+  rv = binaryStream->ReadCString(group);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsCString origin;
+  rv = binaryStream->ReadCString(origin);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  *aTimestamp = timestamp;
+  aGroup = group;
+  aOrigin = origin;
+  return NS_OK;
+}
+
+nsresult
+MaybeUpgradeOriginDirectory(nsIFile* aDirectory)
 {
-  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 (PatternMatchesOrigin(*pattern, aKey)) {
-    return PL_DHASH_REMOVE;
+  AssertIsOnIOThread();
+  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 (rv == NS_ERROR_FILE_ALREADY_EXISTS) {
+      NS_WARNING("IDB directory already exists!");
+
+      bool isDirectory;
+      rv = idbDirectory->IsDirectory(&isDirectory);
+      NS_ENSURE_SUCCESS(rv, rv);
+      NS_ENSURE_TRUE(isDirectory, NS_ERROR_UNEXPECTED);
+    }
+    else {
+      NS_ENSURE_SUCCESS(rv, rv);
+    }
+
+    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 PL_DHASH_NEXT;
+  return NS_OK;
+}
+
+// This method computes and returns our best guess for the temporary storage
+// limit (in bytes), based on the amount of space users have free on their hard
+// drive and on given temporary storage usage (also in bytes). We use a tiered
+// scheme: the more space available, the larger the temporary storage will be.
+// However, we do not want to enable the temporary storage to grow to an
+// unbounded size, so the larger the user's available space is, the smaller of
+// a percentage we take. We set a lower bound of gTemporaryStorageLimitMinKB
+// and an upper bound of gTemporaryStorageLimitMaxKB.
+nsresult
+GetTemporaryStorageLimit(nsIFile* aDirectory, uint64_t aCurrentUsage,
+                         uint64_t* aLimit)
+{
+  // Check for free space on device where temporary storage directory lives.
+  int64_t bytesAvailable;
+  nsresult rv = aDirectory->GetDiskSpaceAvailable(&bytesAvailable);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  NS_ASSERTION(bytesAvailable >= 0, "Negative bytes available?!");
+
+  uint64_t availableKB =
+    static_cast<uint64_t>((bytesAvailable + aCurrentUsage) / 1024);
+
+  // Grow/shrink in gTemporaryStorageLimitChunkKB units, deliberately, so that
+  // in the common case we don't shrink temporary storage and evict origin data
+  // every time we initialize.
+  availableKB = (availableKB / gSmartLimitChunkKB) * gSmartLimitChunkKB;
+
+  // The tier scheme:
+  // .5 % of space above 25 GB
+  // 1 % of space between 7GB -> 25 GB
+  // 5 % of space between 500 MB -> 7 GB
+  // gTemporaryStorageLimitRatio of space up to 500 MB
+
+  static const uint64_t _25GB = 25 * 1024 * 1024;
+  static const uint64_t _7GB = 7 * 1024 * 1024;
+  static const uint64_t _500MB = 500 * 1024;
+
+#define PERCENTAGE(a, b, c) ((a - b) * c)
+
+  uint64_t resultKB;
+  if (availableKB > _25GB) {
+    resultKB = static_cast<uint64_t>(
+      PERCENTAGE(availableKB, _25GB, .005) +
+      PERCENTAGE(_25GB, _7GB, .01) +
+      PERCENTAGE(_7GB, _500MB, .05) +
+      PERCENTAGE(_500MB, 0, gSmartLimitRatio)
+    );
+  }
+  else if (availableKB > _7GB) {
+    resultKB = static_cast<uint64_t>(
+      PERCENTAGE(availableKB, _7GB, .01) +
+      PERCENTAGE(_7GB, _500MB, .05) +
+      PERCENTAGE(_500MB, 0, gSmartLimitRatio)
+    );
+  }
+  else if (availableKB > _500MB) {
+    resultKB = static_cast<uint64_t>(
+      PERCENTAGE(availableKB, _500MB, .05) +
+      PERCENTAGE(_500MB, 0, gSmartLimitRatio)
+    );
+  }
+  else {
+    resultKB = static_cast<uint64_t>(
+      PERCENTAGE(availableKB, 0, gSmartLimitRatio)
+    );
+  }
+
+#undef PERCENTAGE
+
+  *aLimit = 1024 * mozilla::clamped<uint64_t>(resultKB, gSmartLimitMinKB,
+                                              gSmartLimitMaxKB);
+  return NS_OK;
+}
+
+void
+GetInfoForChrome(nsACString* aGroup, nsACString* aASCIIOrigin,
+                 StoragePrivilege* aPrivilege,
+                 PersistenceType* aDefaultPersistenceType)
+{
+  static const char kChromeOrigin[] = "chrome";
+
+  if (aGroup) {
+    aGroup->AssignLiteral(kChromeOrigin);
+  }
+  if (aASCIIOrigin) {
+    aASCIIOrigin->AssignLiteral(kChromeOrigin);
+  }
+  if (aPrivilege) {
+    *aPrivilege = Chrome;
+  }
+  if (aDefaultPersistenceType) {
+    *aDefaultPersistenceType = PERSISTENCE_TYPE_PERSISTENT;
+  }
 }
 
 } // anonymous namespace
 
 QuotaManager::QuotaManager()
 : mCurrentWindowIndex(BAD_TLS_INDEX),
-  mQuotaMutex("QuotaManager.mQuotaMutex")
+  mQuotaMutex("QuotaManager.mQuotaMutex"),
+  mTemporaryStorageLimit(0),
+  mTemporaryStorageUsage(0),
+  mTemporaryStorageInitialized(false),
+  mStorageAreaInitialized(false)
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
   NS_ASSERTION(!gInstance, "More than one instance!");
 }
 
 QuotaManager::~QuotaManager()
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
@@ -440,106 +1033,317 @@ QuotaManager::Init()
   if (PR_NewThreadPrivateIndex(&mCurrentWindowIndex, nullptr) != PR_SUCCESS) {
     NS_ERROR("PR_NewThreadPrivateIndex failed, QuotaManager disabled");
     mCurrentWindowIndex = BAD_TLS_INDEX;
     return NS_ERROR_FAILURE;
   }
 
   nsresult rv;
   if (IsMainProcess()) {
-    nsCOMPtr<nsIFile> dbBaseDirectory;
+    nsCOMPtr<nsIFile> baseDir;
     rv = NS_GetSpecialDirectory(NS_APP_INDEXEDDB_PARENT_DIR,
-                                getter_AddRefs(dbBaseDirectory));
+                                getter_AddRefs(baseDir));
     if (NS_FAILED(rv)) {
       rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR,
-                                  getter_AddRefs(dbBaseDirectory));
+                                  getter_AddRefs(baseDir));
     }
     NS_ENSURE_SUCCESS(rv, rv);
 
-    rv = dbBaseDirectory->Append(NS_LITERAL_STRING("indexedDB"));
+    nsCOMPtr<nsIFile> indexedDBDir;
+    rv = baseDir->Clone(getter_AddRefs(indexedDBDir));
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    rv = indexedDBDir->Append(NS_LITERAL_STRING("indexedDB"));
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    rv = indexedDBDir->GetPath(mIndexedDBPath);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    rv = baseDir->Append(NS_LITERAL_STRING("storage"));
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    nsCOMPtr<nsIFile> persistentStorageDir;
+    rv = baseDir->Clone(getter_AddRefs(persistentStorageDir));
     NS_ENSURE_SUCCESS(rv, rv);
 
-    rv = dbBaseDirectory->GetPath(mStorageBasePath);
+    rv = persistentStorageDir->Append(NS_LITERAL_STRING("persistent"));
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    rv = persistentStorageDir->GetPath(mPersistentStoragePath);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    nsCOMPtr<nsIFile> temporaryStorageDir;
+    rv = baseDir->Clone(getter_AddRefs(temporaryStorageDir));
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    rv = temporaryStorageDir->Append(NS_LITERAL_STRING("temporary"));
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    rv = temporaryStorageDir->GetPath(mTemporaryStoragePath);
     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))) {
+                                            kDefaultQuotaMB))) {
     NS_WARNING("Unable to respond to quota pref changes!");
-    gStorageQuotaMB = DEFAULT_QUOTA_MB;
+  }
+
+  if (NS_FAILED(Preferences::AddIntVarCache(&gFixedLimitKB, PREF_FIXED_LIMIT,
+                                             kDefaultFixedLimitKB)) ||
+      NS_FAILED(Preferences::AddUintVarCache(&gSmartLimitMinKB,
+                                             PREF_SMART_LIMIT_MIN,
+                                             kDefaultSmartLimitMinKB)) ||
+      NS_FAILED(Preferences::AddUintVarCache(&gSmartLimitMaxKB,
+                                             PREF_SMART_LIMIT_MAX,
+                                             kDefaultSmartLimitMaxKB)) ||
+      NS_FAILED(Preferences::AddUintVarCache(&gSmartLimitChunkKB,
+                                             PREF_SMART_LIMIT_CHUNK,
+                                             kDefaultSmartLimitChunkKB)) ||
+      NS_FAILED(Preferences::AddFloatVarCache(&gSmartLimitRatio,
+                                              PREF_SMART_LIMIT_RATIO,
+                                              kDefaultSmartLimitRatio))) {
+    NS_WARNING("Unable to respond to temp storage pref changes!");
+  }
+
+  if (NS_FAILED(Preferences::AddBoolVarCache(&gTestingEnabled,
+                                             PREF_TESTING_FEATURES, false))) {
+    NS_WARNING("Unable to respond to testing pref changes!");
   }
 
   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 aLimitBytes,
-                                 int64_t aUsageBytes)
+QuotaManager::InitQuotaForOrigin(PersistenceType aPersistenceType,
+                                 const nsACString& aGroup,
+                                 const nsACString& aOrigin,
+                                 uint64_t aLimitBytes,
+                                 uint64_t aUsageBytes,
+                                 int64_t aAccessTime)
 {
-  MOZ_ASSERT(aUsageBytes >= 0);
-  MOZ_ASSERT(aLimitBytes > 0);
-  MOZ_ASSERT(aUsageBytes <= aLimitBytes);
-
-  OriginInfo* info = new OriginInfo(aOrigin, aLimitBytes, aUsageBytes);
+  AssertIsOnIOThread();
+  MOZ_ASSERT(aLimitBytes > 0 ||
+             aPersistenceType == PERSISTENCE_TYPE_TEMPORARY);
+  MOZ_ASSERT(aUsageBytes <= aLimitBytes ||
+             aPersistenceType == PERSISTENCE_TYPE_TEMPORARY);
 
   MutexAutoLock lock(mQuotaMutex);
 
-  NS_ASSERTION(!mOriginInfos.GetWeak(aOrigin), "Replacing an existing entry!");
-  mOriginInfos.Put(aOrigin, info);
+  GroupInfoPair* pair;
+  if (!mGroupInfoPairs.Get(aGroup, &pair)) {
+    pair = new GroupInfoPair();
+    mGroupInfoPairs.Put(aGroup, pair);
+    // The hashtable is now responsible to delete the GroupInfoPair.
+  }
+
+  nsRefPtr<GroupInfo> groupInfo = pair->LockedGetGroupInfo(aPersistenceType);
+  if (!groupInfo) {
+    groupInfo = new GroupInfo(aPersistenceType, aGroup);
+    pair->LockedSetGroupInfo(groupInfo);
+  }
+
+  nsRefPtr<OriginInfo> originInfo =
+    new OriginInfo(groupInfo, aOrigin, aLimitBytes, aUsageBytes, aAccessTime);
+  groupInfo->LockedAddOriginInfo(originInfo);
 }
 
 void
-QuotaManager::DecreaseUsageForOrigin(const nsACString& aOrigin,
+QuotaManager::DecreaseUsageForOrigin(PersistenceType aPersistenceType,
+                                     const nsACString& aGroup,
+                                     const nsACString& aOrigin,
                                      int64_t aSize)
 {
+  AssertIsOnIOThread();
+
   MutexAutoLock lock(mQuotaMutex);
 
-  nsRefPtr<OriginInfo> originInfo;
-  mOriginInfos.Get(aOrigin, getter_AddRefs(originInfo));
-
+  GroupInfoPair* pair;
+  if (!mGroupInfoPairs.Get(aGroup, &pair)) {
+    return;
+  }
+
+  nsRefPtr<GroupInfo> groupInfo = pair->LockedGetGroupInfo(aPersistenceType);
+  if (!groupInfo) {
+    return;
+  }
+
+  nsRefPtr<OriginInfo> originInfo = groupInfo->LockedGetOriginInfo(aOrigin);
   if (originInfo) {
-    originInfo->mUsage -= aSize;
+    originInfo->LockedDecreaseUsage(aSize);
   }
 }
 
 void
-QuotaManager::RemoveQuotaForPattern(const nsACString& aPattern)
+QuotaManager::UpdateOriginAccessTime(PersistenceType aPersistenceType,
+                                     const nsACString& aGroup,
+                                     const nsACString& aOrigin)
+{
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+
+  MutexAutoLock lock(mQuotaMutex);
+
+  GroupInfoPair* pair;
+  if (!mGroupInfoPairs.Get(aGroup, &pair)) {
+    return;
+  }
+
+  nsRefPtr<GroupInfo> groupInfo =
+    pair->LockedGetGroupInfo(PERSISTENCE_TYPE_TEMPORARY);
+  if (!groupInfo) {
+    return;
+  }
+
+  nsRefPtr<OriginInfo> originInfo = groupInfo->LockedGetOriginInfo(aOrigin);
+  if (originInfo) {
+    int64_t timestamp = PR_Now();
+    originInfo->LockedUpdateAccessTime(timestamp);
+
+    if (!groupInfo->IsForTemporaryStorage()) {
+      return;
+    }
+
+    MutexAutoUnlock autoUnlock(mQuotaMutex);
+
+    SaveOriginAccessTime(aOrigin, timestamp);
+  }
+}
+
+// static
+PLDHashOperator
+QuotaManager::RemoveQuotaCallback(const nsACString& aKey,
+                                  nsAutoPtr<GroupInfoPair>& aValue,
+                                  void* aUserArg)
+{
+  NS_ASSERTION(!aKey.IsEmpty(), "Empty key!");
+  NS_ASSERTION(aValue, "Null pointer!");
+
+  nsRefPtr<GroupInfo> groupInfo =
+    aValue->LockedGetGroupInfo(PERSISTENCE_TYPE_TEMPORARY);
+  if (groupInfo) {
+    groupInfo->LockedRemoveOriginInfos();
+  }
+
+  return PL_DHASH_REMOVE;
+}
+
+void
+QuotaManager::RemoveQuota()
+{
+  MutexAutoLock lock(mQuotaMutex);
+
+  mGroupInfoPairs.Enumerate(RemoveQuotaCallback, nullptr);
+
+  NS_ASSERTION(mTemporaryStorageUsage == 0, "Should be zero!");
+}
+
+// static
+PLDHashOperator
+QuotaManager::RemoveQuotaForPersistenceTypeCallback(
+                                               const nsACString& aKey,
+                                               nsAutoPtr<GroupInfoPair>& aValue,
+                                               void* aUserArg)
+{
+  NS_ASSERTION(!aKey.IsEmpty(), "Empty key!");
+  NS_ASSERTION(aValue, "Null pointer!");
+  NS_ASSERTION(aUserArg, "Null pointer!");
+
+  PersistenceType& persistenceType = *static_cast<PersistenceType*>(aUserArg);
+
+  if (persistenceType == PERSISTENCE_TYPE_TEMPORARY) {
+    nsRefPtr<GroupInfo> groupInfo =
+      aValue->LockedGetGroupInfo(persistenceType);
+    if (groupInfo) {
+      groupInfo->LockedRemoveOriginInfos();
+    }
+  }
+
+  aValue->LockedClearGroupInfo(persistenceType);
+
+  return aValue->LockedHasGroupInfos() ? PL_DHASH_NEXT : PL_DHASH_REMOVE;
+}
+
+void
+QuotaManager::RemoveQuotaForPersistenceType(PersistenceType aPersistenceType)
+{
+  MutexAutoLock lock(mQuotaMutex);
+
+  mGroupInfoPairs.Enumerate(RemoveQuotaForPersistenceTypeCallback,
+                            &aPersistenceType);
+
+  NS_ASSERTION(aPersistenceType == PERSISTENCE_TYPE_PERSISTENT ||
+               mTemporaryStorageUsage == 0, "Should be zero!");
+}
+
+// static
+PLDHashOperator
+QuotaManager::RemoveQuotaForPatternCallback(const nsACString& aKey,
+                                            nsAutoPtr<GroupInfoPair>& aValue,
+                                            void* aUserArg)
+{
+  NS_ASSERTION(!aKey.IsEmpty(), "Empty key!");
+  NS_ASSERTION(aValue, "Null pointer!");
+  NS_ASSERTION(aUserArg, "Null pointer!");
+
+  RemoveQuotaInfo* info = static_cast<RemoveQuotaInfo*>(aUserArg);
+
+  nsRefPtr<GroupInfo> groupInfo =
+    aValue->LockedGetGroupInfo(info->persistenceType);
+  if (groupInfo) {
+    groupInfo->LockedRemoveOriginInfosForPattern(info->pattern);
+
+    if (!groupInfo->LockedHasOriginInfos()) {
+      aValue->LockedClearGroupInfo(info->persistenceType);
+
+      if (!aValue->LockedHasGroupInfos()) {
+        return PL_DHASH_REMOVE;
+      }
+    }
+  }
+
+  return PL_DHASH_NEXT;
+}
+
+void
+QuotaManager::RemoveQuotaForPattern(PersistenceType aPersistenceType,
+                                    const nsACString& aPattern)
 {
   NS_ASSERTION(!aPattern.IsEmpty(), "Empty pattern!");
 
+  RemoveQuotaInfo info(aPersistenceType, aPattern);
+
   MutexAutoLock lock(mQuotaMutex);
 
-  mOriginInfos.Enumerate(RemoveQuotaForPatternCallback,
-                         const_cast<nsACString*>(&aPattern));
+  mGroupInfoPairs.Enumerate(RemoveQuotaForPatternCallback, &info);
 }
 
 already_AddRefed<QuotaObject>
-QuotaManager::GetQuotaObject(const nsACString& aOrigin,
+QuotaManager::GetQuotaObject(PersistenceType aPersistenceType,
+                             const nsACString& aGroup,
+                             const nsACString& aOrigin,
                              nsIFile* aFile)
 {
   NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
 
   nsString path;
   nsresult rv = aFile->GetPath(path);
   NS_ENSURE_SUCCESS(rv, nullptr);
 
@@ -552,51 +1356,66 @@ QuotaManager::GetQuotaObject(const nsACS
   if (exists) {
     rv = aFile->GetFileSize(&fileSize);
     NS_ENSURE_SUCCESS(rv, nullptr);
   }
   else {
     fileSize = 0;
   }
 
-  QuotaObject* info = nullptr;
+  // We need this extra raw pointer because we can't assign to the smart
+  // pointer directly since QuotaObject::AddRef needs to acquire the same mutex.
+  QuotaObject* quotaObject;
   {
     MutexAutoLock lock(mQuotaMutex);
 
-    nsRefPtr<OriginInfo> originInfo;
-    mOriginInfos.Get(aOrigin, getter_AddRefs(originInfo));
+    GroupInfoPair* pair;
+    if (!mGroupInfoPairs.Get(aGroup, &pair)) {
+      return nullptr;
+    }
+
+    nsRefPtr<GroupInfo> groupInfo = pair->LockedGetGroupInfo(aPersistenceType);
+
+    if (!groupInfo) {
+      return nullptr;
+    }
+
+    nsRefPtr<OriginInfo> originInfo = groupInfo->LockedGetOriginInfo(aOrigin);
 
     if (!originInfo) {
       return nullptr;
     }
 
-    originInfo->mQuotaObjects.Get(path, &info);
-
-    if (!info) {
-      info = new QuotaObject(originInfo, path, fileSize);
-      originInfo->mQuotaObjects.Put(path, info);
+    if (!originInfo->mQuotaObjects.Get(path, &quotaObject)) {
+      quotaObject = new QuotaObject(originInfo, path, fileSize);
+      originInfo->mQuotaObjects.Put(path, quotaObject);
+      // The hashtable is not responsible to delete the QuotaObject.
     }
   }
 
-  nsRefPtr<QuotaObject> result = info;
+  // The caller becomes the owner of the QuotaObject, that is, the caller is
+  // is responsible to delete it when the last reference is removed.
+  nsRefPtr<QuotaObject> result = quotaObject;
   return result.forget();
 }
 
 already_AddRefed<QuotaObject>
-QuotaManager::GetQuotaObject(const nsACString& aOrigin,
+QuotaManager::GetQuotaObject(PersistenceType aPersistenceType,
+                             const nsACString& aGroup,
+                             const nsACString& aOrigin,
                              const nsAString& aPath)
 {
   nsresult rv;
   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);
+  return GetQuotaObject(aPersistenceType, aGroup, aOrigin, file);
 }
 
 bool
 QuotaManager::RegisterStorage(nsIOfflineStorage* aStorage)
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
   NS_ASSERTION(aStorage, "Null pointer!");
 
@@ -606,16 +1425,18 @@ QuotaManager::RegisterStorage(nsIOffline
   }
 
   // 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);
+
+    UpdateOriginAccessTime(aStorage->Type(), aStorage->Group(), origin);
   }
   (*cluster)[aStorage->GetClient()->GetType()].AppendElement(aStorage);
 
   return true;
 }
 
 void
 QuotaManager::UnregisterStorage(nsIOfflineStorage* aStorage)
@@ -626,32 +1447,36 @@ QuotaManager::UnregisterStorage(nsIOffli
   // 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);
+
+      UpdateOriginAccessTime(aStorage->Type(), aStorage->Group(), 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());
+    FindSynchronizedOp(aStorage->Origin(),
+                       Nullable<PersistenceType>(aStorage->Type()),
+                       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,
@@ -741,24 +1566,27 @@ QuotaManager::HasOpenTransactions(nsPIDO
     }
   }
 
   return false;
 }
 
 nsresult
 QuotaManager::WaitForOpenAllowed(const OriginOrPatternString& aOriginOrPattern,
+                                 Nullable<PersistenceType> aPersistenceType,
                                  nsIAtom* aId,
                                  nsIRunnable* aRunnable)
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
-  NS_ASSERTION(!aOriginOrPattern.IsEmpty(), "Empty pattern!");
+  NS_ASSERTION(!aOriginOrPattern.IsEmpty() || aOriginOrPattern.IsNull(),
+               "Empty pattern!");
   NS_ASSERTION(aRunnable, "Null pointer!");
 
-  nsAutoPtr<SynchronizedOp> op(new SynchronizedOp(aOriginOrPattern, aId));
+  nsAutoPtr<SynchronizedOp> op(new SynchronizedOp(aOriginOrPattern,
+                                                  aPersistenceType, 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;
@@ -775,28 +1603,49 @@ QuotaManager::WaitForOpenAllowed(const O
   // 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::AddSynchronizedOp(const OriginOrPatternString& aOriginOrPattern,
+                                Nullable<PersistenceType> aPersistenceType,
+                                nsIAtom* aId)
+{
+  nsAutoPtr<SynchronizedOp> op(new SynchronizedOp(aOriginOrPattern,
+                                                  aPersistenceType, nullptr));
+
+#ifdef DEBUG
+  for (uint32_t index = mSynchronizedOps.Length(); index > 0; index--) {
+    nsAutoPtr<SynchronizedOp>& existingOp = mSynchronizedOps[index - 1];
+    NS_ASSERTION(!op->MustWaitFor(*existingOp), "What?");
+  }
+#endif
+
+  mSynchronizedOps.AppendElement(op.forget());
+}
+
+void
 QuotaManager::AllowNextSynchronizedOp(
                                   const OriginOrPatternString& aOriginOrPattern,
+                                  Nullable<PersistenceType> aPersistenceType,
                                   nsIAtom* aId)
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
-  NS_ASSERTION(!aOriginOrPattern.IsEmpty(), "Empty origin/pattern!");
+  NS_ASSERTION(!aOriginOrPattern.IsEmpty() || aOriginOrPattern.IsNull(),
+               "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) {
+        op->mOriginOrPattern == aOriginOrPattern &&
+        op->mPersistenceType == aPersistenceType) {
       if (op->mId == aId) {
         NS_ASSERTION(op->mStorages.IsEmpty(), "How did this happen?");
 
         op->DispatchDelayedRunnables();
 
         mSynchronizedOps.RemoveElementAt(index);
         return;
       }
@@ -806,287 +1655,535 @@ QuotaManager::AllowNextSynchronizedOp(
       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,
+QuotaManager::GetDirectoryForOrigin(PersistenceType aPersistenceType,
+                                    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);
+  rv = directory->InitWithPath(GetStoragePath(aPersistenceType));
   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,
-                                        bool aTrackQuota,
-                                        nsIFile** aDirectory)
+QuotaManager::InitializeOrigin(PersistenceType aPersistenceType,
+                               const nsACString& aGroup,
+                               const nsACString& aOrigin,
+                               bool aTrackQuota,
+                               int64_t aAccessTime,
+                               nsIFile* aDirectory)
 {
   AssertIsOnIOThread();
 
-  nsCOMPtr<nsIFile> directory;
-  nsresult rv = GetDirectoryForOrigin(aOrigin, getter_AddRefs(directory));
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  bool exists;
-  rv = directory->Exists(&exists);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  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);
+  nsresult rv;
+
+  bool temporaryStorage = aPersistenceType == PERSISTENCE_TYPE_TEMPORARY;
+  if (!temporaryStorage) {
+    rv = MaybeUpgradeOriginDirectory(aDirectory);
     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;
+  nsAutoPtr<UsageInfo> usageInfo;
   if (aTrackQuota) {
-    runnable = new UsageRunnable();
+    usageInfo = new UsageInfo();
   }
 
   nsCOMPtr<nsISimpleEnumerator> entries;
-  rv = directory->GetDirectoryEntries(getter_AddRefs(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.EqualsLiteral(METADATA_FILE_NAME)) {
+    if (leafName.EqualsLiteral(METADATA_FILE_NAME) ||
+        leafName.EqualsLiteral(DSSTORE_FILE_NAME)) {
       continue;
     }
 
-#ifdef XP_MACOSX
-    if (leafName.EqualsLiteral(DSSTORE_FILE_NAME)) {
-      continue;
-    }
-#endif
-
     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);
+    rv = mClients[clientType]->InitOrigin(aPersistenceType, aGroup, aOrigin,
+                                          usageInfo);
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   if (aTrackQuota) {
-    uint64_t quotaMaxBytes = GetStorageQuotaMB() * 1024 * 1024;
-    uint64_t totalUsageBytes = runnable->TotalUsage();
-
-    if (totalUsageBytes > quotaMaxBytes) {
-      NS_WARNING("Origin is already using more storage than allowed by quota!");
-      return NS_ERROR_UNEXPECTED;
+    uint64_t quotaMaxBytes;
+    uint64_t totalUsageBytes = usageInfo->TotalUsage();
+
+    if (temporaryStorage) {
+      // Temporary storage has no limit for origin usage (there's a group and
+      // the global limit though).
+      quotaMaxBytes = 0;
+    }
+    else {
+      quotaMaxBytes = GetStorageQuotaMB() * 1024 * 1024;
+      if (totalUsageBytes > quotaMaxBytes) {
+        NS_WARNING("Origin is already using more storage than allowed!");
+        return NS_ERROR_UNEXPECTED;
+      }
     }
 
-    // XXX This signed/unsigned mismatch must be fixed.
-    int64_t limit = quotaMaxBytes >= uint64_t(INT64_MAX) ?
-                    INT64_MAX :
-                    int64_t(quotaMaxBytes);
-
-    int64_t usage = totalUsageBytes >= uint64_t(INT64_MAX) ?
-                    INT64_MAX :
-                    int64_t(totalUsageBytes);
-
-    InitQuotaForOrigin(aOrigin, limit, usage);
+    InitQuotaForOrigin(aPersistenceType, aGroup, aOrigin, quotaMaxBytes,
+                       totalUsageBytes, aAccessTime);
+  }
+
+  return NS_OK;
+}
+
+nsresult
+QuotaManager::MaybeUpgradeIndexedDBDirectory()
+{
+  AssertIsOnIOThread();
+
+  if (mStorageAreaInitialized) {
+    return NS_OK;
+  }
+
+  nsresult rv;
+
+  nsCOMPtr<nsIFile> indexedDBDir =
+    do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  rv = indexedDBDir->InitWithPath(mIndexedDBPath);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  bool exists;
+  rv = indexedDBDir->Exists(&exists);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  if (!exists) {
+    // Nothing to upgrade.
+    mStorageAreaInitialized = true;
+
+    return NS_OK;
+  }
+
+  bool isDirectory;
+  rv = indexedDBDir->IsDirectory(&isDirectory);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  if (!isDirectory) {
+    NS_WARNING("indexedDB entry is not a directory!");
+    return NS_OK;
+  }
+
+  nsCOMPtr<nsIFile> persistentStorageDir =
+    do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  rv = persistentStorageDir->InitWithPath(mPersistentStoragePath);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  rv = persistentStorageDir->Exists(&exists);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  if (exists) {
+    NS_WARNING("indexedDB directory shouldn't exist after the upgrade!");
+    return NS_OK;
   }
 
-  mInitializedOrigins.AppendElement(aOrigin);
-
-  NS_ADDREF(*aDirectory = directory);
+  nsCOMPtr<nsIFile> storageDir;
+  rv = persistentStorageDir->GetParent(getter_AddRefs(storageDir));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsString persistentStorageName;
+  rv = persistentStorageDir->GetLeafName(persistentStorageName);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  // MoveTo() is atomic if the move happens on the same volume which should
+  // be our case, so even if we crash in the middle of the operation nothing
+  // breaks next time we try to initialize.
+  // However there's a theoretical possibility that the indexedDB directory
+  // is on different volume, but it should be rare enough that we don't have
+  // to worry about it.
+  rv = indexedDBDir->MoveTo(storageDir, persistentStorageName);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  mStorageAreaInitialized = true;
+
+  return NS_OK;
+}
+
+nsresult
+QuotaManager::EnsureOriginIsInitialized(PersistenceType aPersistenceType,
+                                        const nsACString& aGroup,
+                                        const nsACString& aOrigin,
+                                        bool aTrackQuota,
+                                        nsIFile** aDirectory)
+{
+  AssertIsOnIOThread();
+
+  nsresult rv = MaybeUpgradeIndexedDBDirectory();
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  // Get directory for this origin and persistence type.
+  nsCOMPtr<nsIFile> directory;
+  rv = GetDirectoryForOrigin(aPersistenceType, aOrigin,
+                             getter_AddRefs(directory));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  if (aPersistenceType == PERSISTENCE_TYPE_PERSISTENT) {
+    if (mInitializedOrigins.Contains(aOrigin)) {
+      NS_ADDREF(*aDirectory = directory);
+      return NS_OK;
+    }
+
+    bool created;
+    rv = EnsureDirectory(directory, &created);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    if (created) {
+      rv = CreateDirectoryUpgradeStamp(directory);
+      NS_ENSURE_SUCCESS(rv, rv);
+    }
+
+    rv = InitializeOrigin(aPersistenceType, aGroup, aOrigin, aTrackQuota, 0,
+                          directory);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    mInitializedOrigins.AppendElement(aOrigin);
+
+    directory.forget(aDirectory);
+    return NS_OK;
+  }
+
+  NS_ASSERTION(aPersistenceType == PERSISTENCE_TYPE_TEMPORARY, "Huh?");
+  NS_ASSERTION(aTrackQuota, "Huh?");
+
+  if (!mTemporaryStorageInitialized) {
+    nsCOMPtr<nsIFile> parentDirectory;
+    rv = directory->GetParent(getter_AddRefs(parentDirectory));
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    bool created;
+    rv = EnsureDirectory(parentDirectory, &created);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    nsCOMPtr<nsISimpleEnumerator> entries;
+    rv = parentDirectory->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> childDirectory = do_QueryInterface(entry);
+      NS_ENSURE_TRUE(childDirectory, NS_NOINTERFACE);
+
+      bool isDirectory;
+      rv = childDirectory->IsDirectory(&isDirectory);
+      NS_ENSURE_SUCCESS(rv, rv);
+      NS_ENSURE_TRUE(isDirectory, NS_ERROR_UNEXPECTED);
+
+      int64_t timestamp;
+      nsCString group;
+      nsCString origin;
+      rv = GetDirectoryMetadata(childDirectory, &timestamp, group, origin);
+      NS_ENSURE_SUCCESS(rv, rv);
+
+      rv = InitializeOrigin(aPersistenceType, group, origin, aTrackQuota,
+                            timestamp, childDirectory);
+      if (NS_FAILED(rv)) {
+        NS_WARNING("Failed to initialize origin!");
+
+        // We have to cleanup partially initialized quota for temporary storage.
+        RemoveQuotaForPersistenceType(aPersistenceType);
+
+        return rv;
+      }
+    }
+
+    if (gFixedLimitKB >= 0) {
+      mTemporaryStorageLimit = gFixedLimitKB * 1024;
+    }
+    else {
+      rv = GetTemporaryStorageLimit(parentDirectory, mTemporaryStorageUsage,
+                                    &mTemporaryStorageLimit);
+      NS_ENSURE_SUCCESS(rv, rv);
+    }
+
+    CheckTemporaryStorageLimits();
+
+    mTemporaryStorageInitialized = true;
+  }
+
+  bool created;
+  rv = EnsureDirectory(directory, &created);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  if (created) {
+    int64_t timestamp = PR_Now();
+
+    rv = CreateDirectoryMetadata(directory, timestamp, aGroup, aOrigin);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    rv = InitializeOrigin(aPersistenceType, aGroup, aOrigin, aTrackQuota,
+                          timestamp, directory);
+    NS_ENSURE_SUCCESS(rv, rv);
+  }
+
+  directory.forget(aDirectory);
   return NS_OK;
 }
 
 void
-QuotaManager::OriginClearCompleted(const nsACString& aPattern)
+QuotaManager::OriginClearCompleted(
+                                  PersistenceType aPersistenceType,
+                                  const OriginOrPatternString& aOriginOrPattern)
 {
   AssertIsOnIOThread();
 
-  int32_t i;
-  for (i = mInitializedOrigins.Length() - 1; i >= 0; i--) {
-    if (PatternMatchesOrigin(aPattern, mInitializedOrigins[i])) {
-      mInitializedOrigins.RemoveElementAt(i);
+  if (aPersistenceType == PERSISTENCE_TYPE_PERSISTENT) {
+    if (aOriginOrPattern.IsOrigin()) {
+      mInitializedOrigins.RemoveElement(aOriginOrPattern);
+    }
+    else {
+      for (uint32_t index = mInitializedOrigins.Length(); index > 0; index--) {
+        if (PatternMatchesOrigin(aOriginOrPattern,
+                                 mInitializedOrigins[index - 1])) {
+          mInitializedOrigins.RemoveElementAt(index - 1);
+        }
+      }
     }
   }
 
-  for (i = 0; i < Client::TYPE_MAX; i++) {
-    mClients[i]->OnOriginClearCompleted(aPattern);
+  for (uint32_t index = 0; index < Client::TYPE_MAX; index++) {
+    mClients[index]->OnOriginClearCompleted(aPersistenceType, aOriginOrPattern);
   }
 }
 
+void
+QuotaManager::ResetOrClearCompleted()
+{
+  AssertIsOnIOThread();
+
+  mInitializedOrigins.Clear();
+  mTemporaryStorageInitialized = false;
+
+  ReleaseIOThreadObjects();
+}
+
 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,
+QuotaManager::GetStorageId(PersistenceType aPersistenceType,
+                           const nsACString& aOrigin,
                            const nsAString& aName)
 {
-  nsCString str(aOrigin);
-  str.Append("*");
+  nsAutoCString str;
+  str.AppendInt(aPersistenceType);
+  str.Append('*');
+  str.Append(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)
+QuotaManager::GetInfoFromURI(nsIURI* aURI,
+                             uint32_t aAppId,
+                             bool aInMozBrowser,
+                             nsACString* aGroup,
+                             nsACString* aASCIIOrigin,
+                             StoragePrivilege* aPrivilege,
+                             PersistenceType* aDefaultPersistenceType)
 {
   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);
+  nsIScriptSecurityManager* secMan = nsContentUtils::GetSecurityManager();
+  NS_ENSURE_TRUE(secMan, NS_ERROR_FAILURE);
+
+  nsCOMPtr<nsIPrincipal> principal;
+  nsresult rv = secMan->GetAppCodebasePrincipal(aURI, aAppId, aInMozBrowser,
+                                                getter_AddRefs(principal));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  rv = GetInfoFromPrincipal(principal, aGroup, aASCIIOrigin, aPrivilege,
+                            aDefaultPersistenceType);
+  NS_ENSURE_SUCCESS(rv, rv);
+
   return NS_OK;
 }
 
 // static
 nsresult
-QuotaManager::GetASCIIOriginFromPrincipal(nsIPrincipal* aPrincipal,
-                                          nsACString& aASCIIOrigin)
+QuotaManager::GetInfoFromPrincipal(nsIPrincipal* aPrincipal,
+                                   nsACString* aGroup,
+                                   nsACString* aASCIIOrigin,
+                                   StoragePrivilege* aPrivilege,
+                                   PersistenceType* aDefaultPersistenceType)
 {
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
   NS_ASSERTION(aPrincipal, "Don't hand me a null principal!");
 
-  static const char kChromeOrigin[] = "chrome";
+  if (nsContentUtils::IsSystemPrincipal(aPrincipal)) {
+    GetInfoForChrome(aGroup, aASCIIOrigin, aPrivilege, aDefaultPersistenceType);
+    return NS_OK;
+  }
+
+  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;
+  }
 
   nsCString origin;
-  if (nsContentUtils::IsSystemPrincipal(aPrincipal)) {
-    origin.AssignLiteral(kChromeOrigin);
+  rv = aPrincipal->GetOrigin(getter_Copies(origin));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  if (origin.EqualsLiteral("chrome")) {
+    NS_WARNING("Non-chrome principal can't use chrome origin!");
+    return NS_ERROR_FAILURE;
   }
-  else {
-    bool isNullPrincipal;
-    nsresult rv = aPrincipal->GetIsNullPrincipal(&isNullPrincipal);
+
+  nsCString jarPrefix;
+  if (aGroup || aASCIIOrigin) {
+    rv = aPrincipal->GetJarPrefix(jarPrefix);
     NS_ENSURE_SUCCESS(rv, rv);
-
-    if (isNullPrincipal) {
-      NS_WARNING("IndexedDB not supported from this principal!");
-      return NS_ERROR_FAILURE;
+  }
+
+  if (aGroup) {
+    nsCString baseDomain;
+    rv = aPrincipal->GetBaseDomain(baseDomain);
+    if (NS_FAILED(rv)) {
+      // A hack for JetPack.
+
+      nsCOMPtr<nsIURI> uri;
+      rv = aPrincipal->GetURI(getter_AddRefs(uri));
+      NS_ENSURE_SUCCESS(rv, rv);
+
+      bool isIndexedDBURI = false;
+      rv = uri->SchemeIs("indexedDB", &isIndexedDBURI);
+      NS_ENSURE_SUCCESS(rv, rv);
+
+      if (isIndexedDBURI) {
+        rv = NS_OK;
+      }
     }
-
-    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;
+    if (baseDomain.IsEmpty()) {
+      aGroup->Assign(jarPrefix + origin);
+    }
+    else {
+      aGroup->Assign(jarPrefix + baseDomain);
     }
   }
 
-  aASCIIOrigin.Assign(origin);
+  if (aASCIIOrigin) {
+    aASCIIOrigin->Assign(jarPrefix + origin);
+  }
+
+  if (aPrivilege) {
+    *aPrivilege = Content;
+  }
+
+  if (aDefaultPersistenceType) {
+    *aDefaultPersistenceType = PERSISTENCE_TYPE_PERSISTENT;
+  }
+
   return NS_OK;
 }
 
 // static
 nsresult
-QuotaManager::GetASCIIOriginFromWindow(nsPIDOMWindow* aWindow,
-                                       nsACString& aASCIIOrigin)
+QuotaManager::GetInfoFromWindow(nsPIDOMWindow* aWindow,
+                                nsACString* aGroup,
+                                nsACString* aASCIIOrigin,
+                                StoragePrivilege* aPrivilege,
+                                PersistenceType* aDefaultPersistenceType)
 {
   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!");
+    GetInfoForChrome(aGroup, aASCIIOrigin, aPrivilege, aDefaultPersistenceType);
     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);
+  nsresult rv = GetInfoFromPrincipal(principal, aGroup, aASCIIOrigin,
+                                     aPrivilege, aDefaultPersistenceType);
   NS_ENSURE_SUCCESS(rv, rv);
 
   return NS_OK;
 }
 
 NS_IMPL_ISUPPORTS2(QuotaManager, nsIQuotaManager, nsIObserver)
 
 NS_IMETHODIMP
@@ -1105,36 +2202,76 @@ QuotaManager::GetUsageForURI(nsIURI* aUR
   // 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 group;
   nsCString origin;
-  nsresult rv = GetASCIIOriginFromURI(aURI, aAppId, aInMozBrowserOnly, origin);
+  nsresult rv = GetInfoFromURI(aURI, aAppId, aInMozBrowserOnly, &group, &origin,
+                               nullptr, nullptr);
   NS_ENSURE_SUCCESS(rv, rv);
 
   OriginOrPatternString oops = OriginOrPatternString::FromOrigin(origin);
 
   nsRefPtr<AsyncUsageRunnable> runnable =
-    new AsyncUsageRunnable(aAppId, aInMozBrowserOnly, oops, aURI, aCallback);
+    new AsyncUsageRunnable(aAppId, aInMozBrowserOnly, group, oops, aURI,
+                           aCallback);
 
   // Put the computation runnable in the queue.
-  rv = WaitForOpenAllowed(oops, nullptr, runnable);
+  rv = WaitForOpenAllowed(oops, Nullable<PersistenceType>(), nullptr, runnable);
   NS_ENSURE_SUCCESS(rv, rv);
 
   runnable->AdvanceState();
 
   runnable.forget(_retval);
   return NS_OK;
 }
 
 NS_IMETHODIMP
+QuotaManager::Clear()
+{
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+
+  if (!gTestingEnabled) {
+    NS_WARNING("Testing features are not enabled!");
+    return NS_OK;
+  }
+
+  OriginOrPatternString oops = OriginOrPatternString::FromNull();
+
+  nsRefPtr<ResetOrClearRunnable> runnable = new ResetOrClearRunnable(true);
+
+  // Put the clear runnable in the queue.
+  nsresult rv =
+    WaitForOpenAllowed(oops, Nullable<PersistenceType>(), 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);
+
+  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::ClearStoragesForURI(nsIURI* aURI,
                                   uint32_t aAppId,
                                   bool aInMozBrowserOnly,
                                   uint8_t aOptionalArgCount)
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
   NS_ENSURE_ARG_POINTER(aURI);
@@ -1143,34 +2280,35 @@ QuotaManager::ClearStoragesForURI(nsIURI
   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);
+  nsresult rv = GetInfoFromURI(aURI, aAppId, aInMozBrowserOnly, nullptr, &origin,
+                               nullptr, nullptr);
   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);
+  rv = WaitForOpenAllowed(oops, Nullable<PersistenceType>(), 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);
 
@@ -1182,16 +2320,53 @@ QuotaManager::ClearStoragesForURI(nsIURI
   }
 
   // After everything has been invalidated the helper should be dispatched to
   // the end of the event queue.
   return NS_OK;
 }
 
 NS_IMETHODIMP
+QuotaManager::Reset()
+{
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+
+  if (!gTestingEnabled) {
+    NS_WARNING("Testing features are not enabled!");
+    return NS_OK;
+  }
+
+  OriginOrPatternString oops = OriginOrPatternString::FromNull();
+
+  nsRefPtr<ResetOrClearRunnable> runnable = new ResetOrClearRunnable(false);
+
+  // Put the reset runnable in the queue.
+  nsresult rv =
+    WaitForOpenAllowed(oops, Nullable<PersistenceType>(), 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);
+
+  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
@@ -1368,17 +2543,20 @@ QuotaManager::LockedQuotaIsLifted()
 
   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
+    // Unlock while calling out to XPCOM (code behind the dispatch method needs
+    // to acquire its own lock which can potentially lead to a deadlock and it
+    // also calls an observer that can do various stuff like IO, so it's better
+    // to not hold our mutex while that happens).
     {
       MutexAutoUnlock autoUnlock(mQuotaMutex);
 
       nsresult rv = NS_DispatchToMainThread(helper);
       NS_ENSURE_SUCCESS(rv, false);
     }
 
     // Relocked.  If any other threads hit the quota limit on the same Window,
@@ -1392,29 +2570,87 @@ QuotaManager::LockedQuotaIsLifted()
   // must remove it.
   if (createdHelper) {
     mCheckQuotaHelpers.Remove(window);
   }
 
   return result;
 }
 
+uint64_t
+QuotaManager::LockedCollectOriginsForEviction(
+                                            uint64_t aMinSizeToBeFreed,
+                                            nsTArray<OriginInfo*>& aOriginInfos)
+{
+  mQuotaMutex.AssertCurrentThreadOwns();
+
+  nsRefPtr<CollectOriginsHelper> helper =
+    new CollectOriginsHelper(mQuotaMutex, aMinSizeToBeFreed);
+
+  // Unlock while calling out to XPCOM (see the detailed comment in
+  // LockedQuotaIsLifted)
+  {
+    MutexAutoUnlock autoUnlock(mQuotaMutex);
+
+    if (NS_FAILED(NS_DispatchToMainThread(helper))) {
+      NS_WARNING("Failed to dispatch to the main thread!");
+    }
+  }
+
+  return helper->BlockAndReturnOriginsForEviction(aOriginInfos);
+}
+
+void
+QuotaManager::LockedRemoveQuotaForOrigin(PersistenceType aPersistenceType,
+                                         const nsACString& aGroup,
+                                         const nsACString& aOrigin)
+{
+  mQuotaMutex.AssertCurrentThreadOwns();
+
+  GroupInfoPair* pair;
+  mGroupInfoPairs.Get(aGroup, &pair);
+
+  if (!pair) {
+    return;
+  }
+
+  nsRefPtr<GroupInfo> groupInfo = pair->LockedGetGroupInfo(aPersistenceType);
+  if (groupInfo) {
+    groupInfo->LockedRemoveOriginInfo(aOrigin);
+
+    if (!groupInfo->LockedHasOriginInfos()) {
+      pair->LockedClearGroupInfo(aPersistenceType);
+
+      if (!pair->LockedHasGroupInfos()) {
+        mGroupInfoPairs.Remove(aGroup);
+      }
+    }
+  }
+}
+
 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);
+  SynchronizedOp* op;
+  if (aStorage) {
+    op = FindSynchronizedOp(aPattern,
+                            Nullable<PersistenceType>(aStorage->Type()),
+                            aStorage->Id());
+  }
+  else {
+    op = FindSynchronizedOp(aPattern, Nullable<PersistenceType>(), 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.
@@ -1442,17 +2678,22 @@ QuotaManager::AcquireExclusiveAccess(con
     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 (aPattern.IsVoid()) {
+      matches.Find(mLiveStorages);
+    }
+    else {
+      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?");
@@ -1552,21 +2793,24 @@ QuotaManager::RunSynchronizedOp(nsIOffli
   nsresult rv = runnable->Run();
   NS_ENSURE_SUCCESS(rv, rv);
 
   return NS_OK;
 }
 
 SynchronizedOp*
 QuotaManager::FindSynchronizedOp(const nsACString& aPattern,
+                                 Nullable<PersistenceType> aPersistenceType,
                                  nsISupports* aId)
 {
   for (uint32_t index = 0; index < mSynchronizedOps.Length(); index++) {
     const nsAutoPtr<SynchronizedOp>& currentOp = mSynchronizedOps[index];
     if (PatternMatchesOrigin(aPattern, currentOp->mOriginOrPattern) &&
+        (!currentOp->mPersistenceType ||
+         currentOp->mPersistenceType == aPersistenceType) &&
         (!currentOp->mId || currentOp->mId == aId)) {
       return currentOp;
     }
   }
 
   return nullptr;
 }
 
@@ -1589,17 +2833,18 @@ QuotaManager::ClearStoragesForApp(uint32
     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);
+  nsresult rv =
+    WaitForOpenAllowed(oops, Nullable<PersistenceType>(), 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);
 
@@ -1608,80 +2853,342 @@ QuotaManager::ClearStoragesForApp(uint32
     // we invalidate it.
     nsCOMPtr<nsIOfflineStorage> storage = matches[index];
     storage->Invalidate();
   }
 
   return NS_OK;
 }
 
-nsresult
-QuotaManager::MaybeUpgradeOriginDirectory(nsIFile* aDirectory)
+// static
+PLDHashOperator
+QuotaManager::GetOriginsExceedingGroupLimit(const nsACString& aKey,
+                                            GroupInfoPair* aValue,
+                                            void* aUserArg)
+{
+  NS_ASSERTION(!aKey.IsEmpty(), "Empty key!");
+  NS_ASSERTION(aValue, "Null pointer!");
+
+  nsRefPtr<GroupInfo> groupInfo =
+    aValue->LockedGetGroupInfo(PERSISTENCE_TYPE_TEMPORARY);
+  if (groupInfo) {
+    QuotaManager* quotaManager = QuotaManager::Get();
+    NS_ASSERTION(quotaManager, "Shouldn't be null!");
+
+    if (groupInfo->mUsage > quotaManager->GetGroupLimit()) {
+      nsTArray<OriginInfo*>* doomedOriginInfos =
+        static_cast<nsTArray<OriginInfo*>*>(aUserArg);
+
+      nsTArray<nsRefPtr<OriginInfo> >& originInfos = groupInfo->mOriginInfos;
+      originInfos.Sort(OriginInfoLRUComparator());
+
+      uint64_t usage = groupInfo->mUsage;
+      for (uint32_t i = 0; i < originInfos.Length(); i++) {
+        OriginInfo* originInfo = originInfos[i];
+
+        doomedOriginInfos->AppendElement(originInfo);
+        usage -= originInfo->mUsage;
+
+        if (usage <= quotaManager->GetGroupLimit()) {
+          break;
+        }
+      }
+    }
+  }
+
+  return PL_DHASH_NEXT;
+}
+
+// static
+PLDHashOperator
+QuotaManager::GetAllTemporaryStorageOrigins(const nsACString& aKey,
+                                            GroupInfoPair* aValue,
+                                            void* aUserArg)
+{
+  NS_ASSERTION(!aKey.IsEmpty(), "Empty key!");
+  NS_ASSERTION(aValue, "Null pointer!");
+
+  nsRefPtr<GroupInfo> groupInfo =
+    aValue->LockedGetGroupInfo(PERSISTENCE_TYPE_TEMPORARY);
+  if (groupInfo) {
+    nsTArray<OriginInfo*>* originInfos =
+      static_cast<nsTArray<OriginInfo*>*>(aUserArg);
+
+    originInfos->AppendElements(groupInfo->mOriginInfos);
+  }
+
+  return PL_DHASH_NEXT;
+}
+
+void
+QuotaManager::CheckTemporaryStorageLimits()
 {
-  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!");
+  AssertIsOnIOThread();
+
+  nsTArray<OriginInfo*> doomedOriginInfos;
+  {
+    MutexAutoLock lock(mQuotaMutex);
+
+    mGroupInfoPairs.EnumerateRead(GetOriginsExceedingGroupLimit,
+                                  &doomedOriginInfos);
+
+    uint64_t usage = 0;
+    for (uint32_t index = 0; index < doomedOriginInfos.Length(); index++) {
+      usage += doomedOriginInfos[index]->mUsage;
+    }
+
+    if (mTemporaryStorageUsage - usage > mTemporaryStorageLimit) {
+      nsTArray<OriginInfo*> originInfos;
+
+      mGroupInfoPairs.EnumerateRead(GetAllTemporaryStorageOrigins,
+                                    &originInfos);
+
+      for (uint32_t index = originInfos.Length(); index > 0; index--) {
+        if (doomedOriginInfos.Contains(originInfos[index - 1])) {
+          originInfos.RemoveElementAt(index - 1);
+        }
+      }
+
+      originInfos.Sort(OriginInfoLRUComparator());
+
+      for (uint32_t i = 0; i < originInfos.Length(); i++) {
+        if (mTemporaryStorageUsage - usage <= mTemporaryStorageLimit) {
+          originInfos.TruncateLength(i);
+          break;
+        }
+
+        usage += originInfos[i]->mUsage;
+      }
+
+      doomedOriginInfos.AppendElements(originInfos);
     }
-
-    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);
+  }
+
+  for (uint32_t index = 0; index < doomedOriginInfos.Length(); index++) {
+    DeleteTemporaryFilesForOrigin(doomedOriginInfos[index]->mOrigin);
+  }
+
+  nsTArray<nsCString> doomedOrigins;
+  {
+    MutexAutoLock lock(mQuotaMutex);
+
+    for (uint32_t index = 0; index < doomedOriginInfos.Length(); index++) {
+      OriginInfo* doomedOriginInfo = doomedOriginInfos[index];
+
+      nsCString group = doomedOriginInfo->mGroupInfo->mGroup;
+      nsCString origin = doomedOriginInfo->mOrigin;
+      LockedRemoveQuotaForOrigin(PERSISTENCE_TYPE_TEMPORARY, group, origin);
+
+#ifdef DEBUG
+      doomedOriginInfos[index] = nullptr;
+#endif
+
+      doomedOrigins.AppendElement(origin);
+    }
+  }
+
+  for (uint32_t index = 0; index < doomedOrigins.Length(); index++) {
+    OriginClearCompleted(
+                       PERSISTENCE_TYPE_TEMPORARY,
+                       OriginOrPatternString::FromOrigin(doomedOrigins[index]));
+  }
+}
+
+// static
+PLDHashOperator
+QuotaManager::AddTemporaryStorageOrigins(
+                                       const nsACString& aKey,
+                                       ArrayCluster<nsIOfflineStorage*>* 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!");
+
+  OriginCollection& collection = *static_cast<OriginCollection*>(aUserArg);
+
+  if (collection.ContainsOrigin(aKey)) {
+    return PL_DHASH_NEXT;
+  }
+
+  for (uint32_t i = 0; i < Client::TYPE_MAX; i++) {
+    nsTArray<nsIOfflineStorage*>& array = (*aValue)[i];
+    for (uint32_t j = 0; j < array.Length(); j++) {
+      nsIOfflineStorage*& storage = array[j];
+      if (storage->Type() == PERSISTENCE_TYPE_TEMPORARY) {
+        collection.AddOrigin(aKey);
+        return PL_DHASH_NEXT;
       }
     }
-
-    rv = metadataFile->Create(nsIFile::NORMAL_FILE_TYPE, 0644);
-    NS_ENSURE_SUCCESS(rv, rv);
+  }
+
+  return PL_DHASH_NEXT;
+}
+
+// static
+PLDHashOperator
+QuotaManager::GetInactiveTemporaryStorageOrigins(const nsACString& aKey,
+                                                 GroupInfoPair* aValue,
+                                                 void* aUserArg)
+{
+  NS_ASSERTION(!aKey.IsEmpty(), "Empty key!");
+  NS_ASSERTION(aValue, "Null pointer!");
+  NS_ASSERTION(aUserArg, "Null pointer!");
+
+  nsRefPtr<GroupInfo> groupInfo =
+    aValue->LockedGetGroupInfo(PERSISTENCE_TYPE_TEMPORARY);
+  if (groupInfo) {
+    InactiveOriginsInfo* info = static_cast<InactiveOriginsInfo*>(aUserArg);
+
+    nsTArray<nsRefPtr<OriginInfo> >& originInfos = groupInfo->mOriginInfos;
+
+    for (uint32_t i = 0; i < originInfos.Length(); i++) {
+      OriginInfo* originInfo = originInfos[i];
+
+      if (!info->collection.ContainsOrigin(originInfo->mOrigin)) {
+        NS_ASSERTION(!originInfo->mQuotaObjects.Count(),
+                     "Inactive origin shouldn't have open files!");
+        info->origins.AppendElement(originInfo);
+      }
+    }
+  }
+
+  return PL_DHASH_NEXT;
+}
+
+uint64_t
+QuotaManager::CollectOriginsForEviction(uint64_t aMinSizeToBeFreed,
+                                        nsTArray<OriginInfo*>& aOriginInfos)
+{
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+
+  // Collect active origins first.
+  OriginCollection originCollection;
+
+  // Add patterns and origins that have running or pending synchronized ops.
+  // (add patterns first to reduce redundancy in the origin collection).
+  uint32_t index;
+  for (index = 0; index < mSynchronizedOps.Length(); index++) {
+    nsAutoPtr<SynchronizedOp>& op = mSynchronizedOps[index];
+    if (!op->mPersistenceType ||
+        op->mPersistenceType.Value() == PERSISTENCE_TYPE_TEMPORARY) {
+      if (op->mOriginOrPattern.IsPattern() &&
+          !originCollection.ContainsPattern(op->mOriginOrPattern)) {
+        originCollection.AddPattern(op->mOriginOrPattern);
+      }
+    }
+  }
+
+  for (index = 0; index < mSynchronizedOps.Length(); index++) {
+    nsAutoPtr<SynchronizedOp>& op = mSynchronizedOps[index];
+    if (!op->mPersistenceType ||
+        op->mPersistenceType.Value() == PERSISTENCE_TYPE_TEMPORARY) {
+      if (op->mOriginOrPattern.IsOrigin() &&
+          !originCollection.ContainsOrigin(op->mOriginOrPattern)) {
+        originCollection.AddOrigin(op->mOriginOrPattern);
+      }
+    }
+  }
+
+  // Add origins that have live temporary storages.
+  mLiveStorages.EnumerateRead(AddTemporaryStorageOrigins, &originCollection);
+
+  // Enumerate inactive origins. This must be protected by the mutex.
+  nsTArray<OriginInfo*> inactiveOrigins;
+  {
+    InactiveOriginsInfo info(originCollection, inactiveOrigins);
+    MutexAutoLock lock(mQuotaMutex);
+    mGroupInfoPairs.EnumerateRead(GetInactiveTemporaryStorageOrigins, &info);
   }
 
-  return NS_OK;
+  // We now have a list of all inactive origins. So it's safe to sort the list
+  // and calculate available size without holding the lock.
+
+  // Sort by the origin access time.
+  inactiveOrigins.Sort(OriginInfoLRUComparator());
+
+  // Create a list of inactive and the least recently used origins
+  // whose aggregate size is greater or equals the minimal size to be freed.
+  uint64_t sizeToBeFreed = 0;
+  for(index = 0; index < inactiveOrigins.Length(); index++) {
+    if (sizeToBeFreed >= aMinSizeToBeFreed) {
+      inactiveOrigins.TruncateLength(index);
+      break;
+    }
+
+    sizeToBeFreed += inactiveOrigins[index]->mUsage;
+  }
+
+  if (sizeToBeFreed >= aMinSizeToBeFreed) {
+    // Success, add synchronized ops for these origins, so any other
+    // operations for them will be delayed (until origin eviction is finalized).
+
+    for(index = 0; index < inactiveOrigins.Length(); index++) {
+      OriginOrPatternString oops =
+        OriginOrPatternString::FromOrigin(inactiveOrigins[index]->mOrigin);
+
+      AddSynchronizedOp(oops,
+                        Nullable<PersistenceType>(PERSISTENCE_TYPE_TEMPORARY),
+                        nullptr);
+    }
+
+    inactiveOrigins.SwapElements(aOriginInfos);
+    return sizeToBeFreed;
+  }
+
+  return 0;
+}
+
+void
+QuotaManager::DeleteTemporaryFilesForOrigin(const nsACString& aOrigin)
+{
+  nsCOMPtr<nsIFile> directory;
+  nsresult rv = GetDirectoryForOrigin(PERSISTENCE_TYPE_TEMPORARY, aOrigin,
+                                      getter_AddRefs(directory));
+  NS_ENSURE_SUCCESS_VOID(rv);
+
+  rv = directory->Remove(true);
+  if (rv != NS_ERROR_FILE_TARGET_DOES_NOT_EXIST &&
+      rv != NS_ERROR_FILE_NOT_FOUND && NS_FAILED(rv)) {
+    // This should never fail if we've closed all storage connections
+    // correctly...
+    NS_ERROR("Failed to remove directory!");
+  }
+}
+
+void
+QuotaManager::FinalizeOriginEviction(nsTArray<nsCString>& aOrigins)
+{
+  NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
+
+  nsRefPtr<FinalizeOriginEvictionRunnable> runnable =
+    new FinalizeOriginEvictionRunnable(aOrigins);
+
+  nsresult rv = IsOnIOThread() ? runnable->RunImmediately()
+                               : runnable->Dispatch();
+  NS_ENSURE_SUCCESS_VOID(rv);
+}
+
+void
+QuotaManager::SaveOriginAccessTime(const nsACString& aOrigin,
+                                   int64_t aTimestamp)
+{
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+
+  if (QuotaManager::IsShuttingDown()) {
+    return;
+  }
+
+  nsRefPtr<SaveOriginAccessTimeRunnable> runnable =
+    new SaveOriginAccessTimeRunnable(aOrigin, aTimestamp);
+
+  if (NS_FAILED(mIOThread->Dispatch(runnable, NS_DISPATCH_NORMAL))) {
+    NS_WARNING("Failed to dispatch runnable!");
+  }
 }
 
 void
 QuotaManager::GetOriginPatternString(uint32_t aAppId,
                                      MozBrowserPatternFlag aBrowserFlag,
                                      const nsACString& aOrigin,
                                      nsAutoCString& _retval)
 {
@@ -1718,34 +3225,40 @@ QuotaManager::GetOriginPatternString(uin
                  "Origin doesn't match parameters!");
   }
 #endif
 
   _retval = aOrigin;
 }
 
 SynchronizedOp::SynchronizedOp(const OriginOrPatternString& aOriginOrPattern,
+                               Nullable<PersistenceType> aPersistenceType,
                                nsISupports* aId)
-: mOriginOrPattern(aOriginOrPattern), mId(aId)
+: mOriginOrPattern(aOriginOrPattern), mPersistenceType(aPersistenceType),
+  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!");
 
+  if (aExistingOp.mOriginOrPattern.IsNull() || mOriginOrPattern.IsNull()) {
+    return true;
+  }
+
   bool match;
 
   if (aExistingOp.mOriginOrPattern.IsOrigin()) {
     if (mOriginOrPattern.IsOrigin()) {
       match = aExistingOp.mOriginOrPattern.Equals(mOriginOrPattern);
     }
     else {
       match = PatternMatchesOrigin(mOriginOrPattern, aExistingOp.mOriginOrPattern);
@@ -1759,16 +3272,23 @@ SynchronizedOp::MustWaitFor(const Synchr
             PatternMatchesOrigin(aExistingOp.mOriginOrPattern, mOriginOrPattern);
   }
 
   // If the origins don't match, the second can proceed.
   if (!match) {
     return false;
   }
 
+  // If the origins match but the persistence types are different, the second
+  // can proceed.
+  if (!aExistingOp.mPersistenceType.IsNull() && !mPersistenceType.IsNull() &&
+      aExistingOp.mPersistenceType.Value() != mPersistenceType.Value()) {
+    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) {
@@ -1799,16 +3319,69 @@ SynchronizedOp::DispatchDelayedRunnables
   uint32_t count = mDelayedRunnables.Length();
   for (uint32_t index = 0; index < count; index++) {
     NS_DispatchToCurrentThread(mDelayedRunnables[index]);
   }
 
   mDelayedRunnables.Clear();
 }
 
+CollectOriginsHelper::CollectOriginsHelper(mozilla::Mutex& aMutex,
+                                           uint64_t aMinSizeToBeFreed)
+: mMinSizeToBeFreed(aMinSizeToBeFreed),
+  mMutex(aMutex),
+  mCondVar(aMutex, "CollectOriginsHelper::mCondVar"),
+  mSizeToBeFreed(0),
+  mWaiting(true)
+{
+  MOZ_ASSERT(!NS_IsMainThread(), "Wrong thread!");
+  mMutex.AssertCurrentThreadOwns();
+}
+
+int64_t
+CollectOriginsHelper::BlockAndReturnOriginsForEviction(
+                                            nsTArray<OriginInfo*>& aOriginInfos)
+{
+  MOZ_ASSERT(!NS_IsMainThread(), "Wrong thread!");
+  mMutex.AssertCurrentThreadOwns();
+
+  while (mWaiting) {
+    mCondVar.Wait();
+  }
+
+  mOriginInfos.SwapElements(aOriginInfos);
+  return mSizeToBeFreed;
+}
+
+NS_IMETHODIMP
+CollectOriginsHelper::Run()
+{
+  MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
+
+  QuotaManager* quotaManager = QuotaManager::Get();
+  NS_ASSERTION(quotaManager, "Shouldn't be null!");
+
+  // We use extra stack vars here to avoid race detector warnings (the same
+  // memory accessed with and without the lock held).
+  nsTArray<OriginInfo*> originInfos;
+  uint64_t sizeToBeFreed =
+    quotaManager->CollectOriginsForEviction(mMinSizeToBeFreed, originInfos);
+
+  MutexAutoLock lock(mMutex);
+
+  NS_ASSERTION(mWaiting, "Huh?!");
+
+  mOriginInfos.SwapElements(originInfos);
+  mSizeToBeFreed = sizeToBeFreed;
+  mWaiting = false;
+  mCondVar.Notify();
+
+  return NS_OK;
+}
+
 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);
@@ -1828,28 +3401,29 @@ OriginClearRunnable::InvalidateOpenedSto
   storages.SwapElements(aStorages);
 
   for (uint32_t index = 0; index < storages.Length(); index++) {
     storages[index]->Invalidate();
   }
 }
 
 void
-OriginClearRunnable::DeleteFiles(QuotaManager* aQuotaManager)
+OriginClearRunnable::DeleteFiles(QuotaManager* aQuotaManager,
+                                 PersistenceType aPersistenceType)
 {
   AssertIsOnIOThread();
   NS_ASSERTION(aQuotaManager, "Don't pass me null!");
 
   nsresult rv;
 
   nsCOMPtr<nsIFile> directory =
     do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv);
   NS_ENSURE_SUCCESS_VOID(rv);
 
-  rv = directory->InitWithPath(aQuotaManager->GetBaseDirectory());
+  rv = directory->InitWithPath(aQuotaManager->GetStoragePath(aPersistenceType));
   NS_ENSURE_SUCCESS_VOID(rv);
 
   nsCOMPtr<nsISimpleEnumerator> entries;
   if (NS_FAILED(directory->GetDirectoryEntries(getter_AddRefs(entries))) ||
       !entries) {
     return;
   }
 
@@ -1886,22 +3460,22 @@ OriginClearRunnable::DeleteFiles(QuotaMa
 
     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->OriginClearCompleted(mOriginOrPattern);
+  aQuotaManager->RemoveQuotaForPattern(aPersistenceType, mOriginOrPattern);
+
+  aQuotaManager->OriginClearCompleted(aPersistenceType, mOriginOrPattern);
 }
 
-NS_IMPL_ISUPPORTS1(OriginClearRunnable, nsIRunnable)
+NS_IMPL_ISUPPORTS_INHERITED0(OriginClearRunnable, nsRunnable)
 
 NS_IMETHODIMP
 OriginClearRunnable::Run()
 {
   PROFILER_LABEL("Quota", "OriginClearRunnable::Run");
 
   QuotaManager* quotaManager = QuotaManager::Get();
   NS_ASSERTION(quotaManager, "This should never fail!");
@@ -1927,59 +3501,66 @@ OriginClearRunnable::Run()
       return NS_OK;
     }
 
     case IO: {
       AssertIsOnIOThread();
 
       AdvanceState();
 
-      DeleteFiles(quotaManager);
+      DeleteFiles(quotaManager, PERSISTENCE_TYPE_PERSISTENT);
+
+      DeleteFiles(quotaManager, PERSISTENCE_TYPE_TEMPORARY);
 
       // 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!");
 
       // Tell the QuotaManager that we're done.
-      quotaManager->AllowNextSynchronizedOp(mOriginOrPattern, nullptr);
+      quotaManager->AllowNextSynchronizedOp(mOriginOrPattern,
+                                            Nullable<PersistenceType>(),
+                                            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 nsACString& aGroup,
                                        const OriginOrPatternString& aOrigin,
                                        nsIURI* aURI,
                                        nsIUsageCallback* aCallback)
 : mURI(aURI),
   mCallback(aCallback),
   mAppId(aAppId),
+  mGroup(aGroup),
   mOrigin(aOrigin),
   mCallbackState(Pending),
   mInMozBrowserOnly(aInMozBrowserOnly)
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
   NS_ASSERTION(aURI, "Null pointer!");
+  NS_ASSERTION(!aGroup.IsEmpty(), "Empty group!");
   NS_ASSERTION(aOrigin.IsOrigin(), "Expect origin only here!");
   NS_ASSERTION(!aOrigin.IsEmpty(), "Empty origin!");
   NS_ASSERTION(aCallback, "Null pointer!");
 }
 
 nsresult
 AsyncUsageRunnable::TakeShortcut()
 {
@@ -2019,96 +3600,23 @@ AsyncUsageRunnable::RunInternal()
       return NS_OK;
     }
 
     case IO: {
       AssertIsOnIOThread();
 
       AdvanceState();
 
-      // Get the directory that contains all the storage files we care about.
-      nsCOMPtr<nsIFile> directory;
-      rv = quotaManager->GetDirectoryForOrigin(mOrigin,
-                                               getter_AddRefs(directory));
-      NS_ENSURE_SUCCESS(rv, rv);
-
-      bool exists;
-      rv = directory->Exists(&exists);
+      // Add all the persistent storage files we care about.
+      rv = AddToUsage(quotaManager, PERSISTENCE_TYPE_PERSISTENT);
       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;
-          }
-
-#ifdef XP_MACOSX
-          if (leafName.EqualsLiteral(DSSTORE_FILE_NAME)) {
-            continue;
-          }
-#endif
-
-          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);
-        }
-      }
+      // Add all the temporary storage files we care about.
+      rv = AddToUsage(quotaManager, PERSISTENCE_TYPE_TEMPORARY);
+      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!");
@@ -2120,34 +3628,126 @@ AsyncUsageRunnable::RunInternal()
       }
 
       // Clean up.
       mURI = nullptr;
       mCallback = nullptr;
 
       // And tell the QuotaManager that we're done.
       if (mCallbackState == Complete) {
-        quotaManager->AllowNextSynchronizedOp(mOrigin, nullptr);
+        quotaManager->AllowNextSynchronizedOp(mOrigin,
+                                              Nullable<PersistenceType>(),
+                                              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_ISUPPORTS2(AsyncUsageRunnable,
-                   nsIRunnable,
-                   nsIQuotaRequest)
+nsresult
+AsyncUsageRunnable::AddToUsage(QuotaManager* aQuotaManager,
+                               PersistenceType aPersistenceType)
+{
+  AssertIsOnIOThread();
+
+  nsCOMPtr<nsIFile> directory;
+  nsresult rv = aQuotaManager->GetDirectoryForOrigin(aPersistenceType, 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;
+
+    if (aPersistenceType == PERSISTENCE_TYPE_PERSISTENT) {
+      initialized = aQuotaManager->mInitializedOrigins.Contains(mOrigin);
+
+      if (!initialized) {
+        rv = MaybeUpgradeOriginDirectory(directory);
+        NS_ENSURE_SUCCESS(rv, rv);
+      }
+    }
+    else {
+      NS_ASSERTION(aPersistenceType == PERSISTENCE_TYPE_TEMPORARY, "Huh?");
+      initialized = aQuotaManager->mTemporaryStorageInitialized;
+    }
+
+    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) ||
+          leafName.EqualsLiteral(DSSTORE_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 = aQuotaManager->mClients[clientType];
+
+      if (initialized) {
+        rv = client->GetUsageForOrigin(aPersistenceType, mGroup, mOrigin, this);
+      }
+      else {
+        rv = client->InitOrigin(aPersistenceType, mGroup, mOrigin, this);
+      }
+      NS_ENSURE_SUCCESS(rv, rv);
+    }
+  }
+
+  return NS_OK;
+}
+
+NS_IMPL_ISUPPORTS_INHERITED1(AsyncUsageRunnable, nsRunnable, nsIQuotaRequest)
 
 NS_IMETHODIMP
 AsyncUsageRunnable::Run()
 {
   PROFILER_LABEL("Quota", "AsyncUsageRunnable::Run");
 
   nsresult rv = RunInternal();
 
@@ -2170,17 +3770,227 @@ AsyncUsageRunnable::Cancel()
   if (mCanceled.exchange(1)) {
     NS_WARNING("Canceled more than once?!");
     return NS_ERROR_UNEXPECTED;
   }
 
   return NS_OK;
 }
 
-NS_IMPL_ISUPPORTS1(WaitForTransactionsToFinishRunnable, nsIRunnable)
+nsresult
+ResetOrClearRunnable::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
+ResetOrClearRunnable::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
+ResetOrClearRunnable::DeleteFiles(QuotaManager* aQuotaManager,
+                                  PersistenceType aPersistenceType)
+{
+  AssertIsOnIOThread();
+  NS_ASSERTION(aQuotaManager, "Don't pass me null!");
+
+  nsresult rv;
+
+  nsCOMPtr<nsIFile> directory =
+    do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv);
+  NS_ENSURE_SUCCESS_VOID(rv);
+
+  rv = directory->InitWithPath(aQuotaManager->GetStoragePath(aPersistenceType));
+  NS_ENSURE_SUCCESS_VOID(rv);
+
+  rv = directory->Remove(true);
+  if (rv != NS_ERROR_FILE_TARGET_DOES_NOT_EXIST &&
+      rv != NS_ERROR_FILE_NOT_FOUND && NS_FAILED(rv)) {
+    // This should never fail if we've closed all storage connections
+    // correctly...
+    NS_ERROR("Failed to remove directory!");
+  }
+}
+
+NS_IMPL_ISUPPORTS_INHERITED0(ResetOrClearRunnable, nsRunnable)
+
+NS_IMETHODIMP
+ResetOrClearRunnable::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(NullCString(), this,
+                                             InvalidateOpenedStorages, nullptr);
+      NS_ENSURE_SUCCESS(rv, rv);
+
+      return NS_OK;
+    }
+
+    case IO: {
+      AssertIsOnIOThread();
+
+      AdvanceState();
+
+      if (mClear) {
+        DeleteFiles(quotaManager, PERSISTENCE_TYPE_PERSISTENT);
+        DeleteFiles(quotaManager, PERSISTENCE_TYPE_TEMPORARY);
+      }
+
+      quotaManager->RemoveQuota();
+      quotaManager->ResetOrClearCompleted();
+
+      // 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!");
+
+      // Tell the QuotaManager that we're done.
+      quotaManager->AllowNextSynchronizedOp(OriginOrPatternString::FromNull(),
+                                            Nullable<PersistenceType>(),
+                                            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_IMETHODIMP
+FinalizeOriginEvictionRunnable::Run()
+{
+  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: {
+      AssertIsOnIOThread();
+
+      AdvanceState();
+
+      for (uint32_t index = 0; index < mOrigins.Length(); index++) {
+        quotaManager->OriginClearCompleted(
+                            PERSISTENCE_TYPE_TEMPORARY,
+                            OriginOrPatternString::FromOrigin(mOrigins[index]));
+      }
+
+      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 < mOrigins.Length(); index++) {
+        quotaManager->AllowNextSynchronizedOp(
+                          OriginOrPatternString::FromOrigin(mOrigins[index]),
+                          Nullable<PersistenceType>(PERSISTENCE_TYPE_TEMPORARY),
+                          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
+FinalizeOriginEvictionRunnable::Dispatch()
+{
+  NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
+  NS_ASSERTION(mCallbackState == Pending, "Huh?");
+
+  mCallbackState = OpenAllowed;
+  return NS_DispatchToMainThread(this);
+}
+
+nsresult
+FinalizeOriginEvictionRunnable::RunImmediately()
+{
+  AssertIsOnIOThread();
+  NS_ASSERTION(mCallbackState == Pending, "Huh?");
+
+  mCallbackState = IO;
+  return this->Run();
+}
 
 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!");
@@ -2198,19 +4008,44 @@ WaitForTransactionsToFinishRunnable::Run
   nsresult rv = listener->OnExclusiveAccessAcquired();
   NS_ENSURE_SUCCESS(rv, rv);
 
   // The listener is responsible for calling
   // QuotaManager::AllowNextSynchronizedOp.
   return NS_OK;
 }
 
-NS_IMPL_ISUPPORTS1(WaitForLockedFilesToFinishRunnable, nsIRunnable)
-
 NS_IMETHODIMP
 WaitForLockedFilesToFinishRunnable::Run()
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
   mBusy = false;
 
   return NS_OK;
 }
+
+NS_IMETHODIMP
+SaveOriginAccessTimeRunnable::Run()
+{
+  AssertIsOnIOThread();
+
+  QuotaManager* quotaManager = QuotaManager::Get();
+  NS_ASSERTION(quotaManager, "This should never fail!");
+
+  nsCOMPtr<nsIFile> directory;
+  nsresult rv =
+    quotaManager->GetDirectoryForOrigin(PERSISTENCE_TYPE_TEMPORARY, mOrigin,
+                                        getter_AddRefs(directory));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsCOMPtr<nsIBinaryOutputStream> stream;
+  rv = GetDirectoryMetadataStream(directory, true, getter_AddRefs(stream));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  // The origin directory may not exist anymore.
+  if (stream) {
+    rv = stream->Write64(mTimestamp);
+    NS_ENSURE_SUCCESS(rv, rv);
+  }
+
+  return NS_OK;
+}
--- a/dom/quota/QuotaManager.h
+++ b/dom/quota/QuotaManager.h
@@ -7,23 +7,26 @@
 #ifndef mozilla_dom_quota_quotamanager_h__
 #define mozilla_dom_quota_quotamanager_h__
 
 #include "QuotaCommon.h"
 
 #include "nsIObserver.h"
 #include "nsIQuotaManager.h"
 
+#include "mozilla/dom/Nullable.h"
 #include "mozilla/Mutex.h"
+
 #include "nsClassHashtable.h"
 #include "nsRefPtrHashtable.h"
 #include "nsThreadUtils.h"
 
 #include "ArrayCluster.h"
 #include "Client.h"
+#include "PersistenceType.h"
 #include "StoragePrivilege.h"
 
 #define QUOTA_MANAGER_CONTRACTID "@mozilla.org/dom/quota/manager;1"
 
 class nsIAtom;
 class nsIOfflineStorage;
 class nsIPrincipal;
 class nsIThread;
@@ -31,29 +34,38 @@ class nsITimer;
 class nsIURI;
 class nsPIDOMWindow;
 
 BEGIN_QUOTA_NAMESPACE
 
 class AcquireListener;
 class AsyncUsageRunnable;
 class CheckQuotaHelper;
+class CollectOriginsHelper;
+class FinalizeOriginEvictionRunnable;
+class GroupInfo;
+class GroupInfoPair;
 class OriginClearRunnable;
 class OriginInfo;
 class OriginOrPatternString;
 class QuotaObject;
+class ResetOrClearRunnable;
 struct SynchronizedOp;
 
 class QuotaManager MOZ_FINAL : public nsIQuotaManager,
                                public nsIObserver
 {
   friend class AsyncUsageRunnable;
+  friend class CollectOriginsHelper;
+  friend class FinalizeOriginEvictionRunnable;
+  friend class GroupInfo;
   friend class OriginClearRunnable;
   friend class OriginInfo;
   friend class QuotaObject;
+  friend class ResetOrClearRunnable;
 
   enum MozBrowserPatternFlag
   {
     MozBrowser = 0,
     NotMozBrowser,
     IgnoreMozBrowser
   };
 
@@ -76,33 +88,63 @@ public:
   // 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 aLimitBytes,
-                     int64_t aUsageBytes);
+  InitQuotaForOrigin(PersistenceType aPersistenceType,
+                     const nsACString& aGroup,
+                     const nsACString& aOrigin,
+                     uint64_t aLimitBytes,
+                     uint64_t aUsageBytes,
+                     int64_t aAccessTime);
 
   void
-  DecreaseUsageForOrigin(const nsACString& aOrigin,
+  DecreaseUsageForOrigin(PersistenceType aPersistenceType,
+                         const nsACString& aGroup,
+                         const nsACString& aOrigin,
                          int64_t aSize);
 
   void
-  RemoveQuotaForPattern(const nsACString& aPattern);
+  UpdateOriginAccessTime(PersistenceType aPersistenceType,
+                         const nsACString& aGroup,
+                         const nsACString& aOrigin);
+
+  void
+  RemoveQuota();
+
+  void
+  RemoveQuotaForPersistenceType(PersistenceType);
+
+  void
+  RemoveQuotaForOrigin(PersistenceType aPersistenceType,
+                       const nsACString& aGroup,
+                       const nsACString& aOrigin)
+  {
+    MutexAutoLock lock(mQuotaMutex);
+    LockedRemoveQuotaForOrigin(aPersistenceType, aGroup, aOrigin);
+  }
+
+  void
+  RemoveQuotaForPattern(PersistenceType aPersistenceType,
+                        const nsACString& aPattern);
 
   already_AddRefed<QuotaObject>
-  GetQuotaObject(const nsACString& aOrigin,
+  GetQuotaObject(PersistenceType aPersistenceType,
+                 const nsACString& aGroup,
+                 const nsACString& aOrigin,
                  nsIFile* aFile);
 
   already_AddRefed<QuotaObject>
-  GetQuotaObject(const nsACString& aOrigin,
+  GetQuotaObject(PersistenceType aPersistenceType,
+                 const nsACString& aGroup,
+                 const nsACString& aOrigin,
                  const nsAString& aPath);
 
   // 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)
   {
     QuotaManager* quotaManager = Get();
@@ -143,16 +185,17 @@ public:
   // 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,
+                     Nullable<PersistenceType> aPersistenceType,
                      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,
@@ -173,72 +216,108 @@ public:
                          void* aClosure)
   {
     return AcquireExclusiveAccess(aOrigin, nullptr, aListener, aCallback,
                                   aClosure);
   }
 
   void
   AllowNextSynchronizedOp(const OriginOrPatternString& aOriginOrPattern,
+                          Nullable<PersistenceType> aPersistenceType,
                           nsIAtom* aId);
 
   bool
   IsClearOriginPending(const nsACString& aPattern)
   {
-    return !!FindSynchronizedOp(aPattern, nullptr);
+    return !!FindSynchronizedOp(aPattern, Nullable<PersistenceType>(), nullptr);
   }
 
   nsresult
-  GetDirectoryForOrigin(const nsACString& aASCIIOrigin,
+  GetDirectoryForOrigin(PersistenceType aPersistenceType,
+                        const nsACString& aASCIIOrigin,
                         nsIFile** aDirectory) const;
 
   nsresult
-  EnsureOriginIsInitialized(const nsACString& aOrigin,
+  EnsureOriginIsInitialized(PersistenceType aPersistenceType,
+                            const nsACString& aGroup,
+                            const nsACString& aOrigin,
                             bool aTrackQuota,
                             nsIFile** aDirectory);
 
   void
-  OriginClearCompleted(const nsACString& aPattern);
+  OriginClearCompleted(PersistenceType aPersistenceType,
+                       const OriginOrPatternString& aOriginOrPattern);
+
+  void
+  ResetOrClearCompleted();
+
+  void
+  AssertCurrentThreadOwnsQuotaMutex()
+  {
+    mQuotaMutex.AssertCurrentThreadOwns();
+  }
 
   nsIThread*
   IOThread()
   {
     NS_ASSERTION(mIOThread, "This should never be null!");
     return mIOThread;
   }
 
   already_AddRefed<Client>
   GetClient(Client::Type aClientType);
 
   const nsString&
-  GetBaseDirectory() const
+  GetStoragePath(PersistenceType aPersistenceType) const
   {
-    return mStorageBasePath;
+    if (aPersistenceType == PERSISTENCE_TYPE_PERSISTENT) {
+      return mPersistentStoragePath;
+    }
+
+    NS_ASSERTION(aPersistenceType == PERSISTENCE_TYPE_TEMPORARY, "Huh?");
+
+    return mTemporaryStoragePath;
+  }
+
+  uint64_t
+  GetGroupLimit() const
+  {
+    return mTemporaryStorageLimit / 5;
   }
 
   static uint32_t
   GetStorageQuotaMB();
 
   static already_AddRefed<nsIAtom>
-  GetStorageId(const nsACString& aOrigin,
+  GetStorageId(PersistenceType aPersistenceType,
+               const nsACString& aOrigin,
                const nsAString& aName);
 
   static nsresult
-  GetASCIIOriginFromURI(nsIURI* aURI,
-                        uint32_t aAppId,
-                        bool aInMozBrowser,
-                        nsACString& aASCIIOrigin);
+  GetInfoFromURI(nsIURI* aURI,
+                 uint32_t aAppId,
+                 bool aInMozBrowser,
+                 nsACString* aGroup,
+                 nsACString* aASCIIOrigin,
+                 StoragePrivilege* aPrivilege,
+                 PersistenceType* aDefaultPersistenceType);
 
   static nsresult
-  GetASCIIOriginFromPrincipal(nsIPrincipal* aPrincipal,
-                              nsACString& aASCIIOrigin);
+  GetInfoFromPrincipal(nsIPrincipal* aPrincipal,
+                       nsACString* aGroup,
+                       nsACString* aASCIIOrigin,
+                       StoragePrivilege* aPrivilege,
+                       PersistenceType* aDefaultPersistenceType);
 
   static nsresult
-  GetASCIIOriginFromWindow(nsPIDOMWindow* aWindow,
-                           nsACString& aASCIIOrigin);
+  GetInfoFromWindow(nsPIDOMWindow* aWindow,
+                    nsACString* aGroup,
+                    nsACString* aASCIIOrigin,
+                    StoragePrivilege* aPrivilege,
+                    PersistenceType* aDefaultPersistenceType);
 
   static void
   GetOriginPatternString(uint32_t aAppId, bool aBrowserOnly,
                          const nsACString& aOrigin, nsAutoCString& _retval)
   {
     return GetOriginPatternString(aAppId,
                                   aBrowserOnly ? MozBrowser : NotMozBrowser,
                                   aOrigin, _retval);
@@ -267,36 +346,76 @@ private:
   void
   CancelPromptsForWindowInternal(nsPIDOMWindow* aWindow);
 
   // Determine if the quota is lifted for the Window the current thread is
   // using.
   bool
   LockedQuotaIsLifted();
 
+  uint64_t
+  LockedCollectOriginsForEviction(uint64_t aMinSizeToBeFreed,
+                                  nsTArray<OriginInfo*>& aOriginInfos);
+
+  void
+  LockedRemoveQuotaForOrigin(PersistenceType aPersistenceType,
+                             const nsACString& aGroup,
+                             const nsACString& aOrigin);
+
   nsresult
   AcquireExclusiveAccess(const nsACString& aOrigin,
                          nsIOfflineStorage* aStorage,
                          AcquireListener* aListener,
                          WaitingOnStoragesCallback aCallback,
                          void* aClosure);
 
+  void
+  AddSynchronizedOp(const OriginOrPatternString& aOriginOrPattern,
+                    Nullable<PersistenceType> aPersistenceType,
+                    nsIAtom* aId);
+
   nsresult
   RunSynchronizedOp(nsIOfflineStorage* aStorage,
                     SynchronizedOp* aOp);
 
   SynchronizedOp*
   FindSynchronizedOp(const nsACString& aPattern,
+                     Nullable<PersistenceType> aPersistenceType,
                      nsISupports* aId);
 
   nsresult
+  MaybeUpgradeIndexedDBDirectory();
+
+  nsresult
+  InitializeOrigin(PersistenceType aPersistenceType,
+                   const nsACString& aGroup,
+                   const nsACString& aOrigin,
+                   bool aTrackQuota,
+                   int64_t aAccessTime,
+                   nsIFile* aDirectory);
+
+  nsresult
   ClearStoragesForApp(uint32_t aAppId, bool aBrowserOnly);
 
-  nsresult
-  MaybeUpgradeOriginDirectory(nsIFile* aDirectory);
+  void
+  CheckTemporaryStorageLimits();
+
+  // Collect inactive and the least recently used origins.
+  uint64_t
+  CollectOriginsForEviction(uint64_t aMinSizeToBeFreed,
+                            nsTArray<OriginInfo*>& aOriginInfos);
+
+  void
+  DeleteTemporaryFilesForOrigin(const nsACString& aOrigin);
+
+  void
+  FinalizeOriginEviction(nsTArray<nsCString>& aOrigins);
+
+  void
+  SaveOriginAccessTime(const nsACString& aOrigin, int64_t aTimestamp);
 
   void
   ReleaseIOThreadObjects()
   {
     AssertIsOnIOThread();
 
     for (uint32_t index = 0; index < Client::TYPE_MAX; index++) {
       mClients[index]->ReleaseIOThreadObjects();
@@ -304,22 +423,57 @@ private:
   }
 
   static void
   GetOriginPatternString(uint32_t aAppId,
                          MozBrowserPatternFlag aBrowserFlag,
                          const nsACString& aOrigin,
                          nsAutoCString& _retval);
 
+  static PLDHashOperator
+  RemoveQuotaForPersistenceTypeCallback(const nsACString& aKey,
+                                        nsAutoPtr<GroupInfoPair>& aValue,
+                                        void* aUserArg);
+
+  static PLDHashOperator
+  RemoveQuotaCallback(const nsACString& aKey,
+                      nsAutoPtr<GroupInfoPair>& aValue,
+                      void* aUserArg);
+
+  static PLDHashOperator
+  RemoveQuotaForPatternCallback(const nsACString& aKey,
+                                nsAutoPtr<GroupInfoPair>& aValue,
+                                void* aUserArg);
+
+  static PLDHashOperator
+  GetOriginsExceedingGroupLimit(const nsACString& aKey,
+                                GroupInfoPair* aValue,
+                                void* aUserArg);
+
+  static PLDHashOperator
+  GetAllTemporaryStorageOrigins(const nsACString& aKey,
+                                GroupInfoPair* aValue,
+                                void* aUserArg);
+
+  static PLDHashOperator
+  AddTemporaryStorageOrigins(const nsACString& aKey,
+                             ArrayCluster<nsIOfflineStorage*>* aValue,
+                             void* aUserArg);
+
+  static PLDHashOperator
+  GetInactiveTemporaryStorageOrigins(const nsACString& aKey,
+                                     GroupInfoPair* aValue,
+                                     void* aUserArg);
+
   // TLS storage index for the current thread's window.
   unsigned int mCurrentWindowIndex;
 
   mozilla::Mutex mQuotaMutex;
 
-  nsRefPtrHashtable<nsCStringHashKey, OriginInfo> mOriginInfos;
+  nsClassHashtable<nsCStringHashKey, GroupInfoPair> mGroupInfoPairs;
 
   // 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;
@@ -334,17 +488,25 @@ private:
   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;
+  nsString mIndexedDBPath;
+  nsString mPersistentStoragePath;
+  nsString mTemporaryStoragePath;
+
+  uint64_t mTemporaryStorageLimit;
+  uint64_t mTemporaryStorageUsage;
+  bool mTemporaryStorageInitialized;
+
+  bool mStorageAreaInitialized;
 };
 
 class AutoEnterWindow
 {
 public:
   AutoEnterWindow(nsPIDOMWindow* aWindow)
   {
     QuotaManager::SetCurrentWindow(aWindow);
--- a/dom/quota/QuotaObject.cpp
+++ b/dom/quota/QuotaObject.cpp
@@ -2,16 +2,17 @@
 /* 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"
+#include "Utilities.h"
 
 USING_QUOTA_NAMESPACE
 
 void
 QuotaObject::AddRef()
 {
   QuotaManager* quotaManager = QuotaManager::Get();
   if (!quotaManager) {
@@ -63,20 +64,34 @@ QuotaObject::Release()
 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;
+  if (!mOriginInfo) {
+    return;
+  }
+
+  GroupInfo* groupInfo = mOriginInfo->mGroupInfo;
+
+  if (groupInfo->IsForTemporaryStorage()) {
+    quotaManager->mTemporaryStorageUsage -= mSize;
+  }
+  groupInfo->mUsage -= mSize;
+  mOriginInfo->mUsage -= mSize;
+
+  mSize = aSize;
+
+  mOriginInfo->mUsage += mSize;
+  groupInfo->mUsage += mSize;
+  if (groupInfo->IsForTemporaryStorage()) {
+    quotaManager->mTemporaryStorageUsage += mSize;
   }
 }
 
 bool
 QuotaObject::MaybeAllocateMoreSpace(int64_t aOffset, int32_t aCount)
 {
   int64_t end = aOffset + aCount;
 
@@ -84,76 +99,325 @@ QuotaObject::MaybeAllocateMoreSpace(int6
   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;
-    }
+  GroupInfo* groupInfo = mOriginInfo->mGroupInfo;
+
+  if (groupInfo->IsForPersistentStorage()) {
+    uint64_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;
+      // 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 group = mOriginInfo->mGroupInfo->mGroup;
+      nsCString origin = mOriginInfo->mOrigin;
+
+      mOriginInfo->LockedClearOriginInfos();
+      NS_ASSERTION(!mOriginInfo,
+                   "Should have cleared in LockedClearOriginInfos!");
+
+      quotaManager->LockedRemoveQuotaForOrigin(PERSISTENCE_TYPE_PERSISTENT,
+                                               group, 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;
     }
 
-    nsCString origin = mOriginInfo->mOrigin;
-
-    mOriginInfo->LockedClearOriginInfos();
-    NS_ASSERTION(!mOriginInfo,
-                 "Should have cleared in LockedClearOriginInfos!");
+    mOriginInfo->mUsage = newUsage;
 
-    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!");
+    groupInfo->mUsage = groupInfo->mUsage - mSize + end;
 
     mSize = end;
 
     return true;
   }
 
+  NS_ASSERTION(groupInfo->mPersistenceType == PERSISTENCE_TYPE_TEMPORARY,
+               "Huh?");
+
+  uint64_t delta = end - mSize;
+
+  uint64_t newUsage = mOriginInfo->mUsage + delta;
+
+  // Temporary storage has no limit for origin usage (there's a group and the
+  // global limit though).
+
+  uint64_t newGroupUsage = groupInfo->mUsage + delta;
+
+  // Temporary storage has a hard limit for group usage (20 % of the global
+  // limit).
+  if (newGroupUsage > quotaManager->GetGroupLimit()) {
+    return false;
+  }
+
+  uint64_t newTemporaryStorageUsage = quotaManager->mTemporaryStorageUsage +
+                                      delta;
+
+  if (newTemporaryStorageUsage > quotaManager->mTemporaryStorageLimit) {
+    // This will block the thread without holding the lock while waitting.
+
+    nsAutoTArray<OriginInfo*, 10> originInfos;
+    uint64_t sizeToBeFreed =
+      quotaManager->LockedCollectOriginsForEviction(delta, originInfos);
+
+    if (!sizeToBeFreed) {
+      return false;
+    }
+
+    NS_ASSERTION(sizeToBeFreed >= delta, "Huh?");
+
+    {
+      MutexAutoUnlock autoUnlock(quotaManager->mQuotaMutex);
+
+      for (uint32_t i = 0; i < originInfos.Length(); i++) {
+        quotaManager->DeleteTemporaryFilesForOrigin(originInfos[i]->mOrigin);
+      }
+    }
+
+    // Relocked.
+
+    NS_ASSERTION(mOriginInfo, "How come?!");
+
+    nsTArray<nsCString> origins;
+    for (uint32_t i = 0; i < originInfos.Length(); i++) {
+      OriginInfo* originInfo = originInfos[i];
+
+      NS_ASSERTION(originInfo != mOriginInfo, "Deleted itself!");
+
+      nsCString group = originInfo->mGroupInfo->mGroup;
+      nsCString origin = originInfo->mOrigin;
+      quotaManager->LockedRemoveQuotaForOrigin(PERSISTENCE_TYPE_TEMPORARY,
+                                               group, origin);
+
+#ifdef DEBUG
+      originInfos[i] = nullptr;
+#endif
+
+      origins.AppendElement(origin);
+    }
+
+    // We unlocked and relocked several times so we need to recompute all the
+    // essential variables and recheck the group limit.
+
+    delta = end - mSize;
+
+    newUsage = mOriginInfo->mUsage + delta;
+
+    newGroupUsage = groupInfo->mUsage + delta;
+
+    if (newGroupUsage > quotaManager->GetGroupLimit()) {
+      // Unfortunately some other thread increased the group usage in the
+      // meantime and we are not below the group limit anymore.
+
+      // However, the origin eviction must be finalized in this case too.
+      MutexAutoUnlock autoUnlock(quotaManager->mQuotaMutex);
+
+      quotaManager->FinalizeOriginEviction(origins);
+
+      return false;
+    }
+
+    newTemporaryStorageUsage = quotaManager->mTemporaryStorageUsage + delta;
+
+    NS_ASSERTION(newTemporaryStorageUsage <=
+                 quotaManager->mTemporaryStorageLimit, "How come?!");
+
+    // Ok, we successfully freed enough space and the operation can continue
+    // without throwing the quota error.
+
+    mOriginInfo->mUsage = newUsage;
+    groupInfo->mUsage = newGroupUsage;
+    quotaManager->mTemporaryStorageUsage = newTemporaryStorageUsage;;
+
+    // Some other thread could increase the size in the meantime, but no more
+    // than this one.
+    NS_ASSERTION(mSize < end, "This shouldn't happen!");
+    mSize = end;
+
+    // Finally, release IO thread only objects and allow next synchronized
+    // ops for the evicted origins.
+    MutexAutoUnlock autoUnlock(quotaManager->mQuotaMutex);
+
+    quotaManager->FinalizeOriginEviction(origins);
+
+    return true;
+  }
+
   mOriginInfo->mUsage = newUsage;
+  groupInfo->mUsage = newGroupUsage;
+  quotaManager->mTemporaryStorageUsage = newTemporaryStorageUsage;
+
   mSize = end;
 
   return true;
 }
 
-#ifdef DEBUG
 void
-OriginInfo::LockedClearOriginInfos()
+OriginInfo::LockedDecreaseUsage(int64_t aSize)
 {
-  QuotaManager* quotaManager = QuotaManager::Get();
-  NS_ASSERTION(quotaManager, "Shouldn't be null!");
+  AssertCurrentThreadOwnsQuotaMutex();
+
+  mUsage -= aSize;
+
+  mGroupInfo->mUsage -= aSize;
 
-  quotaManager->mQuotaMutex.AssertCurrentThreadOwns();
+  if (mGroupInfo->IsForTemporaryStorage()) {
+    QuotaManager* quotaManager = QuotaManager::Get();
+    NS_ASSERTION(quotaManager, "Shouldn't be null!");
 
-  mQuotaObjects.EnumerateRead(ClearOriginInfoCallback, nullptr);
+    quotaManager->mTemporaryStorageUsage -= aSize;
+  }
 }
-#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;
 }
+
+already_AddRefed<OriginInfo>
+GroupInfo::LockedGetOriginInfo(const nsACString& aOrigin)
+{
+  AssertCurrentThreadOwnsQuotaMutex();
+
+  for (uint32_t index = 0; index < mOriginInfos.Length(); index++) {
+    nsRefPtr<OriginInfo>& originInfo = mOriginInfos[index];
+
+    if (originInfo->mOrigin == aOrigin) {
+      nsRefPtr<OriginInfo> result = originInfo;
+      return result.forget();
+    }
+  }
+
+  return nullptr;
+}
+
+void
+GroupInfo::LockedAddOriginInfo(OriginInfo* aOriginInfo)
+{
+  AssertCurrentThreadOwnsQuotaMutex();
+
+  NS_ASSERTION(!mOriginInfos.Contains(aOriginInfo),
+               "Replacing an existing entry!");
+  mOriginInfos.AppendElement(aOriginInfo);
+
+  mUsage += aOriginInfo->mUsage;
+
+  if (IsForTemporaryStorage()) {
+    QuotaManager* quotaManager = QuotaManager::Get();
+    NS_ASSERTION(quotaManager, "Shouldn't be null!");
+
+    quotaManager->mTemporaryStorageUsage += aOriginInfo->mUsage;
+  }
+}
+
+void
+GroupInfo::LockedRemoveOriginInfo(const nsACString& aOrigin)
+{
+  AssertCurrentThreadOwnsQuotaMutex();
+
+  for (uint32_t index = 0; index < mOriginInfos.Length(); index++) {
+    if (mOriginInfos[index]->mOrigin == aOrigin) {
+      mUsage -= mOriginInfos[index]->mUsage;
+
+      if (IsForTemporaryStorage()) {
+        QuotaManager* quotaManager = QuotaManager::Get();
+        NS_ASSERTION(quotaManager, "Shouldn't be null!");
+
+        quotaManager->mTemporaryStorageUsage -= mOriginInfos[index]->mUsage;
+      }
+
+      mOriginInfos.RemoveElementAt(index);
+
+      return;
+    }
+  }
+}
+
+void
+GroupInfo::LockedRemoveOriginInfos()
+{
+  AssertCurrentThreadOwnsQuotaMutex();
+
+  for (uint32_t index = mOriginInfos.Length(); index > 0; index--) {
+    mUsage -= mOriginInfos[index - 1]->mUsage;
+
+    if (IsForTemporaryStorage()) {
+      QuotaManager* quotaManager = QuotaManager::Get();
+      NS_ASSERTION(quotaManager, "Shouldn't be null!");
+
+      quotaManager->mTemporaryStorageUsage -= mOriginInfos[index - 1]->mUsage;
+    }
+
+    mOriginInfos.RemoveElementAt(index - 1);
+  }
+}
+
+void
+GroupInfo::LockedRemoveOriginInfosForPattern(const nsACString& aPattern)
+{
+  AssertCurrentThreadOwnsQuotaMutex();
+
+  for (uint32_t index = mOriginInfos.Length(); index > 0; index--) {
+    if (PatternMatchesOrigin(aPattern, mOriginInfos[index - 1]->mOrigin)) {
+      mUsage -= mOriginInfos[index - 1]->mUsage;
+
+      if (IsForTemporaryStorage()) {
+        QuotaManager* quotaManager = QuotaManager::Get();
+        NS_ASSERTION(quotaManager, "Shouldn't be null!");
+
+        quotaManager->mTemporaryStorageUsage -= mOriginInfos[index - 1]->mUsage;
+      }
+
+      mOriginInfos.RemoveElementAt(index - 1);
+    }
+  }
+}
+
+nsRefPtr<GroupInfo>&
+GroupInfoPair::GetGroupInfoForPersistenceType(PersistenceType aPersistenceType)
+{
+  switch (aPersistenceType) {
+    case PERSISTENCE_TYPE_PERSISTENT:
+      return mPersistentStorageGroupInfo;
+    case PERSISTENCE_TYPE_TEMPORARY:
+      return mTemporaryStorageGroupInfo;
+
+    case PERSISTENCE_TYPE_INVALID:
+    default:
+      MOZ_CRASH("Bad persistence type value!");
+      return mPersistentStorageGroupInfo;
+  }
+}
--- a/dom/quota/QuotaObject.h
+++ b/dom/quota/QuotaObject.h
@@ -6,18 +6,22 @@
 
 #ifndef mozilla_dom_quota_quotaobject_h__
 #define mozilla_dom_quota_quotaobject_h__
 
 #include "mozilla/dom/quota/QuotaCommon.h"
 
 #include "nsDataHashtable.h"
 
+#include "PersistenceType.h"
+
 BEGIN_QUOTA_NAMESPACE
 
+class GroupInfo;
+class GroupInfoPair;
 class OriginInfo;
 class QuotaManager;
 
 class QuotaObject
 {
   friend class OriginInfo;
   friend class QuotaManager;
 
@@ -32,58 +36,230 @@ public:
   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)
-  { }
+  {
+    MOZ_COUNT_CTOR(QuotaObject);
+  }
 
-  virtual ~QuotaObject()
-  { }
+  ~QuotaObject()
+  {
+    MOZ_COUNT_DTOR(QuotaObject);
+  }
 
   mozilla::ThreadSafeAutoRefCnt mRefCnt;
 
   OriginInfo* mOriginInfo;
   nsString mPath;
   int64_t mSize;
 };
 
 class OriginInfo
 {
+  friend class GroupInfo;
   friend class QuotaManager;
   friend class QuotaObject;
 
 public:
-  OriginInfo(const nsACString& aOrigin, int64_t aLimit, int64_t aUsage)
-  : mOrigin(aOrigin), mLimit(aLimit), mUsage(aUsage)
+  OriginInfo(GroupInfo* aGroupInfo, const nsACString& aOrigin, uint64_t aLimit,
+             uint64_t aUsage, int64_t aAccessTime)
+  : mGroupInfo(aGroupInfo), mOrigin(aOrigin), mLimit(aLimit), mUsage(aUsage),
+    mAccessTime(aAccessTime)
   {
+    MOZ_COUNT_CTOR(OriginInfo);
+  }
+
+  ~OriginInfo()
+  {
+    MOZ_COUNT_DTOR(OriginInfo);
   }
 
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(OriginInfo)
 
+  int64_t
+  AccessTime() const
+  {
+    return mAccessTime;
+  }
+
 private:
   void
-#ifdef DEBUG
-  LockedClearOriginInfos();
-#else
+  LockedDecreaseUsage(int64_t aSize);
+
+  void
+  LockedUpdateAccessTime(int64_t aAccessTime)
+  {
+    AssertCurrentThreadOwnsQuotaMutex();
+
+    mAccessTime = aAccessTime;
+  }
+
+  void
   LockedClearOriginInfos()
   {
+    AssertCurrentThreadOwnsQuotaMutex();
+
     mQuotaObjects.EnumerateRead(ClearOriginInfoCallback, nullptr);
   }
-#endif
 
   static PLDHashOperator
   ClearOriginInfoCallback(const nsAString& aKey,
                           QuotaObject* aValue, void* aUserArg);
 
   nsDataHashtable<nsStringHashKey, QuotaObject*> mQuotaObjects;
 
+  GroupInfo* mGroupInfo;
   nsCString mOrigin;
-  int64_t mLimit;
-  int64_t mUsage;
+  uint64_t mLimit;
+  uint64_t mUsage;
+  int64_t mAccessTime;
+};
+
+class OriginInfoLRUComparator
+{
+public:
+  bool
+  Equals(const OriginInfo* a, const OriginInfo* b) const
+  {
+    return
+      a && b ? a->AccessTime() == b->AccessTime() : !a && !b ? true : false;
+  }
+
+  bool
+  LessThan(const OriginInfo* a, const OriginInfo* b) const
+  {
+    return a && b ? a->AccessTime() < b->AccessTime() : b ? true : false;
+  }
+};
+
+class GroupInfo
+{
+  friend class GroupInfoPair;
+  friend class OriginInfo;
+  friend class QuotaManager;
+  friend class QuotaObject;
+
+public:
+  GroupInfo(PersistenceType aPersistenceType, const nsACString& aGroup)
+  : mPersistenceType(aPersistenceType), mGroup(aGroup), mUsage(0)
+  {
+    MOZ_COUNT_CTOR(GroupInfo);
+  }
+
+  ~GroupInfo()
+  {
+    MOZ_COUNT_DTOR(GroupInfo);
+  }
+
+  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(GroupInfo)
+
+  bool
+  IsForPersistentStorage() const
+  {
+    return mPersistenceType == PERSISTENCE_TYPE_PERSISTENT;
+  }
+
+  bool
+  IsForTemporaryStorage() const
+  {
+    return mPersistenceType == PERSISTENCE_TYPE_TEMPORARY;
+  }
+
+private:
+  already_AddRefed<OriginInfo>
+  LockedGetOriginInfo(const nsACString& aOrigin);
+
+  void
+  LockedAddOriginInfo(OriginInfo* aOriginInfo);
+
+  void
+  LockedRemoveOriginInfo(const nsACString& aOrigin);
+
+  void
+  LockedRemoveOriginInfos();
+
+  void
+  LockedRemoveOriginInfosForPattern(const nsACString& aPattern);
+
+  bool
+  LockedHasOriginInfos()
+  {
+    AssertCurrentThreadOwnsQuotaMutex();
+
+    return !mOriginInfos.IsEmpty();
+  }
+
+  nsTArray<nsRefPtr<OriginInfo> > mOriginInfos;
+
+  PersistenceType mPersistenceType;
+  nsCString mGroup;
+  uint64_t mUsage;
+};
+
+class GroupInfoPair
+{
+  friend class QuotaManager;
+
+public:
+  GroupInfoPair()
+  {
+    MOZ_COUNT_CTOR(GroupInfoPair);
+  }
+
+  ~GroupInfoPair()
+  {
+    MOZ_COUNT_DTOR(GroupInfoPair);
+  }
+
+private:
+  already_AddRefed<GroupInfo>
+  LockedGetGroupInfo(PersistenceType aPersistenceType)
+  {
+    AssertCurrentThreadOwnsQuotaMutex();
+
+    nsRefPtr<GroupInfo> groupInfo =
+      GetGroupInfoForPersistenceType(aPersistenceType);
+    return groupInfo.forget();
+  }
+
+  void
+  LockedSetGroupInfo(GroupInfo* aGroupInfo)
+  {
+    AssertCurrentThreadOwnsQuotaMutex();
+
+    nsRefPtr<GroupInfo>& groupInfo =
+      GetGroupInfoForPersistenceType(aGroupInfo->mPersistenceType);
+    groupInfo = aGroupInfo;
+  }
+
+  void
+  LockedClearGroupInfo(PersistenceType aPersistenceType)
+  {
+    AssertCurrentThreadOwnsQuotaMutex();
+
+    nsRefPtr<GroupInfo>& groupInfo =
+      GetGroupInfoForPersistenceType(aPersistenceType);
+    groupInfo = nullptr;
+  }
+
+  bool
+  LockedHasGroupInfos()
+  {
+    AssertCurrentThreadOwnsQuotaMutex();
+
+    return mPersistentStorageGroupInfo || mTemporaryStorageGroupInfo;
+  }
+
+  nsRefPtr<GroupInfo>&
+  GetGroupInfoForPersistenceType(PersistenceType aPersistenceType);
+
+  nsRefPtr<GroupInfo> mPersistentStorageGroupInfo;
+  nsRefPtr<GroupInfo> mTemporaryStorageGroupInfo;
 };
 
 END_QUOTA_NAMESPACE
 
 #endif // mozilla_dom_quota_quotaobject_h__
--- a/dom/quota/StoragePrivilege.h
+++ b/dom/quota/StoragePrivilege.h
@@ -7,15 +7,20 @@
 #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
+  // Quota not tracked, persistence type is always "persistent".
+  Chrome,
+
+  // Quota tracked, persistence type can be either "persistent" or "temporary".
+  // The permission "defaul-persistent-storage" is used to determine the
+  // default persistence type.
+  Content
 };
 
 END_QUOTA_NAMESPACE
 
 #endif // mozilla_dom_quota_storageprivilege_h__
rename from dom/quota/UsageRunnable.h
rename to dom/quota/UsageInfo.h
--- a/dom/quota/UsageRunnable.h
+++ b/dom/quota/UsageInfo.h