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 146523 8734ea74053b8cbcce4085cd1b6972e80b30b600
parent 146522 45975a8112078e3a80c57de34f3bcfebdd2ef031
child 146524 78f1f02b6b360a537451274aa1a7f95284a77cd2
push id1
push userroot
push dateMon, 20 Oct 2014 17:29:22 +0000
reviewersehsan, bent
bugs785884
milestone26.0a1
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
@@ -1,32 +1,32 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-#ifndef mozilla_dom_quota_usagerunnable_h__
-#define mozilla_dom_quota_usagerunnable_h__
+#ifndef mozilla_dom_quota_usageinfo_h__
+#define mozilla_dom_quota_usageinfo_h__
 
 #include "mozilla/dom/quota/QuotaCommon.h"
 
 #include "mozilla/Atomics.h"
 #include "Utilities.h"
 
 BEGIN_QUOTA_NAMESPACE
 
-class UsageRunnable
+class UsageInfo
 {
 public:
-  UsageRunnable()
+  UsageInfo()
   : mCanceled(0), mDatabaseUsage(0), mFileUsage(0)
   { }
 
-  virtual ~UsageRunnable()
+  virtual ~UsageInfo()
   { }
 
   bool
   Canceled()
   {
     return mCanceled;
   }
 
@@ -74,9 +74,9 @@ protected:
 
 private:
   uint64_t mDatabaseUsage;
   uint64_t mFileUsage;
 };
 
 END_QUOTA_NAMESPACE
 
-#endif // mozilla_dom_quota_usagerunnable_h__
+#endif // mozilla_dom_quota_usageinfo_h__
--- a/dom/quota/moz.build
+++ b/dom/quota/moz.build
@@ -19,21 +19,22 @@ EXPORTS += [
 ]
 
 EXPORTS.mozilla.dom.quota += [
     'AcquireListener.h',
     'ArrayCluster.h',
     'Client.h',
     'FileStreams.h',
     'OriginOrPatternString.h',
+    'PersistenceType.h',
     'QuotaCommon.h',
     'QuotaManager.h',
     'QuotaObject.h',
     'StoragePrivilege.h',
-    'UsageRunnable.h',
+    'UsageInfo.h',
     'Utilities.h',
 ]
 
 CPP_SOURCES += [
     'CheckQuotaHelper.cpp',
     'FileStreams.cpp',
     'QuotaManager.cpp',
     'QuotaObject.cpp',
--- a/dom/quota/nsIOfflineStorage.h
+++ b/dom/quota/nsIOfflineStorage.h
@@ -4,57 +4,85 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef nsIOfflineStorage_h__
 #define nsIOfflineStorage_h__
 
 #include "nsIFileStorage.h"
 
+#include "mozilla/dom/quota/PersistenceType.h"
+
 #define NS_OFFLINESTORAGE_IID \
-  {0xe531b6e0, 0x55b8, 0x4f39, \
-  { 0x95, 0xbb, 0x97, 0x21, 0x4c, 0xb0, 0xf6, 0x1a } }
+  {0xec7e878d, 0xc8c1, 0x402e, \
+  { 0xa2, 0xc4, 0xf6, 0x82, 0x29, 0x4e, 0x3c, 0xb1 } }
+
+class nsPIDOMWindow;
 
 namespace mozilla {
 namespace dom {
 namespace quota {
 class Client;
 }
 }
 }
 
 class nsIOfflineStorage : public nsIFileStorage
 {
 public:
   typedef mozilla::dom::quota::Client Client;
+  typedef mozilla::dom::quota::PersistenceType PersistenceType;
 
   NS_DECLARE_STATIC_IID_ACCESSOR(NS_OFFLINESTORAGE_IID)
 
   NS_IMETHOD_(Client*)
   GetClient() = 0;